add lsp-web-extension-sample

This commit is contained in:
Martin Aeschlimann
2021-07-21 18:13:58 +02:00
parent 710ba4ef67
commit d885d442fa
21 changed files with 5543 additions and 1 deletions

View File

@ -274,7 +274,14 @@ const lspSamples = [
'https://github.com/Microsoft/vscode/wiki/Extension-Authoring:-Adopting-Multi-Root-Workspace-APIs#language-client--language-server',
apis: [],
contributions: []
}
},
{
description: 'LSP Web Extension Sample',
path: 'lsp-web-extension-sample',
guide: '/api/language-extensions/language-server-extension-guide',
apis: [],
contributions: []
},
]
/**
* LSP specific samples

View File

@ -0,0 +1,6 @@
node_modules/**
client/node_modules/**
client/out/**
server/node_modules/**
server/out/**
./webpack.config.js

View File

@ -0,0 +1,20 @@
/**@type {import('eslint').Linter.Config} */
// eslint-disable-next-line no-undef
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: [
'@typescript-eslint',
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
],
rules: {
'semi': [2, "always"],
'@typescript-eslint/no-unused-vars': 0,
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/no-non-null-assertion': 0,
}
};

4
lsp-web-extension-sample/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
dist
node_modules
client/server
.vscode-test-web

View File

@ -0,0 +1,10 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"dbaeumer.vscode-eslint",
"eamodio.tsl-problem-matcher"
]
}

View File

@ -0,0 +1,42 @@
// A launch configuration that compiles the extension and then opens it inside a new window
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Client",
"type": "pwa-extensionHost",
"debugWebWorkerHost": true,
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceRoot}",
"--extensionDevelopmentKind=web"
],
"outFiles": [
"${workspaceRoot}/client/dist/**/*.js"
],
"preLaunchTask": {
"type": "npm",
"script": "watch"
}
},
{
"type": "node",
"request": "attach",
"name": "Attach to Server",
"port": 6009,
"restart": true,
"outFiles": [
"${workspaceRoot}/server/dist/**/*.js"
]
}
],
"compounds": [
{
"name": "Client + Server",
"configurations": [
"Launch Client",
"Attach to Server"
]
}
]
}

View File

@ -0,0 +1,9 @@
{
"editor.insertSpaces": false,
"tslint.enable": true,
"typescript.tsc.autoDetect": "off",
"typescript.preferences.quoteStyle": "single",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}

View File

@ -0,0 +1,35 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "compile",
"group": "build",
"presentation": {
"panel": "dedicated",
"reveal": "never"
},
"problemMatcher": [
"$ts-webpack",
"$tslint-webpack"
]
},
{
"type": "npm",
"script": "watch",
"isBackground": true,
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"panel": "dedicated",
"reveal": "never"
},
"problemMatcher": [
"$ts-webpack-watch",
"$tslint-webpack-watch"
]
}
]
}

View File

