Skip to content

Commit 4ecadc6

Browse files
fix54092: return replacement ranges for completions on unclosed strings (#57839)
Co-authored-by: Daniel Rosenwasser <[email protected]>
1 parent f70b068 commit 4ecadc6

File tree

2 files changed

+80
-2
lines changed

2 files changed

+80
-2
lines changed

src/services/utilities.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -2322,8 +2322,13 @@ export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile, endN
23222322

23232323
/** @internal */
23242324
export function createTextSpanFromStringLiteralLikeContent(node: StringLiteralLike) {
2325-
if (node.isUnterminated) return undefined;
2326-
return createTextSpanFromBounds(node.getStart() + 1, node.getEnd() - 1);
2325+
let replacementEnd = node.getEnd() - 1;
2326+
if (node.isUnterminated) {
2327+
// we return no replacement range only if unterminated string is empty
2328+
if (node.getStart() === replacementEnd) return undefined;
2329+
replacementEnd = node.getEnd();
2330+
}
2331+
return createTextSpanFromBounds(node.getStart() + 1, replacementEnd);
23272332
}
23282333

23292334
/** @internal */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
///<reference path="fourslash.ts" />
2+
3+
// @Filename: file0.ts
4+
//// const a = { "foo.bar": 1 }
5+
//// a["[|foo.b/*0*/|]
6+
7+
// @Filename: file1.ts
8+
//// const a = { "foo.bar": 1 }
9+
//// a["[|foo.b /*1*/|]
10+
11+
// @Filename: file2.ts
12+
//// const a = { "foo.bar": 1 }
13+
//// a[ "[|foo.b/*2*/|]
14+
15+
// @Filename: file3.ts
16+
//// const a = { "foo.bar": 1 }
17+
//// a["[|foo.b/*3*/|]"
18+
19+
// @Filename: file4.ts
20+
//// const a = { "foo.bar": 1 }
21+
//// a["[|foo./*4*/b|]
22+
23+
// @Filename: file5.ts
24+
//// const a = { "foo.bar": 1 }
25+
//// a['[|foo./*5*/b|]
26+
27+
// @Filename: file6.ts
28+
//// const a = { "foo.bar": 1 }
29+
//// a[`[|foo./*6*/b|]
30+
31+
// @Filename: file7.ts
32+
//// const a = { "foo.bar": 1 }
33+
//// a["[|foo./*7*/|]
34+
35+
// @Filename: file8.ts
36+
//// const a = { "foo.bar": 1 }
37+
//// a["[|foo/*8*/|]
38+
39+
// @Filename: file9.ts
40+
//// const a = { "foo.bar": 1 }
41+
//// a["[|foo./*9*/|]"
42+
43+
// @Filename: file10.ts
44+
//// const a = { "foo.bar": 1 }
45+
//// a["[|foo/*10*/|]"
46+
47+
// @Filename: empty1.ts
48+
//// const a = { "foo.bar": 1 }
49+
//// a[`/*11*/
50+
51+
// @Filename: empty2.ts
52+
//// const a = { "foo.bar": 1 }
53+
//// a["/*12*/
54+
55+
// @Filename: empty3.ts
56+
//// const a = { "foo.bar": 1 }
57+
//// a['/*13*/
58+
59+
// tests 11-13 should return no replacementSpan
60+
const markers = test.markers();
61+
const ranges = test.ranges();
62+
63+
for (let i = 0; i < markers.length; i++) {
64+
verify.completions({
65+
marker: markers[i],
66+
includes: [{
67+
"name": "foo.bar",
68+
"kind": "property",
69+
"kindModifiers": "",
70+
"replacementSpan": ranges[i],
71+
}],
72+
});
73+
}

0 commit comments

Comments
 (0)