diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-07-25 18:42:55 +0100 |
---|---|---|
committer | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-07-25 18:42:55 +0100 |
commit | 4647e89defd367a92d00d3bbb11c2463408bb3ad (patch) | |
tree | b1d01cf8f7751daad61ee16fef82e616f55a8261 | |
parent | ceb16591deef2190daaf5eb9a6480f20dc5d169c (diff) | |
parent | f1ba963a3097106ad6daa41d04c51b7f2d418d8c (diff) |
Merge #1586
1586: Add type decorators r=matklad a=SomeoneToIgnore
A follow-up of https://github.com/rust-analyzer/rust-analyzer/pull/1549
Now the frontend shows inlay hints as VS Code Decorators:
<img width="666" alt="image" src="https://user-images.githubusercontent.com/2690773/61802687-918fcc80-ae39-11e9-97b0-3195ab467393.png">
<img width="893" alt="image" src="https://user-images.githubusercontent.com/2690773/61802688-93599000-ae39-11e9-8bcb-4512e22aa3ed.png">
A few notes on the implementation:
* I could not find a normal way to run and display the hints for the file that's already open in the VS Code when it starts.
The updating code runs ok, but does not actually show anything.
Seems like I miss some event that I could add a handler to.
I've also experimented with `setTimeout` and it worked, but this is too ugly.
The hints appear now when a new file is open or when some change is done in the existing file.
* If there's a `dbg!` used in the lsp_server, the frontend starts receiving change events that contain the string from the `dbg!` output.
It should not be the case in a real life, but I've decided to cover this case, just in case.
* For bigger files, ~500 lines, the decorators start to blink, when updated, this does not seem to be very much of a problem for me at this particular stage of the feature development and can be optimized later. In the worst case, those decorators can be turned off in settings.
* Cursor movement is rather non-intuitive on the right edge of the decorator.
Seems like a thing to fix in the VS Code, not in the plugin.
Co-authored-by: Kirill Bulatov <[email protected]>
-rw-r--r-- | editors/code/package.json | 14 | ||||
-rw-r--r-- | editors/code/src/commands/index.ts | 4 | ||||
-rw-r--r-- | editors/code/src/commands/inlay_hints.ts | 104 | ||||
-rw-r--r-- | editors/code/src/config.ts | 5 | ||||
-rw-r--r-- | editors/code/src/extension.ts | 24 |
5 files changed, 150 insertions, 1 deletions
diff --git a/editors/code/package.json b/editors/code/package.json index fd30c7946..060a3a247 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -238,6 +238,11 @@ | |||
238 | "type": "number", | 238 | "type": "number", |
239 | "default": null, | 239 | "default": null, |
240 | "description": "Number of syntax trees rust-analyzer keeps in memory" | 240 | "description": "Number of syntax trees rust-analyzer keeps in memory" |
241 | }, | ||
242 | "rust-analyzer.displayInlayHints": { | ||
243 | "type": "boolean", | ||
244 | "default": true, | ||
245 | "description": "Display additional type information in the editor" | ||
241 | } | 246 | } |
242 | } | 247 | } |
243 | }, | 248 | }, |
@@ -444,6 +449,15 @@ | |||
444 | "light": "#000000", | 449 | "light": "#000000", |
445 | "highContrast": "#FFFFFF" | 450 | "highContrast": "#FFFFFF" |
446 | } | 451 | } |
452 | }, | ||
453 | { | ||
454 | "id": "ralsp.inlayHint", | ||
455 | "description": "Color for inlay hints", | ||
456 | "defaults": { | ||
457 | "dark": "#A0A0A0F0", | ||
458 | "light": "#747474", | ||
459 | "highContrast": "#BEBEBE" | ||
460 | } | ||
447 | } | 461 | } |
448 | ] | 462 | ] |
449 | } | 463 | } |
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index 194658497..c194bd2ea 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import * as analyzerStatus from './analyzer_status'; | 1 | import * as analyzerStatus from './analyzer_status'; |
2 | import * as applySourceChange from './apply_source_change'; | 2 | import * as applySourceChange from './apply_source_change'; |
3 | import * as inlayHints from './inlay_hints'; | ||
3 | import * as joinLines from './join_lines'; | 4 | import * as joinLines from './join_lines'; |
4 | import * as matchingBrace from './matching_brace'; | 5 | import * as matchingBrace from './matching_brace'; |
5 | import * as onEnter from './on_enter'; | 6 | import * as onEnter from './on_enter'; |
@@ -15,5 +16,6 @@ export { | |||
15 | parentModule, | 16 | parentModule, |
16 | runnables, | 17 | runnables, |
17 | syntaxTree, | 18 | syntaxTree, |
18 | onEnter | 19 | onEnter, |
20 | inlayHints | ||
19 | }; | 21 | }; |
diff --git a/editors/code/src/commands/inlay_hints.ts b/editors/code/src/commands/inlay_hints.ts new file mode 100644 index 000000000..8154af8dc --- /dev/null +++ b/editors/code/src/commands/inlay_hints.ts | |||
@@ -0,0 +1,104 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | import { Range, TextDocumentChangeEvent, TextEditor } from 'vscode'; | ||
3 | import { TextDocumentIdentifier } from 'vscode-languageclient'; | ||
4 | import { Server } from '../server'; | ||
5 | |||
6 | interface InlayHintsParams { | ||
7 | textDocument: TextDocumentIdentifier; | ||
8 | } | ||
9 | |||
10 | interface InlayHint { | ||
11 | range: Range; | ||
12 | kind: string; | ||
13 | label: string; | ||
14 | } | ||
15 | |||
16 | const typeHintDecorationType = vscode.window.createTextEditorDecorationType({ | ||
17 | after: { | ||
18 | color: new vscode.ThemeColor('ralsp.inlayHint') | ||
19 | } | ||
20 | }); | ||
21 | |||
22 | export class HintsUpdater { | ||
23 | private displayHints = true; | ||
24 | |||
25 | public async loadHints( | ||
26 | editor: vscode.TextEditor | undefined | ||
27 | ): Promise<void> { | ||
28 | if ( | ||
29 | this.displayHints && | ||
30 | editor !== undefined && | ||
31 | this.isRustDocument(editor.document) | ||
32 | ) { | ||
33 | await this.updateDecorationsFromServer( | ||
34 | editor.document.uri.toString(), | ||
35 | editor | ||
36 | ); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | public async toggleHintsDisplay(displayHints: boolean): Promise<void> { | ||
41 | if (this.displayHints !== displayHints) { | ||
42 | this.displayHints = displayHints; | ||
43 | |||
44 | if (displayHints) { | ||
45 | return this.updateHints(); | ||
46 | } else { | ||
47 | const editor = vscode.window.activeTextEditor; | ||
48 | if (editor != null) { | ||
49 | return editor.setDecorations(typeHintDecorationType, []); | ||
50 | } | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
55 | public async updateHints(cause?: TextDocumentChangeEvent): Promise<void> { | ||
56 | if (!this.displayHints) { | ||
57 | return; | ||
58 | } | ||
59 | const editor = vscode.window.activeTextEditor; | ||
60 | if (editor == null) { | ||
61 | return; | ||
62 | } | ||
63 | const document = cause == null ? editor.document : cause.document; | ||
64 | if (!this.isRustDocument(document)) { | ||
65 | return; | ||
66 | } | ||
67 | |||
68 | return await this.updateDecorationsFromServer( | ||
69 | document.uri.toString(), | ||
70 | editor | ||
71 | ); | ||
72 | } | ||
73 | |||
74 | private isRustDocument(document: vscode.TextDocument): boolean { | ||
75 | return document && document.languageId === 'rust'; | ||
76 | } | ||
77 | |||
78 | private async updateDecorationsFromServer( | ||
79 | documentUri: string, | ||
80 | editor: TextEditor | ||
81 | ): Promise<void> { | ||
82 | const newHints = (await this.queryHints(documentUri)) || []; | ||
83 | const newDecorations = newHints.map(hint => ({ | ||
84 | range: hint.range, | ||
85 | renderOptions: { after: { contentText: `: ${hint.label}` } } | ||
86 | })); | ||
87 | return editor.setDecorations(typeHintDecorationType, newDecorations); | ||
88 | } | ||
89 | |||
90 | private async queryHints(documentUri: string): Promise<InlayHint[] | null> { | ||
91 | const request: InlayHintsParams = { | ||
92 | textDocument: { uri: documentUri } | ||
93 | }; | ||
94 | const client = Server.client; | ||
95 | return client | ||
96 | .onReady() | ||
97 | .then(() => | ||
98 | client.sendRequest<InlayHint[] | null>( | ||
99 | 'rust-analyzer/inlayHints', | ||
100 | request | ||
101 | ) | ||
102 | ); | ||
103 | } | ||
104 | } | ||
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 3f1b482e3..4d58a1a93 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -21,6 +21,7 @@ export class Config { | |||
21 | public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server'; | 21 | public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server'; |
22 | public showWorkspaceLoadedNotification = true; | 22 | public showWorkspaceLoadedNotification = true; |
23 | public lruCapacity: null | number = null; | 23 | public lruCapacity: null | number = null; |
24 | public displayInlayHints = true; | ||
24 | public cargoWatchOptions: CargoWatchOptions = { | 25 | public cargoWatchOptions: CargoWatchOptions = { |
25 | enableOnStartup: 'ask', | 26 | enableOnStartup: 'ask', |
26 | trace: 'off', | 27 | trace: 'off', |
@@ -123,5 +124,9 @@ export class Config { | |||
123 | if (config.has('lruCapacity')) { | 124 | if (config.has('lruCapacity')) { |
124 | this.lruCapacity = config.get('lruCapacity') as number; | 125 | this.lruCapacity = config.get('lruCapacity') as number; |
125 | } | 126 | } |
127 | |||
128 | if (config.has('displayInlayHints')) { | ||
129 | this.displayInlayHints = config.get('displayInlayHints') as boolean; | ||
130 | } | ||
126 | } | 131 | } |
127 | } | 132 | } |
diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index c8c3004a7..c6efc2e7e 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts | |||
@@ -3,6 +3,7 @@ import * as lc from 'vscode-languageclient'; | |||
3 | 3 | ||
4 | import * as commands from './commands'; | 4 | import * as commands from './commands'; |
5 | import { CargoWatchProvider } from './commands/cargo_watch'; | 5 | import { CargoWatchProvider } from './commands/cargo_watch'; |
6 | import { HintsUpdater } from './commands/inlay_hints'; | ||
6 | import { | 7 | import { |
7 | interactivelyStartCargoWatch, | 8 | interactivelyStartCargoWatch, |
8 | startCargoWatch | 9 | startCargoWatch |
@@ -147,6 +148,29 @@ export function activate(context: vscode.ExtensionContext) { | |||
147 | 148 | ||
148 | // Start the language server, finally! | 149 | // Start the language server, finally! |
149 | startServer(); | 150 | startServer(); |
151 | |||
152 | if (Server.config.displayInlayHints) { | ||
153 | const hintsUpdater = new HintsUpdater(); | ||
154 | hintsUpdater.loadHints(vscode.window.activeTextEditor).then(() => { | ||
155 | disposeOnDeactivation( | ||
156 | vscode.window.onDidChangeActiveTextEditor(editor => | ||
157 | hintsUpdater.loadHints(editor) | ||
158 | ) | ||
159 | ); | ||
160 | disposeOnDeactivation( | ||
161 | vscode.workspace.onDidChangeTextDocument(e => | ||
162 | hintsUpdater.updateHints(e) | ||
163 | ) | ||
164 | ); | ||
165 | disposeOnDeactivation( | ||
166 | vscode.workspace.onDidChangeConfiguration(_ => | ||
167 | hintsUpdater.toggleHintsDisplay( | ||
168 | Server.config.displayInlayHints | ||
169 | ) | ||
170 | ) | ||
171 | ); | ||
172 | }); | ||
173 | } | ||
150 | } | 174 | } |
151 | 175 | ||
152 | export function deactivate(): Thenable<void> { | 176 | export function deactivate(): Thenable<void> { |