Skip to content

Commit 584be33

Browse files
committed
Add properties priority for completion
1 parent e8bf958 commit 584be33

File tree

5 files changed

+91
-13
lines changed

5 files changed

+91
-13
lines changed

src/harness/fourslash.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,19 @@ namespace FourSlash {
841841
assert.equal(actual.hasAction, hasAction);
842842
assert.equal(actual.isRecommended, isRecommended);
843843
assert.equal(actual.source, source);
844-
assert.equal(actual.sortText, sortText || ts.Completions.SortText.LocationPriority, this.messageAtLastKnownMarker(`Actual entry: ${JSON.stringify(actual)}`));
844+
845+
let isSortTextEqual: boolean;
846+
if (!sortText) {
847+
isSortTextEqual = actual.isFulfilled
848+
? actual.sortText === ts.Completions.SortText.LocationPriorityFulfilled
849+
: actual.kindModifiers === 'optional'
850+
? actual.sortText === ts.Completions.SortText.LocationPriorityOptional
851+
: actual.sortText === ts.Completions.SortText.LocationPriority
852+
}
853+
else {
854+
isSortTextEqual = actual.sortText === sortText;
855+
}
856+
assert.equal(isSortTextEqual, true, this.messageAtLastKnownMarker(`Actual entry: ${JSON.stringify(actual)}`));
845857

846858
if (text !== undefined) {
847859
const actualDetails = this.getCompletionEntryDetails(actual.name, actual.source)!;

src/services/completions.ts

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
namespace ts.Completions {
33
export enum SortText {
44
LocationPriority = "0",
5-
SuggestedClassMembers = "1",
6-
GlobalsOrKeywords = "2",
7-
AutoImportSuggestions = "3",
8-
JavascriptIdentifiers = "4"
5+
LocationPriorityOptional = "1",
6+
LocationPriorityFulfilled = "2",
7+
SuggestedClassMembers = "3",
8+
GlobalsOrKeywords = "4",
9+
AutoImportSuggestions = "5",
10+
JavascriptIdentifiers = "6"
911
}
1012
export type Log = (message: string) => void;
1113

@@ -103,6 +105,7 @@ namespace ts.Completions {
103105
isJsxInitializer,
104106
insideJsDocTagTypeExpression,
105107
symbolToSortTextMap,
108+
fulfilledSymbols,
106109
} = completionData;
107110

108111
if (location && location.parent && isJsxClosingElement(location.parent)) {
@@ -163,7 +166,8 @@ namespace ts.Completions {
163166
isJsxInitializer,
164167
recommendedCompletion,
165168
symbolToOriginInfoMap,
166-
symbolToSortTextMap
169+
symbolToSortTextMap,
170+
fulfilledSymbols
167171
);
168172
}
169173

@@ -240,6 +244,7 @@ namespace ts.Completions {
240244
propertyAccessToConvert: PropertyAccessExpression | undefined,
241245
isJsxInitializer: IsJsxInitializer | undefined,
242246
preferences: UserPreferences,
247+
isFulfilled: boolean
243248
): CompletionEntry | undefined {
244249
let insertText: string | undefined;
245250
let replacementSpan: TextSpan | undefined;
@@ -286,6 +291,7 @@ namespace ts.Completions {
286291
isRecommended: trueOrUndefined(isRecommendedCompletionMatch(symbol, recommendedCompletion, typeChecker)),
287292
insertText,
288293
replacementSpan,
294+
isFulfilled
289295
};
290296
}
291297

@@ -317,6 +323,7 @@ namespace ts.Completions {
317323
recommendedCompletion?: Symbol,
318324
symbolToOriginInfoMap?: SymbolOriginInfoMap,
319325
symbolToSortTextMap?: SymbolSortTextMap,
326+
fulfilledSymbols?: ReadonlyArray<Symbol>,
320327
): Map<true> {
321328
const start = timestamp();
322329
// Tracks unique names.
@@ -335,9 +342,23 @@ namespace ts.Completions {
335342
continue;
336343
}
337344

345+
let sortText = symbolToSortTextMap && symbolToSortTextMap[getSymbolId(symbol)];
346+
let isFulfilled = false;
347+
if (!sortText) {
348+
fulfilledSymbols && fulfilledSymbols.forEach(fulfilledSymbol => {
349+
if (fulfilledSymbol.name === symbol.name) {
350+
sortText = SortText.LocationPriorityFulfilled;
351+
isFulfilled = true;
352+
}
353+
});
354+
}
355+
if (!sortText) {
356+
sortText = SymbolDisplay.getSymbolModifiers(symbol) === 'optional' ? SortText.LocationPriorityOptional : SortText.LocationPriority;
357+
}
358+
338359
const entry = createCompletionEntry(
339360
symbol,
340-
symbolToSortTextMap && symbolToSortTextMap[getSymbolId(symbol)] || SortText.LocationPriority,
361+
sortText,
341362
location,
342363
sourceFile,
343364
typeChecker,
@@ -347,7 +368,8 @@ namespace ts.Completions {
347368
recommendedCompletion,
348369
propertyAccessToConvert,
349370
isJsxInitializer,
350-
preferences
371+
preferences,
372+
isFulfilled
351373
);
352374
if (!entry) {
353375
continue;
@@ -581,6 +603,7 @@ namespace ts.Completions {
581603
readonly isJsxInitializer: IsJsxInitializer;
582604
readonly insideJsDocTagTypeExpression: boolean;
583605
readonly symbolToSortTextMap: SymbolSortTextMap;
606+
readonly fulfilledSymbols?: ReadonlyArray<Symbol>;
584607
}
585608
type Request = { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag } | { readonly kind: CompletionDataKind.JsDocParameterName, tag: JSDocParameterTag };
586609

@@ -872,6 +895,7 @@ namespace ts.Completions {
872895
let isNewIdentifierLocation = false;
873896
let keywordFilters = KeywordCompletionFilters.None;
874897
let symbols: Symbol[] = [];
898+
let fulfilledSymbols: Symbol[] | undefined = [];
875899
const symbolToOriginInfoMap: SymbolOriginInfoMap = [];
876900
const symbolToSortTextMap: SymbolSortTextMap = [];
877901

@@ -924,7 +948,8 @@ namespace ts.Completions {
924948
previousToken,
925949
isJsxInitializer,
926950
insideJsDocTagTypeExpression,
927-
symbolToSortTextMap
951+
symbolToSortTextMap,
952+
fulfilledSymbols
928953
};
929954

930955
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
@@ -1494,6 +1519,14 @@ namespace ts.Completions {
14941519
if (typeMembers && typeMembers.length > 0) {
14951520
// Add filtered items to the completion list
14961521
symbols = filterObjectMembersList(typeMembers, Debug.assertDefined(existingMembers));
1522+
existingMembers && existingMembers.forEach(member => {
1523+
if (member.kind === SyntaxKind.SpreadAssignment) {
1524+
const expression = (<SpreadAssignment>member).expression;
1525+
const symbol = typeChecker.getSymbolAtLocation(expression);
1526+
const type = symbol && typeChecker.getTypeOfSymbolAtLocation(symbol, expression);
1527+
fulfilledSymbols = type && (<ObjectType>type).properties;
1528+
}
1529+
});
14971530
}
14981531
return GlobalsSearch.Success;
14991532
}

src/services/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,7 @@ namespace ts {
919919
hasAction?: true;
920920
source?: string;
921921
isRecommended?: true;
922+
isFulfilled?: boolean;
922923
}
923924

924925
export interface CompletionEntryDetails {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/// <reference path="fourslash.ts" />
2+
// @strict: true
3+
4+
//// interface I {
5+
//// B?: number;
6+
//// a: number;
7+
//// c?: string;
8+
//// d: string
9+
//// }
10+
11+
//// const foo = {
12+
//// a: 1,
13+
//// B: 2
14+
//// }
15+
16+
//// const i: I = {
17+
//// ...foo,
18+
//// /*a*/
19+
//// }
20+
21+
verify.completions({
22+
marker: ['a'],
23+
exact: [
24+
{ name: 'B', isFulfilled: true, kindModifiers: 'optional', sortText: completion.SortText.LocationPriorityFulfilled, kind: 'property' },
25+
{ name: 'a', isFulfilled: true, sortText: completion.SortText.LocationPriorityFulfilled, kind: 'property' },
26+
{ name: 'c', isFulfilled: false, kindModifiers: 'optional', sortText: completion.SortText.LocationPriorityOptional, kind: 'property' },
27+
{ name: 'd', isFulfilled: false, sortText: completion.SortText.LocationPriority, kind: 'property' }
28+
]
29+
});

tests/cases/fourslash/fourslash.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ declare namespace FourSlashInterface {
548548
readonly kind?: string;
549549
readonly kindModifiers?: string;
550550
readonly sortText?: completion.SortText;
551+
readonly isFulfilled?: boolean;
551552

552553
// details
553554
readonly text?: string;
@@ -672,10 +673,12 @@ declare namespace completion {
672673
type Entry = FourSlashInterface.ExpectedCompletionEntryObject;
673674
export const enum SortText {
674675
LocationPriority = "0",
675-
SuggestedClassMembers = "1",
676-
GlobalsOrKeywords = "2",
677-
AutoImportSuggestions = "3",
678-
JavascriptIdentifiers = "4"
676+
LocationPriorityOptional = "1",
677+
LocationPriorityFulfilled = "2",
678+
SuggestedClassMembers = "3",
679+
GlobalsOrKeywords = "4",
680+
AutoImportSuggestions = "5",
681+
JavascriptIdentifiers = "6"
679682
}
680683
export const globalThisEntry: Entry;
681684
export const undefinedVarEntry: Entry;

0 commit comments

Comments
 (0)