diff --git a/examples/json-vscode-extension/.vscode/launch.json b/examples/json-vscode-extension/.vscode/launch.json deleted file mode 100644 index 7ac28d73..00000000 --- a/examples/json-vscode-extension/.vscode/launch.json +++ /dev/null @@ -1,53 +0,0 @@ -// A launch configuration that compiles the extension and then opens it inside a new window -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Launch Client", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceRoot}"], - "outFiles": ["${workspaceRoot}/client/out/**/*.js"], - "preLaunchTask": { - "type": "npm", - "script": "compile" - }, - "env": { - "VSCODE_DEBUG_MODE": "true" - } - }, - { - "name": "Launch Server", - "type": "python", - "request": "launch", - "module": "server", - "args": ["--tcp"], - "justMyCode": false, - "python": "${command:python.interpreterPath}", - "cwd": "${workspaceFolder}", - "env": { - "PYTHONPATH": "${workspaceFolder}" - } - }, - { - "name": "Launch Server [WebSockets]", - "type": "python", - "request": "launch", - "module": "server", - "args": ["--ws"], - "justMyCode": false, - "python": "${command:python.interpreterPath}", - "cwd": "${workspaceFolder}", - "env": { - "PYTHONPATH": "${workspaceFolder}" - } - } - ], - "compounds": [ - { - "name": "Server + Client", - "configurations": ["Launch Server", "Launch Client"] - } - ] -} diff --git a/examples/json-vscode-extension/README.md b/examples/json-vscode-extension/README.md deleted file mode 100644 index 62957b66..00000000 --- a/examples/json-vscode-extension/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Example JSON VSCode extension with example Pygls Language Server - -This is a slightly more detailed example Language Server than the Hello World one. Like Hello World, it uses LSP Completions as its central feature, but adds some long-running background work to demonstrate methods to handle asynchronicity and progress notifications. - -Although we include a complete VSCode client here, you may also be interested in Microsoft's [client template for Python tools](https://github.com/microsoft/vscode-python-tools-extension-template). It is focussed on Python-related Language Server tooling, but it is specifically tailored to Pygls, so may have some unique insights. - -## Install Server Dependencies - -1. `python -m venv env` -1. `python -m pip install -e .` from root directory -1. Create `.vscode/settings.json` file and set `python.interpreterPath` to point to your python environment where `pygls` is installed - -## Install Client Dependencies - -Open terminal and execute following commands: - -1. `npm install` -1. `cd client/ && npm install` - -## Run Example - -1. Open this directory in VS Code -1. Open debug view (`ctrl + shift + D`) -1. Select `Server + Client` and press `F5` diff --git a/examples/json-vscode-extension/client/src/extension.ts b/examples/json-vscode-extension/client/src/extension.ts deleted file mode 100644 index d2c52db4..00000000 --- a/examples/json-vscode-extension/client/src/extension.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* ------------------------------------------------------------------------- - * Original work Copyright (c) Microsoft Corporation. All rights reserved. - * Original work licensed under the MIT License. - * See ThirdPartyNotices.txt in the project root for license information. - * All modifications Copyright (c) Open Law Library. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License") - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http: // www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ----------------------------------------------------------------------- */ -"use strict"; - -import * as net from "net"; -import * as path from "path"; -import { ExtensionContext, ExtensionMode, workspace } from "vscode"; -import { - LanguageClient, - LanguageClientOptions, - ServerOptions, -} from "vscode-languageclient/node"; - -let client: LanguageClient; - -function getClientOptions(): LanguageClientOptions { - return { - // Register the server for plain text documents - documentSelector: [ - { scheme: "file", language: "json" }, - { scheme: "untitled", language: "json" }, - ], - outputChannelName: "[pygls] JsonLanguageServer", - synchronize: { - // Notify the server about file changes to '.clientrc files contain in the workspace - fileEvents: workspace.createFileSystemWatcher("**/.clientrc"), - }, - }; -} - -function startLangServerTCP(addr: number): LanguageClient { - const serverOptions: ServerOptions = () => { - return new Promise((resolve /*, reject */) => { - const clientSocket = new net.Socket(); - clientSocket.connect(addr, "127.0.0.1", () => { - resolve({ - reader: clientSocket, - writer: clientSocket, - }); - }); - }); - }; - - return new LanguageClient( - `tcp lang server (port ${addr})`, - serverOptions, - getClientOptions() - ); -} - -function startLangServer( - command: string, - args: string[], - cwd: string -): LanguageClient { - const serverOptions: ServerOptions = { - args, - command, - options: { cwd }, - }; - - return new LanguageClient(command, serverOptions, getClientOptions()); -} - -export function activate(context: ExtensionContext): void { - if (context.extensionMode === ExtensionMode.Development) { - // Development - Run the server manually - client = startLangServerTCP(2087); - } else { - // Production - Client is going to run the server (for use within `.vsix` package) - const cwd = path.join(__dirname, "..", ".."); - const pythonPath = workspace - .getConfiguration("python") - .get("pythonPath"); - - if (!pythonPath) { - throw new Error("`python.pythonPath` is not set"); - } - - client = startLangServer(pythonPath, ["-m", "server"], cwd); - } - - context.subscriptions.push(client.start()); -} - -export function deactivate(): Thenable { - return client ? client.stop() : Promise.resolve(); -} diff --git a/examples/json-vscode-extension/package.json b/examples/json-vscode-extension/package.json deleted file mode 100644 index 3ce5b77c..00000000 --- a/examples/json-vscode-extension/package.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "name": "json-extension", - "description": "Simple json extension example", - "author": "Open Law Library", - "repository": "https://github.com/openlawlibrary/pygls", - "license": "Apache-2.0", - "version": "0.11.3", - "publisher": "openlawlibrary", - "engines": { - "vscode": "^1.62.0" - }, - "categories": [ - "Other" - ], - "activationEvents": [ - "onLanguage:json" - ], - "contributes": { - "commands": [ - { - "command": "countDownBlocking", - "title": "Count down 10 seconds [Blocking]" - }, - { - "command": "countDownNonBlocking", - "title": "Count down 10 seconds [Non Blocking]" - }, - { - "command": "progress", - "title": "Start the progress" - }, - { - "command": "registerCompletions", - "title": "Register completions" - }, - { - "command": "showConfigurationAsync", - "title": "Show Json Example Configuration [Async]" - }, - { - "command": "showConfigurationCallback", - "title": "Show Json Example Configuration [Callback]" - }, - { - "command": "showConfigurationThread", - "title": "Show Json Example Configuration [Thread]" - }, - { - "command": "unregisterCompletions", - "title": "Unregister completions" - } - ], - "configuration": { - "type": "object", - "title": "Json Server Configuration", - "properties": { - "jsonServer.exampleConfiguration": { - "scope": "resource", - "type": "string", - "default": "You can override this message." - } - } - } - }, - "main": "./client/out/extension", - "scripts": { - "vscode:prepublish": "npm run compile", - "compile": "tsc -b", - "watch": "tsc -b -w" - }, - "devDependencies": { - "@types/node": "^16.11.6", - "@types/vscode": "^1.62.0", - "@typescript-eslint/eslint-plugin": "^5.3.0", - "@typescript-eslint/parser": "^5.3.0", - "eslint": "^8.2.0", - "typescript": "^4.4.4" - }, - "dependencies": { - "vscode-languageclient": "^7.0.0" - } -} diff --git a/examples/json-vscode-extension/tsconfig.json b/examples/json-vscode-extension/tsconfig.json deleted file mode 100644 index 8abbad23..00000000 --- a/examples/json-vscode-extension/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "es2019", - "lib": ["ES2019"], - "rootDir": "client/src", - "outDir": "client/out", - "sourceMap": true - }, - "include": ["client/src"], - "exclude": ["node_modules"] -} diff --git a/examples/servers/json_server.py b/examples/servers/json_server.py index 77d5086f..027c12b5 100644 --- a/examples/servers/json_server.py +++ b/examples/servers/json_server.py @@ -360,5 +360,4 @@ def main(): if __name__ == "__main__": - logging.basicConfig(filename="pygls.log", level=logging.DEBUG, filemode="w") main() diff --git a/examples/json-vscode-extension/.eslintrc.yml b/examples/vscode-playground/.eslintrc.yml similarity index 100% rename from examples/json-vscode-extension/.eslintrc.yml rename to examples/vscode-playground/.eslintrc.yml diff --git a/examples/json-vscode-extension/.gitignore b/examples/vscode-playground/.gitignore similarity index 100% rename from examples/json-vscode-extension/.gitignore rename to examples/vscode-playground/.gitignore diff --git a/examples/vscode-playground/.vscode/launch.json b/examples/vscode-playground/.vscode/launch.json new file mode 100644 index 00000000..4db80cba --- /dev/null +++ b/examples/vscode-playground/.vscode/launch.json @@ -0,0 +1,24 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Client", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceRoot}", + "--folder-uri=${workspaceRoot}/../workspace", + "--folder-uri=${workspaceRoot}/../servers", + ], + "outFiles": [ + "${workspaceRoot}/out/**/*.js" + ], + // "preLaunchTask": { + // "type": "npm", + // "script": "compile" + // }, + }, + ], +} diff --git a/examples/json-vscode-extension/.vscode/tasks.json b/examples/vscode-playground/.vscode/tasks.json similarity index 100% rename from examples/json-vscode-extension/.vscode/tasks.json rename to examples/vscode-playground/.vscode/tasks.json diff --git a/examples/json-vscode-extension/.vscodeignore b/examples/vscode-playground/.vscodeignore similarity index 100% rename from examples/json-vscode-extension/.vscodeignore rename to examples/vscode-playground/.vscodeignore diff --git a/examples/json-vscode-extension/LICENSE.txt b/examples/vscode-playground/LICENSE.txt similarity index 100% rename from examples/json-vscode-extension/LICENSE.txt rename to examples/vscode-playground/LICENSE.txt diff --git a/examples/vscode-playground/README.md b/examples/vscode-playground/README.md new file mode 100644 index 00000000..aa8813d7 --- /dev/null +++ b/examples/vscode-playground/README.md @@ -0,0 +1,47 @@ +# Pygls Playground + +This VSCode extension aims to serve two purposes. + +- Provide an environment in which you can easily experiment with the pygls framework by trying some of our example servers - or by writing your own + +- Provide a minimal example of what it takes to integrate a pygls powered language server into VSCode. + +For an example of a more complete VSCode client, including details on how to bundle your Python code with the VSCode extension itself you may also be interested in Microsoft's [template extension for Python tools](https://github.com/microsoft/vscode-python-tools-extension-template). + +## Setup + +### Install Server Dependencies + +Open a terminal in the repository's root directory + +1. Create a virtual environment + ``` + python -m venv env + ``` + +1. Install pygls + ``` + python -m pip install -e . + ``` + +### Install Client Dependencies + +Open terminal in the same directory as this file and execute following commands: + +1. Install node dependencies + + ``` + npm install + ``` +1. Compile the extension + + ``` + npm run compile + ``` + Alternatively you can run `npm run watch` if you are going to be actively working on the extension itself. + +### Run Extension + +1. Open this directory in VS Code +1. Open debug view (`ctrl + shift + D`) +1. Select `Launch Client` and press `F5` diff --git a/examples/json-vscode-extension/package-lock.json b/examples/vscode-playground/package-lock.json similarity index 93% rename from examples/json-vscode-extension/package-lock.json rename to examples/vscode-playground/package-lock.json index ba48b084..a7017226 100644 --- a/examples/json-vscode-extension/package-lock.json +++ b/examples/vscode-playground/package-lock.json @@ -1,26 +1,30 @@ { - "name": "json-extension", - "version": "0.11.3", + "name": "pygls-playground", + "version": "1.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "json-extension", - "version": "0.11.3", + "name": "pygls-playground", + "version": "1.0.2", "license": "Apache-2.0", "dependencies": { - "vscode-languageclient": "^7.0.0" + "@vscode/python-extension": "^1.0.4", + "semver": "^7.5.4", + "vscode-languageclient": "^8.1.0" }, "devDependencies": { "@types/node": "^16.11.6", - "@types/vscode": "^1.62.0", + "@types/semver": "^7.5.0", + "@types/vscode": "^1.78.0", "@typescript-eslint/eslint-plugin": "^5.3.0", "@typescript-eslint/parser": "^5.3.0", "eslint": "^8.2.0", - "typescript": "^4.4.4" + "typescript": "^5.1.0" }, "engines": { - "vscode": "^1.62.0" + "node": ">=16.17.1", + "vscode": "^1.78.0" } }, "node_modules/@eslint/eslintrc": { @@ -119,10 +123,16 @@ "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==", "dev": true }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, "node_modules/@types/vscode": { - "version": "1.62.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.62.0.tgz", - "integrity": "sha512-iGlQJ1w5e3qPUryroO6v4lxg3ql1ztdTCwQW3xEwFawdyPLoeUSv48SYfMwc7kQA7h6ThUqflZIjgKAykeF9oA==", + "version": "1.79.1", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.79.1.tgz", + "integrity": "sha512-Ikwc4YbHABzqthrWfeAvItaAIfX9mdjMWxqNgTpGjhgOu0TMRq9LzyZ2yBK0JhYqoSjEubEPawf6zJgnl6Egtw==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { @@ -282,6 +292,15 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@vscode/python-extension": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@vscode/python-extension/-/python-extension-1.0.4.tgz", + "integrity": "sha512-+m9VOUqv5TXZD52Ad8FjGbYGch7VqLAIys3NyVhgU6eSxmXVcRgqeon5ee224tOkTGtRQHdH5kDCa1Va/6LwjQ==", + "engines": { + "node": ">=16.17.1", + "vscode": "^1.78.0" + } + }, "node_modules/acorn": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", @@ -376,6 +395,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -439,7 +459,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", @@ -1087,6 +1108,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1300,9 +1322,9 @@ } }, "node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -1443,16 +1465,16 @@ } }, "node_modules/typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/uri-js": { @@ -1471,39 +1493,58 @@ "dev": true }, "node_modules/vscode-jsonrpc": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", - "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz", + "integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==", "engines": { - "node": ">=8.0.0 || >=10.0.0" + "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz", - "integrity": "sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.1.0.tgz", + "integrity": "sha512-GL4QdbYUF/XxQlAsvYWZRV3V34kOkpRlvV60/72ghHfsYFnS/v2MANZ9P6sHmxFcZKOse8O+L9G7Czg0NUWing==", "dependencies": { - "minimatch": "^3.0.4", - "semver": "^7.3.4", - "vscode-languageserver-protocol": "3.16.0" + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.3" }, "engines": { - "vscode": "^1.52.0" + "vscode": "^1.67.0" + } + }, + "node_modules/vscode-languageclient/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/vscode-languageclient/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, "node_modules/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==", + "version": "3.17.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz", + "integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==", "dependencies": { - "vscode-jsonrpc": "6.0.0", - "vscode-languageserver-types": "3.16.0" + "vscode-jsonrpc": "8.1.0", + "vscode-languageserver-types": "3.17.3" } }, "node_modules/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==" + "version": "3.17.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz", + "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" }, "node_modules/which": { "version": "2.0.2", @@ -1622,10 +1663,16 @@ "integrity": "sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==", "dev": true }, + "@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, "@types/vscode": { - "version": "1.62.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.62.0.tgz", - "integrity": "sha512-iGlQJ1w5e3qPUryroO6v4lxg3ql1ztdTCwQW3xEwFawdyPLoeUSv48SYfMwc7kQA7h6ThUqflZIjgKAykeF9oA==", + "version": "1.79.1", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.79.1.tgz", + "integrity": "sha512-Ikwc4YbHABzqthrWfeAvItaAIfX9mdjMWxqNgTpGjhgOu0TMRq9LzyZ2yBK0JhYqoSjEubEPawf6zJgnl6Egtw==", "dev": true }, "@typescript-eslint/eslint-plugin": { @@ -1711,6 +1758,11 @@ "eslint-visitor-keys": "^3.0.0" } }, + "@vscode/python-extension": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@vscode/python-extension/-/python-extension-1.0.4.tgz", + "integrity": "sha512-+m9VOUqv5TXZD52Ad8FjGbYGch7VqLAIys3NyVhgU6eSxmXVcRgqeon5ee224tOkTGtRQHdH5kDCa1Va/6LwjQ==" + }, "acorn": { "version": "8.5.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", @@ -1778,6 +1830,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1826,7 +1879,8 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "cross-spawn": { "version": "7.0.3", @@ -2326,6 +2380,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2459,9 +2514,9 @@ } }, "semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -2557,9 +2612,9 @@ "dev": true }, "typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true }, "uri-js": { @@ -2578,33 +2633,51 @@ "dev": true }, "vscode-jsonrpc": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", - "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==" + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz", + "integrity": "sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==" }, "vscode-languageclient": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz", - "integrity": "sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.1.0.tgz", + "integrity": "sha512-GL4QdbYUF/XxQlAsvYWZRV3V34kOkpRlvV60/72ghHfsYFnS/v2MANZ9P6sHmxFcZKOse8O+L9G7Czg0NUWing==", "requires": { - "minimatch": "^3.0.4", - "semver": "^7.3.4", - "vscode-languageserver-protocol": "3.16.0" + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.3" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "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==", + "version": "3.17.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.3.tgz", + "integrity": "sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==", "requires": { - "vscode-jsonrpc": "6.0.0", - "vscode-languageserver-types": "3.16.0" + "vscode-jsonrpc": "8.1.0", + "vscode-languageserver-types": "3.17.3" } }, "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==" + "version": "3.17.3", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz", + "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" }, "which": { "version": "2.0.2", diff --git a/examples/vscode-playground/package.json b/examples/vscode-playground/package.json new file mode 100644 index 00000000..31d75f5a --- /dev/null +++ b/examples/vscode-playground/package.json @@ -0,0 +1,111 @@ +{ + "name": "pygls-playground", + "description": "Extension for experimenting with pygls powered language servers", + "author": "Open Law Library", + "repository": "https://github.com/openlawlibrary/pygls", + "license": "Apache-2.0", + "version": "1.0.2", + "publisher": "openlawlibrary", + "engines": { + "node": ">=16.17.1", + "vscode": "^1.78.0" + }, + "extensionDependencies": [ + "ms-python.python" + ], + "categories": [ + "Programming Languages" + ], + "activationEvents": [ + "onStartupFinished" + ], + "contributes": { + "commands": [ + { + "command": "pygls.server.restart", + "title": "Restart Language Server", + "category": "pygls" + }, + { + "command": "pygls.server.executeCommand", + "title": "Execute Command", + "category": "pygls" + } + ], + "configuration": [ + { + "type": "object", + "title": "Server Configuration", + "properties": { + "pygls.server.cwd": { + "scope": "window", + "type": "string", + "description": "The working directory from which to launch the server.", + "markdownDescription": "The working directory from which to launch the server.\nIf blank, this will default to the `examples/servers` directory." + }, + "pygls.server.launchScript": { + "scope": "window", + "type": "string", + "default": "json_server.py", + "description": "The python script to run when launching the server." + }, + "pygls.trace.server": { + "scope": "window", + "type": "string", + "default": "off", + "enum": [ + "off", + "verbose" + ], + "description": "Controls if LSP messages send to/from the server should be logged.", + "enumDescriptions": [ + "do not log any lsp messages", + "log all lsp messages sent to/from the server" + ] + } + } + }, + { + "type": "object", + "title": "Client Configuration", + "properties": { + "pygls.client.documentSelector": { + "scope": "window", + "type": "array", + "items": { + "type": "object" + }, + "default": [ + { + "scheme": "file", + "language": "json" + } + ], + "description": "The client uses this to decide which documents the server is able to help with.", + "markdownDescription": "The client uses this to decide which documents the server is able to help with.\n See [DocumentSelector](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#documentFilter) in the LSP Specification for more details." + } + } + } + ] + }, + "main": "./out/extension", + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -p .", + "watch": "tsc -p . -w" + }, + "devDependencies": { + "@types/node": "^16.11.6", + "@types/semver": "^7.5.0", + "@types/vscode": "^1.78.0", + "@typescript-eslint/eslint-plugin": "^5.3.0", + "@typescript-eslint/parser": "^5.3.0", + "eslint": "^8.2.0", + "typescript": "^5.1.0" + }, + "dependencies": { + "@vscode/python-extension": "^1.0.4", + "semver": "^7.5.4", + "vscode-languageclient": "^8.1.0" + } +} diff --git a/examples/vscode-playground/src/extension.ts b/examples/vscode-playground/src/extension.ts new file mode 100644 index 00000000..b8a6abe1 --- /dev/null +++ b/examples/vscode-playground/src/extension.ts @@ -0,0 +1,300 @@ +/* ------------------------------------------------------------------------- + * Original work Copyright (c) Microsoft Corporation. All rights reserved. + * Original work licensed under the MIT License. + * See ThirdPartyNotices.txt in the project root for license information. + * All modifications Copyright (c) Open Law Library. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http: // www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ----------------------------------------------------------------------- */ +"use strict"; + +import * as net from "net"; +import * as path from "path"; +import * as vscode from "vscode"; +import * as semver from "semver"; + +import { PythonExtension } from "@vscode/python-extension"; +import { LanguageClient, LanguageClientOptions, ServerOptions, State } from "vscode-languageclient/node"; + +const MIN_PYTHON = semver.parse("3.7.9") + +// Some other nice to haves. +// TODO: Check selected env satisfies pygls' requirements - if not offer to run the select env command. +// TODO: Inspect ServerCapabilities and present a quick pick list of runnable commands. +// TODO: Start a debug session for the currently configured server. +// TODO: TCP Transport +// TODO: WS Transport +// TODO: Web Extension support (requires WASM-WASI!) + +let client: LanguageClient; +let clientStarting = false +let python: PythonExtension; +let logger: vscode.LogOutputChannel + +/** + * This is the main entry point. + * Called when vscode first activates the extension + */ +export async function activate(context: vscode.ExtensionContext) { + logger = vscode.window.createOutputChannel('pygls', { log: true }) + logger.info("Extension activated.") + + await getPythonExtension(); + if (!python) { + return + } + + // Restart language server command + context.subscriptions.push( + vscode.commands.registerCommand("pygls.server.restart", async () => { + logger.info('restarting server...') + await startLangServer() + }) + ) + + // Restart the language server if the user switches Python envs... + context.subscriptions.push( + python.environments.onDidChangeActiveEnvironmentPath(async () => { + logger.info('python env modified, restarting server...') + await startLangServer() + }) + ) + + // ... or if they change a relevant config option + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration(async (event) => { + if (event.affectsConfiguration("pygls.server") || event.affectsConfiguration("pygls.client")) { + logger.info('config modified, restarting server...') + await startLangServer() + } + }) + ) + + // Start the language server once the user opens the first text document... + context.subscriptions.push( + vscode.workspace.onDidOpenTextDocument( + async () => { + if (!client) { + await startLangServer() + } + } + ) + ) + + // ...or notebook. + context.subscriptions.push( + vscode.workspace.onDidOpenNotebookDocument( + async () => { + if (!client) { + await startLangServer() + } + } + ) + ) + + // Restart the server if the user modifies it. + context.subscriptions.push( + vscode.workspace.onDidSaveTextDocument(async (document: vscode.TextDocument) => { + const expectedUri = vscode.Uri.file(path.join(getCwd(), getServerPath())) + + if (expectedUri.toString() === document.uri.toString()) { + logger.info('server modified, restarting...') + await startLangServer() + } + }) + ) +} + +export function deactivate(): Thenable { + return stopLangServer() +} + +/** + * Start (or restart) the language server. + * + * @param command The executable to run + * @param args Arguments to pass to the executable + * @param cwd The working directory in which to run the executable + * @returns + */ +async function startLangServer() { + + // Don't interfere if we are already in the process of launching the server. + if (clientStarting) { + return + } + + clientStarting = true + if (client) { + await stopLangServer() + } + + const pythonPath = await getPythonPath() + if (!pythonPath) { + clientStarting = false + return + } + + const cwd = getCwd() + const serverPath = getServerPath() + + logger.info(`cwd: '${cwd}'`) + logger.info(`server: '${serverPath}'`) + + const serverOptions: ServerOptions = { + command: pythonPath, + args: [serverPath], + options: { cwd }, + }; + + client = new LanguageClient('pygls', serverOptions, getClientOptions()); + try { + await client.start() + clientStarting = false + } catch (err) { + clientStarting = false + logger.error(`Unable to start server: ${err}`) + } +} + +async function stopLangServer(): Promise { + if (!client) { + return + } + + if (client.state === State.Running) { + await client.stop() + } + + client.dispose() + client = undefined +} + +function getClientOptions(): LanguageClientOptions { + const config = vscode.workspace.getConfiguration('pygls.client') + const options = { + documentSelector: config.get('documentSelector'), + outputChannel: logger, + connectionOptions: { + maxRestartCount: 0 // don't restart on server failure. + }, + }; + logger.info(`client options: ${JSON.stringify(options, undefined, 2)}`) + return options +} + +function startLangServerTCP(addr: number): LanguageClient { + const serverOptions: ServerOptions = () => { + return new Promise((resolve /*, reject */) => { + const clientSocket = new net.Socket(); + clientSocket.connect(addr, "127.0.0.1", () => { + resolve({ + reader: clientSocket, + writer: clientSocket, + }); + }); + }); + }; + + return new LanguageClient( + `tcp lang server (port ${addr})`, + serverOptions, + getClientOptions() + ); +} + +/** + * If the user has explicitly provided a src directory use that. + * Otherwise, fallback to the examples/servers directory. + * + * @returns The working directory from which to launch the server + */ +function getCwd(): string { + const config = vscode.workspace.getConfiguration("pygls.server") + const cwd = config.get('cwd') + if (cwd) { + return cwd + } + + const serverDir = path.resolve( + path.join(__dirname, "..", "..", "servers") + ) + return serverDir +} + +/** + * + * @returns The python script to launch the server with + */ +function getServerPath(): string { + const config = vscode.workspace.getConfiguration("pygls.server") + const server = config.get('launchScript') + return server +} + +/** + * This uses the official python extension to grab the user's currently + * configured environment. + * + * @returns The python interpreter to use to launch the server + */ +async function getPythonPath(): Promise { + if (!python) { + return + } + + // Use whichever python interpreter the user has configured. + const activeEnvPath = python.environments.getActiveEnvironmentPath() + logger.info(`Using environment: ${activeEnvPath.id}: ${activeEnvPath.path}`) + + const activeEnv = await python.environments.resolveEnvironment(activeEnvPath) + if (!activeEnv) { + logger.error(`Unable to resolve envrionment: ${activeEnvPath}`) + return + } + + const v = activeEnv.version + const pythonVersion = semver.parse(`${v.major}.${v.minor}.${v.micro}`) + + // Check to see if the environment satisfies the min Python version. + if (semver.lt(pythonVersion, MIN_PYTHON)) { + const message = [ + `Your currently configured environment provides Python v${pythonVersion} `, + `but pygls requires v${MIN_PYTHON}.\n\nPlease choose another environment.` + ].join('') + + const response = await vscode.window.showErrorMessage(message, "Change Environment") + if (!response) { + return + } else { + await vscode.commands.executeCommand('python.setInterpreter') + return + } + } + + const pythonUri = activeEnv.executable.uri + if (!pythonUri) { + logger.error(`URI of Python executable is undefined!`) + return + } + + return pythonUri.fsPath +} + +async function getPythonExtension() { + try { + python = await PythonExtension.api(); + } catch (err) { + logger.error(`Unable to load python extension: ${err}`) + } +} diff --git a/examples/vscode-playground/tsconfig.json b/examples/vscode-playground/tsconfig.json new file mode 100644 index 00000000..5d7a3218 --- /dev/null +++ b/examples/vscode-playground/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2019", + "lib": [ + "ES2019" + ], + "rootDir": "src", + "outDir": "out", + "sourceMap": true + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/examples/workspace/test.json b/examples/workspace/test.json new file mode 100644 index 00000000..21da3b26 --- /dev/null +++ b/examples/workspace/test.json @@ -0,0 +1,3 @@ +{ + "key": "value" +}