Skip to content

Commit

Permalink
At <div x=/**/, completion insertText should be wrapped in braces (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy authored Jan 25, 2018
1 parent 9e87f23 commit a453149
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 16 deletions.
49 changes: 33 additions & 16 deletions src/services/completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ namespace ts.Completions {
}

function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, includeInsertTextCompletions: boolean): CompletionInfo {
const { symbols, completionKind, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, symbolToOriginInfoMap, recommendedCompletion } = completionData;
const { symbols, completionKind, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer } = completionData;

if (sourceFile.languageVariant === LanguageVariant.JSX && location && location.parent && isJsxClosingElement(location.parent)) {
// In the TypeScript JSX element, if such element is not defined. When users query for completion at closing tag,
Expand All @@ -99,15 +99,15 @@ namespace ts.Completions {
const entries: CompletionEntry[] = [];

if (isSourceFileJavaScript(sourceFile)) {
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, includeInsertTextCompletions, propertyAccessToConvert, recommendedCompletion, symbolToOriginInfoMap);
const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, includeInsertTextCompletions, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap);
getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames, compilerOptions.target, entries);
}
else {
if ((!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) {
return undefined;
}

getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, includeInsertTextCompletions, propertyAccessToConvert, recommendedCompletion, symbolToOriginInfoMap);
getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, includeInsertTextCompletions, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap);
}

// TODO add filter for keyword based on type/value/namespace and also location
Expand Down Expand Up @@ -167,26 +167,35 @@ namespace ts.Completions {
origin: SymbolOriginInfo | undefined,
recommendedCompletion: Symbol | undefined,
propertyAccessToConvert: PropertyAccessExpression | undefined,
isJsxInitializer: boolean,
includeInsertTextCompletions: boolean,
): CompletionEntry | undefined {
const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind);
if (!info) {
return undefined;
}
const { name, needsConvertPropertyAccess } = info;
if (needsConvertPropertyAccess && !includeInsertTextCompletions) {
return undefined;
}

