mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 20:25:23 -06:00
Cleanup, merge #19475
This commit is contained in:
parent
24437774a8
commit
bfba32b71d
@ -159,12 +159,6 @@ namespace ts {
|
||||
return <Path>getCanonicalFileName(nonCanonicalizedPath);
|
||||
}
|
||||
|
||||
export const enum Comparison {
|
||||
LessThan = -1,
|
||||
EqualTo = 0,
|
||||
GreaterThan = 1
|
||||
}
|
||||
|
||||
export function length(array: ReadonlyArray<any>) {
|
||||
return array ? array.length : 0;
|
||||
}
|
||||
@ -301,17 +295,33 @@ namespace ts {
|
||||
Debug.fail();
|
||||
}
|
||||
|
||||
export function contains<T>(array: ReadonlyArray<T>, value: T): boolean {
|
||||
if (array) {
|
||||
for (const v of array) {
|
||||
if (v === value) {
|
||||
return true;
|
||||
}
|
||||
function containsWithoutEqualityComparer<T>(array: ReadonlyArray<T>, value: T) {
|
||||
for (const v of array) {
|
||||
if (v === value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function containsWithEqualityComparer<T>(array: ReadonlyArray<T>, value: T, equalityComparer: EqualityComparer<T>) {
|
||||
for (const v of array) {
|
||||
if (equalityComparer(v, value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function contains<T>(array: ReadonlyArray<T>, value: T, equalityComparer?: EqualityComparer<T>): boolean {
|
||||
if (array) {
|
||||
return equalityComparer
|
||||
? containsWithEqualityComparer(array, value, equalityComparer)
|
||||
: containsWithoutEqualityComparer(array, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function indexOf<T>(array: ReadonlyArray<T>, value: T): number {
|
||||
if (array) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
@ -649,21 +659,36 @@ namespace ts {
|
||||
return [...array1, ...array2];
|
||||
}
|
||||
|
||||
// TODO: fixme (N^2) - add optional comparer so collection can be sorted before deduplication.
|
||||
export function deduplicate<T>(array: ReadonlyArray<T>, equalityComparer: (a: T, b: T) => boolean = equateValues): T[] {
|
||||
let result: T[];
|
||||
if (array) {
|
||||
result = [];
|
||||
loop: for (const item of array) {
|
||||
for (const res of result) {
|
||||
if (equalityComparer(res, item)) {
|
||||
continue loop;
|
||||
}
|
||||
/**
|
||||
* Creates a new array with duplicate entries removed.
|
||||
* @param equalityComparer An optional `EqualityComparer` used to determine if two values are duplicates.
|
||||
* @param comparer An optional `Comparer` used to sort entries before comparison. If supplied,
|
||||
* results are returned in the original order found in `array`.
|
||||
*/
|
||||
export function deduplicate<T>(array: ReadonlyArray<T>, equalityComparer?: EqualityComparer<T>, comparer?: Comparer<T>): T[] {
|
||||
if (!array) return undefined;
|
||||
if (!comparer) return addRangeIfUnique([], array, equalityComparer);
|
||||
return deduplicateWorker(array, equalityComparer, comparer);
|
||||
}
|
||||
|
||||
function deduplicateWorker<T>(array: ReadonlyArray<T>, equalityComparer: EqualityComparer<T> = equateValues, comparer: Comparer<T>) {
|
||||
// Perform a stable sort of the array. This ensures the first entry in a list of
|
||||
// duplicates remains the first entry in the result.
|
||||
const indices = sequence(0, array.length);
|
||||
stableSortIndices(array, indices, comparer);
|
||||
|
||||
const deduplicated: number[] = [];
|
||||
loop: for (const sourceIndex of indices) {
|
||||
for (const targetIndex of deduplicated) {
|
||||
if (equalityComparer(array[sourceIndex], array[targetIndex])) {
|
||||
continue loop;
|
||||
}
|
||||
result.push(item);
|
||||
}
|
||||
deduplicated.push(sourceIndex);
|
||||
}
|
||||
return result;
|
||||
|
||||
// return deduplicated items in original order
|
||||
return deduplicated.sort().map(i => array[i]);
|
||||
}
|
||||
|
||||
export function arrayIsEqualTo<T>(array1: ReadonlyArray<T>, array2: ReadonlyArray<T>, equalityComparer: (a: T, b: T) => boolean = equateValues): boolean {
|
||||
@ -731,7 +756,7 @@ namespace ts {
|
||||
* are not present in `arrayA` but are present in `arrayB`. Assumes both arrays are sorted
|
||||
* based on the provided comparer.
|
||||
*/
|
||||
export function relativeComplement<T>(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: Comparer<T> = compareValues, offsetA = 0, offsetB = 0): T[] | undefined {
|
||||
export function relativeComplement<T>(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: Comparer<T>, offsetA = 0, offsetB = 0): T[] | undefined {
|
||||
if (!arrayB || !arrayA || arrayB.length === 0 || arrayA.length === 0) return arrayB;
|
||||
const result: T[] = [];
|
||||
outer: for (; offsetB < arrayB.length; offsetB++) {
|
||||
@ -795,19 +820,27 @@ namespace ts {
|
||||
start = start === undefined ? 0 : toOffset(from, start);
|
||||
end = end === undefined ? from.length : toOffset(from, end);
|
||||
for (let i = start; i < end && i < from.length; i++) {
|
||||
const v = from[i];
|
||||
if (v !== undefined) {
|
||||
if (from[i] !== undefined) {
|
||||
to.push(from[i]);
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
function addRangeIfUnique<T>(to: T[], from: ReadonlyArray<T>, equalityComparer?: EqualityComparer<T>): T[] | undefined {
|
||||
for (let i = 0; i < from.length; i++) {
|
||||
if (from[i] !== undefined) {
|
||||
pushIfUnique(to, from[i], equalityComparer);
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the value was added.
|
||||
*/
|
||||
export function pushIfUnique<T>(array: T[], toAdd: T): boolean {
|
||||
if (contains(array, toAdd)) {
|
||||
export function pushIfUnique<T>(array: T[], toAdd: T, equalityComparer?: EqualityComparer<T>): boolean {
|
||||
if (contains(array, toAdd, equalityComparer)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
@ -819,9 +852,9 @@ namespace ts {
|
||||
/**
|
||||
* Unlike `pushIfUnique`, this can take `undefined` as an input, and returns a new array.
|
||||
*/
|
||||
export function appendIfUnique<T>(array: T[] | undefined, toAdd: T): T[] {
|
||||
export function appendIfUnique<T>(array: T[] | undefined, toAdd: T, equalityComparer?: EqualityComparer<T>): T[] {
|
||||
if (array) {
|
||||
pushIfUnique(array, toAdd);
|
||||
pushIfUnique(array, toAdd, equalityComparer);
|
||||
return array;
|
||||
}
|
||||
else {
|
||||
@ -829,14 +862,29 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an array of integers starting at `from` (inclusive) and ending at `to` (exclusive).
|
||||
*/
|
||||
function sequence(from: number, to: number) {
|
||||
const numbers: number[] = [];
|
||||
for (let i = from; i < to; i++) {
|
||||
numbers.push(i);
|
||||
}
|
||||
return numbers;
|
||||
}
|
||||
|
||||
function stableSortIndices<T>(array: ReadonlyArray<T>, indices: number[], comparer: Comparer<T>) {
|
||||
// sort indices by value then position
|
||||
indices.sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stable sort of an array. Elements equal to each other maintain their relative position in the array.
|
||||
*/
|
||||
export function stableSort<T>(array: ReadonlyArray<T>, comparer: Comparer<T> = compareValues) {
|
||||
return array
|
||||
.map((_, i) => i) // create array of indices
|
||||
.sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y)) // sort indices by value then position
|
||||
.map(i => array[i]); // get sorted array
|
||||
export function stableSort<T>(array: ReadonlyArray<T>, comparer: Comparer<T>) {
|
||||
const indices = sequence(0, array.length);
|
||||
stableSortIndices(array, indices, comparer);
|
||||
return indices.map(i => array[i]);
|
||||
}
|
||||
|
||||
export function rangeEquals<T>(array1: ReadonlyArray<T>, array2: ReadonlyArray<T>, pos: number, end: number) {
|
||||
@ -914,9 +962,6 @@ namespace ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
export type Comparer<T> = (a: T, b: T) => Comparison;
|
||||
export type EqualityComparer<T> = (a: T, b: T) => boolean;
|
||||
|
||||
/**
|
||||
* Performs a binary search, finding the index at which 'value' occurs in 'array'.
|
||||
* If no such index is found, returns the 2's-complement of first index at which
|
||||
@ -1483,7 +1528,7 @@ namespace ts {
|
||||
return headChain;
|
||||
}
|
||||
|
||||
function equateValues<T>(a: T, b: T) {
|
||||
export function equateValues<T>(a: T, b: T) {
|
||||
return a === b;
|
||||
}
|
||||
|
||||
@ -1512,10 +1557,9 @@ namespace ts {
|
||||
return equateValues(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two values for their order relative to each other.
|
||||
*/
|
||||
export function compareValues<T>(a: T, b: T) {
|
||||
function compareComparableValues(a: string, b: string): Comparison;
|
||||
function compareComparableValues(a: number, b: number): Comparison;
|
||||
function compareComparableValues(a: string | number, b: string | number) {
|
||||
return a === b ? Comparison.EqualTo :
|
||||
a === undefined ? Comparison.LessThan :
|
||||
b === undefined ? Comparison.GreaterThan :
|
||||
@ -1523,6 +1567,13 @@ namespace ts {
|
||||
Comparison.GreaterThan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two values for their order relative to each other.
|
||||
*/
|
||||
export function compareValues(a: number, b: number) {
|
||||
return compareComparableValues(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two strings using a case-insensitive ordinal comparison.
|
||||
*
|
||||
@ -1555,7 +1606,7 @@ namespace ts {
|
||||
* value of each code-point.
|
||||
*/
|
||||
export function compareStringsCaseSensitive(a: string, b: string) {
|
||||
return compareValues(a, b);
|
||||
return compareComparableValues(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2488,7 +2539,11 @@ namespace ts {
|
||||
if (!extraFileExtensions || extraFileExtensions.length === 0 || !needAllExtensions) {
|
||||
return needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions;
|
||||
}
|
||||
return deduplicate([...allSupportedExtensions, ...extraFileExtensions.map(e => e.extension)]);
|
||||
return deduplicate(
|
||||
[...allSupportedExtensions, ...extraFileExtensions.map(e => e.extension)],
|
||||
equateStringsCaseSensitive,
|
||||
compareStringsCaseSensitive
|
||||
);
|
||||
}
|
||||
|
||||
export function hasJavaScriptFileExtension(fileName: string) {
|
||||
|
||||
@ -36,6 +36,22 @@ namespace ts {
|
||||
push(...values: T[]): void;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export type EqualityComparer<T> = (a: T, b: T) => boolean;
|
||||
|
||||
/* @internal */
|
||||
export type Comparer<T> = (a: T, b: T) => Comparison;
|
||||
|
||||
/* @internal */
|
||||
export const enum Comparison {
|
||||
LessThan = -1,
|
||||
EqualTo = 0,
|
||||
GreaterThan = 1
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export type Selector<T, U> = (v: T) => U;
|
||||
|
||||
// branded string type used to store absolute, normalized and canonicalized paths
|
||||
// arbitrary file name can be converted to Path via toPath function
|
||||
export type Path = string & { __pathBrand: any };
|
||||
|
||||
@ -860,7 +860,8 @@ namespace ts.server {
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(inserted, this.directoryStructureHost);
|
||||
scriptInfo.attachToProject(this);
|
||||
},
|
||||
removed => this.detachScriptInfoFromProject(removed)
|
||||
removed => this.detachScriptInfoFromProject(removed),
|
||||
compareStringsCaseSensitive
|
||||
);
|
||||
const elapsed = timestamp() - start;
|
||||
this.writeLog(`Finishing updateGraphWorker: Project: ${this.getProjectName()} structureChanged: ${hasChanges} Elapsed: ${elapsed}ms`);
|
||||
|
||||
@ -958,7 +958,7 @@ namespace ts.server {
|
||||
projects,
|
||||
project => project.getLanguageService().findReferences(file, position),
|
||||
/*comparer*/ undefined,
|
||||
/*areEqual (TODO: fixme)*/ undefined
|
||||
equateValues
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -288,8 +288,7 @@ namespace ts.server {
|
||||
return index === 0 || value !== array[index - 1];
|
||||
}
|
||||
|
||||
export function enumerateInsertsAndDeletes<T>(newItems: SortedReadonlyArray<T>, oldItems: SortedReadonlyArray<T>, inserted: (newItem: T) => void, deleted: (oldItem: T) => void, compare?: Comparer<T>) {
|
||||
compare = compare || compareValues;
|
||||
export function enumerateInsertsAndDeletes<T>(newItems: SortedReadonlyArray<T>, oldItems: SortedReadonlyArray<T>, inserted: (newItem: T) => void, deleted: (oldItem: T) => void, comparer: Comparer<T>) {
|
||||
let newIndex = 0;
|
||||
let oldIndex = 0;
|
||||
const newLen = newItems.length;
|
||||
@ -297,7 +296,7 @@ namespace ts.server {
|
||||
while (newIndex < newLen && oldIndex < oldLen) {
|
||||
const newItem = newItems[newIndex];
|
||||
const oldItem = oldItems[oldIndex];
|
||||
const compareResult = compare(newItem, oldItem);
|
||||
const compareResult = comparer(newItem, oldItem);
|
||||
if (compareResult === Comparison.LessThan) {
|
||||
inserted(newItem);
|
||||
newIndex++;
|
||||
|
||||
@ -115,7 +115,10 @@ namespace ts.JsTyping {
|
||||
|
||||
// add typings for unresolved imports
|
||||
if (unresolvedImports) {
|
||||
const module = deduplicate(unresolvedImports.map(moduleId => nodeCoreModules.has(moduleId) ? "node" : moduleId));
|
||||
const module = deduplicate(
|
||||
unresolvedImports.map(moduleId => nodeCoreModules.has(moduleId) ? "node" : moduleId),
|
||||
equateStringsCaseSensitive,
|
||||
compareStringsCaseSensitive);
|
||||
addInferredTypings(module, "Inferred typings from unresolved imports");
|
||||
}
|
||||
// Add the cached typing locations for inferred typings that are already installed
|
||||
|
||||
@ -44,7 +44,10 @@ namespace ts.Completions.PathCompletions {
|
||||
containsPath(rootDirectory, scriptPath, basePath, ignoreCase) ? scriptPath.substr(rootDirectory.length) : undefined);
|
||||
|
||||
// Now find a path for each potential directory that is to be merged with the one containing the script
|
||||
return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory)));
|
||||
return deduplicate(
|
||||
map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory)),
|
||||
equateStringsCaseSensitive,
|
||||
compareStringsCaseSensitive);
|
||||
}
|
||||
|
||||
function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: ReadonlyArray<string>, includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): CompletionEntry[] {
|
||||
@ -271,7 +274,7 @@ namespace ts.Completions.PathCompletions {
|
||||
}
|
||||
}
|
||||
|
||||
return deduplicate(nonRelativeModuleNames);
|
||||
return deduplicate(nonRelativeModuleNames, equateStringsCaseSensitive, compareStringsCaseSensitive);
|
||||
}
|
||||
|
||||
export function getTripleSlashReferenceCompletion(sourceFile: SourceFile, position: number, compilerOptions: CompilerOptions, host: LanguageServiceHost): CompletionInfo {
|
||||
|
||||
@ -1757,7 +1757,7 @@ namespace ts {
|
||||
const newLineCharacter = getNewLineOrDefaultFromHost(host);
|
||||
const rulesProvider = getRuleProvider(formatOptions);
|
||||
|
||||
return flatMap(deduplicate(errorCodes), errorCode => {
|
||||
return flatMap(deduplicate(errorCodes, equateValues, compareValues), errorCode => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
return codefix.getFixes({ errorCode, sourceFile, span, program, newLineCharacter, host, cancellationToken, rulesProvider });
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user