-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Implement LSP #16
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cardoso , thanks for your contribution! I will review this more throughly soon. I feel some tests are needed in this PR. Ideally we need both unit tests and some integration tests. Could you create some unit tests in the LSP rust code, please? After this is merged, I will work on the integration tests.
Thanks @eduardomourar - I'll get started on the tests Some updates: Ultimately I decided to fork wit-parser so I could make some items public for more advanced features than linting such as semantic tokens, contextual hovering, etc. This code would more appropriately live in the wasm-tools repository alongside wit-parser. My current thinking is for this to become a binary crate "wit-lsp" in wasm-tools with some of the logic pushed to the wit-parser crate under an "lsp" feature flag. This isn't blocking as I can keep using the fork for now, but I think moving to that approach would make the LSP more robust and easier to keep up-to-date. Also, keeping it separate from the VSCode extension would make it easier to port for other editors. I can make that move if it makes sense. |
@cardoso , let's keep the LSP code here for now in order to iterate more quickly (specially because I don't have permissions to the wasm-tools repository). To simplify that migration later on, we need to move all LSP related files to a folder called |
"build:lsp:debug": "cargo build", | ||
"build:lsp:release": "cargo build --release", | ||
"build:ext:debug": "esbuild ./extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node", | ||
"build:ext:release": "esbuild ./extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --minify", | ||
"build:debug": "npm run build:lsp:debug && npm run build:ext:debug", | ||
"build:release": "npm run build:lsp:release && npm run build:ext:release", | ||
"build": "npm run build:debug", | ||
"package:lsp:debug": "cp ./target/debug/wit-lsp ./out/wit-lsp", | ||
"package:lsp:release": "cp ./target/release/wit-lsp ./out/wit-lsp", | ||
"package:ext:debug": "vsce package -o wit-idl.vsix", | ||
"package:ext:release": "vsce package -o wit-idl.vsix", | ||
"package:debug": "npm run package:lsp:debug && npm run package:ext:debug", | ||
"package:release": "npm run package:lsp:release && npm run package:ext:release", | ||
"package": "npm run package:debug", | ||
"install:insiders": "code-insiders --install-extension wit-idl.vsix", | ||
"install:stable": "code --install-extension wit-idl.vsix", | ||
"install": "npm run install:stable || npm run install:insiders", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After the move to the lsp folder:
"build:lsp:debug": "cargo build", | |
"build:lsp:release": "cargo build --release", | |
"build:ext:debug": "esbuild ./extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node", | |
"build:ext:release": "esbuild ./extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --minify", | |
"build:debug": "npm run build:lsp:debug && npm run build:ext:debug", | |
"build:release": "npm run build:lsp:release && npm run build:ext:release", | |
"build": "npm run build:debug", | |
"package:lsp:debug": "cp ./target/debug/wit-lsp ./out/wit-lsp", | |
"package:lsp:release": "cp ./target/release/wit-lsp ./out/wit-lsp", | |
"package:ext:debug": "vsce package -o wit-idl.vsix", | |
"package:ext:release": "vsce package -o wit-idl.vsix", | |
"package:debug": "npm run package:lsp:debug && npm run package:ext:debug", | |
"package:release": "npm run package:lsp:release && npm run package:ext:release", | |
"package": "npm run package:debug", | |
"install:insiders": "code-insiders --install-extension wit-idl.vsix", | |
"install:stable": "code --install-extension wit-idl.vsix", | |
"install": "npm run install:stable || npm run install:insiders", | |
"build:lsp:debug": "cargo build --manifest-path=lsp/Cargo.toml", | |
"build:lsp:release": "cargo build --manifest-path=lsp/Cargo.toml --release", | |
"build:ext:debug": "esbuild ./extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node", | |
"build:ext:release": "esbuild ./extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node --minify", | |
"build:debug": "npm run build:lsp:debug && npm run build:ext:debug", | |
"build:release": "npm run build:lsp:release && npm run build:ext:release", | |
"build": "npm run build:debug", | |
"package:lsp:debug": "cp ./lsp/target/debug/wit-lsp ./out/wit-lsp", | |
"package:lsp:release": "cp ./lsp/target/release/wit-lsp ./out/wit-lsp", | |
"package:ext:debug": "vsce package -o wit-idl.vsix", | |
"package:ext:release": "vsce package -o wit-idl.vsix", | |
"package:debug": "npm run package:lsp:debug && npm run package:ext:debug", | |
"package:release": "npm run package:lsp:release && npm run package:ext:release", | |
"package": "npm run package:debug", | |
"install:plugin:insiders": "code-insiders --install-extension wit-idl.vsix", | |
"install:plugin:stable": "code --install-extension wit-idl.vsix", | |
"install:plugin": "npm run install:plugin:stable", |
"dependencies": { | ||
"vscode-languageclient": "8.1.0" | ||
}, | ||
"devDependencies": { | ||
"@vscode/vsce": "^2.19.0", | ||
"vscode-tmgrammar-test": "^0.1.1" | ||
"@types/node": "20.3.0", | ||
"@types/vscode": "1.79.0", | ||
"@typescript-eslint/eslint-plugin": "5.59.9", | ||
"@typescript-eslint/parser": "5.59.9", | ||
"@vscode/vsce": "2.19.0", | ||
"esbuild": "0.18.0", | ||
"eslint": "8.42.0", | ||
"eslint-config-xo": "0.43.1", | ||
"eslint-config-xo-typescript": "0.57.0", | ||
"tmlanguage-generator": "0.4.1", | ||
"typescript": "5.1.3", | ||
"vscode-tmgrammar-test": "0.1.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep all of these with a caret (^
) to allow security patches to go through. That is until we have dependabot enabled and fully automated release process.
target/** | ||
node_modules/** | ||
|
||
commitlint.config.js | ||
|
||
Cargo.lock | ||
Cargo.toml | ||
.DS_Store |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
target/** | |
node_modules/** | |
commitlint.config.js | |
Cargo.lock | |
Cargo.toml | |
.DS_Store | |
node_modules/** | |
commitlint.config.js | |
lsp/** | |
.DS_Store |
@@ -1,2 +1,2 @@ | |||
[*] | |||
indent_size = 2 | |||
indent_size = 4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we really need to change the indentation?
lazy_static = "1" | ||
wit-parser = { git = "https://github.com/cardoso/wasm-tools", branch = "lsp" } | ||
ropey = { version = "1.6" } | ||
pulldown-cmark = "0.9" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the logger can be used add debug information specially in this exploratory phase
pulldown-cmark = "0.9" | |
pulldown-cmark = "0.9" | |
log = "0.4" | |
env_logger = "0.10" |
use tower_lsp::{LspService, Server}; | ||
use wit_lsp::WitLsp; | ||
|
||
async fn start() { | ||
let stdin = tokio::io::stdin(); | ||
let stdout = tokio::io::stdout(); | ||
let (service, socket) = LspService::new(WitLsp::new); | ||
let server = Server::new(stdin, stdout, socket); | ||
server.serve(service).await; | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
start().await; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use tower_lsp::{LspService, Server}; | |
use wit_lsp::WitLsp; | |
async fn start() { | |
let stdin = tokio::io::stdin(); | |
let stdout = tokio::io::stdout(); | |
let (service, socket) = LspService::new(WitLsp::new); | |
let server = Server::new(stdin, stdout, socket); | |
server.serve(service).await; | |
} | |
#[tokio::main] | |
async fn main() { | |
start().await; | |
} | |
#[macro_use] | |
extern crate log; | |
use tower_lsp::{LspService, Server}; | |
use wit_lsp::WitLsp; | |
async fn start() { | |
let stdin = tokio::io::stdin(); | |
let stdout = tokio::io::stdout(); | |
let (service, socket) = LspService::new(WitLsp::new); | |
let server = Server::new(stdin, stdout, socket); | |
server.serve(service).await; | |
} | |
#[tokio::main] | |
async fn main() { | |
env_logger::init(); | |
debug!("starting language server up..."); | |
start().await; | |
} |
import {workspace, type ExtensionContext} from 'vscode'; | ||
import {LanguageClient} from 'vscode-languageclient/node'; | ||
|
||
let client: LanguageClient; | ||
|
||
export function activate(context: ExtensionContext) { | ||
client = new LanguageClient( | ||
'witLanguageClient', | ||
'WIT Language Client', | ||
{ | ||
command: context.asAbsolutePath('./out/wit-lsp'), | ||
}, | ||
{ | ||
documentSelector: [{ | ||
language: 'wit', | ||
pattern: '**/*.wit', | ||
}], | ||
synchronize: { | ||
fileEvents: workspace.createFileSystemWatcher('**/*.wit'), | ||
}, | ||
}, | ||
); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
client.start(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use an environment variable, for now, to change the log level.
import {workspace, type ExtensionContext} from 'vscode'; | |
import {LanguageClient} from 'vscode-languageclient/node'; | |
let client: LanguageClient; | |
export function activate(context: ExtensionContext) { | |
client = new LanguageClient( | |
'witLanguageClient', | |
'WIT Language Client', | |
{ | |
command: context.asAbsolutePath('./out/wit-lsp'), | |
}, | |
{ | |
documentSelector: [{ | |
language: 'wit', | |
pattern: '**/*.wit', | |
}], | |
synchronize: { | |
fileEvents: workspace.createFileSystemWatcher('**/*.wit'), | |
}, | |
}, | |
); | |
// eslint-disable-next-line @typescript-eslint/no-floating-promises | |
client.start(); | |
} | |
import {window, workspace, type ExtensionContext} from 'vscode'; | |
import {LanguageClient} from 'vscode-languageclient/node'; | |
let client: LanguageClient; | |
export async function activate(context: ExtensionContext): Promise<void> { | |
const traceOutputChannel = window.createOutputChannel('WIT Language Server trace'); | |
client = new LanguageClient( | |
'witLanguageClient', | |
'WIT Language Client', | |
{ | |
command: context.asAbsolutePath('./out/wit-lsp'), | |
options: { | |
env: { | |
...process.env, | |
// eslint-disable-next-line @typescript-eslint/naming-convention | |
RUST_LOG: 'debug', | |
}, | |
}, | |
}, | |
{ | |
documentSelector: [{ | |
scheme: 'file', | |
language: 'wit', | |
}], | |
synchronize: { | |
fileEvents: workspace.createFileSystemWatcher('**/*.wit'), | |
}, | |
traceOutputChannel, | |
}, | |
); | |
await client.start(); | |
} |
semantic_tokens_provider: Some(SemanticTokensServerCapabilities::SemanticTokensOptions( | ||
SemanticTokensOptions { | ||
work_done_progress_options: Default::default(), | ||
legend: wit::token::legend(), | ||
full: Some(SemanticTokensFullOptions::Delta { delta: Some(false) }), | ||
range: None, | ||
}, | ||
)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
by adding the semantic tokens provider here, the TextMate scopes are being overwritten. consequently, the highlighting is different before and after the LSP kicks in. while keeping the aesthetic as close as possible to the current setup, the scopes within syntaxes/wit.tmLanguage.json
have to be modified to match this expected mapping table or we have to define a custom mapping.
Closes #3
Super WIP, but thought it might be good to have some initial feedback on the approach.
For now, https://github.com/bytecodealliance/wasm-tools is used, but I'm thinking about adding support for https://github.com/bytecodealliance/cargo-component since currently it has its own logic for resolving wit packages, which seems to have some advantages.
To test it out, have a recent version of wasm-tools installed, run
npm install
,npm start
& reload the vscode window.Preview
Diagnostics
Hover
TODO