diff --git a/lsp-sample/client/src/extension.ts b/lsp-sample/client/src/extension.ts index 377fd02e..d4f97f11 100644 --- a/lsp-sample/client/src/extension.ts +++ b/lsp-sample/client/src/extension.ts @@ -5,41 +5,54 @@ 'use strict'; import * as path from 'path'; - import { workspace, ExtensionContext } from 'vscode'; import { - LanguageClient, LanguageClientOptions, ServerOptions, TransportKind + LanguageClient, + LanguageClientOptions, + ServerOptions, + TransportKind } from 'vscode-languageclient'; let client: LanguageClient; export function activate(context: ExtensionContext) { - // The server is implemented in node - let serverModule = context.asAbsolutePath(path.join('server', 'out', 'server.js')); + let serverModule = context.asAbsolutePath( + path.join('server', 'out', 'server.js') + ); // The debug options for the server - let debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] }; + // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging + let debugOptions = { execArgv: ['--nolazy', '--inspect=6009'] }; // 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 } - } + 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'}], + documentSelector: [{ scheme: 'file', language: 'plaintext' }], synchronize: { // Notify the server about file changes to '.clientrc files contained in the workspace - fileEvents: workspace.createFileSystemWatcher('**/.clientrc'), + fileEvents: workspace.createFileSystemWatcher('**/.clientrc') } - } + }; // Create the language client and start the client. - client = new LanguageClient('languageServerExample', 'Language Server Example', serverOptions, clientOptions); + client = new LanguageClient( + 'languageServerExample', + 'Language Server Example', + serverOptions, + clientOptions + ); // Start the client. This will also launch the server client.start(); @@ -50,4 +63,4 @@ export function deactivate(): Thenable { return undefined; } return client.stop(); -} \ No newline at end of file +} diff --git a/lsp-sample/client/src/test/completion.test.ts b/lsp-sample/client/src/test/completion.test.ts index 8a562315..2cb65642 100644 --- a/lsp-sample/client/src/test/completion.test.ts +++ b/lsp-sample/client/src/test/completion.test.ts @@ -4,37 +4,41 @@ * ------------------------------------------------------------------------------------------ */ 'use strict'; -import * as vscode from 'vscode' -import * as assert from 'assert' -import { getDocUri, activate } from './helper' +import * as vscode from 'vscode'; +import * as assert from 'assert'; +import { getDocUri, activate } from './helper'; describe('Should do completion', () => { - const docUri = getDocUri('completion.txt') + const docUri = getDocUri('completion.txt'); - it('Completes JS/TS in txt file', async () => { - await testCompletion(docUri, new vscode.Position(0, 0), { - items: [ - { label: 'JavaScript', kind: vscode.CompletionItemKind.Text }, - { label: 'TypeScript', kind: vscode.CompletionItemKind.Text } - ] - }) - }) -}) + it('Completes JS/TS in txt file', async () => { + await testCompletion(docUri, new vscode.Position(0, 0), { + items: [ + { label: 'JavaScript', kind: vscode.CompletionItemKind.Text }, + { label: 'TypeScript', kind: vscode.CompletionItemKind.Text } + ] + }); + }); +}); -async function testCompletion(docUri: vscode.Uri, position: vscode.Position, expectedCompletionList: vscode.CompletionList) { - await activate(docUri) +async function testCompletion( + docUri: vscode.Uri, + position: vscode.Position, + expectedCompletionList: vscode.CompletionList +) { + await activate(docUri); - // Executing the command `vscode.executeCompletionItemProvider` to simulate triggering completion - const actualCompletionList = (await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - docUri, - position - )) as vscode.CompletionList + // Executing the command `vscode.executeCompletionItemProvider` to simulate triggering completion + const actualCompletionList = (await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + docUri, + position + )) as vscode.CompletionList; - assert.equal(actualCompletionList.items.length, expectedCompletionList.items.length); - expectedCompletionList.items.forEach((expectedItem, i) => { - const actualItem = actualCompletionList.items[i] - assert.equal(actualItem.label, expectedItem.label) - assert.equal(actualItem.kind, expectedItem.kind) - }) -} + assert.equal(actualCompletionList.items.length, expectedCompletionList.items.length); + expectedCompletionList.items.forEach((expectedItem, i) => { + const actualItem = actualCompletionList.items[i]; + assert.equal(actualItem.label, expectedItem.label); + assert.equal(actualItem.kind, expectedItem.kind); + }); +} diff --git a/lsp-sample/client/src/test/helper.ts b/lsp-sample/client/src/test/helper.ts index fea53008..22121c1c 100644 --- a/lsp-sample/client/src/test/helper.ts +++ b/lsp-sample/client/src/test/helper.ts @@ -4,42 +4,45 @@ * ------------------------------------------------------------------------------------------ */ 'use strict'; -import * as vscode from 'vscode' -import * as path from 'path' +import * as vscode from 'vscode'; +import * as path from 'path'; -export let doc: vscode.TextDocument -export let editor: vscode.TextEditor -export let documentEol: string -export let platformEol: string +export let doc: vscode.TextDocument; +export let editor: vscode.TextEditor; +export let documentEol: string; +export let platformEol: string; /** * Activates the vscode.lsp-sample extension */ export async function activate(docUri: vscode.Uri) { - // The extensionId is `publisher.name` from package.json - const ext = vscode.extensions.getExtension('vscode.lsp-sample') - await ext.activate(); - try { - doc = await vscode.workspace.openTextDocument(docUri) - editor = await vscode.window.showTextDocument(doc) - await sleep(2000) // Wait for server activation - } catch (e) { - console.error(e) - } + // The extensionId is `publisher.name` from package.json + const ext = vscode.extensions.getExtension('vscode.lsp-sample'); + await ext.activate(); + try { + doc = await vscode.workspace.openTextDocument(docUri); + editor = await vscode.window.showTextDocument(doc); + await sleep(2000); // Wait for server activation + } catch (e) { + console.error(e); + } } async function sleep(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); + return new Promise(resolve => setTimeout(resolve, ms)); } export const getDocPath = (p: string) => { - return path.resolve(__dirname, '../../testFixture', p) -} + return path.resolve(__dirname, '../../testFixture', p); +}; export const getDocUri = (p: string) => { - return vscode.Uri.file(getDocPath(p)) -} + return vscode.Uri.file(getDocPath(p)); +}; export async function setTestContent(content: string): Promise { - const all = new vscode.Range(doc.positionAt(0), doc.positionAt(doc.getText().length)) - return editor.edit(eb => eb.replace(all, content)) + const all = new vscode.Range( + doc.positionAt(0), + doc.positionAt(doc.getText().length) + ); + return editor.edit(eb => eb.replace(all, content)); } diff --git a/lsp-sample/package.json b/lsp-sample/package.json index c5350460..8ed0f5b9 100644 --- a/lsp-sample/package.json +++ b/lsp-sample/package.json @@ -40,7 +40,7 @@ "verbose" ], "default": "off", - "description": "Traces the communication between VSCode and the language server." + "description": "Traces the communication between VS Code and the language server." } } } diff --git a/lsp-sample/server/src/server.ts b/lsp-sample/server/src/server.ts index 095ab248..bee1a7fb 100644 --- a/lsp-sample/server/src/server.ts +++ b/lsp-sample/server/src/server.ts @@ -5,9 +5,17 @@ 'use strict'; import { - createConnection, TextDocuments, TextDocument, Diagnostic, DiagnosticSeverity, - ProposedFeatures, InitializeParams, DidChangeConfigurationNotification, CompletionItem, - CompletionItemKind, TextDocumentPositionParams + createConnection, + TextDocuments, + TextDocument, + Diagnostic, + DiagnosticSeverity, + ProposedFeatures, + InitializeParams, + DidChangeConfigurationNotification, + CompletionItem, + CompletionItemKind, + TextDocumentPositionParams } from 'vscode-languageserver'; // Create a connection for the server. The connection uses Node's IPC as a transport. @@ -22,34 +30,41 @@ let hasConfigurationCapability: boolean = false; let hasWorkspaceFolderCapability: boolean = false; let hasDiagnosticRelatedInformationCapability: boolean = false; - connection.onInitialize((params: InitializeParams) => { let capabilities = params.capabilities; // Does the client support the `workspace/configuration` request? // If not, we will fall back using global settings - hasConfigurationCapability = capabilities.workspace && !!capabilities.workspace.configuration; - hasWorkspaceFolderCapability = capabilities.workspace && !!capabilities.workspace.workspaceFolders; - hasDiagnosticRelatedInformationCapability = capabilities.textDocument && capabilities.textDocument.publishDiagnostics && capabilities.textDocument.publishDiagnostics.relatedInformation; + hasConfigurationCapability = + capabilities.workspace && !!capabilities.workspace.configuration; + hasWorkspaceFolderCapability = + capabilities.workspace && !!capabilities.workspace.workspaceFolders; + hasDiagnosticRelatedInformationCapability = + capabilities.textDocument && + capabilities.textDocument.publishDiagnostics && + capabilities.textDocument.publishDiagnostics.relatedInformation; return { capabilities: { textDocumentSync: documents.syncKind, - // Tell the client that the server supports code completion - completionProvider: { - resolveProvider: true - } + // Tell the client that the server supports code completion + completionProvider: { + resolveProvider: true + } } - } + }; }); connection.onInitialized(() => { if (hasConfigurationCapability) { // Register for all configuration changes. - connection.client.register(DidChangeConfigurationNotification.type, undefined); + connection.client.register( + DidChangeConfigurationNotification.type, + undefined + ); } if (hasWorkspaceFolderCapability) { - connection.workspace.onDidChangeWorkspaceFolders((_event) => { + connection.workspace.onDidChangeWorkspaceFolders(_event => { connection.console.log('Workspace folder change event received.'); }); } @@ -74,7 +89,9 @@ connection.onDidChangeConfiguration(change => { // Reset all cached document settings documentSettings.clear(); } else { - globalSettings = (change.settings.languageServerExample || defaultSettings); + globalSettings = ( + (change.settings.languageServerExample || defaultSettings) + ); } // Revalidate all open text documents @@ -87,7 +104,10 @@ function getDocumentSettings(resource: string): Thenable { } let result = documentSettings.get(resource); if (!result) { - result = connection.workspace.getConfiguration({ scopeUri: resource, section: 'languageServerExample' }); + result = connection.workspace.getConfiguration({ + scopeUri: resource, + section: 'languageServerExample' + }); documentSettings.set(resource, result); } return result; @@ -100,7 +120,7 @@ documents.onDidClose(e => { // 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) => { +documents.onDidChangeContent(change => { validateTextDocument(change.document); }); @@ -151,43 +171,46 @@ async function validateTextDocument(textDocument: TextDocument): Promise { connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); } -connection.onDidChangeWatchedFiles((_change) => { +connection.onDidChangeWatchedFiles(_change => { // Monitored files have change in VSCode connection.console.log('We received an file change event'); }); - // This handler provides the initial list of the completion items. -connection.onCompletion((_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => { - // The pass parameter contains the position of the text document in - // which code complete got requested. For the example we ignore this - // info and always provide the same completion items. - return [ - { - label: 'TypeScript', - kind: CompletionItemKind.Text, - data: 1 - }, - { - label: 'JavaScript', - kind: CompletionItemKind.Text, - data: 2 - } - ] -}); +connection.onCompletion( + (_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => { + // The pass parameter contains the position of the text document in + // which code complete got requested. For the example we ignore this + // info and always provide the same completion items. + return [ + { + label: 'TypeScript', + kind: CompletionItemKind.Text, + data: 1 + }, + { + label: 'JavaScript', + kind: CompletionItemKind.Text, + data: 2 + } + ]; + } +); // This handler resolve additional information for the item selected in // the completion list. -connection.onCompletionResolve((item: CompletionItem): CompletionItem => { - if (item.data === 1) { - item.detail = 'TypeScript details', - item.documentation = 'TypeScript documentation' - } else if (item.data === 2) { - item.detail = 'JavaScript details', - item.documentation = 'JavaScript documentation' +connection.onCompletionResolve( + (item: CompletionItem): CompletionItem => { + if (item.data === 1) { + (item.detail = 'TypeScript details'), + (item.documentation = 'TypeScript documentation'); + } else if (item.data === 2) { + (item.detail = 'JavaScript details'), + (item.documentation = 'JavaScript documentation'); + } + return item; } - return item; -}); +); /* connection.onDidOpenTextDocument((params) => { @@ -214,4 +237,4 @@ connection.onDidCloseTextDocument((params) => { documents.listen(connection); // Listen on the connection -connection.listen(); \ No newline at end of file +connection.listen();