export async function startLanguageServer()

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)
        )
    })
}