Link Search Menu Expand Document

Language Server Protocol

  • With Language Servers, you can implement autocomplete, error-checking (diagnostics), jump-to-definition, and many other language features supported in VS Code.

Why

  • First, Language Servers are usually implemented in their native programming languages, and that presents a challenge in integrating them with VS Code, which has a Node.js runtime.
  • language features can be resource intensive. For example, to correctly validate a file, Language Server needs to parse a large amount of files, build up Abstract Syntax Trees for them and perform static program analysis. Those operations could incur significant CPU and memory usage and we need to ensure that VS Code’s performance remains unaffected.
  • integrating multiple language toolings with multiple code editors could involve significant effort. From language toolings’ perspective, they need to adapt to code editors with different APIs

Implementing a Language Server

  • two parts
    • Language Client: A normal VS Code extension written in JavaScript / TypeScript. This extension has access to all VS Code Namespace API.
    • Language Server: A language analysis tool running in a separate process.

lang server

Explaining the ‘Language Client’

  • First look the activationEvents:
"activationEvents": [
    "onLanguage:plaintext"
]
  • dependencies
"engines": {
    "vscode": "^1.43.0"
},
"dependencies": {
    "vscode-languageclient": "^6.1.3"
}
  • extension
import {
  LanguageClient,
  LanguageClientOptions,
  ServerOptions,
  TransportKind
} from 'vscode-languageclient';

...

  // 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: { module: serverModule, transport: TransportKind.ipc },
    debug: {
      module: serverModule,
      transport: TransportKind.ipc,
      options: debugOptions
    }
  };

  // Options to control the language client
  let clientOptions: LanguageClientOptions = {
    // Register the server for plain text documents
    documentSelector: [{ scheme: 'file', language: 'plaintext' }],
    synchronize: {
      // Notify the server about file changes to '.clientrc files contained in the workspace
      fileEvents: workspace.createFileSystemWatcher('**/.clientrc')
    }
  };

client = new LanguageClient(
    serverOptions,
    clientOptions
)

client.start();

Explaining the ‘Language Server’

  • deps
"dependencies": {
    "vscode-languageserver": "^6.1.1",
    "vscode-languageserver-textdocument": "^1.0.1"
}
import {
  createConnection,
  TextDocuments,
  Diagnostic,
  DiagnosticSeverity,
  ProposedFeatures,
  InitializeParams,
  DidChangeConfigurationNotification,
  CompletionItem,
  CompletionItemKind,
  TextDocumentPositionParams,
  TextDocumentSyncKind,
  InitializeResult
} from 'vscode-languageserver';

let connection = createConnection(ProposedFeatures.all);
let documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);

connection.onInitialize(params: InitializeParams) {
  const result: InitializeResult = {
    capabilities: {
      textDocumentSync: TextDocumentSyncKind.Incremental,
      // Tell the client that this server supports code completion.
      completionProvider: {
        resolveProvider: true
      }
    }
  };
  ...
  return result
}

connection.onDidChangeConfiguration(change => {
  if (hasConfigurationCapability) {
    // Reset all cached document settings
    documentSettings.clear();
  } else {
    globalSettings = <ExampleSettings>(
      (change.settings.languageServerExample || defaultSettings)
    );
  }

  // Revalidate all open text documents
  documents.all().forEach(validateTextDocument);
});

...

// The content of a text document has changed. This event is emitted
// when the text document first opened or when its content has changed.
documents.onDidChangeContent(change => {
  validateTextDocument(change.document);
});

async function validateTextDocument(textDocument: TextDocument): Promise<void> {
        text = textDocument.getText
        let diagnostics: Diagnostic[] = [];
    	while ((m = pattern.exec(text)) && problems < settings.maxNumberOfProblems) {
            let diagnostic: Diagnostic = {
                severity: DiagnosticSeverity.Warning,
                range: {
                    start: textDocument.positionAt(m.index),
                    end: textDocument.positionAt(m.index + m[0].length)
                },
                message: `${m[0]} is all uppercase.`,
                source: 'ex'
            };
			diagnostic.relatedInformation = [
				{
					location: {
						uri: textDocument.uri,
						range: Object.assign({}, diagnostic.range)
					},
					message: 'Spelling matters'
				},
				{
					location: {
						uri: textDocument.uri,
						range: Object.assign({}, diagnostic.range)
					},
					message: 'Particularly for names'
				}
			];
        }
        diagnostics.push(diagnostic)
        connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
}

Diagnostics Tips and Tricks

  • If the start and end positions are the same, VS Code will underline with a squiggle the word at that position.
  • If you want to underline with a squiggle until the end of the line, then set the character of the end position to Number.MAX_VALUE.

Features

  • code completion
  • Find All References
  • Go To Definition.

  • Document Highlights: highlights all ‘equal’ symbols in a text document. #todo
  • Hover: provides hover information for a symbol selected in a text document.
  • Signature Help: provides signature help for a symbol selected in a text document.
  • Goto Definition: provides go to definition support for a symbol selected in a text document.
  • Goto Type Definition: provides go to type/interface definition support for a symbol selected in a text document.
  • Goto Implementation: provides go to implementation definition support for a symbol selected in a text document.
  • Find References: finds all project-wide references for a symbol selected in a text document.
  • List Document Symbols: lists all symbols defined in a text document.
  • List Workspace Symbols: lists all project-wide symbols.
  • Code Actions: compute commands to run (typically beautify/refactor) for a given text document and range.
  • CodeLens: compute CodeLens statistics for a given text document.
  • Document Formatting: this includes formatting of whole documents, document ranges and formatting on type.
  • Rename: project-wide rename of a symbol.
  • Document Links: compute and resolve links inside a document.
  • Document Colors: compute and resolve colors inside a document to provide color picker in editor.

Topics

Incremental Text Document Synchronization

Using VS Code API directly to implement Language Features

Error Tolerant Parser for Language Server#

Related


Table of contents


Copyright © 2020 Thence LLC