Skip to content

Commit 60b2856

Browse files
CopilotDanielRosenwasserandrewbranch
authored
Fix go-to-definition functionality at JS JSX tag edge (microsoft/typescript-go#4909)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com> Co-authored-by: andrewbranch <3277153+andrewbranch@users.noreply.github.com>
1 parent 41ec0d4 commit 60b2856

4 files changed

Lines changed: 60 additions & 4 deletions

File tree

tsc/_packages/native-preview/src/ast/astnav.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,19 @@ function getTokenAtPositionImpl(
130130
left: 0,
131131
};
132132

133+
const getIncludedPrecedingToken = (subtree: Node): Node | undefined => {
134+
const child = findPrecedingTokenImpl(sourceFile, position, subtree);
135+
if (child !== undefined && child.end === position && includePrecedingTokenAtEndPosition!(child)) {
136+
return child;
137+
}
138+
return undefined;
139+
};
140+
133141
const testNode = (node: Node): number => {
134142
if (node.kind !== SyntaxKind.EndOfFile && node.end === position && includePrecedingTokenAtEndPosition !== undefined) {
143+
if (state.prevSubtree !== undefined && getIncludedPrecedingToken(state.prevSubtree) !== undefined) {
144+
return 0;
145+
}
135146
state.prevSubtree = node;
136147
}
137148
// A node "contains" the position if position < end, except nodes at the file end
@@ -243,8 +254,8 @@ function getTokenAtPositionImpl(
243254

244255
// If prevSubtree was set, check if the rightmost token of prevSubtree should be returned.
245256
if (state.prevSubtree !== undefined) {
246-
const child = findPrecedingTokenImpl(sourceFile, position, state.prevSubtree);
247-
if (child !== undefined && child.end === position && includePrecedingTokenAtEndPosition!(child)) {
257+
const child = getIncludedPrecedingToken(state.prevSubtree);
258+
if (child !== undefined) {
248259
return child;
249260
}
250261
state.prevSubtree = undefined;

tsc/internal/astnav/tokens.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,20 @@ func getTokenAtPosition(
6363
// When scanning in between nodes for token, we should only scan up to the start of `nodeAfterLeft`.
6464
var nodeAfterLeft *ast.Node
6565

66+
getIncludedPrecedingToken := func(subtree *ast.Node) *ast.Node {
67+
child := FindPrecedingTokenEx(sourceFile, position, subtree, false /*excludeJSDoc*/)
68+
if child != nil && child.End() == position && includePrecedingTokenAtEndPosition(child) {
69+
return child
70+
}
71+
return nil
72+
}
73+
6674
testNode := func(node *ast.Node) int {
6775
if node.Kind != ast.KindEndOfFile && node.End() == position &&
6876
includePrecedingTokenAtEndPosition != nil && node.Flags&ast.NodeFlagsReparsed == 0 {
77+
if prevSubtree != nil && getIncludedPrecedingToken(prevSubtree) != nil {
78+
return 0
79+
}
6980
prevSubtree = node
7081
}
7182

@@ -191,8 +202,7 @@ func getTokenAtPosition(
191202
// Check if the rightmost token of prevSubtree should be returned based on the
192203
// `includePrecedingTokenAtEndPosition` callback.
193204
if prevSubtree != nil {
194-
child := FindPrecedingTokenEx(sourceFile, position, prevSubtree, false /*excludeJSDoc*/)
195-
if child != nil && child.End() == position && includePrecedingTokenAtEndPosition(child) {
205+
if child := getIncludedPrecedingToken(prevSubtree); child != nil {
196206
// Optimization: includePrecedingTokenAtEndPosition only ever returns true
197207
// for real AST nodes, so we don't run the scanner here.
198208
return child
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package fourslash_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/microsoft/typescript-go/internal/fourslash"
7+
"github.com/microsoft/typescript-go/internal/testutil"
8+
)
9+
10+
func TestGoToDefinitionJsxTagNameRightEdge(t *testing.T) {
11+
t.Parallel()
12+
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
13+
const content = `// @allowJs: true
14+
// @jsx: react-jsx
15+
// @filename: /a.jsx
16+
export function Component() {
17+
return null;
18+
}
19+
20+
export function App() {
21+
return <Component/*use*/ />
22+
}`
23+
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
24+
defer done()
25+
f.VerifyBaselineGoToDefinition(t, true, "use")
26+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// === goToDefinition ===
2+
// === /a.jsx ===
3+
// <|export function [|Component|]() {
4+
// return null;
5+
// }|>
6+
//
7+
// export function App() {
8+
// return <Component/*GOTO DEF*/ />
9+
// }

0 commit comments

Comments
 (0)