let insertText: string | undefined;
let replacementSpan: TextSpan | undefined;
if (origin && origin.type === "this-type") {
insertText = needsConvertPropertyAccess ? `this["${name}"]` : `this.${name}`;
if (includeInsertTextCompletions) {
if (origin && origin.type === "this-type") {
insertText = needsConvertPropertyAccess ? `this["${name}"]` : `this.${name}`;
}
else if (needsConvertPropertyAccess) {
// TODO: GH#20619 Use configured quote style
insertText = `["${name}"]`;
replacementSpan = createTextSpanFromBounds(findChildOfKind(propertyAccessToConvert!, SyntaxKind.DotToken, sourceFile)!.getStart(sourceFile), propertyAccessToConvert!.name.end);
}

if (isJsxInitializer) {
if (insertText === undefined) insertText = name;
insertText = `{${insertText}}`;
}
}
else if (needsConvertPropertyAccess) {
// TODO: GH#20619 Use configured quote style
insertText = `["${name}"]`;
replacementSpan = createTextSpanFromBounds(findChildOfKind(propertyAccessToConvert!, SyntaxKind.DotToken, sourceFile)!.getStart(sourceFile), propertyAccessToConvert!.name.end);

if (insertText !== undefined && !includeInsertTextCompletions) {
return undefined;
}

// TODO(drosen): Right now we just permit *all* semantic meanings when calling
Expand Down Expand Up @@ -235,6 +244,7 @@ namespace ts.Completions {
kind: CompletionKind,
includeInsertTextCompletions?: boolean,
propertyAccessToConvert?: PropertyAccessExpression | undefined,
isJsxInitializer?: boolean,
recommendedCompletion?: Symbol,
symbolToOriginInfoMap?: SymbolOriginInfoMap,
): Map<true> {
Expand All @@ -246,7 +256,7 @@ namespace ts.Completions {
const uniques = createMap<true>();
for (const symbol of symbols) {
const origin = symbolToOriginInfoMap ? symbolToOriginInfoMap[getSymbolId(symbol)] : undefined;
const entry = createCompletionEntry(symbol, location, sourceFile, typeChecker, target, kind, origin, recommendedCompletion, propertyAccessToConvert, includeInsertTextCompletions);
const entry = createCompletionEntry(symbol, location, sourceFile, typeChecker, target, kind, origin, recommendedCompletion, propertyAccessToConvert, isJsxInitializer, includeInsertTextCompletions);
if (!entry) {
continue;
}
Expand Down Expand Up @@ -483,6 +493,7 @@ namespace ts.Completions {
location: Node;
symbolToOriginInfoMap: SymbolOriginInfoMap;
previousToken: Node;
readonly isJsxInitializer: boolean;
}
function getSymbolCompletionFromEntryId(
typeChecker: TypeChecker,
Expand All @@ -501,7 +512,7 @@ namespace ts.Completions {
return { type: "request", request: completionData };
}

const { symbols, location, completionKind, symbolToOriginInfoMap, previousToken } = completionData;
const { symbols, location, completionKind, symbolToOriginInfoMap, previousToken, isJsxInitializer } = completionData;

// Find the symbol with the matching entry name.
// We don't need to perform character checks here because we're only comparing the
Expand All @@ -510,7 +521,7 @@ namespace ts.Completions {
return firstDefined<Symbol, SymbolCompletion>(symbols, (symbol): SymbolCompletion => { // TODO: Shouldn't need return type annotation (GH#12632)
const origin = symbolToOriginInfoMap[getSymbolId(symbol)];
const info = getCompletionEntryDisplayNameForSymbol(symbol, compilerOptions.target, origin, completionKind);
return info && info.name === name && getSourceFromOrigin(origin) === source ? { type: "symbol" as "symbol", symbol, location, symbolToOriginInfoMap, previousToken } : undefined;
return info && info.name === name && getSourceFromOrigin(origin) === source ? { type: "symbol" as "symbol", symbol, location, symbolToOriginInfoMap, previousToken, isJsxInitializer } : undefined;
}) || { type: "none" };
}

Expand Down Expand Up @@ -678,6 +689,7 @@ namespace ts.Completions {
readonly symbolToOriginInfoMap: SymbolOriginInfoMap;
readonly recommendedCompletion: Symbol | undefined;
readonly previousToken: Node | undefined;
readonly isJsxInitializer: boolean;
}
type Request = { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag } | { readonly kind: CompletionDataKind.JsDocParameterName, tag: JSDocParameterTag };

Expand Down Expand Up @@ -859,6 +871,7 @@ namespace ts.Completions {
let isRightOfDot = false;
let isRightOfOpenTag = false;
let isStartingCloseTag = false;
let isJsxInitializer = false;

let location = getTouchingPropertyName(sourceFile, position, insideJsDocTagTypeExpression); // TODO: GH#15853
if (contextToken) {
Expand Down Expand Up @@ -917,6 +930,10 @@ namespace ts.Completions {
location = contextToken;
}
break;

case SyntaxKind.JsxAttribute:
isJsxInitializer = previousToken.kind === SyntaxKind.EqualsToken;
break;
}
}
}
Expand Down Expand Up @@ -962,7 +979,7 @@ namespace ts.Completions {
log("getCompletionData: Semantic work: " + (timestamp() - semanticStart));

const recommendedCompletion = previousToken && getRecommendedCompletion(previousToken, typeChecker);
return { kind: CompletionDataKind.Data, symbols, completionKind, propertyAccessToConvert, isNewIdentifierLocation, location, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, previousToken };
return { kind: CompletionDataKind.Data, symbols, completionKind, propertyAccessToConvert, isNewIdentifierLocation, location, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, previousToken, isJsxInitializer };

type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;

Expand Down
23 changes: 23 additions & 0 deletions tests/cases/fourslash/completionsJsxAttributeInitializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/// <reference path="fourslash.ts" />

// @Filename: /a.tsx
////function f(this: { p: number; "a b": number }, x: number): void {
//// <div foo=/**/ />;
////}

goTo.marker();

verify.completionListContains("x", "(parameter) x: number", "", "parameter", undefined, undefined, {
includeInsertTextCompletions: true,
insertText: "{x}",
});

verify.completionListContains("p", "(property) p: number", "", "property", undefined, undefined, {
includeInsertTextCompletions: true,
insertText: "{this.p}",
});

verify.completionListContains("a b", '(property) "a b": number', "", "property", undefined, undefined, {
includeInsertTextCompletions: true,
insertText: '{this["a b"]}',
});

0 comments on commit a453149

Please sign in to comment.