Skip to content
Merged
Show file tree
Hide file tree
Changes from 51 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
17 changes: 14 additions & 3 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 Down
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
16 changes: 16 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
1 change: 1 addition & 0 deletions internal/fourslash/baselineutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const (
renameCmd baselineCommand = "findRenameLocations"
signatureHelpCmd baselineCommand = "SignatureHelp"
smartSelectionCmd baselineCommand = "Smart Selection"
codeLensesCmd baselineCommand = "Code Lenses"
)

type baselineCommand string
Expand Down
87 changes: 83 additions & 4 deletions internal/fourslash/fourslash.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ func (w *lspWriter) Write(msg *lsproto.Message) error {
return nil
}

func (r *lspWriter) Close() {
close(r.c)
func (w *lspWriter) Close() {
close(w.c)
}

var (
Expand Down Expand Up @@ -277,13 +277,16 @@ func (f *FourslashTest) nextID() int32 {
return id
}

const showCodeLensLocationsCommandName = "typescript.showCodeLensLocations"

func (f *FourslashTest) initialize(t *testing.T, capabilities *lsproto.ClientCapabilities) {
params := &lsproto.InitializeParams{
Locale: ptrTo("en-US"),
InitializationOptions: &lsproto.InitializationOptions{
// Hack: disable push diagnostics entirely, since the fourslash runner does not
// yet gracefully handle non-request messages.
DisablePushDiagnostics: ptrTo(true),
DisablePushDiagnostics: ptrTo(true),
CodeLensShowLocationsCommandName: ptrTo(showCodeLensLocationsCommandName),
},
}
params.Capabilities = getCapabilitiesWithDefaults(capabilities)
Expand Down Expand Up @@ -1302,7 +1305,9 @@ func (f *FourslashTest) VerifyBaselineFindAllReferences(
Uri: lsconv.FileNameToDocumentURI(f.activeFilename),
},
Position: f.currentCaretPosition,
Context: &lsproto.ReferenceContext{},
Context: &lsproto.ReferenceContext{
IncludeDeclaration: true,
},
}
result := sendRequest(t, f, lsproto.TextDocumentReferencesInfo, params)
f.addResultToBaseline(t, findAllReferencesCmd, f.getBaselineForLocationsWithFileContents(*result.Locations, baselineFourslashLocationsOptions{
Expand All @@ -1313,6 +1318,61 @@ func (f *FourslashTest) VerifyBaselineFindAllReferences(
}
}

func (f *FourslashTest) VerifyBaselineCodeLens(t *testing.T, preferences *lsutil.UserPreferences) {
if preferences != nil {
reset := f.ConfigureWithReset(t, preferences)
defer reset()
}

foundAtLeastOneCodeLens := false
for _, openFile := range slices.Sorted(maps.Keys(f.openFiles)) {
params := &lsproto.CodeLensParams{
TextDocument: lsproto.TextDocumentIdentifier{
Uri: lsconv.FileNameToDocumentURI(openFile),
},
}

unresolvedCodeLensList := sendRequest(t, f, lsproto.TextDocumentCodeLensInfo, params)
if unresolvedCodeLensList.CodeLenses == nil || len(*unresolvedCodeLensList.CodeLenses) == 0 {
continue
}
foundAtLeastOneCodeLens = true

for _, unresolvedCodeLens := range *unresolvedCodeLensList.CodeLenses {
assert.Assert(t, unresolvedCodeLens != nil)
resolvedCodeLens := sendRequest(t, f, lsproto.CodeLensResolveInfo, unresolvedCodeLens)
assert.Assert(t, resolvedCodeLens != nil)
assert.Assert(t, resolvedCodeLens.Command != nil, "Expected resolved code lens to have a command.")
if len(resolvedCodeLens.Command.Command) > 0 {
assert.Equal(t, resolvedCodeLens.Command.Command, showCodeLensLocationsCommandName)
}

var locations []lsproto.Location
// commandArgs: (DocumentUri, Position, Location[])
if commandArgs := resolvedCodeLens.Command.Arguments; commandArgs != nil {
locs, err := roundtripThroughJson[[]lsproto.Location]((*commandArgs)[2])
if err != nil {
t.Fatalf("failed to re-encode code lens locations: %v", err)
}
locations = locs
}

f.addResultToBaseline(t, codeLensesCmd, f.getBaselineForLocationsWithFileContents(locations, baselineFourslashLocationsOptions{
marker: &RangeMarker{
fileName: openFile,
LSRange: resolvedCodeLens.Range,
Range: f.converters.FromLSPRange(f.getScriptInfo(openFile), resolvedCodeLens.Range),
},
markerName: "/*CODELENS: " + resolvedCodeLens.Command.Title + "*/",
}))
}
}

if !foundAtLeastOneCodeLens {
t.Fatalf("Expected at least one code lens in any open file, but got none.")
}
}

func (f *FourslashTest) MarkTestAsStradaServer() {
f.isStradaServer = true
}
Expand Down Expand Up @@ -1834,6 +1894,25 @@ func ptrTo[T any](v T) *T {
return &v
}

// This function is intended for spots where a complex
// value needs to be reinterpreted following some prior JSON deserialization.
// The default deserializer for `any` properties will give us a map at runtime,
// but we want to validate against, and use, the types as returned from the the language service.
//
// Use this function sparingly. You can treat it as a "map-to-struct" converter,
// but updating the original types is probably better in most cases.
func roundtripThroughJson[T any](value any) (T, error) {
var result T
bytes, err := json.Marshal(value)
if err != nil {
return result, fmt.Errorf("failed to marshal value to JSON: %w", err)
}
if err := json.Unmarshal(bytes, &result); err != nil {
return result, fmt.Errorf("failed to unmarshal value from JSON: %w", err)
}
return result, nil
}

// Insert text at the current caret position.
func (f *FourslashTest) Insert(t *testing.T, text string) {
f.typeText(t, text)
Expand Down
53 changes: 53 additions & 0 deletions internal/fourslash/tests/codeLensFunctionExpressions01_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package fourslash_test

import (
"testing"

"github.com/microsoft/typescript-go/internal/fourslash"
"github.com/microsoft/typescript-go/internal/ls/lsutil"
"github.com/microsoft/typescript-go/internal/testutil"
)

func TestCodeLensFunctionExpressions01(t *testing.T) {
t.Parallel()
defer testutil.RecoverAndFail(t, "Panic on fourslash test")

const content = `
// @filename: anonymousFunctionExpressions.ts
export let anonFn1 = function () {};
export const anonFn2 = function () {};

let anonFn3 = function () {};
const anonFn4 = function () {};

// @filename: arrowFunctions.ts
export let arrowFn1 = () => {};
export const arrowFn2 = () => {};

let arrowFn3 = () => {};
const arrowFn4 = () => {};

// @filename: namedFunctions.ts
export let namedFn1 = function namedFn1() {
namedFn1();
}
namedFn1();

export const namedFn2 = function namedFn2() {
namedFn2();
}
namedFn2();

let namedFn3 = function namedFn3() {};
const namedFn4 = function namedFn4() {};
`
f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
f.VerifyBaselineCodeLens(t, &lsutil.UserPreferences{
ReferencesCodeLensEnabled: true,
ReferencesCodeLensShowOnAllFunctions: true,

ImplementationsCodeLensEnabled: true,
ImplementationsCodeLensShowOnInterfaceMethods: true,
ImplementationsCodeLensShowOnAllClassMethods: true,
})
}
Loading