diff options
-rw-r--r-- | docs/dev/debugging.md | 64 | ||||
-rw-r--r-- | editors/code/src/config.ts | 1 | ||||
-rw-r--r-- | editors/code/src/ctx.ts | 12 | ||||
-rw-r--r-- | editors/code/src/inlay_hints.ts | 49 |
4 files changed, 74 insertions, 52 deletions
diff --git a/docs/dev/debugging.md b/docs/dev/debugging.md index 1ccf4dca2..e6b082156 100644 --- a/docs/dev/debugging.md +++ b/docs/dev/debugging.md | |||
@@ -1,44 +1,66 @@ | |||
1 | # Debugging vs Code plugin and the Language Server | 1 | # Debugging VSCode plugin and the language server |
2 | 2 | ||
3 | **NOTE:** the information here is mostly obsolete | 3 | ## Prerequisites |
4 | 4 | ||
5 | Install [LLDB](https://lldb.llvm.org/) and the [LLDB Extension](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb). | 5 | - Install [LLDB](https://lldb.llvm.org/) and the [LLDB Extension](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb). |
6 | - Open the root folder in VSCode. Here you can access the preconfigured debug setups. | ||
6 | 7 | ||
7 | Checkout rust rust-analyzer and open it in vscode. | 8 | <img height=150px src="https://user-images.githubusercontent.com/36276403/74611090-92ec5380-5101-11ea-8a41-598f51f3f3e3.png" alt="Debug options view"> |
9 | |||
10 | - Install all TypeScript dependencies | ||
11 | ```bash | ||
12 | cd editors/code | ||
13 | npm install | ||
14 | ``` | ||
15 | |||
16 | ## Common knowledge | ||
17 | |||
18 | * All debug configurations open a new `[Extension Development Host]` VSCode instance | ||
19 | where **only** the `rust-analyzer` extension being debugged is enabled. | ||
20 | * To activate the extension you need to open any Rust project folder in `[Extension Development Host]`. | ||
8 | 21 | ||
9 | ``` | ||
10 | $ git clone https://github.com/rust-analyzer/rust-analyzer.git --depth 1 | ||
11 | $ cd rust-analyzer | ||
12 | $ code . | ||
13 | ``` | ||
14 | 22 | ||
15 | - To attach to the `lsp server` in linux you'll have to run: | 23 | ## Debug TypeScript VSCode extension |
16 | 24 | ||
17 | `echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope` | 25 | - `Run Extension` - runs the extension with the globally installed `ra_lsp_server` binary. |
26 | - `Run Extension (Dev Server)` - runs extension with the locally built LSP server (`target/debug/ra_lsp_server`). | ||
27 | |||
28 | TypeScript debugging is configured to watch your source edits and recompile. | ||
29 | To apply changes to an already running debug process press <kbd>Ctrl+Shift+P</kbd> and run the following command in your `[Extension Development Host]` | ||
30 | |||
31 | ``` | ||
32 | > Developer: Reload Window | ||
33 | ``` | ||
18 | 34 | ||
19 | This enables ptrace on non forked processes | 35 | ## Debug Rust LSP server |
20 | 36 | ||
21 | - Ensure the dependencies for the extension are installed, run the `npm: install - editors/code` task in vscode. | 37 | - When attaching a debugger to an already running `rust-analyzer` server on Linux you might need to enable `ptrace` for unrelated processes by running: |
22 | 38 | ||
23 | - Launch the `Debug Extension`, this will build the extension and the `lsp server`. | 39 | ``` |
40 | echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope | ||
41 | ``` | ||
24 | 42 | ||
25 | - A new instance of vscode with `[Extension Development Host]` in the title. | ||
26 | 43 | ||
27 | Don't worry about disabling `rls` all other extensions will be disabled but this one. | 44 | - By default, the LSP server is built without debug information. To enable it, you'll need to change `Cargo.toml`: |
45 | ```toml | ||
46 | [profile.dev] | ||
47 | debug = 2 | ||
48 | ``` | ||
28 | 49 | ||
29 | - In the new vscode instance open a rust project, and navigate to a rust file | 50 | - Select `Run Extension (Dev Server)` to run your locally built `target/debug/ra_lsp_server`. |
30 | 51 | ||
31 | - In the original vscode start an additional debug session (the three periods in the launch) and select `Debug Lsp Server`. | 52 | - In the original VSCode window once again select the `Attach To Server` debug configuration. |
32 | 53 | ||
33 | - A list of running processes should appear select the `ra_lsp_server` from this repo. | 54 | - A list of running processes should appear. Select the `ra_lsp_server` from this repo. |
34 | 55 | ||
35 | - Navigate to `crates/ra_lsp_server/src/main_loop.rs` and add a breakpoint to the `on_task` function. | 56 | - Navigate to `crates/ra_lsp_server/src/main_loop.rs` and add a breakpoint to the `on_task` function. |
36 | 57 | ||
37 | - Go back to the `[Extension Development Host]` instance and hover over a rust variable and your breakpoint should hit. | 58 | - Go back to the `[Extension Development Host]` instance and hover over a Rust variable and your breakpoint should hit. |
38 | 59 | ||
39 | ## Demo | 60 | ## Demo |
40 | 61 | ||
41 | ![demonstration of debugging](https://user-images.githubusercontent.com/1711539/51384036-254fab80-1b2c-11e9-824d-95f9a6e9cf4f.gif) | 62 | - [Debugging TypeScript VScode extension](https://www.youtube.com/watch?v=T-hvpK6s4wM). |
63 | - [Debugging Rust LSP server](https://www.youtube.com/watch?v=EaNb5rg4E0M). | ||
42 | 64 | ||
43 | ## Troubleshooting | 65 | ## Troubleshooting |
44 | 66 | ||
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 70cb0a612..53e2a414b 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -44,7 +44,6 @@ export class Config { | |||
44 | this.refreshConfig(); | 44 | this.refreshConfig(); |
45 | } | 45 | } |
46 | 46 | ||
47 | |||
48 | private refreshConfig() { | 47 | private refreshConfig() { |
49 | this.cfg = vscode.workspace.getConfiguration(Config.rootSection); | 48 | this.cfg = vscode.workspace.getConfiguration(Config.rootSection); |
50 | console.log("Using configuration:", this.cfg); | 49 | console.log("Using configuration:", this.cfg); |
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 9fcf2ec38..ff6245f78 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts | |||
@@ -91,15 +91,11 @@ export async function sendRequestWithRetry<R>( | |||
91 | for (const delay of [2, 4, 6, 8, 10, null]) { | 91 | for (const delay of [2, 4, 6, 8, 10, null]) { |
92 | try { | 92 | try { |
93 | return await (token ? client.sendRequest(method, param, token) : client.sendRequest(method, param)); | 93 | return await (token ? client.sendRequest(method, param, token) : client.sendRequest(method, param)); |
94 | } catch (e) { | 94 | } catch (err) { |
95 | if ( | 95 | if (delay === null || err.code !== lc.ErrorCodes.ContentModified) { |
96 | e.code === lc.ErrorCodes.ContentModified && | 96 | throw err; |
97 | delay !== null | ||
98 | ) { | ||
99 | await sleep(10 * (1 << delay)); | ||
100 | continue; | ||
101 | } | 97 | } |
102 | throw e; | 98 | await sleep(10 * (1 << delay)); |
103 | } | 99 | } |
104 | } | 100 | } |
105 | throw 'unreachable'; | 101 | throw 'unreachable'; |
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index c317a9213..3896878cd 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts | |||
@@ -27,7 +27,9 @@ export function activateInlayHints(ctx: Ctx) { | |||
27 | ctx.subscriptions | 27 | ctx.subscriptions |
28 | ); | 28 | ); |
29 | 29 | ||
30 | ctx.onDidRestart(_ => hintsUpdater.setEnabled(ctx.config.displayInlayHints)); | 30 | // We pass async function though it will not be awaited when called, |
31 | // thus Promise rejections won't be handled, but this should never throw in fact... | ||
32 | ctx.onDidRestart(async _ => hintsUpdater.setEnabled(ctx.config.displayInlayHints)); | ||
31 | } | 33 | } |
32 | 34 | ||
33 | interface InlayHintsParams { | 35 | interface InlayHintsParams { |
@@ -36,7 +38,7 @@ interface InlayHintsParams { | |||
36 | 38 | ||
37 | interface InlayHint { | 39 | interface InlayHint { |
38 | range: vscode.Range; | 40 | range: vscode.Range; |
39 | kind: string; | 41 | kind: "TypeHint" | "ParameterHint"; |
40 | label: string; | 42 | label: string; |
41 | } | 43 | } |
42 | 44 | ||
@@ -53,7 +55,7 @@ const parameterHintDecorationType = vscode.window.createTextEditorDecorationType | |||
53 | }); | 55 | }); |
54 | 56 | ||
55 | class HintsUpdater { | 57 | class HintsUpdater { |
56 | private pending: Map<string, vscode.CancellationTokenSource> = new Map(); | 58 | private pending = new Map<string, vscode.CancellationTokenSource>(); |
57 | private ctx: Ctx; | 59 | private ctx: Ctx; |
58 | private enabled: boolean; | 60 | private enabled: boolean; |
59 | 61 | ||
@@ -62,30 +64,36 @@ class HintsUpdater { | |||
62 | this.enabled = ctx.config.displayInlayHints; | 64 | this.enabled = ctx.config.displayInlayHints; |
63 | } | 65 | } |
64 | 66 | ||
65 | async setEnabled(enabled: boolean) { | 67 | async setEnabled(enabled: boolean): Promise<void> { |
66 | if (this.enabled == enabled) return; | 68 | if (this.enabled == enabled) return; |
67 | this.enabled = enabled; | 69 | this.enabled = enabled; |
68 | 70 | ||
69 | if (this.enabled) { | 71 | if (this.enabled) { |
70 | await this.refresh(); | 72 | return await this.refresh(); |
71 | } else { | ||
72 | this.allEditors.forEach(it => { | ||
73 | this.setTypeDecorations(it, []); | ||
74 | this.setParameterDecorations(it, []); | ||
75 | }); | ||
76 | } | 73 | } |
74 | this.allEditors.forEach(it => { | ||
75 | this.setTypeDecorations(it, []); | ||
76 | this.setParameterDecorations(it, []); | ||
77 | }); | ||
77 | } | 78 | } |
78 | 79 | ||
79 | async refresh() { | 80 | async refresh() { |
80 | if (!this.enabled) return; | 81 | if (!this.enabled) return; |
81 | const promises = this.allEditors.map(it => this.refreshEditor(it)); | 82 | await Promise.all(this.allEditors.map(it => this.refreshEditor(it))); |
82 | await Promise.all(promises); | 83 | } |
84 | |||
85 | private get allEditors(): vscode.TextEditor[] { | ||
86 | return vscode.window.visibleTextEditors.filter( | ||
87 | editor => editor.document.languageId === 'rust', | ||
88 | ); | ||
83 | } | 89 | } |
84 | 90 | ||
85 | private async refreshEditor(editor: vscode.TextEditor): Promise<void> { | 91 | private async refreshEditor(editor: vscode.TextEditor): Promise<void> { |
86 | const newHints = await this.queryHints(editor.document.uri.toString()); | 92 | const newHints = await this.queryHints(editor.document.uri.toString()); |
87 | if (newHints == null) return; | 93 | if (newHints == null) return; |
88 | const newTypeDecorations = newHints.filter(hint => hint.kind === 'TypeHint') | 94 | |
95 | const newTypeDecorations = newHints | ||
96 | .filter(hint => hint.kind === 'TypeHint') | ||
89 | .map(hint => ({ | 97 | .map(hint => ({ |
90 | range: hint.range, | 98 | range: hint.range, |
91 | renderOptions: { | 99 | renderOptions: { |
@@ -96,7 +104,8 @@ class HintsUpdater { | |||
96 | })); | 104 | })); |
97 | this.setTypeDecorations(editor, newTypeDecorations); | 105 | this.setTypeDecorations(editor, newTypeDecorations); |
98 | 106 | ||
99 | const newParameterDecorations = newHints.filter(hint => hint.kind === 'ParameterHint') | 107 | const newParameterDecorations = newHints |
108 | .filter(hint => hint.kind === 'ParameterHint') | ||
100 | .map(hint => ({ | 109 | .map(hint => ({ |
101 | range: hint.range, | 110 | range: hint.range, |
102 | renderOptions: { | 111 | renderOptions: { |
@@ -108,12 +117,6 @@ class HintsUpdater { | |||
108 | this.setParameterDecorations(editor, newParameterDecorations); | 117 | this.setParameterDecorations(editor, newParameterDecorations); |
109 | } | 118 | } |
110 | 119 | ||
111 | private get allEditors(): vscode.TextEditor[] { | ||
112 | return vscode.window.visibleTextEditors.filter( | ||
113 | editor => editor.document.languageId === 'rust', | ||
114 | ); | ||
115 | } | ||
116 | |||
117 | private setTypeDecorations( | 120 | private setTypeDecorations( |
118 | editor: vscode.TextEditor, | 121 | editor: vscode.TextEditor, |
119 | decorations: vscode.DecorationOptions[], | 122 | decorations: vscode.DecorationOptions[], |
@@ -137,12 +140,14 @@ class HintsUpdater { | |||
137 | private async queryHints(documentUri: string): Promise<InlayHint[] | null> { | 140 | private async queryHints(documentUri: string): Promise<InlayHint[] | null> { |
138 | const client = this.ctx.client; | 141 | const client = this.ctx.client; |
139 | if (!client) return null; | 142 | if (!client) return null; |
143 | |||
140 | const request: InlayHintsParams = { | 144 | const request: InlayHintsParams = { |
141 | textDocument: { uri: documentUri }, | 145 | textDocument: { uri: documentUri }, |
142 | }; | 146 | }; |
143 | const tokenSource = new vscode.CancellationTokenSource(); | 147 | const tokenSource = new vscode.CancellationTokenSource(); |
144 | const prev = this.pending.get(documentUri); | 148 | const prevHintsRequest = this.pending.get(documentUri); |
145 | if (prev) prev.cancel(); | 149 | prevHintsRequest?.cancel(); |
150 | |||
146 | this.pending.set(documentUri, tokenSource); | 151 | this.pending.set(documentUri, tokenSource); |
147 | try { | 152 | try { |
148 | return await sendRequestWithRetry<InlayHint[] | null>( | 153 | return await sendRequestWithRetry<InlayHint[] | null>( |