import * as vscode from 'vscode'; import { Range, TextDocumentChangeEvent, TextDocumentContentChangeEvent, TextEditor } from 'vscode'; import { TextDocumentIdentifier } from 'vscode-languageclient'; import { Server } from '../server'; interface InlayHintsParams { textDocument: TextDocumentIdentifier; } interface InlayHint { range: Range, kind: string, label: string, } const typeHintDecorationType = vscode.window.createTextEditorDecorationType({ after: { color: new vscode.ThemeColor('ralsp.inlayHint'), }, }); export class HintsUpdater { private displayHints = true; public async loadHints(editor: vscode.TextEditor | undefined): Promise { if (this.displayHints && editor !== undefined) { await this.updateDecorationsFromServer(editor.document.uri.toString(), editor); } } public async toggleHintsDisplay(displayHints: boolean): Promise { if (this.displayHints !== displayHints) { this.displayHints = displayHints; if (displayHints) { return this.updateHints(); } else { const editor = vscode.window.activeTextEditor; if (editor != null) { return editor.setDecorations(typeHintDecorationType, []) } } } } public async updateHints(cause?: TextDocumentChangeEvent): Promise { if (!this.displayHints) { return; } const editor = vscode.window.activeTextEditor; if (editor == null) { return; } const document = cause == null ? editor.document : cause.document; if (document.languageId !== 'rust') { return; } // If the dbg! macro is used in the lsp-server, an endless stream of events with `cause.contentChanges` with the dbg messages. // Should not be a real situation, but better to filter such things out. if (cause !== undefined && cause.contentChanges.filter(changeEvent => this.isEventInFile(document.lineCount, changeEvent)).length === 0) { return; } return await this.updateDecorationsFromServer(document.uri.toString(), editor); } private isEventInFile(documentLineCount: number, event: TextDocumentContentChangeEvent): boolean { const eventText = event.text; if (eventText.length === 0) { return event.range.start.line <= documentLineCount || event.range.end.line <= documentLineCount; } else { return event.range.start.line <= documentLineCount && event.range.end.line <= documentLineCount; } } private async updateDecorationsFromServer(documentUri: string, editor: TextEditor): Promise { const newHints = await this.queryHints(documentUri) || []; const newDecorations = newHints.map(hint => ( { range: hint.range, renderOptions: { after: { contentText: `: ${hint.label}` } }, } )); return editor.setDecorations(typeHintDecorationType, newDecorations); } private async queryHints(documentUri: string): Promise { const request: InlayHintsParams = { textDocument: { uri: documentUri } }; const client = Server.client; return client.onReady().then(() => client.sendRequest( 'rust-analyzer/inlayHints', request )); } }