Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds an “expandable hover” feature end-to-end: a proposed VS Code hover API that can request increased “verbosity”, an LSP extension (verbosityLevel on HoverParams + canIncreaseVerbosity on Hover), server plumbing to honor it, and fourslash coverage/baselines for the new hover behavior.
Changes:
- Extend LSP hover request/response shapes to support verbosity (
verbosityLevel) and server-provided expandability (canIncreaseVerbosity). - Add a VS Code extension hover provider that uses the proposed hover verbosity API and sends
verbosityLevelover LSP (disabling the languageclient’s built-in hover). - Add fourslash harness support + many new baselines/tests for hover verbosity scenarios, plus conversion-script support for
verify.baselineQuickInfo({...}).
Reviewed changes
Copilot reviewed 91 out of 93 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
internal/lsp/server.go |
Routes hover requests to verbose hover implementation when verbosityLevel is present. |
internal/lsp/lsproto/lsp_generated.go |
Adds verbosityLevel to HoverParams, canIncreaseVerbosity to Hover, and a capabilities flag. |
internal/lsp/lsproto/_generate/generate.mts |
Patches the LSP model so the generator emits the new hover fields/capabilities. |
_extension/src/languageFeatures/hover.ts |
New VS Code hover provider that requests verbose hover via LSP and maps expand/decrease flags. |
_extension/src/vscode.proposed.editorHoverVerbosityLevel.d.ts |
Declares the proposed VS Code API surface (VerboseHover, HoverContext, etc.). |
_extension/src/client.ts |
Advertises hover verbosity capability + registers the custom hover provider; disables LSP hover via middleware. |
_extension/package.json |
Enables the editorHoverVerbosityLevel proposal and bumps VS Code engine/types. |
package-lock.json |
Updates VS Code typings version to match the engine bump. |
internal/checker/printer.go |
Adds verbosity-aware type/signature/symbol-to-declaration stringification helpers. |
internal/checker/nodebuilder.go |
Adds verbosity-aware nodebuilder entry points and symbol-declaration rendering helpers. |
internal/checker/services.go |
Introduces Checker.IsLibType used to prevent expanding default-lib types. |
internal/ls/completions.go |
Refactors completion item detail generation; threads hover quickinfo via the updated quickinfo helper signature. |
internal/ls/string_completions.go |
Updates call into completion details helper after signature refactor. |
internal/fourslash/fourslash.go |
Adds VerifyBaselineHoverWithVerbosity helper for producing multi-verbosity hover baselines. |
internal/fourslash/baselineutil.go |
Uses stable sorting when inserting multiple tooltips at the same position. |
internal/fourslash/_scripts/convertFourslash.mts |
Adds support for verify.baselineQuickInfo({...verbosity...}) conversion into Go tests. |
internal/fourslash/_scripts/unparsedTests.txt |
Removes now-supported quickinfoVerbosity tests from the “unparsed” list. |
internal/fourslash/_scripts/failingTests.txt |
Updates the list of failing tests (removals). |
internal/fourslash/tests/quickinfoVerbosityNamespaceDefaultExport_test.go |
New manual fourslash test for namespace default export verbosity behavior. |
internal/fourslash/tests/gen/quickinfoVerbosity1_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosity2_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosity3_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityClass1_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityClass2_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityConditionalType_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityEnum_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityFunction_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityImport_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityIndexedAccessType_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityIndexSignature_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityIndexType_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityInterface1_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityInterface2_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityIntersection1_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityJs_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityLibType_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityMappedType_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityNamespace_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityNamespaceDefaultExport_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityNoErrorTruncation1_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityObjectType1_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityRecursiveType_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityServer_test.go |
Generated hover verbosity test (server mode). |
internal/fourslash/tests/gen/quickinfoVerbosityToplevelTruncation1_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityToplevelTruncation2_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityTruncation1_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityTruncation2_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityTuple_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityTypeof_test.go |
Generated hover verbosity test. |
internal/fourslash/tests/gen/quickinfoVerbosityTypeParameter_test.go |
Generated hover verbosity test. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosity1.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosity2.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityConditionalType.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityFunction.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityIndexSignature.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityIndexType.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityIndexedAccessType.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityIntersection1.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityLibType.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityMappedType.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityNamespaceDefaultExport.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityNoErrorTruncation1.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityObjectType1.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityRecursiveType.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityServer.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityTruncation1.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityToplevelTruncation1.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityToplevelTruncation2.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityTypeof.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickinfoVerbosityTypeParameter.baseline |
New hover verbosity baseline. |
testdata/baselines/reference/fourslash/quickInfo/quickInfoDisplayPartsTypeParameterInFunction.baseline |
Updates baseline output for type parameter/parameter quickinfo display parts. |
testdata/baselines/reference/fourslash/quickInfo/quickInfoDisplayPartsTypeParameterInClass.baseline |
Updates baseline output for class type parameter/parameter quickinfo display parts. |
testdata/baselines/reference/fourslash/quickInfo/quickInfoDisplayPartsTypeParameterInTypeAlias.baseline |
Updates baseline output for type parameter quickinfo display parts. |
testdata/baselines/reference/fourslash/quickInfo/quickInfoDisplayPartsTypeParameterInFunctionLikeInTypeAlias.baseline |
Updates baseline output for type parameter quickinfo display parts. |
testdata/baselines/reference/fourslash/quickInfo/quickInfoDisplayPartsEnum1.baseline |
Updates enum quickinfo baseline (e.g. const enum rendering). |
testdata/baselines/reference/fourslash/quickInfo/quickInfoDisplayPartsEnum2.baseline |
Updates enum quickinfo baseline (e.g. const enum rendering). |
testdata/baselines/reference/fourslash/quickInfo/quickInfoDisplayPartsEnum3.baseline |
Updates enum quickinfo baseline (e.g. const enum rendering). |
testdata/baselines/reference/submodule/compiler/privateFieldsInClassExpressionDeclaration.js |
Updates compiler baseline output for private fields in .d.ts emit. |
testdata/baselines/reference/submodule/compiler/privateFieldsInClassExpressionDeclaration.js.diff |
Updates compiler baseline diff for the same private field emit change. |
| const verbosityLevels: Record<string, number[]> = {}; | ||
| for (const prop of verbosityArg.properties) { | ||
| if (!ts.isPropertyAssignment(prop)) { | ||
| return undefined; | ||
| } | ||
| let name: string; | ||
| if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) { | ||
| name = prop.name.text; | ||
| } | ||
| else if (ts.isNumericLiteral(prop.name)) { | ||
| name = prop.name.text; | ||
| } | ||
| else { | ||
| return undefined; | ||
| } | ||
| if (ts.isArrayLiteralExpression(prop.initializer)) { | ||
| const levels: number[] = []; | ||
| for (const elem of prop.initializer.elements) { | ||
| if (!ts.isNumericLiteral(elem)) { | ||
| return undefined; | ||
| } | ||
| levels.push(Number(elem.text)); | ||
| } | ||
| verbosityLevels[name] = levels; | ||
| } | ||
| else if (ts.isNumericLiteral(prop.initializer)) { | ||
| verbosityLevels[name] = [Number(prop.initializer.text)]; | ||
| } | ||
| else { | ||
| return undefined; | ||
| } |
There was a problem hiding this comment.
parseBaselineQuickInfo is declared to return VerifyBaselineQuickInfoCmd[], but several branches return undefined (e.g. when encountering unsupported property shapes). With strict: true in this folder’s tsconfig, this won’t type-check and will break the fourslash conversion scripts. Replace these return undefined paths with throw new Error(...) (or change the function’s return type to include undefined and handle it at the call site).
| // ^ | ||
| // | ---------------------------------------------------------------------- | ||
| // | ```tsx | ||
| // | (parameter) a: U | ||
| // | (parameter) a: U extends string | ||
| // | ``` |
There was a problem hiding this comment.
The hover text shows (parameter) a: U extends string, which is not valid TypeScript type syntax for a parameter (the parameter’s type is U; the extends string part is a constraint on the type parameter declaration). This looks like a regression in quickinfo rendering for constrained type parameters; the hover should keep the parameter type as U (while type parameter hovers can show the constraint).
| // ^ | ||
| // | ---------------------------------------------------------------------- | ||
| // | ```tsx | ||
| // | (parameter) a: T | ||
| // | (parameter) a: T extends c<string> | ||
| // | ``` | ||
| // | |
There was a problem hiding this comment.
The baseline now renders constructor/method parameters as T extends c<string> / U extends c<string>, which is not valid parameter type syntax (constraints belong on the type parameter declarations, not at use sites). This suggests the quickinfo printer is incorrectly formatting type parameter uses as declarations when constrained.
|
One icky thing is that |
| // At maxExpansionDepth <= 0, no expansion will occur so we return false to allow type-node reuse. | ||
| func (b *NodeBuilderImpl) canPossiblyExpandType(t *Type) bool { | ||
| if b.ctx.maxExpansionDepth < 0 { | ||
| if b.ctx.maxExpansionDepth <= 0 { |
There was a problem hiding this comment.
Why did this change? Couldn't we still fall into the case where maxDepth is 0 and depth is 0 and canIncreaseExpansionDepth is false and so this would return true before?
There was a problem hiding this comment.
This is the bit that makes it so that simply enabling the client capability / sending level 0 does not change the output formatting. But otherwise I'm not sure what you mean
There was a problem hiding this comment.
Yeah, but IIRC if we have a request with level set to 0, and we make canPossiblyExpandType return false, we will do type node reuse and not compute canIncreaseVerbosityLevel.
There was a problem hiding this comment.
Ah, yeah, we have to still do a little work I guess so this is set. I guess I didn't notice because the boolean is hidden if false, but obviously fourslash will keep querying with higher levels. Probably I should assert if we attempt to go higher but the previous response said no... maybe.
Closes #2457
This is a proposed VS Code feature. To make this work, we add the new API ontop of the LSP, then disable the LSP client's hover provider, in favor of one with the feature.