Add example to use a single server on a multi workspace folder setup

This commit is contained in:
Dirk Baeumer
2017-09-04 16:09:40 +02:00
parent 522898aa8e
commit 4f46a914e8
18 changed files with 562 additions and 8 deletions

View File

@ -0,0 +1,26 @@
{
"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/src/**/*.js" ],
"preLaunchTask": "Client Watch"
},
{
"name": "Attach to Server",
"type": "node",
"request": "attach",
"port": 6009,
"sourceMaps": true,
"outFiles": [ "${workspaceRoot}/client/server/**/*.js" ],
"preLaunchTask": "Server Watch"
}
]
}

View File

@ -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"
}

View File

@ -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"
]
}
]
}

View File

@ -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.

View File

@ -0,0 +1,16 @@
# LSP Multi Root Example
A language server example that demonstrates how to handle configuration settings in a workspace that uses multi root folders. Since settings in VS Code in this setupare typically scoped to a resource the example reads the resource settings from the client using the new proposed API getConfiguration. This is analogous to a reading settings in a multi root folder setup directly in the extension host.
The example uses proposed Language Server protocol. So the code demoed here might change when the final version of the configuration and workspace folder protocol is released.
## Compile and Run
- run `npm install` in this folder. This installs all necessary npm modules in both the client and server folder
- open VS Code on this folder.
- Press Ctrl+Shift+B to compile the client and server
- Switch to the Debug viewlet
- Select `Launch Client` from the drop down
- Run the lauch config
- If you want to debug the server as well use the launch configuration `Attach to Server`

View File

@ -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.

View File

@ -1,6 +1,8 @@
.vscode/**
typings/**
**/*.ts
out/test/**
test/**
src/**
**/*.map
.gitignore
tsconfig.json

View File

@ -0,0 +1,58 @@
{
"name": "lsp-mulit-root-sample-client-part",
"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": {
"lsp-multi-root-sample.maxNumberOfProblems": {
"scope": "resource",
"type": "number",
"default": 100,
"description": "Controls the maximum number of problems produced by the server."
},
"lsp-multi-root-sample.trace.server": {
"scope": "window",
"type": "string",
"enum": [
"off",
"messages",
"verbose"
],
"default": "off",
"description": "Traces the communication between VSCode and the languageServerExample service."
}
}
}
},
"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": "next"
}
}

View File

@ -0,0 +1,89 @@
/* --------------------------------------------------------------------------------------------
* 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, WorkspaceConfiguration } from 'vscode';
import {
LanguageClient, LanguageClientOptions, SettingMonitor, ServerOptions, TransportKind, ProposedProtocol, Middleware, CancellationToken,
ConfigurationMiddleware, GetConfigurationParams
} from 'vscode-languageclient';
// The example settings
interface MultiRootExampleSettings {
maxNumberOfProblems: number;
}
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", "--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 }
}
// Convert VS Code specific settings to a format acceptable by the server. Since
// both client and server do use JSON the conversion is trivial.
let configurationMiddleware: ConfigurationMiddleware = {
configuration: (params: GetConfigurationParams, _token: CancellationToken, _next: Function): any[] => {
if (!params.items) {
return null;
}
let result: (MultiRootExampleSettings | null)[] = [];
for (let item of params.items) {
// The server asks the client for configuration settings without a section
// If a section is present we return null to indicate that the configuration
// is not supported.
if (item.section) {
result.push(null);
continue;
}
let config: WorkspaceConfiguration;
if (item.scopeUri) {
config = workspace.getConfiguration('lsp-multi-root-sample', client.protocol2CodeConverter.asUri(item.scopeUri));
} else {
config = workspace.getConfiguration('lsp-multi-root-sample');
}
result.push({
maxNumberOfProblems: config.get('maxNumberOfProblems')
});
}
return result;
}
};
// 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 contain in the workspace
fileEvents: workspace.createFileSystemWatcher('**/.clientrc')
},
middleware: {
workspace: configurationMiddleware as any // cast to any due to proposed API
}
}
// Create the language client and start the client.
let client = new LanguageClient('languageServerExample', 'Language Server Example', serverOptions, clientOptions);
// Register new propose protocol if available.
client.registerFeatures(ProposedProtocol(client));
// Start the client. This will also launch the server
let disposable = client.start();
// Push the disposable to the context's subscriptions so that the
// client can be deactivated on extension deactivation
context.subscriptions.push(disposable);
}

View File

