Skip to content

Commit c4c9a00

Browse files
author
Andy
authored
Support incomplete identifier in JSX initializer completions (microsoft#21681)
1 parent cd22ea7 commit c4c9a00

File tree

4 files changed

+37
-6
lines changed

4 files changed

+37
-6
lines changed

src/harness/fourslash.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3156,6 +3156,9 @@ Actual: ${stringify(fullActual)}`);
31563156
assert.equal(item.hasAction, hasAction, "hasAction");
31573157
assert.equal(item.isRecommended, options && options.isRecommended, "isRecommended");
31583158
assert.equal(item.insertText, options && options.insertText, "insertText");
3159+
if (options && options.replacementSpan) { // TODO: GH#21679
3160+
assert.deepEqual(item.replacementSpan, options && options.replacementSpan && textSpanFromRange(options.replacementSpan), "replacementSpan");
3161+
}
31593162
}
31603163

31613164
private findFile(indexOrName: string | number) {
@@ -4616,6 +4619,7 @@ namespace FourSlashInterface {
46164619
sourceDisplay: string;
46174620
isRecommended?: true;
46184621
insertText?: string;
4622+
replacementSpan?: FourSlash.Range;
46194623
}
46204624

46214625
export interface VerifyDocumentHighlightsOptions {

src/services/completions.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ namespace ts.Completions {
167167
origin: SymbolOriginInfo | undefined,
168168
recommendedCompletion: Symbol | undefined,
169169
propertyAccessToConvert: PropertyAccessExpression | undefined,
170-
isJsxInitializer: boolean,
170+
isJsxInitializer: IsJsxInitializer,
171171
includeInsertTextCompletions: boolean,
172172
): CompletionEntry | undefined {
173173
const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind);
@@ -193,6 +193,9 @@ namespace ts.Completions {
193193
if (isJsxInitializer) {
194194
if (insertText === undefined) insertText = name;
195195
insertText = `{${insertText}}`;
196+
if (typeof isJsxInitializer !== "boolean") {
197+
replacementSpan = createTextSpanFromNode(isJsxInitializer, sourceFile);
198+
}
196199
}
197200
}
198201

@@ -250,7 +253,7 @@ namespace ts.Completions {
250253
kind: CompletionKind,
251254
includeInsertTextCompletions?: boolean,
252255
propertyAccessToConvert?: PropertyAccessExpression | undefined,
253-
isJsxInitializer?: boolean,
256+
isJsxInitializer?: IsJsxInitializer,
254257
recommendedCompletion?: Symbol,
255258
symbolToOriginInfoMap?: SymbolOriginInfoMap,
256259
): Map<true> {
@@ -499,7 +502,7 @@ namespace ts.Completions {
499502
location: Node;
500503
symbolToOriginInfoMap: SymbolOriginInfoMap;
501504
previousToken: Node;
502-
readonly isJsxInitializer: boolean;
505+
readonly isJsxInitializer: IsJsxInitializer;
503506
}
504507
function getSymbolCompletionFromEntryId(
505508
typeChecker: TypeChecker,
@@ -668,6 +671,8 @@ namespace ts.Completions {
668671
}
669672

670673
const enum CompletionDataKind { Data, JsDocTagName, JsDocTag, JsDocParameterName }
674+
/** true: after the `=` sign but no identifier has been typed yet. Else is the Identifier after the initializer. */
675+
type IsJsxInitializer = boolean | Identifier;
671676
interface CompletionData {
672677
readonly kind: CompletionDataKind.Data;
673678
readonly symbols: ReadonlyArray<Symbol>;
@@ -680,7 +685,7 @@ namespace ts.Completions {
680685
readonly symbolToOriginInfoMap: SymbolOriginInfoMap;
681686
readonly recommendedCompletion: Symbol | undefined;
682687
readonly previousToken: Node | undefined;
683-
readonly isJsxInitializer: boolean;
688+
readonly isJsxInitializer: IsJsxInitializer;
684689
}
685690
type Request = { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag } | { readonly kind: CompletionDataKind.JsDocParameterName, tag: JSDocParameterTag };
686691

@@ -862,7 +867,7 @@ namespace ts.Completions {
862867
let isRightOfDot = false;
863868
let isRightOfOpenTag = false;
864869
let isStartingCloseTag = false;
865-
let isJsxInitializer = false;
870+
let isJsxInitializer: IsJsxInitializer = false;
866871

867872
let location = getTouchingPropertyName(sourceFile, position, insideJsDocTagTypeExpression); // TODO: GH#15853
868873
if (contextToken) {
@@ -923,7 +928,15 @@ namespace ts.Completions {
923928
break;
924929

925930
case SyntaxKind.JsxAttribute:
926-
isJsxInitializer = previousToken.kind === SyntaxKind.EqualsToken;
931+
switch (previousToken.kind) {
932+
case SyntaxKind.EqualsToken:
933+
isJsxInitializer = true;
934+
break;
935+
case SyntaxKind.Identifier:
936+
if (previousToken !== (parent as JsxAttribute).name) {
937+
isJsxInitializer = previousToken as Identifier;
938+
}
939+
}
927940
break;
928941
}
929942
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @Filename: /a.tsx
4+
////const foo = 0;
5+
////<div x=[|f/**/|] />;
6+
7+
const [replacementSpan] = test.ranges();
8+
goTo.marker();
9+
verify.completionListContains("foo", "const foo: 0", undefined, "const", undefined, undefined, {
10+
includeInsertTextCompletions: true,
11+
insertText: "{foo}",
12+
replacementSpan,
13+
});

tests/cases/fourslash/fourslash.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ declare namespace FourSlashInterface {
157157
sourceDisplay?: string,
158158
isRecommended?: true,
159159
insertText?: string,
160+
replacementSpan?: Range,
160161
},
161162
): void;
162163
completionListItemsCountIsGreaterThan(count: number): void;

0 commit comments

Comments
 (0)