diff --git a/docs/dev/debugging.md b/docs/dev/debugging.md index 1ccf4dca2445..e6b0821564df 100644 --- a/docs/dev/debugging.md +++ b/docs/dev/debugging.md @@ -1,44 +1,66 @@ -# Debugging vs Code plugin and the Language Server +# Debugging VSCode plugin and the language server -**NOTE:** the information here is mostly obsolete +## Prerequisites -Install [LLDB](https://lldb.llvm.org/) and the [LLDB Extension](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb). +- Install [LLDB](https://lldb.llvm.org/) and the [LLDB Extension](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb). +- Open the root folder in VSCode. Here you can access the preconfigured debug setups. -Checkout rust rust-analyzer and open it in vscode. + Debug options view + +- Install all TypeScript dependencies + ```bash + cd editors/code + npm install + ``` + +## Common knowledge + +* All debug configurations open a new `[Extension Development Host]` VSCode instance +where **only** the `rust-analyzer` extension being debugged is enabled. +* To activate the extension you need to open any Rust project folder in `[Extension Development Host]`. + + +## Debug TypeScript VSCode extension + +- `Run Extension` - runs the extension with the globally installed `ra_lsp_server` binary. +- `Run Extension (Dev Server)` - runs extension with the locally built LSP server (`target/debug/ra_lsp_server`). + +TypeScript debugging is configured to watch your source edits and recompile. +To apply changes to an already running debug process press Ctrl+Shift+P and run the following command in your `[Extension Development Host]` ``` -$ git clone https://github.com/rust-analyzer/rust-analyzer.git --depth 1 -$ cd rust-analyzer -$ code . +> Developer: Reload Window ``` -- To attach to the `lsp server` in linux you'll have to run: +## Debug Rust LSP server - `echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope` +- When attaching a debugger to an already running `rust-analyzer` server on Linux you might need to enable `ptrace` for unrelated processes by running: - This enables ptrace on non forked processes + ``` + echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope + ``` -- Ensure the dependencies for the extension are installed, run the `npm: install - editors/code` task in vscode. -- Launch the `Debug Extension`, this will build the extension and the `lsp server`. +- By default, the LSP server is built without debug information. To enable it, you'll need to change `Cargo.toml`: + ```toml + [profile.dev] + debug = 2 + ``` -- A new instance of vscode with `[Extension Development Host]` in the title. +- Select `Run Extension (Dev Server)` to run your locally built `target/debug/ra_lsp_server`. - Don't worry about disabling `rls` all other extensions will be disabled but this one. +- In the original VSCode window once again select the `Attach To Server` debug configuration. -- In the new vscode instance open a rust project, and navigate to a rust file - -- In the original vscode start an additional debug session (the three periods in the launch) and select `Debug Lsp Server`. - -- A list of running processes should appear select the `ra_lsp_server` from this repo. +- A list of running processes should appear. Select the `ra_lsp_server` from this repo. - Navigate to `crates/ra_lsp_server/src/main_loop.rs` and add a breakpoint to the `on_task` function. -- Go back to the `[Extension Development Host]` instance and hover over a rust variable and your breakpoint should hit. +- Go back to the `[Extension Development Host]` instance and hover over a Rust variable and your breakpoint should hit. ## Demo -![demonstration of debugging](https://user-images.githubusercontent.com/1711539/51384036-254fab80-1b2c-11e9-824d-95f9a6e9cf4f.gif) +- [Debugging TypeScript VScode extension](https://www.youtube.com/watch?v=T-hvpK6s4wM). +- [Debugging Rust LSP server](https://www.youtube.com/watch?v=EaNb5rg4E0M). ## Troubleshooting diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 70cb0a612a07..53e2a414b05e 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -44,7 +44,6 @@ export class Config { this.refreshConfig(); } - private refreshConfig() { this.cfg = vscode.workspace.getConfiguration(Config.rootSection); console.log("Using configuration:", this.cfg); diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 9fcf2ec3826e..ff6245f78990 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -91,15 +91,11 @@ export async function sendRequestWithRetry( for (const delay of [2, 4, 6, 8, 10, null]) { try { return await (token ? client.sendRequest(method, param, token) : client.sendRequest(method, param)); - } catch (e) { - if ( - e.code === lc.ErrorCodes.ContentModified && - delay !== null - ) { - await sleep(10 * (1 << delay)); - continue; + } catch (err) { + if (delay === null || err.code !== lc.ErrorCodes.ContentModified) { + throw err; } - throw e; + await sleep(10 * (1 << delay)); } } throw 'unreachable'; diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index c317a9213e92..3896878cda52 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts @@ -27,7 +27,9 @@ export function activateInlayHints(ctx: Ctx) { ctx.subscriptions ); - ctx.onDidRestart(_ => hintsUpdater.setEnabled(ctx.config.displayInlayHints)); + // We pass async function though it will not be awaited when called, + // thus Promise rejections won't be handled, but this should never throw in fact... + ctx.onDidRestart(async _ => hintsUpdater.setEnabled(ctx.config.displayInlayHints)); } interface InlayHintsParams { @@ -36,7 +38,7 @@ interface InlayHintsParams { interface InlayHint { range: vscode.Range; - kind: string; + kind: "TypeHint" | "ParameterHint"; label: string; } @@ -53,7 +55,7 @@ const parameterHintDecorationType = vscode.window.createTextEditorDecorationType }); class HintsUpdater { - private pending: Map = new Map(); + private pending = new Map(); private ctx: Ctx; private enabled: boolean; @@ -62,30 +64,36 @@ class HintsUpdater { this.enabled = ctx.config.displayInlayHints; } - async setEnabled(enabled: boolean) { + async setEnabled(enabled: boolean): Promise { if (this.enabled == enabled) return; this.enabled = enabled; if (this.enabled) { - await this.refresh(); - } else { - this.allEditors.forEach(it => { - this.setTypeDecorations(it, []); - this.setParameterDecorations(it, []); - }); + return await this.refresh(); } + this.allEditors.forEach(it => { + this.setTypeDecorations(it, []); + this.setParameterDecorations(it, []); + }); } async refresh() { if (!this.enabled) return; - const promises = this.allEditors.map(it => this.refreshEditor(it)); - await Promise.all(promises); + await Promise.all(this.allEditors.map(it => this.refreshEditor(it))); + } + + private get allEditors(): vscode.TextEditor[] { + return vscode.window.visibleTextEditors.filter( + editor => editor.document.languageId === 'rust', + ); } private async refreshEditor(editor: vscode.TextEditor): Promise { const newHints = await this.queryHints(editor.document.uri.toString()); if (newHints == null) return; - const newTypeDecorations = newHints.filter(hint => hint.kind === 'TypeHint') + + const newTypeDecorations = newHints + .filter(hint => hint.kind === 'TypeHint') .map(hint => ({ range: hint.range, renderOptions: { @@ -96,7 +104,8 @@ class HintsUpdater { })); this.setTypeDecorations(editor, newTypeDecorations); - const newParameterDecorations = newHints.filter(hint => hint.kind === 'ParameterHint') + const newParameterDecorations = newHints + .filter(hint => hint.kind === 'ParameterHint') .map(hint => ({ range: hint.range, renderOptions: { @@ -108,12 +117,6 @@ class HintsUpdater { this.setParameterDecorations(editor, newParameterDecorations); } - private get allEditors(): vscode.TextEditor[] { - return vscode.window.visibleTextEditors.filter( - editor => editor.document.languageId === 'rust', - ); - } - private setTypeDecorations( editor: vscode.TextEditor, decorations: vscode.DecorationOptions[], @@ -137,12 +140,14 @@ class HintsUpdater { private async queryHints(documentUri: string): Promise { const client = this.ctx.client; if (!client) return null; + const request: InlayHintsParams = { textDocument: { uri: documentUri }, }; const tokenSource = new vscode.CancellationTokenSource(); - const prev = this.pending.get(documentUri); - if (prev) prev.cancel(); + const prevHintsRequest = this.pending.get(documentUri); + prevHintsRequest?.cancel(); + this.pending.set(documentUri, tokenSource); try { return await sendRequestWithRetry(