diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-12-30 21:57:08 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2019-12-30 21:57:08 +0000 |
commit | c3d74744cdae29aa6a6bfa0cd7ab64b8b251e287 (patch) | |
tree | bf53aac74110d30eba6da346e764e003ffd8b9c4 /editors/code/src/inlay_hints.ts | |
parent | 17dda0972a68dd88a766c223390317dc2cb3ea00 (diff) | |
parent | cdd7118cbf23e21c376092b3b2734407004b8dbf (diff) |
Merge #2694
2694: Refactor inlay hints r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'editors/code/src/inlay_hints.ts')
-rw-r--r-- | editors/code/src/inlay_hints.ts | 176 |
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 @@ | |||
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 | 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 | ||
41 | interface InlayHintsParams { | 27 | interface InlayHintsParams { |
@@ -55,95 +41,79 @@ const typeHintDecorationType = vscode.window.createTextEditorDecorationType({ | |||
55 | }); | 41 | }); |
56 | 42 | ||
57 | class HintsUpdater { | 43 | class 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 | } |