]> git.lizzy.rs Git - rust.git/blob - editors/code/src/inlay_hints.ts
Drop obsolete color configurations
[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('rust-analyzer.inlayHint'),
40     },
41 });
42
43 class HintsUpdater {
44     private pending: Map<string, vscode.CancellationTokenSource> = new Map();
45     private ctx: Ctx;
46     private enabled = true;
47
48     constructor(ctx: Ctx) {
49         this.ctx = ctx;
50     }
51
52     async setEnabled(enabled: boolean) {
53         if (this.enabled == enabled) return;
54         this.enabled = enabled;
55
56         if (this.enabled) {
57             await this.refresh();
58         } else {
59             this.allEditors.forEach(it => this.setDecorations(it, []));
60         }
61     }
62
63     async refresh() {
64         if (!this.enabled) return;
65         const promises = this.allEditors.map(it => this.refreshEditor(it));
66         await Promise.all(promises);
67     }
68
69     private async refreshEditor(editor: vscode.TextEditor): Promise<void> {
70         const newHints = await this.queryHints(editor.document.uri.toString());
71         if (newHints == null) return;
72         const newDecorations = newHints.map(hint => ({
73             range: hint.range,
74             renderOptions: {
75                 after: {
76                     contentText: `: ${hint.label}`,
77                 },
78             },
79         }));
80         this.setDecorations(editor, newDecorations);
81     }
82
83     private get allEditors(): vscode.TextEditor[] {
84         return vscode.window.visibleTextEditors.filter(
85             editor => editor.document.languageId === 'rust',
86         );
87     }
88
89     private setDecorations(
90         editor: vscode.TextEditor,
91         decorations: vscode.DecorationOptions[],
92     ) {
93         editor.setDecorations(
94             typeHintDecorationType,
95             this.enabled ? decorations : [],
96         );
97     }
98
99     private async queryHints(documentUri: string): Promise<InlayHint[] | 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 this.ctx.sendRequestWithRetry<InlayHint[] | null>(
109                 'rust-analyzer/inlayHints',
110                 request,
111                 tokenSource.token,
112             );
113         } finally {
114             if (!tokenSource.token.isCancellationRequested) {
115                 this.pending.delete(documentUri);
116             }
117         }
118     }
119 }