Skip to content

Commit

Permalink
Completions sorting overhaul (#46703)
Browse files Browse the repository at this point in the history
* Sort resolved auto-import completions by number of directory separators

* Sort completions in services layer

* Finish tests

* Fix more tests

* Respect SortText in completions

* Update tests to use `unsorted` assertion
  • Loading branch information
andrewbranch committed Dec 3, 2021
1 parent 240ba0a commit 2ce05a8
Show file tree
Hide file tree
Showing 153 changed files with 78,027 additions and 77,852 deletions.
9 changes: 8 additions & 1 deletion src/compiler/core.ts
Expand Up @@ -770,7 +770,11 @@ namespace ts {
return deduplicated as any as SortedReadonlyArray<T>;
}

export function insertSorted<T>(array: SortedArray<T>, insert: T, compare: Comparer<T>): void {
export function createSortedArray<T>(): SortedArray<T> {
return [] as any as SortedArray<T>; // TODO: GH#19873
}

export function insertSorted<T>(array: SortedArray<T>, insert: T, compare: Comparer<T>, allowDuplicates?: boolean): void {
if (array.length === 0) {
array.push(insert);
return;
Expand All @@ -780,6 +784,9 @@ namespace ts {
if (insertIndex < 0) {
array.splice(~insertIndex, 0, insert);
}
else if (allowDuplicates) {
array.splice(insertIndex, 0, insert);
}
}

export function sortAndDeduplicate<T>(array: readonly string[]): SortedReadonlyArray<string>;
Expand Down
37 changes: 34 additions & 3 deletions src/harness/fourslashImpl.ts
Expand Up @@ -914,9 +914,26 @@ namespace FourSlash {
}

if (ts.hasProperty(options, "exact")) {
ts.Debug.assert(!ts.hasProperty(options, "includes") && !ts.hasProperty(options, "excludes"));
ts.Debug.assert(!ts.hasProperty(options, "includes") && !ts.hasProperty(options, "excludes") && !ts.hasProperty(options, "unsorted"));
if (options.exact === undefined) throw this.raiseError("Expected no completions");
this.verifyCompletionsAreExactly(actualCompletions.entries, toArray(options.exact), options.marker);
this.verifyCompletionsAreExactly(actualCompletions.entries, options.exact, options.marker);
}
else if (options.unsorted) {
ts.Debug.assert(!ts.hasProperty(options, "includes") && !ts.hasProperty(options, "excludes"));
for (const expectedEntry of options.unsorted) {
const name = typeof expectedEntry === "string" ? expectedEntry : expectedEntry.name;
const found = nameToEntries.get(name);
if (!found) throw this.raiseError(`Unsorted: completion '${name}' not found.`);
if (!found.length) throw this.raiseError(`Unsorted: no completions with name '${name}' remain unmatched.`);
this.verifyCompletionEntry(found.shift()!, expectedEntry);
}
if (actualCompletions.entries.length !== options.unsorted.length) {
const unmatched: string[] = [];
nameToEntries.forEach(entries => {
unmatched.push(...entries.map(e => e.name));
});
this.raiseError(`Additional completions found not included in 'unsorted': ${unmatched.join("\n")}`);
}
}
else {
if (options.includes) {
Expand Down Expand Up @@ -993,7 +1010,11 @@ namespace FourSlash {
}
}

private verifyCompletionsAreExactly(actual: readonly ts.CompletionEntry[], expected: readonly FourSlashInterface.ExpectedCompletionEntry[], marker?: ArrayOrSingle<string | Marker>) {
private verifyCompletionsAreExactly(actual: readonly ts.CompletionEntry[], expected: ArrayOrSingle<FourSlashInterface.ExpectedCompletionEntry> | FourSlashInterface.ExpectedExactCompletionsPlus, marker?: ArrayOrSingle<string | Marker>) {
if (!ts.isArray(expected)) {
expected = [expected];
}

// First pass: test that names are right. Then we'll test details.
assert.deepEqual(actual.map(a => a.name), expected.map(e => typeof e === "string" ? e : e.name), marker ? "At marker " + JSON.stringify(marker) : undefined);

Expand All @@ -1004,6 +1025,16 @@ namespace FourSlash {
}
this.verifyCompletionEntry(completion, expectedCompletion);
});

// All completions were correct in the sort order given. If that order was produced by a function
// like `completion.globalsPlus`, ensure the "plus" array was sorted in the same way.
const { plusArgument, plusFunctionName } = expected as FourSlashInterface.ExpectedExactCompletionsPlus;
if (plusArgument) {
assert.deepEqual(
plusArgument,
expected.filter(entry => plusArgument.includes(entry)),
`At marker ${JSON.stringify(marker)}: Argument to '${plusFunctionName}' was incorrectly sorted.`);
}
}

/** Use `getProgram` instead of accessing this directly. */
Expand Down

0 comments on commit 2ce05a8

Please sign in to comment.