aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/inlay_hints.ts
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src/inlay_hints.ts')
-rw-r--r--editors/code/src/inlay_hints.ts120
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 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3
4import { Ctx, sendRequestWithRetry } from './ctx';
5
6export 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
25interface InlayHintsParams {
26 textDocument: lc.TextDocumentIdentifier;
27}
28
29interface InlayHint {
30 range: vscode.Range;
31 kind: string;
32 label: string;
33}
34
35const typeHintDecorationType = vscode.window.createTextEditorDecorationType({
36 after: {
37 color: new vscode.ThemeColor('rust_analyzer.inlayHint'),
38 },
39});
40
41class 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}