Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c8147cb

Browse filesBrowse files
authored
fix(33377): use quoteStyle option for string literals completions (microsoft#36720)
1 parent d763804 commit c8147cb
Copy full SHA for c8147cb

10 files changed

+149-16Lines changed: 149 additions & 16 deletions
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/services/completions.ts‎

Copy file name to clipboardExpand all lines: src/services/completions.ts
+24-11Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ namespace ts.Completions {
272272
}
273273

274274
for (const literal of literals) {
275-
entries.push(createCompletionEntryForLiteral(literal));
275+
entries.push(createCompletionEntryForLiteral(literal, preferences));
276276
}
277277

278278
return { isGlobalCompletion: isInSnippetScope, isMemberCompletion: isMemberCompletionKind(completionKind), isNewIdentifierLocation, entries };
@@ -317,10 +317,13 @@ namespace ts.Completions {
317317
});
318318
}
319319

320-
const completionNameForLiteral = (literal: string | number | PseudoBigInt) =>
321-
typeof literal === "object" ? pseudoBigIntToString(literal) + "n" : JSON.stringify(literal);
322-
function createCompletionEntryForLiteral(literal: string | number | PseudoBigInt): CompletionEntry {
323-
return { name: completionNameForLiteral(literal), kind: ScriptElementKind.string, kindModifiers: ScriptElementKindModifier.none, sortText: SortText.LocationPriority };
320+
function completionNameForLiteral(literal: string | number | PseudoBigInt, preferences: UserPreferences): string {
321+
return typeof literal === "object" ? pseudoBigIntToString(literal) + "n" :
322+
isString(literal) ? quote(literal, preferences) : JSON.stringify(literal);
323+
}
324+
325+
function createCompletionEntryForLiteral(literal: string | number | PseudoBigInt, preferences: UserPreferences): CompletionEntry {
326+
return { name: completionNameForLiteral(literal, preferences), kind: ScriptElementKind.string, kindModifiers: ScriptElementKindModifier.none, sortText: SortText.LocationPriority };
324327
}
325328

326329
function createCompletionEntry(
@@ -344,13 +347,13 @@ namespace ts.Completions {
344347
const useBraces = origin && originIsSymbolMember(origin) || needsConvertPropertyAccess;
345348
if (origin && originIsThisType(origin)) {
346349
insertText = needsConvertPropertyAccess
347-
? `this${insertQuestionDot ? "?." : ""}[${quote(name, preferences)}]`
350+
? `this${insertQuestionDot ? "?." : ""}[${quotePropertyName(name, preferences)}]`
348351
: `this${insertQuestionDot ? "?." : "."}${name}`;
349352
}
350353
// We should only have needsConvertPropertyAccess if there's a property access to convert. But see #21790.
351354
// Somehow there was a global with a non-identifier name. Hopefully someone will complain about getting a "foo bar" global completion and provide a repro.
352355
else if ((useBraces || insertQuestionDot) && propertyAccessToConvert) {
353-
insertText = useBraces ? needsConvertPropertyAccess ? `[${quote(name, preferences)}]` : `[${name}]` : name;
356+
insertText = useBraces ? needsConvertPropertyAccess ? `[${quotePropertyName(name, preferences)}]` : `[${name}]` : name;
354357
if (insertQuestionDot || propertyAccessToConvert.questionDotToken) {
355358
insertText = `?.${insertText}`;
356359
}
@@ -410,6 +413,14 @@ namespace ts.Completions {
410413
};
411414
}
412415

416+
function quotePropertyName(name: string, preferences: UserPreferences): string {
417+
if (/^\d+$/.test(name)) {
418+
return name;
419+
}
420+
421+
return quote(name, preferences);
422+
}
423+
413424
function isRecommendedCompletionMatch(localSymbol: Symbol, recommendedCompletion: Symbol | undefined, checker: TypeChecker): boolean {
414425
return localSymbol === recommendedCompletion ||
415426
!!(localSymbol.flags & SymbolFlags.ExportValue) && checker.getExportSymbolOfSymbol(localSymbol) === recommendedCompletion;
@@ -531,6 +542,7 @@ namespace ts.Completions {
531542
position: number,
532543
entryId: CompletionEntryIdentifier,
533544
host: LanguageServiceHost,
545+
preferences: UserPreferences,
534546
): SymbolCompletion | { type: "request", request: Request } | { type: "literal", literal: string | number | PseudoBigInt } | { type: "none" } {
535547
const compilerOptions = program.getCompilerOptions();
536548
const completionData = getCompletionData(program, log, sourceFile, isUncheckedFile(sourceFile, compilerOptions), position, { includeCompletionsForModuleExports: true, includeCompletionsWithInsertText: true }, entryId, host);
@@ -543,7 +555,7 @@ namespace ts.Completions {
543555

544556
const { symbols, literals, location, completionKind, symbolToOriginInfoMap, previousToken, isJsxInitializer, isTypeOnlyLocation } = completionData;
545557

546-
const literal = find(literals, l => completionNameForLiteral(l) === entryId.name);
558+
const literal = find(literals, l => completionNameForLiteral(l, preferences) === entryId.name);
547559
if (literal !== undefined) return { type: "literal", literal };
548560

549561
// Find the symbol with the matching entry name.
@@ -585,7 +597,7 @@ namespace ts.Completions {
585597
}
586598

587599
// Compute all the completion symbols again.
588-
const symbolCompletion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host);
600+
const symbolCompletion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host, preferences);
589601
switch (symbolCompletion.type) {
590602
case "request": {
591603
const { request } = symbolCompletion;
@@ -607,7 +619,7 @@ namespace ts.Completions {
607619
}
608620
case "literal": {
609621
const { literal } = symbolCompletion;
610-
return createSimpleDetails(completionNameForLiteral(literal), ScriptElementKind.string, typeof literal === "string" ? SymbolDisplayPartKind.stringLiteral : SymbolDisplayPartKind.numericLiteral);
622+
return createSimpleDetails(completionNameForLiteral(literal, preferences), ScriptElementKind.string, typeof literal === "string" ? SymbolDisplayPartKind.stringLiteral : SymbolDisplayPartKind.numericLiteral);
611623
}
612624
case "none":
613625
// Didn't find a symbol with this name. See if we can find a keyword instead.
@@ -677,8 +689,9 @@ namespace ts.Completions {
677689
position: number,
678690
entryId: CompletionEntryIdentifier,
679691
host: LanguageServiceHost,
692+
preferences: UserPreferences,
680693
): Symbol | undefined {
681-
const completion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host);
694+
const completion = getSymbolCompletionFromEntryId(program, log, sourceFile, position, entryId, host, preferences);
682695
return completion.type === "symbol" ? completion.symbol : undefined;
683696
}
684697

Collapse file

‎src/services/services.ts‎

Copy file name to clipboardExpand all lines: src/services/services.ts
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,9 +1490,9 @@ namespace ts {
14901490
);
14911491
}
14921492

1493-
function getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string): Symbol | undefined {
1493+
function getCompletionEntrySymbol(fileName: string, position: number, name: string, source?: string, preferences: UserPreferences = emptyOptions): Symbol | undefined {
14941494
synchronizeHostData();
1495-
return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source }, host);
1495+
return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source }, host, preferences);
14961496
}
14971497

