]> git.lizzy.rs Git - rust.git/blob - editors/code/src/inlay_hints.ts
Retry inlay hints on content modified error
[rust.git] / editors / code / src / inlay_hints.ts
1 import * as vscode from 'vscode';
2 import * as lc from 'vscode-languageclient';
3
4 import { Ctx } 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     // XXX: don't await here;
23     // Who knows what happens if an exception is thrown here...
24     hintsUpdater.refresh();
25 }
26
27 interface InlayHintsParams {
28     textDocument: lc.TextDocumentIdentifier;
29 }
30
31 interface InlayHint {
32     range: vscode.Range;
33     kind: string;
34     label: string;
35 }
36
37 const typeHintDecorationType = vscode.window.createTextEditorDecorationType({
38     after: {
39         color: new vscode.ThemeColor('ralsp.inlayHint'),
40     },
41 });
42
43 class HintsUpdater {
44     private ctx: Ctx;
45     private enabled = true;
46
47     constructor(ctx: Ctx) {
48         this.ctx = ctx;
49     }
50
51     async setEnabled(enabled: boolean) {
52         if (this.enabled == enabled) return;
53         this.enabled = enabled;
54
55         if (this.enabled) {
56             await this.refresh();
57         } else {
58             this.allEditors.forEach(it => this.setDecorations(it, []));
59         }
60     }
61
62     async refresh() {
63         if (!this.enabled) return;
64         const promises = this.allEditors.map(it => this.refreshEditor(it));
65         await Promise.all(promises);
66     }
67
68     private async refreshEditor(editor: vscode.TextEditor): Promise<void> {
69         const newHints = await this.queryHints(editor.document.uri.toString());
70         const newDecorations = (newHints ? 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         const request: InlayHintsParams = {
99             textDocument: { uri: documentUri },
100         };
101         return this.ctx.sendRequestWithRetry<InlayHint[] | null>(
102             'rust-analyzer/inlayHints',
103             request,
104         );
105     }
106 }