in packages/amazonq/src/lsp/client.ts [50:275]
export async function startLanguageServer(
extensionContext: vscode.ExtensionContext,
resourcePaths: AmazonQResourcePaths
) {
const toDispose = extensionContext.subscriptions
const serverModule = resourcePaths.lsp
const argv = [
'--nolazy',
'--preserve-symlinks',
'--stdio',
'--pre-init-encryption',
'--set-credentials-encryption-key',
]
const documentSelector = [{ scheme: 'file', language: '*' }]
const clientId = 'amazonq'
const traceServerEnabled = Settings.instance.isSet(`${clientId}.trace.server`)
let executable: string[] = []
// apply the GLIBC 2.28 path to node js runtime binary
if (isAmazonInternalOs() && (await hasGlibcPatch())) {
executable = [
'/opt/vsc-sysroot/lib64/ld-linux-x86-64.so.2',
'--library-path',
'/opt/vsc-sysroot/lib64',
resourcePaths.node,
]
getLogger('amazonqLsp').info(`Patched node runtime with GLIBC to ${executable}`)
} else {
executable = [resourcePaths.node]
}
const memoryWarnThreshold = 1024 * processUtils.oneMB
const serverOptions = createServerOptions({
encryptionKey,
executable: executable,
serverModule,
execArgv: argv,
warnThresholds: { memory: memoryWarnThreshold },
})
await validateNodeExe(executable, resourcePaths.lsp, argv, logger)
// Options to control the language client
const clientOptions: LanguageClientOptions = {
// Register the server for json documents
documentSelector,
middleware: {
workspace: {
/**
* Convert VSCode settings format to be compatible with flare's configs
*/
configuration: async (params, token, next) => {
const config = await next(params, token)
const section = params.items[0].section
if (!isValidConfigSection(section)) {
return config
}
return getConfigSection(section)
},
},
},
initializationOptions: {
aws: {
clientInfo: {
name: env.appName,
version: version,
extension: {
name: 'AmazonQ-For-VSCode',
version: '0.0.1',
},
clientId: crypto.randomUUID(),
},
awsClientCapabilities: {
q: {
developerProfiles: true,
},
window: {
notifications: true,
showSaveFileDialog: true,
},
},
logLevel: toAmazonQLSPLogLevel(globals.logOutputChannel.logLevel),
},
credentials: {
providesBearerToken: true,
},
},
/**
* When the trace server is enabled it outputs a ton of log messages so:
* When trace server is enabled, logs go to a seperate "Amazon Q Language Server" output.
* Otherwise, logs go to the regular "Amazon Q Logs" channel.
*/
...(traceServerEnabled
? {}
: {
outputChannel: globals.logOutputChannel,
}),
}
const client = new LanguageClient(
clientId,
localize('amazonq.server.name', 'Amazon Q Language Server'),
serverOptions,
clientOptions
)
const disposable = client.start()
toDispose.push(disposable)
const auth = new AmazonQLspAuth(client)
return client.onReady().then(async () => {
await auth.refreshConnection()
if (Experiments.instance.get('amazonqLSPInline', false)) {
const inlineManager = new InlineCompletionManager(client)
inlineManager.registerInlineCompletion()
toDispose.push(
inlineManager,
Commands.register({ id: 'aws.amazonq.invokeInlineCompletion', autoconnect: true }, async () => {
await vscode.commands.executeCommand('editor.action.inlineSuggest.trigger')
}),
vscode.workspace.onDidCloseTextDocument(async () => {
await vscode.commands.executeCommand('aws.amazonq.rejectCodeSuggestion')
})
)
}
if (Experiments.instance.get('amazonqChatLSP', true)) {
await activate(client, encryptionKey, resourcePaths.ui)
}
const refreshInterval = auth.startTokenRefreshInterval(10 * oneSecond)
const sendProfileToLsp = async () => {
try {
const result = await client.sendRequest(updateConfigurationRequestType.method, {
section: 'aws.q',
settings: {
profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
},
})
client.info(
`Client: Updated Amazon Q Profile ${AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn} to Amazon Q LSP`,
result
)
} catch (err) {
client.error('Error when setting Q Developer Profile to Amazon Q LSP', err)
}
}
// send profile to lsp once.
void sendProfileToLsp()
toDispose.push(
AuthUtil.instance.auth.onDidChangeActiveConnection(async () => {
await auth.refreshConnection()
}),
AuthUtil.instance.auth.onDidDeleteConnection(async () => {
client.sendNotification(notificationTypes.deleteBearerToken.method)
}),
AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(sendProfileToLsp),
vscode.commands.registerCommand('aws.amazonq.getWorkspaceId', async () => {
const requestType = new RequestType<GetConfigurationFromServerParams, ResponseMessage, Error>(
'aws/getConfigurationFromServer'
)
const workspaceIdResp = await client.sendRequest(requestType.method, {
section: 'aws.q.workspaceContext',
})
return workspaceIdResp
}),
vscode.workspace.onDidCreateFiles((e) => {
client.sendNotification('workspace/didCreateFiles', {
files: e.files.map((it) => {
return { uri: it.fsPath }
}),
} as CreateFilesParams)
}),
vscode.workspace.onDidDeleteFiles((e) => {
client.sendNotification('workspace/didDeleteFiles', {
files: e.files.map((it) => {
return { uri: it.fsPath }
}),
} as DeleteFilesParams)
}),
vscode.workspace.onDidRenameFiles((e) => {
client.sendNotification('workspace/didRenameFiles', {
files: e.files.map((it) => {
return { oldUri: it.oldUri.fsPath, newUri: it.newUri.fsPath }
}),
} as RenameFilesParams)
}),
vscode.workspace.onDidSaveTextDocument((e) => {
client.sendNotification('workspace/didSaveTextDocument', {
textDocument: {
uri: e.uri.fsPath,
},
} as DidSaveTextDocumentParams)
}),
vscode.workspace.onDidChangeWorkspaceFolders((e) => {
client.sendNotification('workspace/didChangeWorkspaceFolder', {
event: {
added: e.added.map((it) => {
return {
name: it.name,
uri: it.uri.fsPath,
} as WorkspaceFolder
}),
removed: e.removed.map((it) => {
return {
name: it.name,
uri: it.uri.fsPath,
} as WorkspaceFolder
}),
},
} as DidChangeWorkspaceFoldersParams)
}),
{ dispose: () => clearInterval(refreshInterval) },
// Set this inside onReady so that it only triggers on subsequent language server starts (not the first)
onServerRestartHandler(client, auth)
)
})
}