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