diff options
Diffstat (limited to 'editors/code/src/inlay_hints.ts')
-rw-r--r-- | editors/code/src/inlay_hints.ts | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts new file mode 100644 index 000000000..6dd767d72 --- /dev/null +++ b/editors/code/src/inlay_hints.ts | |||
@@ -0,0 +1,120 @@ | |||
1 | import * as vscode from 'vscode'; | ||
2 | import * as lc from 'vscode-languageclient'; | ||
3 | |||
4 | import { Ctx, sendRequestWithRetry } from './ctx'; | ||
5 | |||
6 | export function activateInlayHints(ctx: Ctx) { | ||
7 | const hintsUpdater = new HintsUpdater(ctx); | ||
8 | vscode.window.onDidChangeVisibleTextEditors(async _ => { | ||
9 | await hintsUpdater.refresh(); | ||
10 | }, ctx.subscriptions); | ||
11 | |||
12 | vscode.workspace.onDidChangeTextDocument(async e => { | ||
13 | if (e.contentChanges.length === 0) return; | ||
14 | if (e.document.languageId !== 'rust') return; | ||
15 | await hintsUpdater.refresh(); | ||
16 | }, ctx.subscriptions); | ||
17 | |||
18 | vscode.workspace.onDidChangeConfiguration(_ => { | ||
19 | hintsUpdater.setEnabled(ctx.config.displayInlayHints); | ||
20 | }, ctx.subscriptions); | ||
21 | |||
22 | ctx.onDidRestart(_ => hintsUpdater.setEnabled(ctx.config.displayInlayHints)); | ||
23 | } | ||
24 | |||
25 | interface InlayHintsParams { | ||
26 | textDocument: lc.TextDocumentIdentifier; | ||
27 | } | ||
28 | |||
29 | interface InlayHint { | ||
30 | range: vscode.Range; | ||
31 | kind: string; | ||
32 | label: string; | ||
33 | } | ||
34 | |||
35 | const typeHintDecorationType = vscode.window.createTextEditorDecorationType({ | ||
36 | after: { | ||
37 | color: new vscode.ThemeColor('rust_analyzer.inlayHint'), | ||
38 | }, | ||
39 | }); | ||
40 | |||
41 | class HintsUpdater { | ||
42 | private pending: Map<string, vscode.CancellationTokenSource> = new Map(); | ||
43 | private ctx: Ctx; | ||
44 | private enabled = true; | ||
45 | |||
46 | constructor(ctx: Ctx) { | ||
47 | this.ctx = ctx; | ||
48 | } | ||
49 | |||
50 | async setEnabled(enabled: boolean) { | ||
51 | if (this.enabled == enabled) return; | ||
52 | this.enabled = enabled; | ||
53 | |||
54 | if (this.enabled) { | ||
55 | await this.refresh(); | ||
56 | } else { | ||
57 | this.allEditors.forEach(it => this.setDecorations(it, [])); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | async refresh() { | ||
62 | if (!this.enabled) return; | ||
63 | const promises = this.allEditors.map(it => this.refreshEditor(it)); | ||
64 | await Promise.all(promises); | ||
65 | } | ||
66 | |||
67 | private async refreshEditor(editor: vscode.TextEditor): Promise<void> { | ||
68 | const newHints = await this.queryHints(editor.document.uri.toString()); | ||
69 | if (newHints == null) return; | ||
70 | const newDecorations = newHints.map(hint => ({ | ||
71 | range: hint.range, | ||
72 | renderOptions: { | ||
73 | after: { | ||
74 | contentText: `: ${hint.label}`, | ||
75 | }, | ||
76 | }, | ||
77 | })); | ||
78 | this.setDecorations(editor, newDecorations); | ||
79 | } | ||
80 | |||
81 | private get allEditors(): vscode.TextEditor[] { | ||
82 | return vscode.window.visibleTextEditors.filter( | ||
83 | editor => editor.document.languageId === 'rust', | ||
84 | ); | ||
85 | } | ||
86 | |||
87 | private setDecorations( | ||
88 | editor: vscode.TextEditor, | ||
89 | decorations: vscode.DecorationOptions[], | ||
90 | ) { | ||
91 | editor.setDecorations( | ||
92 | typeHintDecorationType, | ||
93 | this.enabled ? decorations : [], | ||
94 | ); | ||
95 | } | ||
96 | |||
97 | private async queryHints(documentUri: string): Promise<InlayHint[] | null> { | ||
98 | let client = this.ctx.client; | ||
99 | if (!client) return null; | ||
100 | const request: InlayHintsParams = { | ||
101 | textDocument: { uri: documentUri }, | ||
102 | }; | ||
103 | let tokenSource = new vscode.CancellationTokenSource(); | ||
104 | let prev = this.pending.get(documentUri); | ||
105 | if (prev) prev.cancel(); | ||
106 | this.pending.set(documentUri, tokenSource); | ||
107 | try { | ||
108 | return await sendRequestWithRetry<InlayHint[] | null>( | ||
109 | client, | ||
110 | 'rust-analyzer/inlayHints', | ||
111 | request, | ||
112 | tokenSource.token, | ||
113 | ); | ||
114 | } finally { | ||
115 | if (!tokenSource.token.isCancellationRequested) { | ||
116 | this.pending.delete(documentUri); | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | } | ||