await callWithTelemetryAndErrorHandling()

in src/languageclient/startArmLanguageServer.ts [136:294]


    await callWithTelemetryAndErrorHandling('startArmLanguageClient', async (actionContext: IActionContext) => {
        actionContext.errorHandling.rethrow = true;
        actionContext.errorHandling.suppressDisplay = true; // Let caller handle

        // These trace levels are available in the server:
        //   Trace
        //   Debug
        //   Information
        //   Warning
        //   Error
        //   Critical
        //   None
        let trace: string = workspace.getConfiguration(configPrefix).get<string>(configKeys.traceLevel)
            // tslint:disable-next-line: strict-boolean-expressions
            || defaultTraceLevel;

        let commonArgs = [
            serverDllPath,
            '--logLevel',
            trace
        ];

        const waitForDebugger = workspace.getConfiguration(configPrefix).get<boolean>(configKeys.waitForDebugger, false) === true;
        if (waitForDebugger) {
            commonArgs.push('--wait-for-debugger');
        }
        if (isRunningTests || ext.addCompletedDiagnostic) {
            // Forces the server to add a completion message to its diagnostics
            commonArgs.push('--verbose-diagnostics');
        }

        // If the extension is launched in debug mode then the debug server options are used
        // Otherwise the run options are used
        let serverOptions: ServerOptions = {
            run: { command: dotnetExePath, args: commonArgs, options: { shell: false } },
            debug: { command: dotnetExePath, args: commonArgs, options: { shell: false } },
        };

        // Options to control the language client
        let clientOptions: LanguageClientOptions = {
            documentSelector: templateDocumentSelector,
            diagnosticCollectionName: `${languageServerName} diagnostics`,
            outputChannel: ext.outputChannel, // Use the same output channel as the extension does
            revealOutputChannelOn: RevealOutputChannelOn.Error,
            synchronize: {
                configurationSection: configPrefix
            },
            middleware: {
                handleDiagnostics: (uri: Uri, diagnostics: Diagnostic[], next: (uri: Uri, diagnostics: Diagnostic[]) => void): void => {
                    for (const d of diagnostics) {
                        if (d.source === backendValidationDiagnosticsSource) {
                            convertDiagnosticUrisToLinkedTemplateSchema(d);
                        }
                    }
                    next(uri, diagnostics);
                },
                provideCompletionItem: async (document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken, next: (document: TextDocument, position: Position, context: CompletionContext, token: CancellationToken) => undefined | null | CompletionItem[] | CompletionList | Thenable<undefined | null | CompletionItem[] | CompletionList>): Promise<undefined | null | CompletionItem[] | CompletionList> => {
                    let result: CompletionItem[] | CompletionList | undefined | null = await next(document, position, context, token);

                    if (result) {
                        let items: CompletionList | CompletionItem[] = result;
                        let isIncomplete: boolean = false;
                        if (items instanceof CompletionList) {
                            isIncomplete = items.isIncomplete ?? false;
                            items = items.items;
                        }

                        if (items.every(item => typeof item.label === "string" && isApiVersion(item.label))) {
                            // It's a list of apiVersion completions
                            // Show them in reverse order so the newest is at the top of the list
                            const countItems = items.length;
                            items = items.map((ci, index) => {
                                if (!ci.sortText) {
                                    let sortText = (countItems - index).toString(10);
                                    sortText = sortText.padStart(10 - sortText.length, '0');
                                    ci.sortText = sortText;
                                }
                                return ci;
                            });
                            result = new CompletionList(items, isIncomplete);
                        }
                    }

                    return result;
                },
            }
        };

        // Create the language client and start the client.
        // tslint:disable-next-line: strict-boolean-expressions
        const langServerVersion = (await getLangServerVersion()) || "Unknown";
        actionContext.telemetry.properties.langServerNugetVersion = langServerVersion;
        ext.outputChannel.appendLine(`Starting ${languageServerName} at ${serverDllPath}`);
        ext.outputChannel.appendLine(`Language server nuget version: ${langServerVersion}`);
        ext.outputChannel.appendLine(`Client options:${os.EOL}${JSON.stringify(clientOptions, undefined, 2)}`);
        ext.outputChannel.appendLine(`Server options:${os.EOL}${JSON.stringify(serverOptions, undefined, 2)}`);
        let client: LanguageClient = new LanguageClient(
            armTemplateLanguageId,
            languageFriendlyName, // Used in the Output window combobox
            serverOptions,
            clientOptions,
        );

        // Use an error handler that sends telemetry
        let defaultHandler = client.createDefaultErrorHandler();
        client.clientOptions.errorHandler = new WrappedErrorHandler(defaultHandler);

        if (waitForDebugger) {
            window.showWarningMessage(`The ${configPrefix}.languageServer.waitForDebugger option is set.  The language server will pause on startup until a debugger is attached.`);
        }

        client.onTelemetry((telemetryData: { eventName: string; properties: { [key: string]: string | undefined } }) => {
            const eventName = telemetryData.eventName.replace(/^\/|\/$/g, ""); // Remove slashes at beginning or end
            const fullEventName = `langserver/${eventName}`;

            if (sanitizeTelemetryData(fullEventName, telemetryData.properties)
            ) {
                callWithTelemetryAndErrorHandlingSync(fullEventName, telemetryActionContext => {
                    telemetryActionContext.errorHandling.suppressDisplay = true;

                    for (let prop of Object.getOwnPropertyNames(telemetryData.properties)) {
                        const value = telemetryData.properties[prop];
                        prop = prop.replace(/^\./g, ""); // Remove starting period
                        telemetryActionContext.telemetry.properties[prop] = String(value);
                    }

                    if (telemetryActionContext.telemetry.properties.error) {
                        telemetryActionContext.telemetry.properties.result = 'Failed';
                    }
                });
            }
        });

        try {
            // client.trace = Trace.Messages;

            let disposable = client.start();
            ext.context.subscriptions.push(disposable);

            await client.onReady();
            ext.languageServerClient = client;

            client.onRequest(notifications.requestOpenLinkedTemplate, async (args: IRequestOpenLinkedFileArgs) => {
                return onRequestOpenLinkedFile(args);
            });

            client.onNotification(notifications.notifyTemplateGraph, async (args: INotifyTemplateGraphArgs) => {
                onNotifyTemplateGraph(args);
            });

            client.onNotification(notifications.schemaValidationNotification, async (args: notifications.ISchemaValidationNotificationArgs) => {
                onSchemaValidationNotication(args);
            });
        } catch (error) {
            throw new Error(
                `${languageServerName}: An error occurred starting the language server.${os.EOL}${os.EOL}${parseError(error).message}`
            );
        }
    });