diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 92a36e5f2d7..5d6d60b0c6c 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -13,7 +13,8 @@ "mappedEditsProvider", "codeActionAI", "codeActionRanges", - "documentPaste" + "documentPaste", + "editorHoverVerbosityLevel" ], "capabilities": { "virtualWorkspaces": { @@ -825,16 +826,16 @@ ], "enumDescriptions": [ "%typescript.locale.auto%", - "Deutsch", - "español", + "Deutsch", + "español", "English", - "français", - "italiano", - "日本語", - "한국어", - "русский", - "中文(简体)", - "中文(繁體)" + "français", + "italiano", + "日本語", + "한국어", + "русский", + "中文(简体)", + "中文(繁體)" ], "markdownDescription": "%typescript.locale%", "scope": "window" @@ -1483,14 +1484,27 @@ "type": "boolean", "default": false, "description": "%configuration.updateImportsOnPaste%", - "tags": ["experimental"] + "tags": [ + "experimental" + ] }, "typescript.experimental.updateImportsOnPaste": { "scope": "window", "type": "boolean", "default": false, "description": "%configuration.updateImportsOnPaste%", - "tags": ["experimental"] + "tags": [ + "experimental" + ] + }, + "typescript.experimental.expandableHover": { + "type": "boolean", + "default": false, + "description": "%configuration.expandableHover%", + "scope": "window", + "tags": [ + "experimental" + ] } } }, diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index d2a0ca892fa..d9c4ddb2a65 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -237,6 +237,7 @@ "configuration.tsserver.web.typeAcquisition.enabled": "Enable/disable package acquisition on the web. This enables IntelliSense for imported packages. Requires `#typescript.tsserver.web.projectWideIntellisense.enabled#`. Currently not supported for Safari.", "configuration.tsserver.nodePath": "Run TS Server on a custom Node installation. This can be a path to a Node executable, or 'node' if you want VS Code to detect a Node installation.", "configuration.updateImportsOnPaste": "Automatically update imports when pasting code. Requires TypeScript 5.6+.", + "configuration.expandableHover": "(Experimental) Enable/disable expanding on hover.", "walkthroughs.nodejsWelcome.title": "Get started with JavaScript and Node.js", "walkthroughs.nodejsWelcome.description": "Make the most of Visual Studio Code's first-class JavaScript experience.", "walkthroughs.nodejsWelcome.downloadNode.forMacOrWindows.title": "Install Node.js", diff --git a/extensions/typescript-language-features/src/languageFeatures/hover.ts b/extensions/typescript-language-features/src/languageFeatures/hover.ts index 3012658036f..edb08d029fa 100644 --- a/extensions/typescript-language-features/src/languageFeatures/hover.ts +++ b/extensions/typescript-language-features/src/languageFeatures/hover.ts @@ -11,10 +11,11 @@ import { DocumentSelector } from '../configuration/documentSelector'; import { documentationToMarkdown } from './util/textRendering'; import * as typeConverters from '../typeConverters'; import FileConfigurationManager from './fileConfigurationManager'; - +import { API } from '../tsServer/api'; class TypeScriptHoverProvider implements vscode.HoverProvider { + private lastHoverAndLevel: [vscode.Hover, number] | undefined; public constructor( private readonly client: ITypeScriptServiceClient, @@ -24,17 +25,24 @@ class TypeScriptHoverProvider implements vscode.HoverProvider { public async provideHover( document: vscode.TextDocument, position: vscode.Position, - token: vscode.CancellationToken - ): Promise { + token: vscode.CancellationToken, + context?: vscode.HoverContext, + ): Promise { const filepath = this.client.toOpenTsFilePath(document); if (!filepath) { return undefined; } + const enableExpandableHover = vscode.workspace.getConfiguration('typescript').get('experimental.expandableHover'); + let verbosityLevel: number | undefined; + if (enableExpandableHover && this.client.apiVersion.gte(API.v570)) { + verbosityLevel = Math.max(0, this.getPreviousLevel(context?.previousHover) + (context?.verbosityDelta ?? 0)); + } + const args = { ...typeConverters.Position.toFileLocationRequestArgs(filepath, position), verbosityLevel }; + const response = await this.client.interruptGetErr(async () => { await this.fileConfigurationManager.ensureConfigurationForDocument(document, token); - const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); return this.client.execute('quickinfo', args, token); }); @@ -42,9 +50,24 @@ class TypeScriptHoverProvider implements vscode.HoverProvider { return undefined; } - return new vscode.Hover( - this.getContents(document.uri, response.body, response._serverType), - typeConverters.Range.fromTextSpan(response.body)); + const contents = this.getContents(document.uri, response.body, response._serverType); + const range = typeConverters.Range.fromTextSpan(response.body); + const hover = verbosityLevel !== undefined ? + new vscode.VerboseHover( + contents, + range, + // @ts-expect-error + /*canIncreaseVerbosity*/ response.body.canIncreaseVerbosityLevel, + /*canDecreaseVerbosity*/ verbosityLevel !== 0 + ) : new vscode.Hover( + contents, + range + ); + + if (verbosityLevel !== undefined) { + this.lastHoverAndLevel = [hover, verbosityLevel]; + } + return hover; } private getContents( @@ -72,6 +95,13 @@ class TypeScriptHoverProvider implements vscode.HoverProvider { parts.push(md); return parts; } + + private getPreviousLevel(previousHover: vscode.Hover | undefined): number { + if (previousHover && this.lastHoverAndLevel && this.lastHoverAndLevel[0] === previousHover) { + return this.lastHoverAndLevel[1]; + } + return 0; + } } export function register( diff --git a/extensions/typescript-language-features/src/tsServer/api.ts b/extensions/typescript-language-features/src/tsServer/api.ts index b70810989ec..7b6d160952e 100644 --- a/extensions/typescript-language-features/src/tsServer/api.ts +++ b/extensions/typescript-language-features/src/tsServer/api.ts @@ -30,6 +30,7 @@ export class API { public static readonly v540 = API.fromSimpleString('5.4.0'); public static readonly v550 = API.fromSimpleString('5.5.0'); public static readonly v560 = API.fromSimpleString('5.6.0'); + public static readonly v570 = API.fromSimpleString('5.7.0'); public static fromVersionString(versionString: string): API { let version = semver.valid(versionString); diff --git a/extensions/typescript-language-features/tsconfig.json b/extensions/typescript-language-features/tsconfig.json index 44097665a9c..0b599aecc60 100644 --- a/extensions/typescript-language-features/tsconfig.json +++ b/extensions/typescript-language-features/tsconfig.json @@ -17,5 +17,6 @@ "../../src/vscode-dts/vscode.proposed.multiDocumentHighlightProvider.d.ts", "../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts", "../../src/vscode-dts/vscode.proposed.documentPaste.d.ts", + "../../src/vscode-dts/vscode.proposed.editorHoverVerbosityLevel.d.ts", ] }