@ -0,0 +1,9 @@
.vscode/**
**/*.ts
**/*.map
.gitignore
**/tsconfig.json
**/tsconfig.base.json
contributing.md
.travis.yml
**/node_modules/**

View File

@ -0,0 +1,40 @@
# LSP web extension Example
A LSP server that runs in a web extension
## Functionality
This Language Server add color decorators to plain text files.
- create a plain text file
- enter text that contains colors in hex format (#rrggbb)
- color decorators
It also includes an End-to-End test.
## Structure
```
.
├── client // Language Client
│ ├── src
│ │ └── browserClientMain.ts // Language Client entry point
├── package.json // The extension manifest.
└── server // Language Server
└── src
└── browserServerMain.ts // Language Server entry point
```
## Running the Sample
- 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 launch config.
- If you want to debug the server as well use the launch configuration `Attach to Server`
- In the [Extension Development Host] instance of VSCode, open a document in 'plain text' language mode.
- Type #00ff00 or any other color in hex format
- color decorators will appear

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
{
"name": "lsp-web-extension-sample-client",
"description": "VSCode part of a language server in a web extension",
"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.52.0"
},
"dependencies": {
"vscode-languageclient": "^7.0.0"
},
"devDependencies": {
"@types/vscode": "^1.52.0",
"@vscode/test-web": "^0.0.6"
}
}

View File

@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionContext, Uri } from 'vscode';
import { LanguageClientOptions } from 'vscode-languageclient';
import { LanguageClient } from 'vscode-languageclient/browser';
// this method is called when vs code is activated
export function activate(context: ExtensionContext) {
console.log('lsp-web-extension-sample activated!');
const documentSelector = [{ language: 'plaintext' }];
// Options to control the language client
const clientOptions: LanguageClientOptions = {
documentSelector,
synchronize: {},
initializationOptions: {}
};
// Create a worker. The worker main file implements the language server.
const serverMain = Uri.joinPath(context.extensionUri, 'server/dist/browserServerMain.js');
const worker = new Worker(serverMain.toString());
// create the language server client to communicate with the server running in the worker
const client = new LanguageClient('lsp-web-extension-sample', 'LSP Web Extension Sample', clientOptions, worker);
const disposable = client.start();
context.subscriptions.push(disposable);
client.onReady().then(() => {
console.log('lsp-web-extension-sample server is ready');
});
}

View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2019",
"lib": ["ES2019", "webworker"],
"rootDir": "src",
"sourceMap": true
},
"include": ["src"],
"exclude": ["node_modules", ".vscode-test-web"]
}

3649
lsp-web-extension-sample/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
{
"name": "lsp-web-extension-sample",
"description": "A language server in a web extension",
"author": "Microsoft Corporation",
"license": "MIT",
"version": "1.0.0",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-extension-samples"
},
"publisher": "vscode-samples",
"categories": [],
"keywords": [
"multi-root ready"
],
"engines": {
"vscode": "^1.43.0"
},
"activationEvents": [
"onLanguage:plaintext"
],
"browser": "./client/dist/browserClientMain",
"contributes": {
"configuration": [
{
"order": 22,
"id": "lsp-web-extension-sample",
"title": "lsp-web-extension-sample",
"properties": {
"lsp-web-extension-sample.trace.server": {
"type": "string",
"scope": "window",
"enum": [
"off",
"messages",
"verbose"
],
"default": "verbose",
"description": "Traces the communication between VS Code and the lsp-web-extension-sample language server."
}
}
}
]
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "webpack",
"watch": "webpack --watch",
"package": "webpack --mode production --devtool hidden-source-map",
"postinstall": "cd client && npm install && cd ../server && npm install && cd ..",
"chrome": "npm run compile && vscode-test-web --browserType=chromium --extensionDevelopmentPath=."
},
"devDependencies": {
"@types/mocha": "^8.2.2",
"@types/node": "^12.12.0",
"@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/parser": "^4.23.0",
"eslint": "^7.26.0",
"mocha": "^8.3.2",
"typescript": "^4.3.5",
"ts-loader": "^9.2.3",
"vscode-test-web": "^0.0.5",
"webpack": "^5.44.0",
"webpack-cli": "^4.7.2",
"path-browserify": "^1.0.1"
}
}

View File

@ -0,0 +1,40 @@
{
"name": "lsp-web-extension-sample-server",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"vscode-jsonrpc": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz",
"integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg=="
},
"vscode-languageserver": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz",
"integrity": "sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==",
"requires": {
"vscode-languageserver-protocol": "3.16.0"
}
},
"vscode-languageserver-protocol": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz",
"integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==",
"requires": {
"vscode-jsonrpc": "6.0.0",
"vscode-languageserver-types": "3.16.0"
}
},
"vscode-languageserver-textdocument": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz",
"integrity": "sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA=="
},
"vscode-languageserver-types": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz",
"integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA=="
}
}
}

View File

@ -0,0 +1,19 @@
{
"name": "lsp-web-extension-sample-server",
"description": "Example implementation of a language server in a web extension.",
"version": "1.0.0",
"author": "Microsoft Corporation",
"license": "MIT",
"engines": {
"node": "*"
},
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-extension-samples"
},
"dependencies": {
"vscode-languageserver": "^7.0.0",
"vscode-languageserver-textdocument": "^1.0.1"
},
"scripts": {}
}

View File

@ -0,0 +1,110 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createConnection, BrowserMessageReader, BrowserMessageWriter, } from 'vscode-languageserver/browser';
import { Color, ColorInformation, Range, InitializeParams, InitializeResult, ServerCapabilities, TextDocuments, ColorPresentation, TextEdit, TextDocumentIdentifier } from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';
console.log('running server lsp-web-extension-sample');
/* browser specific setup code */
const messageReader = new BrowserMessageReader(self);
const messageWriter = new BrowserMessageWriter(self);
const connection = createConnection(messageReader, messageWriter);
/* non-browser specific code */
connection.onInitialize((params: InitializeParams): InitializeResult => {
const capabilities: ServerCapabilities = {
colorProvider: {} // provide a color providr
};
return { capabilities };
});
// Track open, change and close text document events
const documents = new TextDocuments(TextDocument);
documents.listen(connection);
// Register providers
connection.onDocumentColor(params => getColorInformation(params.textDocument));
connection.onColorPresentation(params => getColorPresentation(params.color, params.range));
// Listen on the connection
connection.listen();
const colorRegExp = /#([0-9A-Fa-f]{6})/g;
function getColorInformation(textDocument: TextDocumentIdentifier) {
const colorInfos: ColorInformation[] = [];
const document = documents.get(textDocument.uri);
if (document) {
const text = document.getText();
colorRegExp.lastIndex = 0;
let match;
while ((match = colorRegExp.exec(text)) != null) {
const offset = match.index;
const length = match[0].length;
const range = Range.create(document.positionAt(offset), document.positionAt(offset + length));
const color = parseColor(text, offset);
colorInfos.push({ color, range });
}
}
return colorInfos;
}
function getColorPresentation(color: Color, range: Range) {
const result: ColorPresentation[] = [];
const red256 = Math.round(color.red * 255), green256 = Math.round(color.green * 255), blue256 = Math.round(color.blue * 255);
function toTwoDigitHex(n: number): string {
const r = n.toString(16);
return r.length !== 2 ? '0' + r : r;
}
const label = `#${toTwoDigitHex(red256)}${toTwoDigitHex(green256)}${toTwoDigitHex(blue256)}`;
result.push({ label: label, textEdit: TextEdit.replace(range, label) });
return result;
}
const enum CharCode {
Digit0 = 48,
Digit9 = 57,
A = 65,
F = 70,
a = 97,
f = 102,
}
function parseHexDigit(charCode: CharCode): number {
if (charCode >= CharCode.Digit0 && charCode <= CharCode.Digit9) {
return charCode - CharCode.Digit0;
}
if (charCode >= CharCode.A && charCode <= CharCode.F) {
return charCode - CharCode.A;
}
if (charCode >= CharCode.a && charCode <= CharCode.f) {
return charCode - CharCode.a;
}
return 0;
}
function parseColor(content: string, offset: number): Color {
const r = (16 * parseHexDigit(content.charCodeAt(offset + 1)) + parseHexDigit(content.charCodeAt(offset + 2))) / 255;
const g = (16 * parseHexDigit(content.charCodeAt(offset + 3)) + parseHexDigit(content.charCodeAt(offset + 4))) / 255;
const b = (16 * parseHexDigit(content.charCodeAt(offset + 5)) + parseHexDigit(content.charCodeAt(offset + 6))) / 255;
return Color.create(r, g, b, 1);
}

