mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-07-01 02:02:45 -05:00
fix(29881): infer quote preference for property access conversion in completions (#41041)
This commit is contained in:
@@ -42,7 +42,7 @@ namespace ts.codefix {
|
||||
return;
|
||||
}
|
||||
|
||||
const replacement = useHtmlEntity ? htmlEntity[character] : `{${quote(character, preferences)}}`;
|
||||
const replacement = useHtmlEntity ? htmlEntity[character] : `{${quote(sourceFile, preferences, character)}}`;
|
||||
changes.replaceRangeWithText(sourceFile, { pos: start, end: start + 1 }, replacement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ namespace ts.Completions {
|
||||
}
|
||||
|
||||
for (const literal of literals) {
|
||||
entries.push(createCompletionEntryForLiteral(literal, preferences));
|
||||
entries.push(createCompletionEntryForLiteral(sourceFile, preferences, literal));
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -360,13 +360,13 @@ namespace ts.Completions {
|
||||
});
|
||||
}
|
||||
|
||||
function completionNameForLiteral(literal: string | number | PseudoBigInt, preferences: UserPreferences): string {
|
||||
function completionNameForLiteral(sourceFile: SourceFile, preferences: UserPreferences, literal: string | number | PseudoBigInt): string {
|
||||
return typeof literal === "object" ? pseudoBigIntToString(literal) + "n" :
|
||||
isString(literal) ? quote(literal, preferences) : JSON.stringify(literal);
|
||||
isString(literal) ? quote(sourceFile, preferences, literal) : JSON.stringify(literal);
|
||||
}
|
||||
|
||||
function createCompletionEntryForLiteral(literal: string | number | PseudoBigInt, preferences: UserPreferences): CompletionEntry {
|
||||
return { name: completionNameForLiteral(literal, preferences), kind: ScriptElementKind.string, kindModifiers: ScriptElementKindModifier.none, sortText: SortText.LocationPriority };
|
||||
function createCompletionEntryForLiteral(sourceFile: SourceFile, preferences: UserPreferences, literal: string | number | PseudoBigInt): CompletionEntry {
|
||||
return { name: completionNameForLiteral(sourceFile, preferences, literal), kind: ScriptElementKind.string, kindModifiers: ScriptElementKindModifier.none, sortText: SortText.LocationPriority };
|
||||
}
|
||||
|
||||
function createCompletionEntry(
|
||||
@@ -391,13 +391,13 @@ namespace ts.Completions {
|
||||
const useBraces = origin && originIsSymbolMember(origin) || needsConvertPropertyAccess;
|
||||
if (origin && originIsThisType(origin)) {
|
||||
insertText = needsConvertPropertyAccess
|
||||
? `this${insertQuestionDot ? "?." : ""}[${quotePropertyName(name, preferences)}]`
|
||||
? `this${insertQuestionDot ? "?." : ""}[${quotePropertyName(sourceFile, preferences, name)}]`
|
||||
: `this${insertQuestionDot ? "?." : "."}${name}`;
|
||||
}
|
||||
// We should only have needsConvertPropertyAccess if there's a property access to convert. But see #21790.
|
||||
// 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.
|
||||
else if ((useBraces || insertQuestionDot) && propertyAccessToConvert) {
|
||||
insertText = useBraces ? needsConvertPropertyAccess ? `[${quotePropertyName(name, preferences)}]` : `[${name}]` : name;
|
||||
insertText = useBraces ? needsConvertPropertyAccess ? `[${quotePropertyName(sourceFile, preferences, name)}]` : `[${name}]` : name;
|
||||
if (insertQuestionDot || propertyAccessToConvert.questionDotToken) {
|
||||
insertText = `?.${insertText}`;
|
||||
}
|
||||
@@ -458,12 +458,12 @@ namespace ts.Completions {
|
||||
};
|
||||
}
|
||||
|
||||
function quotePropertyName(name: string, preferences: UserPreferences): string {
|
||||
function quotePropertyName(sourceFile: SourceFile, preferences: UserPreferences, name: string,): string {
|
||||
if (/^\d+$/.test(name)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return quote(name, preferences);
|
||||
return quote(sourceFile, preferences, name);
|
||||
}
|
||||
|
||||
function isRecommendedCompletionMatch(localSymbol: Symbol, recommendedCompletion: Symbol | undefined, checker: TypeChecker): boolean {
|
||||
@@ -614,7 +614,7 @@ namespace ts.Completions {
|
||||
|
||||
const { symbols, literals, location, completionKind, symbolToOriginInfoMap, previousToken, isJsxInitializer, isTypeOnlyLocation } = completionData;
|
||||
|
||||
const literal = find(literals, l => completionNameForLiteral(l, preferences) === entryId.name);
|
||||
const literal = find(literals, l => completionNameForLiteral(sourceFile, preferences, l) === entryId.name);
|
||||
if (literal !== undefined) return { type: "literal", literal };
|
||||
|
||||
// Find the symbol with the matching entry name.
|
||||
@@ -678,7 +678,7 @@ namespace ts.Completions {
|
||||
}
|
||||
case "literal": {
|
||||
const { literal } = symbolCompletion;
|
||||
return createSimpleDetails(completionNameForLiteral(literal, preferences), ScriptElementKind.string, typeof literal === "string" ? SymbolDisplayPartKind.stringLiteral : SymbolDisplayPartKind.numericLiteral);
|
||||
return createSimpleDetails(completionNameForLiteral(sourceFile, preferences, literal), ScriptElementKind.string, typeof literal === "string" ? SymbolDisplayPartKind.stringLiteral : SymbolDisplayPartKind.numericLiteral);
|
||||
}
|
||||
case "none":
|
||||
// Didn't find a symbol with this name. See if we can find a keyword instead.
|
||||
|
||||
@@ -2473,20 +2473,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function quote(text: string, preferences: UserPreferences): string {
|
||||
export function quote(sourceFile: SourceFile, preferences: UserPreferences, text: string): string {
|
||||
// Editors can pass in undefined or empty string - we want to infer the preference in those cases.
|
||||
const quotePreference = preferences.quotePreference || "auto";
|
||||
const quotePreference = getQuotePreference(sourceFile, preferences);
|
||||
const quoted = JSON.stringify(text);
|
||||
switch (quotePreference) {
|
||||
// TODO use getQuotePreference to infer the actual quote style.
|
||||
case "auto":
|
||||
case "double":
|
||||
return quoted;
|
||||
case "single":
|
||||
return `'${stripQuotes(quoted).replace("'", "\\'").replace('\\"', '"')}'`;
|
||||
default:
|
||||
return Debug.assertNever(quotePreference);
|
||||
}
|
||||
return quotePreference === QuotePreference.Single ? `'${stripQuotes(quoted).replace("'", "\\'").replace('\\"', '"')}'` : quoted;
|
||||
}
|
||||
|
||||
export function isEqualityOperatorKind(kind: SyntaxKind): kind is EqualityOperator {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
// @filename: /a.ts
|
||||
////export const a = null;
|
||||
|
||||
// @filename: /b.ts
|
||||
////import { a } from './a';
|
||||
////
|
||||
////const foo = { '#': null };
|
||||
////foo[|./**/|]
|
||||
|
||||
goTo.file("/b.ts");
|
||||
verify.completions({
|
||||
marker: "",
|
||||
exact: [
|
||||
{ name: "#", insertText: "['#']", replacementSpan: test.ranges()[0] },
|
||||
],
|
||||
preferences: {
|
||||
includeInsertTextCompletions: true,
|
||||
quotePreference: "auto"
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user