From b399ceb1491467af9c2408acd8a8ec9e8699130d Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Mon, 4 Sep 2017 17:43:23 +0200 Subject: [PATCH] Allign standard lsp example with new lsp structure --- lsp-sample/.gitignore | 3 + lsp-sample/.vscode/launch.json | 33 +++++++ lsp-sample/.vscode/settings.json | 9 ++ lsp-sample/.vscode/tasks.json | 80 +++++++++++++++ lsp-sample/License.txt | 17 ++++ lsp-sample/README.md | 21 ++++ lsp-sample/ThirdPartyNotices.txt | 31 ++++++ lsp-sample/client/.vscodeignore | 9 ++ lsp-sample/client/package.json | 58 +++++++++++ lsp-sample/client/src/extension.ts | 44 +++++++++ lsp-sample/client/tsconfig.json | 15 +++ lsp-sample/package.json | 25 +++++ lsp-sample/server/package.json | 22 +++++ lsp-sample/server/src/server.ts | 153 +++++++++++++++++++++++++++++ lsp-sample/server/tsconfig.json | 13 +++ 15 files changed, 533 insertions(+) create mode 100644 lsp-sample/.gitignore create mode 100644 lsp-sample/.vscode/launch.json create mode 100644 lsp-sample/.vscode/settings.json create mode 100644 lsp-sample/.vscode/tasks.json create mode 100644 lsp-sample/License.txt create mode 100644 lsp-sample/README.md create mode 100644 lsp-sample/ThirdPartyNotices.txt create mode 100644 lsp-sample/client/.vscodeignore create mode 100644 lsp-sample/client/package.json create mode 100644 lsp-sample/client/src/extension.ts create mode 100644 lsp-sample/client/tsconfig.json create mode 100644 lsp-sample/package.json create mode 100644 lsp-sample/server/package.json create mode 100644 lsp-sample/server/src/server.ts create mode 100644 lsp-sample/server/tsconfig.json diff --git a/lsp-sample/.gitignore b/lsp-sample/.gitignore new file mode 100644 index 00000000..4228f913 --- /dev/null +++ b/lsp-sample/.gitignore @@ -0,0 +1,3 @@ +out +node_modules +client/server \ No newline at end of file diff --git a/lsp-sample/.vscode/launch.json b/lsp-sample/.vscode/launch.json new file mode 100644 index 00000000..a0f3fab1 --- /dev/null +++ b/lsp-sample/.vscode/launch.json @@ -0,0 +1,33 @@ +{ + "version": "0.2.0", + // List of configurations. Add new configurations or edit existing ones. + "configurations": [ + { + "name": "Launch Client", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceRoot}/client" + ], + "stopOnEntry": false, + "sourceMaps": true, + "outFiles": [ + "${workspaceRoot}/client/out/**/*.js" + ], + "preLaunchTask": "Client Watch" + }, + { + "name": "Attach to Server", + "type": "node", + "request": "attach", + "port": 6009, + "sourceMaps": true, + "outFiles": [ + "${workspaceRoot}/client/server/**/*.js" + ], + "protocol": "legacy", + "preLaunchTask": "Server Watch" + } + ] +} \ No newline at end of file diff --git a/lsp-sample/.vscode/settings.json b/lsp-sample/.vscode/settings.json new file mode 100644 index 00000000..ed5b51ff --- /dev/null +++ b/lsp-sample/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "files.exclude": { + "out": false // set this to true to hide the "out" folder with the compiled JS files + }, + "search.exclude": { + "out": true // set this to false to include "out" folder in search results + }, + "typescript.tsdk": "./node_modules/typescript/lib" +} \ No newline at end of file diff --git a/lsp-sample/.vscode/tasks.json b/lsp-sample/.vscode/tasks.json new file mode 100644 index 00000000..c3bccf46 --- /dev/null +++ b/lsp-sample/.vscode/tasks.json @@ -0,0 +1,80 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "taskName": "Compile", + "dependsOn": [ + "Client Compile", + "Server Compile" + ], + "problemMatcher": [] + }, + { + "taskName": "Client Compile", + "type": "shell", + "command": "npm run client-compile", + "group": "build", + "presentation": { + "panel": "dedicated", + "reveal": "never" + + }, + "problemMatcher": [ + "$tsc" + ] + }, + { + "taskName": "Server Compile", + "type": "shell", + "command": "npm run server-compile", + "group": "build", + "presentation": { + "panel": "dedicated", + "reveal": "never" + }, + "problemMatcher": [ + "$tsc" + ] + }, + { + "taskName": "Watch", + "dependsOn": [ + "Client Watch", + "Server Watch" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [] + }, + { + "taskName": "Client Watch", + "type": "shell", + "command": "npm run client-watch", + "isBackground": true, + "group": "build", + "presentation": { + "panel": "dedicated", + "reveal": "never" + }, + "problemMatcher": [ + "$tsc-watch" + ] + }, + { + "taskName": "Server Watch", + "type": "shell", + "command": "npm run server-watch", + "isBackground": true, + "group": "build", + "presentation": { + "panel": "dedicated", + "reveal": "never" + }, + "problemMatcher": [ + "$tsc-watch" + ] + } + ] +} \ No newline at end of file diff --git a/lsp-sample/License.txt b/lsp-sample/License.txt new file mode 100644 index 00000000..dcdd1210 --- /dev/null +++ b/lsp-sample/License.txt @@ -0,0 +1,17 @@ +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lsp-sample/README.md b/lsp-sample/README.md new file mode 100644 index 00000000..19471b6c --- /dev/null +++ b/lsp-sample/README.md @@ -0,0 +1,21 @@ +# README +## This is the README for the "languageprovider-sample" +------------------- + +This folder contains a sample VS code extension that demonstrates an extension that runs a language server + +The extension observes all 'plaintext' documents (documents from all editors not associated with a language) +and uses the server to provide validation and completion proposals. + +The code for the extension is in the 'client' folder. It uses the 'vscode-languageclient' node module to launch the language server. + +The language server is located in the 'server' folder. + + +# How to run locally +* `npm install` to initialize the extension and the server +* `npm run compile` to compile the extension and the server +* open this folder in VS Code. In the Debug viewlet, run 'Launch Client' from drop-down to launch the extension and attach to the extension. +* create a file `test.txt`, and type `typescript`. You should see a validation error. +* to debug the server use the 'Attach to Server' launch config. +* set breakpoints in the client or the server. \ No newline at end of file diff --git a/lsp-sample/ThirdPartyNotices.txt b/lsp-sample/ThirdPartyNotices.txt new file mode 100644 index 00000000..114129b2 --- /dev/null +++ b/lsp-sample/ThirdPartyNotices.txt @@ -0,0 +1,31 @@ +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +For Microsoft vscode-languageserver-node-example + +This project incorporates material from the project(s) listed below (collectively, “Third Party Code”). +Microsoft is not the original author of the Third Party Code. The original copyright notice and license +under which Microsoft received such Third Party Code are set out below. This Third Party Code is licensed +to you under their original license terms set forth below. Microsoft reserves all other rights not expressly +granted, whether by implication, estoppel or otherwise. + +1. DefinitelyTyped version 0.0.1 (https://github.com/borisyankov/DefinitelyTyped) + +This project is licensed under the MIT license. +Copyrights are respective of each contributor listed at the beginning of each definition file. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/lsp-sample/client/.vscodeignore b/lsp-sample/client/.vscodeignore new file mode 100644 index 00000000..795e7143 --- /dev/null +++ b/lsp-sample/client/.vscodeignore @@ -0,0 +1,9 @@ +.vscode/** +typings/** +out/test/** +test/** +src/** +**/*.map +.gitignore +tsconfig.json +vsc-extension-quickstart.md diff --git a/lsp-sample/client/package.json b/lsp-sample/client/package.json new file mode 100644 index 00000000..ace90075 --- /dev/null +++ b/lsp-sample/client/package.json @@ -0,0 +1,58 @@ +{ + "name": "lsp-sample", + "description": "VSCode part of a language server", + "author": "Microsoft Corporation", + "license": "MIT", + "version": "0.0.1", + "publisher": "vscode", + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/vscode-extension-samples" + }, + "engines": { + "vscode": "^1.15.0" + }, + "categories": [ + "Other" + ], + "activationEvents": [ + "onLanguage:plaintext" + ], + "main": "./out/src/extension", + "contributes": { + "configuration": { + "type": "object", + "title": "Example configuration", + "properties": { + "lspSample.maxNumberOfProblems": { + "scope": "resource", + "type": "number", + "default": 100, + "description": "Controls the maximum number of problems produced by the server." + }, + "lspSample.trace.server": { + "scope": "window", + "type": "string", + "enum": [ + "off", + "messages", + "verbose" + ], + "default": "off", + "description": "Traces the communication between VSCode and the language server." + } + } + } + }, + "scripts": { + "vscode:prepublish": "tsc -p ./", + "compile": "tsc -p ./", + "watch": "tsc -w -p ./", + "update-vscode": "node ./node_modules/vscode/bin/install", + "postinstall": "node ./node_modules/vscode/bin/install" + }, + "dependencies": { + "vscode": "^1.1.5", + "vscode-languageclient": "^3.3.0" + } +} \ No newline at end of file diff --git a/lsp-sample/client/src/extension.ts b/lsp-sample/client/src/extension.ts new file mode 100644 index 00000000..72df9363 --- /dev/null +++ b/lsp-sample/client/src/extension.ts @@ -0,0 +1,44 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import * as path from 'path'; + +import { workspace, Disposable, ExtensionContext } from 'vscode'; +import { LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, TransportKind } from 'vscode-languageclient'; + +export function activate(context: ExtensionContext) { + + // The server is implemented in node + let serverModule = context.asAbsolutePath(path.join('server', 'server.js')); + // The debug options for the server + let debugOptions = { execArgv: ["--nolazy", "--debug=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 } + } + + // Options to control the language client + let clientOptions: LanguageClientOptions = { + // Register the server for plain text documents + documentSelector: [{scheme: 'file', language: 'plaintext'}], + synchronize: { + // Synchronize the setting section 'languageServerExample' to the server + configurationSection: 'lspSample', + // Notify the server about file changes to '.clientrc files contain in the workspace + fileEvents: workspace.createFileSystemWatcher('**/.clientrc') + } + } + + // Create the language client and start the client. + let disposable = new LanguageClient('lspSample', 'Language Server Example', serverOptions, clientOptions).start(); + + // Push the disposable to the context's subscriptions so that the + // client can be deactivated on extension deactivation + context.subscriptions.push(disposable); +} diff --git a/lsp-sample/client/tsconfig.json b/lsp-sample/client/tsconfig.json new file mode 100644 index 00000000..c8b02609 --- /dev/null +++ b/lsp-sample/client/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "moduleResolution": "node", + "rootDir": ".", + "outDir": "out", + "lib": [ "es2016" ], + "sourceMap": true + }, + "exclude": [ + "node_modules", + "server" + ] +} \ No newline at end of file diff --git a/lsp-sample/package.json b/lsp-sample/package.json new file mode 100644 index 00000000..f36533cd --- /dev/null +++ b/lsp-sample/package.json @@ -0,0 +1,25 @@ +{ + "name": "lsp--sample", + "description": "A language server example", + "author": "Microsoft Corporation", + "license": "MIT", + "version": "0.0.1", + "publisher": "vscode", + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/vscode-extension-samples" + }, + "scripts": { + "postinstall": "cd server && npm install && cd ../client && npm install && cd ..", + "compile": "tsc -p client/tsconfig.json && cd server && npm run installServer && cd .. && tsc -p server/tsconfig.json", + "client-compile": "tsc -p client/tsconfig.json", + "client-watch": "tsc -w -p client/tsconfig.json", + "server-compile": "cd server && npm run installServer && cd .. && tsc -p server/tsconfig.json", + "server-watch": "cd server && npm run installServer && cd .. && tsc -w -p server/tsconfig.json" + }, + "devDependencies": { + "@types/mocha": "^2.2.42", + "@types/node": "^6.0.88", + "typescript": "^2.5.2" + } +} \ No newline at end of file diff --git a/lsp-sample/server/package.json b/lsp-sample/server/package.json new file mode 100644 index 00000000..d95ea630 --- /dev/null +++ b/lsp-sample/server/package.json @@ -0,0 +1,22 @@ +{ + "name": "lsp-sample", + "description": "Example implementation of a language server in node.", + "version": "0.0.1", + "author": "Microsoft Corporation", + "license": "MIT", + "engines": { + "node": "*" + }, + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/vscode-extension-samples" + }, + "dependencies": { + "vscode-languageserver": "^3.3.0" + }, + "scripts": { + "installServer": "installServerIntoExtension ../client ./package.json ./tsconfig.json", + "compile": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -p .", + "watch": "installServerIntoExtension ../client ./package.json ./tsconfig.json && tsc -w -p ." + } +} \ No newline at end of file diff --git a/lsp-sample/server/src/server.ts b/lsp-sample/server/src/server.ts new file mode 100644 index 00000000..84620dca --- /dev/null +++ b/lsp-sample/server/src/server.ts @@ -0,0 +1,153 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +'use strict'; + +import { + IPCMessageReader, IPCMessageWriter, + createConnection, IConnection, TextDocumentSyncKind, + TextDocuments, TextDocument, Diagnostic, DiagnosticSeverity, + InitializeParams, InitializeResult, TextDocumentPositionParams, + CompletionItem, CompletionItemKind +} from 'vscode-languageserver'; + +// Create a connection for the server. The connection uses Node's IPC as a transport +let connection: IConnection = createConnection(new IPCMessageReader(process), new IPCMessageWriter(process)); + +// Create a simple text document manager. The text document manager +// supports full document sync only +let documents: TextDocuments = new TextDocuments(); +// Make the text document manager listen on the connection +// for open, change and close text document events +documents.listen(connection); + +// After the server has started the client sends an initilize request. The server receives +// in the passed params the rootPath of the workspace plus the client capabilites. +let workspaceRoot: string; +connection.onInitialize((params): InitializeResult => { + workspaceRoot = params.rootPath; + return { + capabilities: { + // Tell the client that the server works in FULL text document sync mode + textDocumentSync: documents.syncKind, + // Tell the client that the server support code complete + completionProvider: { + resolveProvider: true + } + } + } +}); + +// 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); +}); + +// The settings interface describe the server relevant settings part +interface Settings { + lspSample: ExampleSettings; +} + +// These are the example settings we defined in the client's package.json +// file +interface ExampleSettings { + maxNumberOfProblems: number; +} + +// hold the maxNumberOfProblems setting +let maxNumberOfProblems: number; +// The settings have changed. Is send on server activation +// as well. +connection.onDidChangeConfiguration((change) => { + let settings = change.settings; + maxNumberOfProblems = settings.lspSample.maxNumberOfProblems || 100; + // Revalidate any open text documents + documents.all().forEach(validateTextDocument); +}); + +function validateTextDocument(textDocument: TextDocument): void { + let diagnostics: Diagnostic[] = []; + let lines = textDocument.getText().split(/\r?\n/g); + let problems = 0; + for (var i = 0; i < lines.length && problems < maxNumberOfProblems; i++) { + let line = lines[i]; + let index = line.indexOf('typescript'); + if (index >= 0) { + problems++; + diagnostics.push({ + severity: DiagnosticSeverity.Warning, + range: { + start: { line: i, character: index }, + end: { line: i, character: index + 10 } + }, + message: `${line.substr(index, 10)} should be spelled TypeScript`, + source: 'ex' + }); + } + } + // Send the computed diagnostics to VSCode. + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); +} + +connection.onDidChangeWatchedFiles((change) => { + // Monitored files have change in VSCode + connection.console.log('We recevied 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 + } + ] +}); + +// 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' + } + return item; +}); + +/* +connection.onDidOpenTextDocument((params) => { + // A text document got opened in VSCode. + // params.uri uniquely identifies the document. For documents store on disk this is a file URI. + // params.text the initial full content of the document. + connection.console.log(`${params.textDocument.uri} opened.`); +}); +connection.onDidChangeTextDocument((params) => { + // The content of a text document did change in VSCode. + // params.uri uniquely identifies the document. + // params.contentChanges describe the content changes to the document. + connection.console.log(`${params.textDocument.uri} changed: ${JSON.stringify(params.contentChanges)}`); +}); +connection.onDidCloseTextDocument((params) => { + // A text document got closed in VSCode. + // params.uri uniquely identifies the document. + connection.console.log(`${params.textDocument.uri} closed.`); +}); +*/ + +// Listen on the connection +connection.listen(); diff --git a/lsp-sample/server/tsconfig.json b/lsp-sample/server/tsconfig.json new file mode 100644 index 00000000..6032b82b --- /dev/null +++ b/lsp-sample/server/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "lib" : [ "es2016" ], + "outDir": "../client/server" + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file