Refactor inlay hints

This commit is contained in:
Aleksey Kladov 2019-12-30 21:28:38 +01:00
parent efbbc903e6
commit ac8a142ddd

View file

@ -1,39 +1,29 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as lc from 'vscode-languageclient'; import * as lc from 'vscode-languageclient';
import { Server } from './server';
import { Ctx } from './ctx'; import { Ctx } from './ctx';
export function activateInlayHints(ctx: Ctx) { export function activateInlayHints(ctx: Ctx) {
const hintsUpdater = new HintsUpdater(); const hintsUpdater = new HintsUpdater(ctx);
hintsUpdater.refreshHintsForVisibleEditors().then(() => { console.log('activateInlayHints');
// vscode may ignore top level hintsUpdater.refreshHintsForVisibleEditors()
// so update the hints once when the focus changes to guarantee their presence
let editorChangeDisposable: vscode.Disposable | null = null;
editorChangeDisposable = vscode.window.onDidChangeActiveTextEditor(
_ => {
if (editorChangeDisposable !== null) {
editorChangeDisposable.dispose();
}
return hintsUpdater.refreshHintsForVisibleEditors();
},
);
ctx.pushCleanup( vscode.window.onDidChangeVisibleTextEditors(async _ => {
vscode.window.onDidChangeVisibleTextEditors(_ => await hintsUpdater.refresh();
hintsUpdater.refreshHintsForVisibleEditors(), }, ctx.subscriptions);
),
); vscode.workspace.onDidChangeTextDocument(async e => {
ctx.pushCleanup( if (e.contentChanges.length === 0) return;
vscode.workspace.onDidChangeTextDocument(e => if (e.document.languageId !== 'rust') return;
hintsUpdater.refreshHintsForVisibleEditors(e), await hintsUpdater.refresh();
), }, ctx.subscriptions);
);
ctx.pushCleanup( vscode.workspace.onDidChangeConfiguration(_ => {
vscode.workspace.onDidChangeConfiguration(_ => hintsUpdater.setEnabled(ctx.config.displayInlayHints);
hintsUpdater.toggleHintsDisplay(ctx.config.displayInlayHints), }, ctx.subscriptions);
),
); // XXX: don't await here;
}); // Who knows what happens if an exception is thrown here...
hintsUpdater.refresh();
} }
interface InlayHintsParams { interface InlayHintsParams {
@ -53,69 +43,34 @@ const typeHintDecorationType = vscode.window.createTextEditorDecorationType({
}); });
class HintsUpdater { class HintsUpdater {
private displayHints = true; private ctx: Ctx;
private enabled = true;
public async toggleHintsDisplay(displayHints: boolean): Promise<void> { constructor(ctx: Ctx) {
if (this.displayHints !== displayHints) { this.ctx = ctx;
this.displayHints = displayHints;
return this.refreshVisibleEditorsHints(
displayHints ? undefined : [],
);
}
} }
public async refreshHintsForVisibleEditors( async setEnabled(enabled: boolean) {
cause?: vscode.TextDocumentChangeEvent, if (this.enabled == enabled) return;
): Promise<void> { this.enabled = enabled;
if (!this.displayHints) return;
if ( if (this.enabled) {
cause !== undefined && await this.refresh();
(cause.contentChanges.length === 0 ||
!this.isRustDocument(cause.document))
) {
return;
}
return this.refreshVisibleEditorsHints();
}
private async refreshVisibleEditorsHints(
newDecorations?: vscode.DecorationOptions[],
) {
const promises: Array<Promise<void>> = [];
for (const rustEditor of vscode.window.visibleTextEditors.filter(
editor => this.isRustDocument(editor.document),
)) {
if (newDecorations !== undefined) {
promises.push(
Promise.resolve(
rustEditor.setDecorations(
typeHintDecorationType,
newDecorations,
),
),
);
} else { } else {
promises.push(this.updateDecorationsFromServer(rustEditor)); this.allEditors.forEach(it => this.setDecorations(it, []));
} }
} }
for (const promise of promises) { async refresh() {
await promise; if (!this.enabled) return;
} const promises = this.allEditors.map(it => this.refreshEditor(it));
await Promise.all(promises);
} }
private isRustDocument(document: vscode.TextDocument): boolean { private async refreshEditor(editor: vscode.TextEditor): Promise<void> {
return document && document.languageId === 'rust';
}
private async updateDecorationsFromServer(
editor: vscode.TextEditor,
): Promise<void> {
const newHints = await this.queryHints(editor.document.uri.toString()); const newHints = await this.queryHints(editor.document.uri.toString());
if (newHints !== null) {
const newDecorations = newHints.map(hint => ({ const newDecorations = (newHints ? newHints : []).map(hint => ({
range: hint.range, range: hint.range,
renderOptions: { renderOptions: {
after: { after: {
@ -123,25 +78,34 @@ class HintsUpdater {
}, },
}, },
})); }));
return editor.setDecorations( this.setDecorations(editor, newDecorations);
typeHintDecorationType, }
newDecorations,
private get allEditors(): vscode.TextEditor[] {
return vscode.window.visibleTextEditors.filter(
editor => editor.document.languageId === 'rust',
); );
} }
private setDecorations(
editor: vscode.TextEditor,
decorations: vscode.DecorationOptions[],
) {
editor.setDecorations(
typeHintDecorationType,
this.enabled ? decorations : [],
);
} }
private async queryHints(documentUri: string): Promise<InlayHint[] | null> { private async queryHints(documentUri: string): Promise<InlayHint[] | null> {
const request: InlayHintsParams = { const request: InlayHintsParams = {
textDocument: { uri: documentUri }, textDocument: { uri: documentUri },
}; };
const client = Server.client; await this.ctx.client.onReady();
return client
.onReady() return this.ctx.client.sendRequest<InlayHint[] | null>(
.then(() =>
client.sendRequest<InlayHint[] | null>(
'rust-analyzer/inlayHints', 'rust-analyzer/inlayHints',
request, request,
),
); );
} }
} }