Skip to content

Commit bdee9eb

Browse files
Copilotjakebailey
andauthored
Fix smart select to include inner content selection for string literals (#2658)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
1 parent 738f1f6 commit bdee9eb

5 files changed

Lines changed: 41 additions & 36 deletions

File tree

internal/ls/selectionranges.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,35 @@ func getSmartSelectionRange(l *LanguageService, sourceFile *ast.SourceFile, pos
142142
}
143143
}
144144

145+
// Synthesize a stop for '${ ... }' since '${' and '}' actually belong to siblings.
146+
if ast.IsTemplateSpan(parent) {
147+
templateSpan := parent.AsTemplateSpan()
148+
if templateSpan.Literal != nil {
149+
// Start from just before the '${' and end after the '}'
150+
// The '${' is 2 characters before the expression start
151+
spanStart := node.Pos() - 2
152+
// The '}' is the first character of the template literal (middle or tail)
153+
spanEnd := astnav.GetStartOfNode(templateSpan.Literal, sourceFile, false) + 1
154+
// Validate the positions are reasonable
155+
text := sourceFile.Text()
156+
if spanStart >= 0 && spanEnd <= len(text) && spanStart < spanEnd {
157+
result = pushSelectionRange(result, spanStart, spanEnd)
158+
}
159+
}
160+
}
161+
145162
if !shouldSkipNode(node, parent) {
146163
start := astnav.GetStartOfNode(node, sourceFile, false)
147164
end := node.End()
148165
result = pushSelectionRange(result, start, end)
166+
167+
// String literals should have a stop both inside and outside their quotes.
168+
if ast.IsStringLiteral(node) || node.Kind == ast.KindTemplateExpression || node.Kind == ast.KindNoSubstitutionTemplateLiteral {
169+
// Only add inner content range if there's actually content (handles unterminated literals)
170+
if start+1 < end-1 {
171+
result = pushSelectionRange(result, start+1, end-1)
172+
}
173+
}
149174
}
150175

151176
next = node
@@ -156,7 +181,7 @@ func getSmartSelectionRange(l *LanguageService, sourceFile *ast.SourceFile, pos
156181

157182
visitNodes := func(nodes *ast.NodeList, v *ast.NodeVisitor) *ast.NodeList {
158183
if nodes != nil && len(nodes.Nodes) > 0 {
159-
shouldSkipList := parent != nil && ast.IsVariableDeclarationList(parent)
184+
shouldSkipList := parent != nil && (ast.IsVariableDeclarationList(parent) || ast.IsTemplateExpression(parent))
160185

161186
if !shouldSkipList {
162187
start := astnav.GetStartOfNode(nodes.Nodes[0], sourceFile, false)

testdata/baselines/reference/submodule/fourslash/smartSelection/smartSelection_templateStrings.baseline

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
} d`
55

66

7+
a b ${
8+
'c'
9+
} d
10+
711
`a b ${
812
'c'
913
} d`
@@ -14,10 +18,16 @@
1418
'/**/c'
1519
} d`
1620

21+
c ↲
1722
'c'↲
1823

24+
${
1925
'c'
20-
} d`
26+
}
27+
28+
a b ${
29+
'c'
30+
} d
2131

2232
`a b ${
2333
'c'
Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,6 @@
11
--- old.smartSelection_templateStrings.baseline
22
+++ new.smartSelection_templateStrings.baseline
3-
@@= skipped -3, +3 lines =@@
4-
} d`
5-
6-
7-
- a b ${
8-
- 'c'
9-
-} d
10-
-
11-
`a b ${
12-
'c'
13-
} d`
14-
@@= skipped -14, +10 lines =@@
3+
@@= skipped -17, +17 lines =@@
154
'/**/c'
165
} d`
176

@@ -21,18 +10,8 @@
2110
-
2211
- 'c'
2312
-
24-
-
25-
- ${
26-
- 'c'
27-
-}
28-
-
29-
- a b ${
30-
- 'c'
31-
-} d
13+
+ c ↲
3214
+ 'c'↲
33-
+
34-
+ 'c'
35-
+} d`
3615

37-
`a b ${
16+
${
3817
'c'
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// === Smart Selection ===
22
`a ${b} /**/c`
33

4-
b} c`
4+
a ${b} c
55
`a ${b} c`

testdata/baselines/reference/submodule/fourslash/smartSelection/smartSelection_templateStrings2.baseline.diff

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)