From efa489ca7e8bb60782fe75aaed740000f6d16497 Mon Sep 17 00:00:00 2001 From: azerr Date: Fri, 11 Mar 2022 17:39:02 +0100 Subject: [PATCH] Support for `textDocument/InlayHint` See https://github.com/redhat-developer/quarkus-ls/issues/595 Signed-off-by: azerr --- docs/qute/README.md | 4 + package-lock.json | 6 +- package.json | 24 ++++- src/qute/languageServer/client.ts | 7 +- src/qute/languageServer/inlayHintsProvider.ts | 96 +++++++++++++++++++ 5 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 src/qute/languageServer/inlayHintsProvider.ts diff --git a/docs/qute/README.md b/docs/qute/README.md index 18007394..4af396f9 100644 --- a/docs/qute/README.md +++ b/docs/qute/README.md @@ -10,5 +10,9 @@ ## Settings * `qute.trace.server`: Trace the communication between VS Code and the Qute language server in the Output view. Default is `off`. + * `qute.codeLens.enabled`: Enable/disable Qute CodeLens. Default is `true`. + * `qute.inlayHint.enabled`: Enable/disable Inlay Hint. Default is `true`. + * `qute.inlayHint.showSectionParameterType`: Show section parameter type. Default is `true`. + * `qute.inlayHint.showMethodParameterType`: Show method parameter type. Default is `true`. * `qute.validation.enabled`: Enable/disable all Qute validation. Default is `false`. * `qute.validation.excluded`: Disable Qute validation for the given file name patterns.\n\nExample:\n```\n[\n \"**/*items.qute.*\"\n]```. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2450374f..570fdd49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -440,9 +440,9 @@ "dev": true }, "@types/vscode": { - "version": "1.53.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.53.0.tgz", - "integrity": "sha512-XjFWbSPOM0EKIT2XhhYm3D3cx3nn3lshMUcWNy1eqefk+oqRuBq8unVb6BYIZqXy9lQZyeUl7eaBCOZWv+LcXQ==", + "version": "1.65.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.65.0.tgz", + "integrity": "sha512-wQhExnh2nEzpjDMSKhUvnNmz3ucpd3E+R7wJkOhBNK3No6fG3VUdmVmMOKD0A8NDZDDDiQcLNxe3oGmX5SjJ5w==", "dev": true }, "@types/which": { diff --git a/package.json b/package.json index 39d99525..104bed24 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "bugs": "https://github.com/redhat-developer/vscode-quarkus/issues", "engines": { - "vscode": "^1.37.0" + "vscode": "^1.65.0" }, "galleryBanner": { "color": "#d8ebff", @@ -245,6 +245,26 @@ "markdownDescription": "Traces the communication between VS Code and the Qute language server in the Output view. Default is `off`.", "scope": "window" }, + "qute.codeLens.enabled": { + "type": "boolean", + "default": true, + "markdownDescription": "Enable/disable Qute CodeLens. Default is `true`." + }, + "qute.inlayHint.enabled": { + "type": "boolean", + "default": true, + "markdownDescription": "Enable/disable Qute Inlay Hint. Default is `true`." + }, + "qute.inlayHint.showSectionParameterType": { + "type": "boolean", + "default": true, + "markdownDescription": "Show section parameter type. Default is `true`." + }, + "qute.inlayHint.showMethodParameterType": { + "type": "boolean", + "default": true, + "markdownDescription": "Show method parameter type. Default is `true`." + }, "qute.validation.enabled": { "type": "boolean", "default": false, @@ -393,7 +413,7 @@ "@types/request": "^2.48.3", "@types/request-promise": "^4.1.44", "@types/semver": "^6.2.0", - "@types/vscode": "^1.37.0", + "@types/vscode": "^1.65.0", "@types/which": "^2.0.1", "@types/yauzl": "^2.9.1", "chai": "^4.2.0", diff --git a/src/qute/languageServer/client.ts b/src/qute/languageServer/client.ts index 9d3c25fb..9e9b12e2 100644 --- a/src/qute/languageServer/client.ts +++ b/src/qute/languageServer/client.ts @@ -2,12 +2,13 @@ import * as requirements from './requirements'; import { DidChangeConfigurationNotification, LanguageClientOptions } from 'vscode-languageclient'; import { LanguageClient } from 'vscode-languageclient/node'; -import { ExtensionContext, commands, workspace, window, ConfigurationTarget, WorkspaceConfiguration } from 'vscode'; +import { ExtensionContext, commands, workspace, window, ConfigurationTarget, WorkspaceConfiguration, InlayHintsProvider, CancellationToken, Event, InlayHint, ProviderResult, Range, TextDocument, languages } from 'vscode'; import { prepareExecutable } from './javaServerStarter'; import { registerQuteExecuteWorkspaceCommand, registerVSCodeQuteCommands, synchronizeQuteValidationButton } from '../commands/registerCommands'; import { QuteClientCommandConstants } from '../commands/commandConstants'; import { QuteSettings } from './settings'; import { JavaExtensionAPI } from '../../extension'; +import { QuteInlayHintsProvider } from './inlayHintsProvider'; export function connectToQuteLS(context: ExtensionContext, api: JavaExtensionAPI) { registerVSCodeQuteCommands(context); @@ -107,6 +108,10 @@ export function connectToQuteLS(context: ExtensionContext, api: JavaExtensionAPI if (!hasShownQuteValidationPopUp(context)) { await showQuteValidationPopUp(context); } + const supportRegisterInlayHintsProvider = (languages as any).registerInlayHintsProvider; + if (supportRegisterInlayHintsProvider) { + context.subscriptions.push(languages.registerInlayHintsProvider(clientOptions.documentSelector, new QuteInlayHintsProvider(quteLanguageClient))); + } }); }); } diff --git a/src/qute/languageServer/inlayHintsProvider.ts b/src/qute/languageServer/inlayHintsProvider.ts new file mode 100644 index 00000000..62fa0453 --- /dev/null +++ b/src/qute/languageServer/inlayHintsProvider.ts @@ -0,0 +1,96 @@ +import { LanguageClient, RequestType, TextDocumentPositionParams } from "vscode-languageclient/node"; + +import * as code from 'vscode'; +import * as ls from 'vscode-languageserver-protocol'; + +/** + * A parameter literal used in inlay hints requests. + * + * @since 3.17.0 - proposed state + */ + export type InlayHintParams = /*WorkDoneProgressParams &*/ { + /** + * The text document. + */ + textDocument: ls.TextDocumentIdentifier; + + /** + * The document range for which inlay hints should be computed. + */ + range: ls.Range; +}; + +/** + * Inlay hint information. + * + * @since 3.17.0 - proposed state + */ + export type LSInlayHint = { + + /** + * The position of this hint. + */ + position: ls.Position; + + /** + * The label of this hint. A human readable string or an array of + * InlayHintLabelPart label parts. + * + * *Note* that neither the string nor the label part can be empty. + */ + label: string; // label: string | InlayHintLabelPart[]; +}; + +namespace InlayHintRequest { + export const type: RequestType = new RequestType('textDocument/inlayHint'); +} + +/** + * @since 3.17.0 - proposed state + */ + namespace InlayHintRefreshRequest { + export const type: RequestType = new RequestType('workspace/inlayHint/refresh'); +} + +export class QuteInlayHintsProvider implements code.InlayHintsProvider { + private readonly _onDidChangeInlayHints = new code.EventEmitter(); + public readonly onDidChangeInlayHints = this._onDidChangeInlayHints.event; + + constructor(private client: LanguageClient) { + this.client.onRequest(InlayHintRefreshRequest.type, async () => { + this._onDidChangeInlayHints.fire(); + }); + } + async provideInlayHints(document: code.TextDocument, range: code.Range, token: code.CancellationToken): Promise { + const requestParams: InlayHintParams = { + textDocument: this.client.code2ProtocolConverter.asTextDocumentIdentifier(document), + range: this.client.code2ProtocolConverter.asRange(range) + }; + + try { + const values = await this.client.sendRequest(InlayHintRequest.type, requestParams, token); + if (token.isCancellationRequested) { + return null; + } + return asInlayHints(values, this.client, token); + } catch (error) { + return this.client.handleFailedRequest(InlayHintRequest.type, token, error); + } + } + async resolveInlayHint?(hint: code.InlayHint, token: code.CancellationToken): Promise { + throw new Error("Method not implemented."); + } +} + +async function asInlayHints(values: LSInlayHint[] | undefined | null, client: LanguageClient, token?: code.CancellationToken): Promise { + if (!Array.isArray(values)) { + return undefined; + } + return values.map(lsHint => asInlayHint(lsHint, client, token)); +} + +function asInlayHint(value: LSInlayHint, client: LanguageClient, token?: code.CancellationToken): code.InlayHint { + const label = value.label; + const result = new code.InlayHint(client.protocol2CodeConverter.asPosition(value.position), label); + return result; +}