@ -0,0 +1,22 @@
//
// Note: This example test is leveraging the Mocha test framework.
// Please refer to their documentation on https://mochajs.org/ for help.
//
// The module 'assert' provides assertion methods from node
import * as assert from 'assert';
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from 'vscode';
import * as myExtension from '../src/extension';
// Defines a Mocha test suite to group tests of similar kind together
suite("Extension Tests", () => {
// Defines a Mocha unit test
test("Something 1", () => {
assert.equal(-1, [1, 2, 3].indexOf(5));
assert.equal(-1, [1, 2, 3].indexOf(0));
});
});

View File

@ -0,0 +1,22 @@
//
// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
//
// This file is providing the test runner to use when running extension tests.
// By default the test runner in use is Mocha based.
//
// You can provide your own test runner if you want to override it by exporting
// a function run(testRoot: string, clb: (error:Error) => void) that the extension
// host can call to run the tests. The test runner is expected to use console.log
// to report the results back to the caller. When the tests are finished, return
// a possible error to the callback or null if none.
var testRunner = require('vscode/lib/testrunner');
// You can directly control Mocha options by uncommenting the following lines
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
testRunner.configure({
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
useColors: true // colored output from test results
});
module.exports = testRunner;

View File

@ -1,13 +1,10 @@
{
"compilerOptions": {
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"module": "commonjs",
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"outDir": "out",
"lib": [ "es6"],
"lib": [ "es2016" ],
"sourceMap": true
},
"exclude": [

View File

@ -0,0 +1,25 @@
{
"name": "lsp-mulit-root-sample",
"description": "A language server example demoing multi root workspaces",
"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"
}
}

View File

@ -0,0 +1,22 @@
{
"name": "lsp-mulit-root-sample-server-part",
"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": "next"
},
"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 ."
}
}

View File

@ -0,0 +1,125 @@
/* --------------------------------------------------------------------------------------------
* 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,
DidChangeConfigurationNotification, ClientCapabilities,
CompletionItem, CompletionItemKind, StreamMessageReader, StreamMessageWriter, ProposedProtocol,
ConfigurationItem
} from 'vscode-languageserver';
// Create a connection for the server. The connection uses Node's IPC as a transport
let connection = createConnection(ProposedProtocol);
// Create a simple text document manager. The text document manager
// supports full document sync only
let documents: TextDocuments = new TextDocuments();
connection.onInitialize((params): InitializeResult => {
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 example settings
interface MultiRootExampleSettings {
maxNumberOfProblems: number;
}
let settings: Map<string, Thenable<MultiRootExampleSettings>> = new Map();
function getConfiguration(resource: string): Thenable<MultiRootExampleSettings> {
let result = settings.get(resource);
if (!result) {
result = connection.workspace.getConfiguration({ section: '', scopeUri: resource });
settings.set(resource, result);
}
return result;
}
connection.onInitialized(() => {
// Register to configuration change events.
connection.client.register(DidChangeConfigurationNotification.type);
});
connection.onNotification(DidChangeConfigurationNotification.type, () => {
let toRequest: ConfigurationItem[] = [];
for (let resource of settings.keys()) {
toRequest.push({ section: '', scopeUri: resource});
}
settings.clear();
// Reread all cached configuration
connection.workspace.getConfiguration(toRequest).then((values: MultiRootExampleSettings[]) => {
let toRevalidate: string[] = [];
for (let i = 0; i < values.length; i++) {
let resource = toRequest[i].scopeUri;
let value = values[i];
// If the value got already added to the settings cache then a change has
// occurred before the configuration request got return. Ignore the value
// in this case.
if (value && !settings.has(resource)) {
settings.set(resource, Promise.resolve(value));
toRevalidate.push(resource);
}
}
for (let resource of toRevalidate) {
validateTextDocument(documents.get(resource));
}
});
});
// 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);
});
function validateTextDocument(textDocument: TextDocument): void {
// In this simple example we get the settings for every validate run.
getConfiguration(textDocument.uri).then((settings: MultiRootExampleSettings) => {
let diagnostics: Diagnostic[] = [];
let lines = textDocument.getText().split(/\r?\n/g);
let problems = 0;
for (var i = 0; i < lines.length && problems < settings.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 });
});
}
// Make the text document manager listen on the connection
// for open, change and close text document events
documents.listen(connection);
// Listen on the connection
connection.listen();

View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"lib" : [ "es2016" ],
"outDir": "../client/server"
},
"exclude": [
"node_modules"
]
}

View File

@ -11,4 +11,4 @@
"compile-languageprovider": "cd languageprovider-sample && npm run compile && cd ..",
"compile-statusbar": "cd statusbar-sample && npm run compile && cd .."
}
}
}