vscode-languageclient/node#State TypeScript Examples

The following examples show how to use vscode-languageclient/node#State. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: language-server.ts    From vscode-cadence with Apache License 2.0 5 votes vote down vote up
constructor (ctx: ExtensionContext, config: Config, emulatorState: EmulatorState, activeAccount: Account | null) {
    // Init running state with false and update, when client is connected to server
    this.running = false

    const activeAccountName = (activeAccount != null) ? activeAccount.name : ''
    const activeAccountAddress = (activeAccount != null) ? activeAccount.address : ''
    const { configPath } = config

    this.client = new LanguageClient(
      'cadence',
      'Cadence',
      {
        command: config.flowCommand,
        args: START_LANGUAGE_SERVER_ARGS
      },
      {
        documentSelector: [{ scheme: 'file', language: 'cadence' }],
        synchronize: {
          configurationSection: 'cadence'
        },
        initializationOptions: {
          accessCheckMode: config.accessCheckMode,
          configPath,
          emulatorState,
          activeAccountName,
          activeAccountAddress
        }
      }
    )

    this.client
      .onReady()
      .then(() =>
        window.showInformationMessage('Cadence language server started')
      )
      .catch((err: Error) =>
        window.showErrorMessage(`Cadence language server failed to start: ${err.message}`)
      )

    this.client.onDidChangeState((e: StateChangeEvent) => {
      this.running = e.newState === State.Running
    })

    const clientDisposable = this.client.start()
    ctx.subscriptions.push(clientDisposable)
  }
