Skip to content

Commit 0f9abf3

Browse files
authored
[typescript-language-features] Expandable hover (#228255)
* add verbosity level to hover provider * cleanup * update minimum ts version * get `canIncreaseVerbosityLevel` from server response * add expandable hover experimental setting
1 parent c45f9a3 commit 0f9abf3

File tree

5 files changed

+66
-19
lines changed

5 files changed

+66
-19
lines changed

extensions/typescript-language-features/package.json

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"mappedEditsProvider",
1414
"codeActionAI",
1515
"codeActionRanges",
16-
"documentPaste"
16+
"documentPaste",
17+
"editorHoverVerbosityLevel"
1718
],
1819
"capabilities": {
1920
"virtualWorkspaces": {
@@ -825,16 +826,16 @@
825826
],
826827
"enumDescriptions": [
827828
"%typescript.locale.auto%",
828-
"Deutsch",
829-
"español",
829+
"Deutsch",
830+
"español",
830831
"English",
831-
"français",
832-
"italiano",
833-
"日本語",
834-
"한국어",
835-
"русский",
836-
"中文(简体)",
837-
"中文(繁體)"
832+
"français",
833+
"italiano",
834+
"日本語",
835+
"한국어",
836+
"русский",
837+
"中文(简体)",
838+
"中文(繁體)"
838839
],
839840
"markdownDescription": "%typescript.locale%",
840841
"scope": "window"
@@ -1483,14 +1484,27 @@
14831484
"type": "boolean",
14841485
"default": false,
14851486
"description": "%configuration.updateImportsOnPaste%",
1486-
"tags": ["experimental"]
1487+
"tags": [
1488+
"experimental"
1489+
]
14871490
},
14881491
"typescript.experimental.updateImportsOnPaste": {
14891492
"scope": "window",
14901493
"type": "boolean",
14911494
"default": false,
14921495
"description": "%configuration.updateImportsOnPaste%",
1493-
"tags": ["experimental"]
1496+
"tags": [
1497+
"experimental"
1498+
]
1499+
},
1500+
"typescript.experimental.expandableHover": {
1501+
"type": "boolean",
1502+
"default": false,
1503+
"description": "%configuration.expandableHover%",
1504+
"scope": "window",
1505+
"tags": [
1506+
"experimental"
1507+
]
14941508
}
14951509
}
14961510
},

extensions/typescript-language-features/package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@
237237
"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.",
238238
"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.",
239239
"configuration.updateImportsOnPaste": "Automatically update imports when pasting code. Requires TypeScript 5.6+.",
240+
"configuration.expandableHover": "(Experimental) Enable/disable expanding on hover.",
240241
"walkthroughs.nodejsWelcome.title": "Get started with JavaScript and Node.js",
241242
"walkthroughs.nodejsWelcome.description": "Make the most of Visual Studio Code's first-class JavaScript experience.",
242243
"walkthroughs.nodejsWelcome.downloadNode.forMacOrWindows.title": "Install Node.js",

extensions/typescript-language-features/src/languageFeatures/hover.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ import { DocumentSelector } from '../configuration/documentSelector';
1111
import { documentationToMarkdown } from './util/textRendering';
1212
import * as typeConverters from '../typeConverters';
1313
import FileConfigurationManager from './fileConfigurationManager';
14-
14+
import { API } from '../tsServer/api';
1515

1616

1717
class TypeScriptHoverProvider implements vscode.HoverProvider {
18+
private lastHoverAndLevel: [vscode.Hover, number] | undefined;
1819

1920
public constructor(
2021
private readonly client: ITypeScriptServiceClient,
@@ -24,27 +25,49 @@ class TypeScriptHoverProvider implements vscode.HoverProvider {
2425
public async provideHover(
2526
document: vscode.TextDocument,
2627
position: vscode.Position,
27-
token: vscode.CancellationToken
28-
): Promise<vscode.Hover | undefined> {
28+
token: vscode.CancellationToken,
29+
context?: vscode.HoverContext,
30+
): Promise<vscode.VerboseHover | undefined> {
2931
const filepath = this.client.toOpenTsFilePath(document);
3032
if (!filepath) {
3133
return undefined;
3234
}
3335

36+
const enableExpandableHover = vscode.workspace.getConfiguration('typescript').get('experimental.expandableHover');
37+
let verbosityLevel: number | undefined;
38+
if (enableExpandableHover && this.client.apiVersion.gte(API.v570)) {
39+
verbosityLevel = Math.max(0, this.getPreviousLevel(context?.previousHover) + (context?.verbosityDelta ?? 0));
40+
}
41+
const args = { ...typeConverters.Position.toFileLocationRequestArgs(filepath, position), verbosityLevel };
42+
3443
const response = await this.client.interruptGetErr(async () => {
3544
await this.fileConfigurationManager.ensureConfigurationForDocument(document, token);
3645

37-
const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position);
3846
return this.client.execute('quickinfo', args, token);
3947
});
4048

4149
if (response.type !== 'response' || !response.body) {
4250
return undefined;
4351
}
4452

45-
return new vscode.Hover(
46-
this.getContents(document.uri, response.body, response._serverType),
47-
typeConverters.Range.fromTextSpan(response.body));
53+
const contents = this.getContents(document.uri, response.body, response._serverType);
54+
const range = typeConverters.Range.fromTextSpan(response.body);
55+
const hover = verbosityLevel !== undefined ?
56+
new vscode.VerboseHover(
57+
contents,
58+
range,
59+
// @ts-expect-error
60+
/*canIncreaseVerbosity*/ response.body.canIncreaseVerbosityLevel,
61+
/*canDecreaseVerbosity*/ verbosityLevel !== 0
62+
) : new vscode.Hover(
63+
contents,
64+
range
65+
);
66+
67+
if (verbosityLevel !== undefined) {
68+
this.lastHoverAndLevel = [hover, verbosityLevel];
69+
}
70+
return hover;
4871
}
4972

5073
private getContents(
@@ -72,6 +95,13 @@ class TypeScriptHoverProvider implements vscode.HoverProvider {
7295
parts.push(md);
7396
return parts;
7497
}
98+
99+
private getPreviousLevel(previousHover: vscode.Hover | undefined): number {
100+
if (previousHover && this.lastHoverAndLevel && this.lastHoverAndLevel[0] === previousHover) {
101+
return this.lastHoverAndLevel[1];
102+
}
103+
return 0;
104+
}
75105
}
76106

77107
export function register(

extensions/typescript-language-features/src/tsServer/api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export class API {
3030
public static readonly v540 = API.fromSimpleString('5.4.0');
3131
public static readonly v550 = API.fromSimpleString('5.5.0');
3232
public static readonly v560 = API.fromSimpleString('5.6.0');
33+
public static readonly v570 = API.fromSimpleString('5.7.0');
3334

3435
public static fromVersionString(versionString: string): API {
3536
let version = semver.valid(versionString);

extensions/typescript-language-features/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@
1717
"../../src/vscode-dts/vscode.proposed.multiDocumentHighlightProvider.d.ts",
1818
"../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts",
1919
"../../src/vscode-dts/vscode.proposed.documentPaste.d.ts",
20+
"../../src/vscode-dts/vscode.proposed.editorHoverVerbosityLevel.d.ts",
2021
]
2122
}

0 commit comments

Comments
 (0)