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.ts149
1 files changed, 149 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..4581e2278
--- /dev/null
+++ b/editors/code/src/inlay_hints.ts
@@ -0,0 +1,149 @@
1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient';
3import { Server } from './server';
4import { Ctx } from './ctx';
5
6export function activateInlayHints(ctx: Ctx) {
7 const hintsUpdater = new HintsUpdater();
8 hintsUpdater.refreshHintsForVisibleEditors().then(() => {
9 // vscode may ignore top level hintsUpdater.refreshHintsForVisibleEditors()
10 // so update the hints once when the focus changes to guarantee their presence
11 let editorChangeDisposable: vscode.Disposable | null = null;
12 editorChangeDisposable = vscode.window.onDidChangeActiveTextEditor(
13 _ => {
14 if (editorChangeDisposable !== null) {
15 editorChangeDisposable.dispose();
16 }
17 return hintsUpdater.refreshHintsForVisibleEditors();
18 },
19 );
20
21 ctx.pushCleanup(
22 vscode.window.onDidChangeVisibleTextEditors(_ =>
23 hintsUpdater.refreshHintsForVisibleEditors(),
24 ),
25 );
26 ctx.pushCleanup(
27 vscode.workspace.onDidChangeTextDocument(e =>
28 hintsUpdater.refreshHintsForVisibleEditors(e),
29 ),
30 );
31 ctx.pushCleanup(
32 vscode.workspace.onDidChangeConfiguration(_ =>
33 hintsUpdater.toggleHintsDisplay(
34 Server.config.displayInlayHints,
35 ),
36 ),
37 );
38 });
39}
40
41interface InlayHintsParams {
42 textDocument: lc.TextDocumentIdentifier;
43}
44
45interface InlayHint {
46 range: vscode.Range;
47 kind: string;
48 label: string;
49}
50
51const typeHintDecorationType = vscode.window.createTextEditorDecorationType({
52 after: {
53 color: new vscode.ThemeColor('ralsp.inlayHint'),
54 },
55});
56
57class HintsUpdater {
58 private displayHints = true;
59
60 public async toggleHintsDisplay(displayHints: boolean): Promise<void> {
61 if (this.displayHints !== displayHints) {
62 this.displayHints = displayHints;
63 return this.refreshVisibleEditorsHints(
64 displayHints ? undefined : [],
65 );
66 }
67 }
68
69 public async refreshHintsForVisibleEditors(
70 cause?: vscode.TextDocumentChangeEvent,
71 ): Promise<void> {
72 if (!this.displayHints) return;
73
74 if (
75 cause !== undefined &&
76 (cause.contentChanges.length === 0 ||
77 !this.isRustDocument(cause.document))
78 ) {
79 return;
80 }
81 return this.refreshVisibleEditorsHints();
82 }
83
84 private async refreshVisibleEditorsHints(
85 newDecorations?: vscode.DecorationOptions[],
86 ) {
87 const promises: Array<Promise<void>> = [];
88
89 for (const rustEditor of vscode.window.visibleTextEditors.filter(
90 editor => this.isRustDocument(editor.document),
91 )) {
92 if (newDecorations !== undefined) {
93 promises.push(
94 Promise.resolve(
95 rustEditor.setDecorations(
96 typeHintDecorationType,
97 newDecorations,
98 ),
99 ),
100 );
101 } else {
102 promises.push(this.updateDecorationsFromServer(rustEditor));
103 }
104 }
105
106 for (const promise of promises) {
107 await promise;
108 }
109 }
110
111 private isRustDocument(document: vscode.TextDocument): boolean {
112 return document && document.languageId === 'rust';
113 }
114
115 private async updateDecorationsFromServer(
116 editor: vscode.TextEditor,
117 ): Promise<void> {
118 const newHints = await this.queryHints(editor.document.uri.toString());
119 if (newHints !== null) {
120 const newDecorations = newHints.map(hint => ({
121 range: hint.range,
122 renderOptions: {
123 after: {
124 contentText: `: ${hint.label}`,
125 },
126 },
127 }));
128 return editor.setDecorations(
129 typeHintDecorationType,
130 newDecorations,
131 );
132 }
133 }
134
135 private async queryHints(documentUri: string): Promise<InlayHint[] | null> {
136 const request: InlayHintsParams = {
137 textDocument: { uri: documentUri },
138 };
139 const client = Server.client;
140 return client
141 .onReady()
142 .then(() =>
143 client.sendRequest<InlayHint[] | null>(
144 'rust-analyzer/inlayHints',
145 request,
146 ),
147 );
148 }
149}