14981498
function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo | undefined {
Collapse file

‎src/services/utilities.ts‎

Copy file name to clipboardExpand all lines: src/services/utilities.ts
-3Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2343,9 +2343,6 @@ namespace ts {
23432343
}
23442344

23452345
export function quote(text: string, preferences: UserPreferences): string {
2346-
if (/^\d+$/.test(text)) {
2347-
return text;
2348-
}
23492346
// Editors can pass in undefined or empty string - we want to infer the preference in those cases.
23502347
const quotePreference = preferences.quotePreference || "auto";
23512348
const quoted = JSON.stringify(text);
Collapse file
+25Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////enum A {
4+
//// A,
5+
//// B,
6+
//// C
7+
////}
8+
////interface B {
9+
//// a: keyof typeof A;
10+
////}
11+
////const b: B = {
12+
//// a: /**/
13+
////}
14+
15+
verify.completions({
16+
marker: "",
17+
includes: [
18+
{ name: "'A'" },
19+
{ name: "'B'" },
20+
{ name: "'C'" },
21+
],
22+
preferences: {
23+
quotePreference: 'single',
24+
},
25+
});
Collapse file
+23Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////enum A {
4+
//// A,
5+
//// B,
6+
//// C
7+
////}
8+
////interface B {
9+
//// a: keyof typeof A;
10+
////}
11+
////const b: B = {
12+
//// a: /**/
13+
////}
14+
15+
verify.completions({
16+
marker: "",
17+
includes: [
18+
{ name: '"A"' },
19+
{ name: '"B"' },
20+
{ name: '"C"' },
21+
],
22+
preferences: { quotePreference: "double" },
23+
});
Collapse file
+17Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////const a = {
4+
//// '#': 'a'
5+
////};
6+
////a[|./**/|]
7+
8+
verify.completions({
9+
marker: "",
10+
includes: [
11+
{ name: "#", insertText: "['#']", replacementSpan: test.ranges()[0] },
12+
],
13+
preferences: {
14+
includeInsertTextCompletions: true,
15+
quotePreference: "single",
16+
},
17+
});
Collapse file
+17Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////const a = {
4+
//// "#": "a"
5+
////};
6+
////a[|./**/|]
7+
8+
verify.completions({
9+
marker: "",
10+
includes: [
11+
{ name: "#", insertText: '["#"]', replacementSpan: test.ranges()[0] },
12+
],
13+
preferences: {
14+
includeInsertTextCompletions: true,
15+
quotePreference: "double"
16+
},
17+
});
Collapse file
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////type T = 0 | 1;
4+
////const t: T = /**/
5+
6+
verify.completions({
7+
marker: "",
8+
includes: [
9+
{ name: "0" },
10+
{ name: "1" },
11+
],
12+
isNewIdentifierLocation: true
13+
});
Collapse file
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////type T = "0" | "1";
4+
////const t: T = /**/
5+
6+
verify.completions({
7+
marker: "",
8+
includes: [
9+
{ name: "'1'" },
10+
{ name: "'0'" },
11+
],
12+
isNewIdentifierLocation: true,
13+
preferences: { quotePreference: "single" }
14+
});
Collapse file
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
////type T = "0" | "1";
4+
////const t: T = /**/
5+
6+
verify.completions({
7+
marker: "",
8+
includes: [
9+
{ name: '"1"' },
10+
{ name: '"0"' },
11+
],
12+
isNewIdentifierLocation: true,
13+
preferences: { quotePreference: "double" }
14+
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.