diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 330a23f33..d83653e2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,6 +57,7 @@ jobs: uses: actions/setup-node@v3 with: node-version: 16 + registry-url: 'https://registry.npmjs.org' - run: npm ci - run: opam install dune cppo @@ -211,3 +212,10 @@ jobs: - name: Publish extension as release if: startsWith(github.ref, 'refs/tags/') run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} ${{ steps.tag_name.outputs.tag }} --no-git-tag-version + + - name: Publish LSP to NPM + if: startsWith(github.ref, 'refs/tags/') + working-directory: server + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9bbc16c1e..b13cdac66 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,8 @@ Thanks for your interest. Below is an informal spec of how the plugin's server c ├── package.json // The extension manifest └── server // Language Server. Usable standalone ├── src - │ └── server.ts // Language Server entry point + │ ├── server.ts // Language Server Module + │ ├── cli.ts // LSP CLI └── analysis_binaries // Prod-time platform-specific analysis binaries ├── darwin ├── linux diff --git a/client/src/extension.ts b/client/src/extension.ts index f8acdb1ba..1ef2e2bca 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -16,7 +16,8 @@ import { LanguageClientOptions, ServerOptions, State, - TransportKind, + Executable, + TransportKind } from "vscode-languageclient/node"; import * as customCommands from "./commands"; @@ -81,7 +82,7 @@ export function activate(context: ExtensionContext) { function createLanguageClient() { // The server is implemented in node let serverModule = context.asAbsolutePath( - path.join("server", "out", "server.js") + path.join("server", "out", "cli.js") ); // The debug options for the server // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging @@ -90,9 +91,10 @@ export function activate(context: ExtensionContext) { // 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 }, + run: { module: serverModule, args: ["--node-ipc"], transport: TransportKind.ipc }, debug: { module: serverModule, + args: ["--node-ipc"], transport: TransportKind.ipc, options: debugOptions, }, diff --git a/server/README.md b/server/README.md new file mode 100644 index 000000000..d144a58f9 --- /dev/null +++ b/server/README.md @@ -0,0 +1,26 @@ +# ReScript Language Server + +## Install + +```sh +npm install -g @rescript/lsp +``` + +## Run + +```sh +resciptls --stdio +``` + +```sh +ReScript Language Server + +Usage: rescriptls [options] + +Options: + +--stdio Use stdio +--node-ipc Use node-ipc +-v, --version Print version +-h, --help Print help +``` diff --git a/server/package-lock.json b/server/package-lock.json index 1e079137f..dc3d20848 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -1,11 +1,11 @@ { - "name": "rescript-language-server", + "name": "@rescript/lsp", "version": "1.20.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "rescript-language-server", + "name": "@rescript/lsp", "version": "1.20.0", "license": "MIT", "dependencies": { @@ -14,6 +14,9 @@ "vscode-languageserver": "^8.0.1", "vscode-languageserver-protocol": "^3.17.1" }, + "bin": { + "rescriptls": "rescriptls" + }, "engines": { "node": "*" } diff --git a/server/package.json b/server/package.json index 0be633571..fbfb20ccb 100644 --- a/server/package.json +++ b/server/package.json @@ -1,21 +1,38 @@ { - "name": "rescript-language-server", - "description": "ReScript's language-server", + "name": "@rescript/lsp", + "description": "LSP server for ReScript", "version": "1.20.0", "author": "chenglou", "license": "MIT", + "bin": { + "rescriptls": "./out/cli.js" + }, + "keywords": [ + "ReScript", + "LSP", + "Language Server" + ], + "files": [ + "out/*", + "analysis_binaries", + "README.md" + ], "engines": { "node": "*" }, + "homepage": "https://github.com/rescript-lang/rescript-vscode/server/README.md", "repository": { "type": "git", - "url": "https://github.com/rescript-lang/rescript-vscode" + "url": "https://github.com/rescript-lang/rescript-vscode", + "directory": "server" + }, + "bugs": { + "url": "https://github.com/rescript-lang/rescript-vscode/issues" }, "dependencies": { "chokidar": "^3.5.1", "vscode-jsonrpc": "^8.0.1", "vscode-languageserver": "^8.0.1", "vscode-languageserver-protocol": "^3.17.1" - }, - "scripts": {} + } } diff --git a/server/src/cli.ts b/server/src/cli.ts new file mode 100644 index 000000000..d67f72a78 --- /dev/null +++ b/server/src/cli.ts @@ -0,0 +1,36 @@ +#!/usr/bin/env node +import fs from "fs"; +import server from "./server"; + +const args = process.argv.slice(2) + +const help = `ReScript Language Server + +Usage: rescriptls [options] + +Options: + +--stdio Use stdio +--node-ipc Use node-ipc +-v, --version Print version +-h, --help Print help`; + +(() => { + switch (args[0]) { + case '--stdio': + return server(true); + case '--node-ipc': + return server(false); + case '--version': + case '-v': + console.log(JSON.parse(fs.readFileSync('./package.json', { encoding: 'utf8' })).version); + process.exit(0); + case '--help': + case '-h': + console.log(help); + process.exit(0); + default: + console.log(help); + process.exit(1) + } +})(); diff --git a/server/src/server.ts b/server/src/server.ts index ca4564902..3e36c990a 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -400,20 +400,22 @@ let getOpenedFileContent = (fileUri: string) => { return content; }; -// Start listening now! -// We support two modes: the regular node RPC mode for VSCode, and the --stdio -// mode for other editors The latter is _technically unsupported_. It's an -// implementation detail that might change at any time -if (process.argv.includes("--stdio")) { - let writer = new rpc.StreamMessageWriter(process.stdout); - let reader = new rpc.StreamMessageReader(process.stdin); - // proper `this` scope for writer - send = (msg: p.Message) => writer.write(msg); - reader.listen(onMessage); -} else { - // proper `this` scope for process - send = (msg: p.Message) => process.send!(msg); - process.on("message", onMessage); +export default function listen(useStdio = false) { + // Start listening now! + // We support two modes: the regular node RPC mode for VSCode, and the --stdio + // mode for other editors The latter is _technically unsupported_. It's an + // implementation detail that might change at any time + if (useStdio) { + let writer = new rpc.StreamMessageWriter(process.stdout); + let reader = new rpc.StreamMessageReader(process.stdin); + // proper `this` scope for writer + send = (msg: p.Message) => writer.write(msg); + reader.listen(onMessage); + } else { + // proper `this` scope for process + send = (msg: p.Message) => process.send!(msg); + process.on("message", onMessage); + } } function hover(msg: p.RequestMessage) {