Example #2
Source File: leanclient.ts    From vscode-lean4 with Apache License 2.0 4 votes vote down vote up
async restart(): Promise<void> {
        const startTime = Date.now()

        console.log('Restarting Lean Language Server')
        if (this.isStarted()) {
            await this.stop()
        }

        this.restartingEmitter.fire(undefined)
        this.toolchainPath = this.storageManager.getLeanPath();
        if (!this.toolchainPath) this.toolchainPath = toolchainPath();
        const version = this.storageManager.getLeanVersion();
        const env = addServerEnvPaths(process.env);
        if (serverLoggingEnabled()) {
            env.LEAN_SERVER_LOG_DIR = serverLoggingPath()
        }

        let executable = (this.toolchainPath) ? join(this.toolchainPath, 'bin', 'lake') : 'lake';

        // check if the lake process will start (skip it on scheme: 'untitled' files)
        let useLake = lakeEnabled() && this.folderUri && this.folderUri.scheme === 'file';
        if (useLake) {
            let knownDate = false;
            const lakefile = Uri.joinPath(this.folderUri, 'lakefile.lean')
            if (!await fileExists(new URL(lakefile.toString()))) {
                useLake = false;
            }
            else {
                // see if we can avoid the more expensive checkLakeVersion call.
                const date = await this.checkToolchainVersion(this.folderUri);
                if (date){
                    // Feb 16 2022 is when the 3.1.0.pre was released.
                    useLake = date >= new Date(2022, 1, 16);
                    knownDate = true;
                }
                if (useLake && !knownDate){
                    useLake = await this.checkLakeVersion(executable, version);
                }
            }
        }

        if (!useLake) {
            executable = (this.toolchainPath) ? join(this.toolchainPath, 'bin', 'lean') : 'lean';
        }

        let options = version ? ['+' + version] :[]
        if (useLake) {
            options = options.concat(['serve', '--'])
        } else{
            options = options.concat(['--server'])
        }

        // Add folder name to command-line so that it shows up in `ps aux`.
        if (this.folderUri) {
            options.push('' + this.folderUri.fsPath)
        } else {
            options.push('untitled')
        }

        const serverOptions: ServerOptions = {
            command: executable,
            args: options.concat(serverArgs()),
            options: {
                cwd: this.folderUri?.fsPath,
                env
            }
        }

        const documentSelector: DocumentFilter = {
            language: 'lean4'
        }

        if (this.folderUri){
            documentSelector.scheme = this.folderUri.scheme
            if (this.folderUri.scheme !== 'untitled') {
                documentSelector.pattern = `${this.folderUri.fsPath}/**/*`
            }
        }

        const clientOptions: LanguageClientOptions = {
            outputChannel: this.outputChannel,
            documentSelector: [documentSelector],
            workspaceFolder: this.workspaceFolder,
            initializationOptions: {
                editDelay: getElaborationDelay(), hasWidgets: true,
            },
            connectionOptions: {
                maxRestartCount: 0,
                cancellationStrategy: undefined as any,
            },
            middleware: {
                handleDiagnostics: (uri, diagnostics, next) => {
                    next(uri, diagnostics);
                    if (!this.client) return;
                    const uri_ = c2pConverter.asUri(uri);
                    const diagnostics_ = [];
                    for (const d of diagnostics) {
                        const d_: ls.Diagnostic = {
                            ...c2pConverter.asDiagnostic(d),
                        };
                        diagnostics_.push(d_);
                    }
                    this.diagnosticsEmitter.fire({uri: uri_, diagnostics: diagnostics_});
                },

                didOpen: async () => {
                    // Note: as per the LSP spec: An open notification must not be sent more than once
                    // without a corresponding close notification send before. This means open and close
                    // notification must be balanced and the max open count for a particular textDocument
                    // is one.  So this even does nothing the notification is handled by the
                    // openLean4Document method below after the 'lean4' languageId is established and
                    // it has weeded out documents opened to invisible editors (like 'git:' schemes and
                    // invisible editors created for Ctrl+Hover events.  A side effect of unbalanced
                    // open/close notification is leaking 'lean --worker' processes.
                    // See https://github.com/microsoft/vscode/issues/78453).
                    return;
                },

                didChange: async (data, next) => {
                    await next(data);
                    if (!this.running || !this.client) return; // there was a problem starting lean server.
                    const params = c2pConverter.asChangeTextDocumentParams(data);
                    this.didChangeEmitter.fire(params);
                },

                didClose: async (doc, next) => {
                    if (!this.isOpen.delete(doc.uri.toString())) {
                        return;
                    }
                    await next(doc);
                    if (!this.running || !this.client) return; // there was a problem starting lean server.
                    const params = c2pConverter.asCloseTextDocumentParams(doc);
                    this.didCloseEmitter.fire(params);
                },

                provideDocumentHighlights: async (doc, pos, ctok, next) => {
                    const leanHighlights = await next(doc, pos, ctok);
                    if (leanHighlights?.length) return leanHighlights;

                    // vscode doesn't fall back to textual highlights,
                    // so we need to do that manually
                    await new Promise((res) => setTimeout(res, 250));
                    if (ctok.isCancellationRequested) return;

                    const wordRange = doc.getWordRangeAtPosition(pos);
                    if (!wordRange) return;
                    const word = doc.getText(wordRange);

                    const highlights: DocumentHighlight[] = [];
                    const text = doc.getText();
                    const nonWordPattern = '[`~@$%^&*()-=+\\[{\\]}⟨⟩⦃⦄⟦⟧⟮⟯‹›\\\\|;:\",./\\s]|^|$'
                    const regexp = new RegExp(`(?<=${nonWordPattern})${escapeRegExp(word)}(?=${nonWordPattern})`, 'g')
                    for (const match of text.matchAll(regexp)) {
                        const start = doc.positionAt(match.index ?? 0)
                        highlights.push({
                            range: new Range(start, start.translate(0, match[0].length)),
                            kind: DocumentHighlightKind.Text,
                        })
                    }

                    return highlights;
                }
            },
        }
        this.client = new LanguageClient(
            'lean4',
            'Lean 4',
            serverOptions,
            clientOptions
        )
        let insideRestart = true;
        patchConverters(this.client.protocol2CodeConverter, this.client.code2ProtocolConverter)
        try {
            this.client.onDidChangeState(async (s) => {
                // see https://github.com/microsoft/vscode-languageserver-node/issues/825
                if (s.newState === State.Starting) {
                    console.log('client starting');
                } else if (s.newState === State.Running) {
                    const end = Date.now()
                    console.log('client running, started in ', end - startTime, 'ms');
                    this.running = true; // may have been auto restarted after it failed.
                    if (!insideRestart) {
                        this.restartedEmitter.fire(undefined)
                    }
                } else if (s.newState === State.Stopped) {
                    this.stoppedEmitter.fire('Lean language server has stopped. ');
                    console.log('client has stopped or it failed to start');
                    this.running = false;
                    if (!this.noPrompt){
                        await this.showRestartMessage();
                    }
                }
            })
            this.client.start()
            await this.client.onReady();
            // tell the new client about the documents that are already open!
            for (const key of this.isOpen.keys()) {
                const doc = this.isOpen.get(key);
                if (doc) this.notifyDidOpen(doc);
            }
            // if we got this far then the client is happy so we are running!
            this.running = true;
        } catch (error) {
            this.outputChannel.appendLine('' + error);
            this.serverFailedEmitter.fire('' + error);
            insideRestart = false;
            return;
        }

        // HACK(WN): Register a default notification handler to fire on custom notifications.
        // A mechanism to do this is provided in vscode-jsonrpc. One can register a `StarNotificationHandler`
        // here: https://github.com/microsoft/vscode-languageserver-node/blob/b2fc85d28a1a44c22896559ee5f4d3ba37a02ef5/jsonrpc/src/common/connection.ts#L497
        // which fires on any LSP notifications not in the standard, for example the `$/lean/..` ones.
        // However this mechanism is not exposed in vscode-languageclient, so we hack around its implementation.
        const starHandler = (method: string, params_: any) => {
            if (method === '$/lean/fileProgress' && this.client) {
                const params = params_ as LeanFileProgressParams;
                const uri = p2cConverter.asUri(params.textDocument.uri)
                this.progressChangedEmitter.fire([uri.toString(), params.processing]);
                // save the latest progress on this Uri in case infoview needs it later.
                this.progress.set(uri, params.processing);
            }

            this.customNotificationEmitter.fire({method, params: params_});
        };
        // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
        this.client.onNotification(starHandler as any, () => {});

        // Reveal the standard error output channel when the server prints something to stderr.
        // The vscode-languageclient library already takes care of writing it to the output channel.
        (this.client as any)._serverProcess.stderr.on('data', () => {
            this.client?.outputChannel.show(true);
        });

        this.restartedEmitter.fire(undefined)
        insideRestart = false;
    }