Skip to content

Commit 5596ed8

Browse files
authored
Add replacement span for string literal (#37490)
* Add replacement span for string literal * fix change requests * fix lint * Avoid flag * Fix baseline * ADd misising baseline
1 parent 15aff05 commit 5596ed8

32 files changed

+334
-86
lines changed

src/services/completions.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ namespace ts.Completions {
245245
const uniqueNames = getCompletionEntriesFromSymbols(
246246
symbols,
247247
entries,
248+
/* contextToken */ undefined,
248249
location,
249250
sourceFile,
250251
typeChecker,
@@ -269,6 +270,7 @@ namespace ts.Completions {
269270
getCompletionEntriesFromSymbols(
270271
symbols,
271272
entries,
273+
/* contextToken */ undefined,
272274
location,
273275
sourceFile,
274276
typeChecker,
@@ -353,6 +355,7 @@ namespace ts.Completions {
353355
function createCompletionEntry(
354356
symbol: Symbol,
355357
sortText: SortText,
358+
contextToken: Node | undefined,
356359
location: Node | undefined,
357360
sourceFile: SourceFile,
358361
typeChecker: TypeChecker,
@@ -365,7 +368,7 @@ namespace ts.Completions {
365368
preferences: UserPreferences,
366369
): CompletionEntry | undefined {
367370
let insertText: string | undefined;
368-
let replacementSpan: TextSpan | undefined;
371+
let replacementSpan = getReplacementSpanForContextToken(contextToken);
369372

370373
const insertQuestionDot = origin && originIsNullableMember(origin);
371374
const useBraces = origin && originIsSymbolMember(origin) || needsConvertPropertyAccess;
@@ -462,6 +465,7 @@ namespace ts.Completions {
462465
export function getCompletionEntriesFromSymbols(
463466
symbols: readonly Symbol[],
464467
entries: Push<CompletionEntry>,
468+
contextToken: Node | undefined,
465469
location: Node | undefined,
466470
sourceFile: SourceFile,
467471
typeChecker: TypeChecker,
@@ -496,6 +500,7 @@ namespace ts.Completions {
496500
const entry = createCompletionEntry(
497501
symbol,
498502
symbolToSortTextMap && symbolToSortTextMap[getSymbolId(symbol)] || SortText.LocationPriority,
503+
contextToken,
499504
location,
500505
sourceFile,
501506
typeChecker,

src/services/stringCompletions.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ namespace ts.Completions.StringCompletions {
88
if (isInString(sourceFile, position, contextToken)) {
99
if (!contextToken || !isStringLiteralLike(contextToken)) return undefined;
1010
const entries = getStringLiteralCompletionEntries(sourceFile, contextToken, position, checker, options, host);
11-
return convertStringLiteralCompletions(entries, sourceFile, checker, log, preferences);
11+
return convertStringLiteralCompletions(entries, contextToken, sourceFile, checker, log, preferences);
1212
}
1313
}
1414

15-
function convertStringLiteralCompletions(completion: StringLiteralCompletion | undefined, sourceFile: SourceFile, checker: TypeChecker, log: Log, preferences: UserPreferences): CompletionInfo | undefined {
15+
function convertStringLiteralCompletions(completion: StringLiteralCompletion | undefined, contextToken: Node, sourceFile: SourceFile, checker: TypeChecker, log: Log, preferences: UserPreferences): CompletionInfo | undefined {
1616
if (completion === undefined) {
1717
return undefined;
1818
}
@@ -24,6 +24,7 @@ namespace ts.Completions.StringCompletions {
2424
getCompletionEntriesFromSymbols(
2525
completion.symbols,
2626
entries,
27+
contextToken,
2728
sourceFile,
2829
sourceFile,
2930
checker,
@@ -35,7 +36,13 @@ namespace ts.Completions.StringCompletions {
3536
return { isGlobalCompletion: false, isMemberCompletion: true, isNewIdentifierLocation: completion.hasIndexSignature, entries };
3637
}
3738
case StringLiteralCompletionKind.Types: {
38-
const entries = completion.types.map(type => ({ name: type.value, kindModifiers: ScriptElementKindModifier.none, kind: ScriptElementKind.string, sortText: "0" }));
39+
const entries = completion.types.map(type => ({
40+
name: type.value,
41+
kindModifiers: ScriptElementKindModifier.none,
42+
kind: ScriptElementKind.string,
43+
sortText: "0",
44+
replacementSpan: getReplacementSpanForContextToken(contextToken)
45+
}));
3946
return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: completion.isNewIdentifier, entries };
4047
}
4148
default:

src/services/utilities.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,10 +1600,27 @@ namespace ts {
16001600
return !!range && shouldBeReference === tripleSlashDirectivePrefixRegex.test(sourceFile.text.substring(range.pos, range.end));
16011601
}
16021602

1603+
export function getReplacementSpanForContextToken(contextToken: Node | undefined) {
1604+
if (!contextToken) return undefined;
1605+
1606+
switch (contextToken.kind) {
1607+
case SyntaxKind.StringLiteral:
1608+
case SyntaxKind.NoSubstitutionTemplateLiteral:
1609+
return createTextSpanFromStringLiteralLikeContent(<StringLiteralLike>contextToken);
1610+
default:
1611+
return createTextSpanFromNode(contextToken);
1612+
}
1613+
}
1614+
16031615
export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile, endNode?: Node): TextSpan {
16041616
return createTextSpanFromBounds(node.getStart(sourceFile), (endNode || node).getEnd());
16051617
}
16061618

1619+
export function createTextSpanFromStringLiteralLikeContent(node: StringLiteralLike) {
1620+
if (node.isUnterminated) return undefined;
1621+
return createTextSpanFromBounds(node.getStart() + 1, node.getEnd() - 1);
1622+
}
1623+
16071624
export function createTextRangeFromNode(node: Node, sourceFile: SourceFile): TextRange {
16081625
return createRange(node.getStart(sourceFile), node.end);
16091626
}

tests/cases/fourslash/completionForQuotedPropertyInPropertyAssignment1.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@
88
//// let files: Configfiles;
99
//// files = {
1010
//// /*0*/: '',
11-
//// '/*1*/': ''
11+
//// '[|/*1*/|]': ''
1212
//// }
1313

14+
const replacementSpan = test.ranges()[0]
1415
verify.completions(
1516
{ marker: "0", exact: ["jspm", '"jspm:browser"'] },
16-
{ marker: "1", exact: ["jspm", "jspm:browser"] },
17+
{ marker: "1", exact: [
18+
{ name: "jspm", replacementSpan },
19+
{ name: "jspm:browser", replacementSpan }
20+
] },
1721
);

tests/cases/fourslash/completionForQuotedPropertyInPropertyAssignment2.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@
1313
//// config = {
1414
//// files: {
1515
//// /*0*/: '',
16-
//// '/*1*/': ''
16+
//// '[|/*1*/|]': ''
1717
//// }
1818
//// }
1919

20+
const replacementSpan = test.ranges()[0]
2021
verify.completions(
2122
{ marker: "0", exact: ["jspm", '"jspm:browser"'] },
22-
{ marker: "1", exact: ["jspm", "jspm:browser"] },
23+
{ marker: "1", exact: [
24+
{ name: "jspm", replacementSpan },
25+
{ name: "jspm:browser", replacementSpan }
26+
] },
2327
);

tests/cases/fourslash/completionForQuotedPropertyInPropertyAssignment3.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@
1212
//// 'jspm:browser': string;
1313
//// } = {
1414
//// jspm: "",
15-
//// '/*1*/': ""
15+
//// '[|/*1*/|]': ""
1616
//// }
1717

18+
const replacementSpan = test.ranges()[0]
1819
verify.completions(
1920
{ marker: "0", exact: ["jspm", '"jspm:browser"'] },
20-
{ marker: "1", exact: ["jspm", "jspm:browser"] },
21+
{ marker: "1", exact: [
22+
{ name: "jspm", replacementSpan },
23+
{ name: "jspm:browser", replacementSpan }
24+
] }
2125
);

tests/cases/fourslash/completionForQuotedPropertyInPropertyAssignment4.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
//// function foo(c: ConfigFiles) {}
99
//// foo({
1010
//// j/*0*/: "",
11-
//// "/*1*/": "",
11+
//// "[|/*1*/|]": "",
1212
//// })
1313

14-
14+
const replacementSpan = test.ranges()[0]
1515
verify.completions(
1616
{ marker: "0", exact: ["jspm", '"jspm:browser"'] },
17-
{ marker: "1", exact: ["jspm", "jspm:browser"] },
17+
{ marker: "1", exact: [
18+
{ name: "jspm", replacementSpan },
19+
{ name: "jspm:browser", replacementSpan }
20+
] }
1821
);
Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
/// <reference path='fourslash.ts'/>
22

33
////type Options = "Option 1" | "Option 2" | "Option 3";
4-
////var x: Options = "/*1*/Option 3";
4+
////var x: Options = "[|/*1*/Option 3|]";
55
////
66
////function f(a: Options) { };
77
////f("/*2*/
88

9-
verify.completions({ marker: ["1", "2"], exact: ["Option 1", "Option 2", "Option 3"] });
9+
verify.completions(
10+
{ marker: "1", exact: [
11+
{ name: "Option 1", replacementSpan: test.ranges()[0] },
12+
{ name: "Option 2", replacementSpan: test.ranges()[0] },
13+
{ name: "Option 3", replacementSpan: test.ranges()[0] }
14+
] },
15+
{ marker: "2", exact: [
16+
{ name: "Option 1", replacementSpan: test.ranges()[1] },
17+
{ name: "Option 2", replacementSpan: test.ranges()[1] },
18+
{ name: "Option 3", replacementSpan: test.ranges()[1] }
19+
] }
20+
);

tests/cases/fourslash/completionForStringLiteral10.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
////type As = 'arf' | 'abacus' | 'abaddon';
44
////let a: As;
5-
////if ('/**/' != a
5+
////if ('[|/**/|]' != a
66

7-
verify.completions({ marker: "", exact: ["arf", "abacus", "abaddon"] });
7+
verify.completions({ marker: "", exact: ["arf", "abacus", "abaddon"].map(name => ({
8+
name,
9+
replacementSpan: test.ranges()[0]
10+
})) });

tests/cases/fourslash/completionForStringLiteral11.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
////type As = 'arf' | 'abacus' | 'abaddon';
44
////let a: As;
55
////switch (a) {
6-
//// case '/**/
6+
//// case '[|/**/|]
77
////}
88

99
verify.completions({ marker: "", exact: ["arf", "abacus", "abaddon" ] });

0 commit comments

Comments
 (0)