Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
42ca92a
Implement support for `IncludeDeclaration` in find-all-references.
DanielRosenwasser Nov 21, 2025
6a1bab5
Added some configuration to go-to-implementation.
DanielRosenwasser Nov 21, 2025
145868f
Just hoist argument into variable.
DanielRosenwasser Nov 21, 2025
ec58571
Implemented CodeLens.
DanielRosenwasser Nov 21, 2025
ddb7ef6
Set `IncludeDeclaration` for all tests.
DanielRosenwasser Nov 21, 2025
3ca859c
Merge remote-tracking branch 'origin/main' into codeLenss
DanielRosenwasser Nov 21, 2025
477863e
Added custom `CodeLensData` types to `generate.mts` .
DanielRosenwasser Nov 21, 2025
0596214
Use a handshake for the CodeLens command name, and use an appropriate…
DanielRosenwasser Nov 21, 2025
b140229
Merge remote-tracking branch 'origin/main' into codeLenss
DanielRosenwasser Nov 22, 2025
f3cc278
Adapt code to changes from merge.
DanielRosenwasser Nov 24, 2025
0349935
Accept baselines.
DanielRosenwasser Nov 24, 2025
ed72b96
Add testing for Code Lens (`VerifyBaselineCodeLensForActiveFile`).
DanielRosenwasser Nov 25, 2025
5e9aca3
Update baselines.
DanielRosenwasser Nov 25, 2025
5e32b67
Request codelenses across all files.
DanielRosenwasser Nov 25, 2025
c6ee5d8
Update baselines.
DanielRosenwasser Nov 25, 2025
6ecdca4
Merge remote-tracking branch 'origin/main' into codeLenss
DanielRosenwasser Nov 25, 2025
5625539
Localize titles for lenses.
DanielRosenwasser Nov 25, 2025
494b5f7
Format.
DanielRosenwasser Nov 25, 2025
42d5741
Force ordering of file iteration.
DanielRosenwasser Nov 25, 2025
704cae6
Drive-by fix.
DanielRosenwasser Nov 25, 2025
188f9c7
Update baselines.
DanielRosenwasser Nov 25, 2025
c69f55a
Add test for overloads.
DanielRosenwasser Nov 25, 2025
a2d62fb
Update baselines.
DanielRosenwasser Nov 25, 2025
901d199
Only print on the first-ish overload.
DanielRosenwasser Nov 25, 2025
5714e7a
Update baselines.
DanielRosenwasser Nov 25, 2025
3595713
Add codelens test for function expressions.
DanielRosenwasser Nov 25, 2025
2f270fe
Update baselines.
DanielRosenwasser Nov 25, 2025
37bb977
Don't provide code lenses on any types of function expressions.
DanielRosenwasser Nov 25, 2025
0947f73
Update baselines.
DanielRosenwasser Nov 25, 2025
c4e242b
Rename test.
DanielRosenwasser Nov 25, 2025
d202d83
Add an actual usage inside/outside the function expression to show wh…
DanielRosenwasser Nov 25, 2025
0b91cbb
Update baselines.
DanielRosenwasser Nov 25, 2025
d050804
Add explicit test for toggling `showOnAllFunctions` for references co…
DanielRosenwasser Nov 26, 2025
37b63d8
Update baselines.
DanielRosenwasser Nov 26, 2025
959b5db
Add test for `showOnInterfaceMethods`.
DanielRosenwasser Nov 26, 2025
73d11c4
Update baselines.
DanielRosenwasser Nov 26, 2025
e2ff436
Switch towards using sub-tests.
DanielRosenwasser Nov 26, 2025
c504544
Update baselines.
DanielRosenwasser Nov 26, 2025
46164b3
Add test for `showOnAllClassMethods`.
DanielRosenwasser Nov 26, 2025
3a58afc
Update baselines.
DanielRosenwasser Nov 26, 2025
4357078
Clarify comment.
DanielRosenwasser Nov 26, 2025
73af385
Update stale baselines from before test was modified
DanielRosenwasser Nov 26, 2025
0113665
Drive-by fix.
DanielRosenwasser Nov 26, 2025
580a3d0
Use the JSON-round-tripping trick for code lens arguments in tests.
DanielRosenwasser Nov 27, 2025
727957b
Fix lint issues.
DanielRosenwasser Dec 1, 2025
e11739a
Merge remote-tracking branch 'origin/main' into codeLenss
DanielRosenwasser Dec 1, 2025
d6396a1
Add early bailout for stale `codeLens/resolve`s.
DanielRosenwasser Dec 1, 2025
ff9d7fb
Fix comment and document follow-up.
DanielRosenwasser Dec 1, 2025
00436de
Back out change to avoid issues with upcoming PR.
DanielRosenwasser Dec 1, 2025
bed760c
Merge remote-tracking branch 'origin/main' into codeLenss
DanielRosenwasser Dec 1, 2025
766a5b4
Update const name.
DanielRosenwasser Dec 1, 2025
1fc1f2b
Merge remote-tracking branch 'origin' into codeLenss
DanielRosenwasser Dec 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions _extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,23 @@
],
"default": "verbose",
"description": "Trace TypeScript Go server communication.",
"tags": ["experimental"]
"tags": [
"experimental"
]
},
"typescript.native-preview.pprofDir": {
"type": "string",
"description": "Directory to write pprof profiles to.",
"tags": ["experimental"]
"tags": [
"experimental"
]
},
"typescript.native-preview.tsdk": {
"type": "string",
"description": "Path to the @typescript/native-preview package or tsgo binary directory. If not specified, the extension will look for it in the default location.",
"tags": ["experimental"]
"tags": [
"experimental"
]
}
}
}
Expand Down Expand Up @@ -91,6 +97,11 @@
"title": "Report Issue",
"enablement": "typescript.native-preview.serverRunning",
"category": "TypeScript Native Preview"
},
{
"title": "Show References of CodeLens",
"command": "typescript.native-preview.codeLens.showLocations",
"enablement": "false"
}
]
},
Expand All @@ -115,4 +126,4 @@
"@vscode/vsce": "^3.7.0",
"esbuild": "^0.27.0"
}
}
}
38 changes: 35 additions & 3 deletions _extension/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as vscode from "vscode";
import {
DocumentUri,
LanguageClient,
LanguageClientOptions,
Location,
NotebookDocumentFilter,
Position,
ServerOptions,
TextDocumentFilter,
TransportKind,
Expand All @@ -14,6 +17,8 @@ import {
} from "./util";
import { getLanguageForUri } from "./util";

const codeLensShowLocationsCommandName = "typescript.native-preview.codeLens.showLocations";

export class Client {
private outputChannel: vscode.OutputChannel;
private traceOutputChannel: vscode.OutputChannel;
Expand All @@ -32,6 +37,9 @@ export class Client {
],
outputChannel: this.outputChannel,
traceOutputChannel: this.traceOutputChannel,
initializationOptions: {
codeLensShowLocationsCommandName,
},
diagnosticPullOptions: {
onChange: true,
onSave: true,
Expand Down Expand Up @@ -119,10 +127,34 @@ export class Client {
await this.client.start();
vscode.commands.executeCommand("setContext", "typescript.native-preview.serverRunning", true);
this.onStartedCallbacks.forEach(callback => callback());
return new vscode.Disposable(() => {
if (this.client) {
this.client.stop();

const codeLensLocationsCommand = vscode.commands.registerCommand(codeLensShowLocationsCommandName, (...args: unknown[]) => {
if (args.length !== 3) {
throw new Error("Unexpected number of arguments.");
}

const lspUri = args[0] as DocumentUri;
const lspPosition = args[1] as Position;
const lspLocations = args[2] as Location[];

const editorUri = vscode.Uri.parse(lspUri);
const editorPosition = new vscode.Position(lspPosition.line, lspPosition.character);
const editorLocations = lspLocations.map(loc =>
new vscode.Location(
vscode.Uri.parse(loc.uri),
new vscode.Range(
new vscode.Position(loc.range.start.line, loc.range.start.character),
new vscode.Position(loc.range.end.line, loc.range.end.character),
),
)
);

vscode.commands.executeCommand("editor.action.showReferences", editorUri, editorPosition, editorLocations);
});

return new vscode.Disposable(() => {
this.client?.stop();
codeLensLocationsCommand.dispose();
vscode.commands.executeCommand("setContext", "typescript.native-preview.serverRunning", false);
});
}
Expand Down
4 changes: 2 additions & 2 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -2214,11 +2214,11 @@ func IsWriteAccess(node *Node) bool {
}

func IsWriteAccessForReference(node *Node) bool {
decl := getDeclarationFromName(node)
decl := GetDeclarationFromName(node)
return (decl != nil && declarationIsWriteAccess(decl)) || node.Kind == KindDefaultKeyword || IsWriteAccess(node)
}

func getDeclarationFromName(name *Node) *Declaration {
func GetDeclarationFromName(name *Node) *Declaration {
if name == nil || name.Parent == nil {
return nil
}
Expand Down
8 changes: 8 additions & 0 deletions internal/diagnostics/diagnostics_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions internal/diagnostics/extraDiagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@
"category": "Message",
"code": 100004
},
"{0} references": {
"category": "Message",
"code": 100005
},
"1 reference": {
"category": "Message",
"code": 100006
},
"{0} implementations": {
"category": "Message",
"code": 100007
},
"1 implementation": {
"category": "Message",
"code": 100008
},
"Non-relative paths are not allowed. Did you forget a leading './'?": {
"category": "Error",
"code": 5090
Expand Down
4 changes: 3 additions & 1 deletion internal/fourslash/fourslash.go
Original file line number Diff line number Diff line change
Expand Up @@ -1235,7 +1235,9 @@ func (f *FourslashTest) VerifyBaselineFindAllReferences(
Uri: lsconv.FileNameToDocumentURI(f.activeFilename),
},
Position: f.currentCaretPosition,
Context: &lsproto.ReferenceContext{},
Context: &lsproto.ReferenceContext{
IncludeDeclaration: true,
},
}
resMsg, result, resultOk := sendRequest(t, f, lsproto.TextDocumentReferencesInfo, params)
if resMsg == nil {
Expand Down
196 changes: 196 additions & 0 deletions internal/ls/codelens.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package ls

import (
"context"

"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/diagnostics"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/scanner"
)

func (l *LanguageService) ProvideCodeLenses(ctx context.Context, documentURI lsproto.DocumentUri) (lsproto.CodeLensResponse, error) {
_, file := l.getProgramAndFile(documentURI)

userPrefs := l.UserPreferences()
if !userPrefs.ReferencesCodeLensEnabled && !userPrefs.ImplementationsCodeLensEnabled {
return lsproto.CodeLensResponse{}, nil
}

var lenses []*lsproto.CodeLens
var visit func(node *ast.Node) bool
visit = func(node *ast.Node) bool {
if ctx.Err() != nil {
return true
}

if userPrefs.ReferencesCodeLensEnabled && isValidReferenceLensNode(node, userPrefs) {
lenses = append(lenses, l.newCodeLensForNode(documentURI, file, node, lsproto.CodeLensKindReferences))
}

if userPrefs.ImplementationsCodeLensEnabled && isValidImplementationsCodeLensNode(node, userPrefs) {
lenses = append(lenses, l.newCodeLensForNode(documentURI, file, node, lsproto.CodeLensKindImplementations))
}

node.ForEachChild(visit)
return false
}

visit(file.AsNode())

return lsproto.CodeLensResponse{
CodeLenses: &lenses,
}, nil
}

func (l *LanguageService) ResolveCodeLens(ctx context.Context, codeLens *lsproto.CodeLens, showLocationsCommandName *string) (*lsproto.CodeLens, error) {
uri := codeLens.Data.Uri
textDoc := lsproto.TextDocumentIdentifier{
Uri: uri,
}

var locs []lsproto.Location
var lensTitle string
switch codeLens.Data.Kind {
case lsproto.CodeLensKindReferences:
references, err := l.ProvideReferences(ctx, &lsproto.ReferenceParams{
TextDocument: textDoc,
Position: codeLens.Range.Start,
Context: &lsproto.ReferenceContext{
// Don't include the declaration in the references count.
IncludeDeclaration: false,
},
})
if err != nil {
return nil, err
}

if references.Locations != nil {
locs = *references.Locations
}

if len(locs) == 1 {
lensTitle = diagnostics.X_1_reference.Message()
} else {
lensTitle = diagnostics.X_0_references.Format(len(locs))
}
case lsproto.CodeLensKindImplementations:
// "Force" link support to be false so that we only get `Locations` back,
// and don't include the "current" node in the results.
findImplsOptions := provideImplementationsOpts{
requireLocationsResult: true,
dropOriginNodes: true,
}
implementations, err := l.provideImplementationsEx(ctx, &lsproto.ImplementationParams{
TextDocument: textDoc,
Position: codeLens.Range.Start,
}, findImplsOptions)
if err != nil {
return nil, err
}

if implementations.Locations != nil {
locs = *implementations.Locations
}

if len(locs) == 1 {
lensTitle = diagnostics.X_1_implementation.Message()
} else {
lensTitle = diagnostics.X_0_implementations.Format(len(locs))
}
}

cmd := &lsproto.Command{
Title: lensTitle,
}
if len(locs) > 0 && showLocationsCommandName != nil {
cmd.Command = *showLocationsCommandName
cmd.Arguments = &[]any{
uri,
codeLens.Range.Start,
locs,
}
}

codeLens.Command = cmd
return codeLens, nil
}

func (l *LanguageService) newCodeLensForNode(fileUri lsproto.DocumentUri, file *ast.SourceFile, node *ast.Node, kind lsproto.CodeLensKind) *lsproto.CodeLens {
nodeForRange := node
nodeName := node.Name()
if nodeName != nil {
nodeForRange = nodeName
}
pos := scanner.SkipTrivia(file.Text(), nodeForRange.Pos())

return &lsproto.CodeLens{
Range: lsproto.Range{
Start: l.converters.PositionToLineAndCharacter(file, core.TextPos(pos)),
End: l.converters.PositionToLineAndCharacter(file, core.TextPos(node.End())),
},
Data: &lsproto.CodeLensData{
Kind: kind,
Uri: fileUri,
},
}
}

func isValidImplementationsCodeLensNode(node *ast.Node, userPrefs *lsutil.UserPreferences) bool {
switch node.Kind {
// Always show on interfaces
case ast.KindInterfaceDeclaration:
// TODO: ast.KindTypeAliasDeclaration?
return true

// If configured, show on interface methods
case ast.KindMethodSignature:
return userPrefs.ImplementationsCodeLensShowOnInterfaceMethods && node.Parent.Kind == ast.KindInterfaceDeclaration

// If configured, show on all class methods - but not private ones.
case ast.KindMethodDeclaration:
if userPrefs.ImplementationsCodeLensShowOnAllClassMethods && node.Parent.Kind == ast.KindClassDeclaration {
return !ast.HasModifier(node, ast.ModifierFlagsPrivate) && node.Name().Kind != ast.KindPrivateIdentifier
}
fallthrough

// Always show on abstract classes/properties/methods
case ast.KindClassDeclaration, ast.KindConstructor,
ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration:
return ast.HasModifier(node, ast.ModifierFlagsAbstract)
}

return false
}

func isValidReferenceLensNode(node *ast.Node, userPrefs *lsutil.UserPreferences) bool {
switch node.Kind {
case ast.KindFunctionDeclaration, ast.KindFunctionExpression:
if userPrefs.ReferencesCodeLensShowOnAllFunctions {
return true
}
fallthrough

case ast.KindVariableDeclaration:
return ast.GetCombinedModifierFlags(node)&ast.ModifierFlagsExport != 0

case ast.KindClassDeclaration, ast.KindInterfaceDeclaration, ast.KindTypeAliasDeclaration, ast.KindEnumDeclaration, ast.KindEnumMember:
return true

case ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor,
ast.KindGetAccessor, ast.KindSetAccessor,
ast.KindPropertyDeclaration, ast.KindPropertySignature:
// Don't show if child and parent have same start
// For https://github.com/microsoft/vscode/issues/90396
// !!!

switch node.Parent.Kind {
case ast.KindClassDeclaration, ast.KindInterfaceDeclaration, ast.KindTypeLiteral:
return true
}
}

return false
}
Loading