View File

@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "es2019",
"lib": ["ES2019", "webworker"],
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"strict": true
},
"include": ["src"],
"exclude": ["node_modules", ".vscode-test-web"]
}

View File

@ -0,0 +1,101 @@
/* eslint-disable no-undef */
/* eslint-disable @typescript-eslint/no-var-requires */
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
//@ts-check
/** @typedef {import('webpack').Configuration} WebpackConfig **/
const path = require('path');
const browserClientConfig = /** @type WebpackConfig */ {
context: path.join(__dirname, 'client'),
mode: 'none',
target: 'webworker', // web extensions run in a webworker context
entry: {
browserClientMain: './src/browserClientMain.ts',
},
output: {
filename: '[name].js',
path: path.join(__dirname, 'client', 'dist'),
libraryTarget: 'commonjs',
},
resolve: {
mainFields: ['module', 'main'],
extensions: ['.ts', '.js'], // support ts-files and js-files
alias: {},
fallback: {
path: require.resolve("path-browserify")
},
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader',
},
],
},
],
},
externals: {
vscode: 'commonjs vscode', // ignored because it doesn't exist
},
performance: {
hints: false,
},
devtool: 'source-map',
};
const browserServerConfig = /** @type WebpackConfig */ {
context: path.join(__dirname, 'server'),
mode: 'none',
target: 'webworker', // web extensions run in a webworker context
entry: {
browserServerMain: './src/browserServerMain.ts',
},
output: {
filename: '[name].js',
path: path.join(__dirname, 'server', 'dist'),
libraryTarget: 'var',
library: 'serverExportVar'
},
resolve: {
mainFields: ['module', 'main'],
extensions: ['.ts', '.js'], // support ts-files and js-files
alias: {},
fallback: {
//path: require.resolve("path-browserify")
},
},
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader',
},
],
},
],
},
externals: {
vscode: 'commonjs vscode', // ignored because it doesn't exist
},
performance: {
hints: false,
},
devtool: 'source-map'
};
module.exports = [browserClientConfig, browserServerConfig];