diff --git a/editors/code/package.json b/editors/code/package.json index fd30c7946669..060a3a247532 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -238,6 +238,11 @@ "type": "number", "default": null, "description": "Number of syntax trees rust-analyzer keeps in memory" + }, + "rust-analyzer.displayInlayHints": { + "type": "boolean", + "default": true, + "description": "Display additional type information in the editor" } } }, @@ -444,6 +449,15 @@ "light": "#000000", "highContrast": "#FFFFFF" } + }, + { + "id": "ralsp.inlayHint", + "description": "Color for inlay hints", + "defaults": { + "dark": "#A0A0A0F0", + "light": "#747474", + "highContrast": "#BEBEBE" + } } ] } diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts index 19465849762f..c194bd2eaccc 100644 --- a/editors/code/src/commands/index.ts +++ b/editors/code/src/commands/index.ts @@ -1,5 +1,6 @@ import * as analyzerStatus from './analyzer_status'; import * as applySourceChange from './apply_source_change'; +import * as inlayHints from './inlay_hints'; import * as joinLines from './join_lines'; import * as matchingBrace from './matching_brace'; import * as onEnter from './on_enter'; @@ -15,5 +16,6 @@ export { parentModule, runnables, syntaxTree, - onEnter + onEnter, + inlayHints }; diff --git a/editors/code/src/commands/inlay_hints.ts b/editors/code/src/commands/inlay_hints.ts new file mode 100644 index 000000000000..8154af8dc263 --- /dev/null +++ b/editors/code/src/commands/inlay_hints.ts @@ -0,0 +1,104 @@ +import * as vscode from 'vscode'; +import { Range, TextDocumentChangeEvent, 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 && + this.isRustDocument(editor.document) + ) { + 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 (!this.isRustDocument(document)) { + return; + } + + return await this.updateDecorationsFromServer( + document.uri.toString(), + editor + ); + } + + private isRustDocument(document: vscode.TextDocument): boolean { + return document && document.languageId === 'rust'; + } + + 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 + ) + ); + } +} diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 3f1b482e3960..4d58a1a9361f 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -21,6 +21,7 @@ export class Config { public raLspServerPath = RA_LSP_DEBUG || 'ra_lsp_server'; public showWorkspaceLoadedNotification = true; public lruCapacity: null | number = null; + public displayInlayHints = true; public cargoWatchOptions: CargoWatchOptions = { enableOnStartup: 'ask', trace: 'off', @@ -123,5 +124,9 @@ export class Config { if (config.has('lruCapacity')) { this.lruCapacity = config.get('lruCapacity') as number; } + + if (config.has('displayInlayHints')) { + this.displayInlayHints = config.get('displayInlayHints') as boolean; + } } } diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index c8c3004a7f63..c6efc2e7e041 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts @@ -3,6 +3,7 @@ import * as lc from 'vscode-languageclient'; import * as commands from './commands'; import { CargoWatchProvider } from './commands/cargo_watch'; +import { HintsUpdater } from './commands/inlay_hints'; import { interactivelyStartCargoWatch, startCargoWatch @@ -147,6 +148,29 @@ export function activate(context: vscode.ExtensionContext) { // Start the language server, finally! startServer(); + + if (Server.config.displayInlayHints) { + const hintsUpdater = new HintsUpdater(); + hintsUpdater.loadHints(vscode.window.activeTextEditor).then(() => { + disposeOnDeactivation( + vscode.window.onDidChangeActiveTextEditor(editor => + hintsUpdater.loadHints(editor) + ) + ); + disposeOnDeactivation( + vscode.workspace.onDidChangeTextDocument(e => + hintsUpdater.updateHints(e) + ) + ); + disposeOnDeactivation( + vscode.workspace.onDidChangeConfiguration(_ => + hintsUpdater.toggleHintsDisplay( + Server.config.displayInlayHints + ) + ) + ); + }); + } } export function deactivate(): Thenable {