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