mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
More control over which collator to use in each situation
This commit is contained in:
+272
-122
@@ -1486,140 +1486,288 @@ namespace ts {
|
||||
/**
|
||||
* Compare two values for their order relative to each other.
|
||||
*/
|
||||
export function compareValues<T>(a: T | undefined, b: T | undefined) {
|
||||
if (a === b) return Comparison.EqualTo;
|
||||
if (a === undefined) return Comparison.LessThan;
|
||||
if (b === undefined) return Comparison.GreaterThan;
|
||||
return a < b ? Comparison.LessThan : Comparison.GreaterThan;
|
||||
export function compareValues<T>(a: T, b: T) {
|
||||
return a === b ? Comparison.EqualTo :
|
||||
a === undefined ? Comparison.LessThan :
|
||||
b === undefined ? Comparison.GreaterThan :
|
||||
a < b ? Comparison.LessThan :
|
||||
Comparison.GreaterThan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two values for their equality.
|
||||
*/
|
||||
export function equateValues<T>(a: T | undefined, b: T | undefined) {
|
||||
export function equateValues<T>(a: T, b: T) {
|
||||
return a === b;
|
||||
}
|
||||
|
||||
interface StringCollator {
|
||||
compare(a: string, b: string): number;
|
||||
equate(a: string, b: string): boolean;
|
||||
export interface StringCollator {
|
||||
compare(a: string | undefined, b: string | undefined): number;
|
||||
equate(a: string | undefined, b: string | undefined): boolean;
|
||||
}
|
||||
|
||||
// Gets string comparers compatible with the current host
|
||||
function createCaseInsensitiveStringCollator() {
|
||||
function createIntlStringCollator(): StringCollator {
|
||||
// Strings that differ in base or accents/diacritic marks compare as unequal.
|
||||
// An `undefined` locale uses the default locale of the host.
|
||||
const sortCollator = new Intl.Collator(/*locales*/ undefined, { usage: "sort", sensitivity: "accent" });
|
||||
const searchCollator = new Intl.Collator(/*locales*/ undefined, { usage: "search", sensitivity: "accent" });
|
||||
return {
|
||||
export interface StringCollators {
|
||||
/**
|
||||
* Gets a string collator for case-insensitive ordinal comparisons of strings.
|
||||
*
|
||||
* Ordinal comparisons are based on the difference between the unicode code points of
|
||||
* both strings. Characters with multiple unicode representations are considered
|
||||
* unequal.
|
||||
*
|
||||
* Case-insensitive comparisons compare both strings after applying `toUpperCase` to
|
||||
* each string.
|
||||
*/
|
||||
readonly ordinalCaseInsensitive: StringCollator;
|
||||
|
||||
/**
|
||||
* Gets a string collator for case-sensitive ordinal comparisons of strings.
|
||||
*
|
||||
* Ordinal comparisons are based on the difference between the unicode code points of
|
||||
* both strings. Characters with multiple unicode representations are considered
|
||||
* unequal. They provide predictable ordering, but place "a" after "B".
|
||||
*/
|
||||
readonly ordinalCaseSensitive: StringCollator;
|
||||
|
||||
/**
|
||||
* Gets or sets a string collator for case-insensitive comparisons of strings in the host default locale.
|
||||
*
|
||||
* UI comparisons are based on the sort order of the host default locale. Ordering is not
|
||||
* predictable between different host locales, but is best for displaying ordered data
|
||||
* for UI presentation. Characters with multiple unicode representations may be considered
|
||||
* equal.
|
||||
*
|
||||
* Case-insensitive comparisons compare strings that differ in only base characters or
|
||||
* accents/diacritic marks as unequal.
|
||||
*/
|
||||
readonly uiCaseInsensitive: StringCollator;
|
||||
|
||||
/**
|
||||
* Gets a string collator for case-sensitive comparisons of strings in the host default locale.
|
||||
*
|
||||
* UI comparisons are based on the sort order of the host default locale. Ordering is not
|
||||
* predictable between different host locales, but is best for displaying ordered data
|
||||
* for UI presentation. Characters with multiple unicode representations may be considered
|
||||
* equal.
|
||||
*/
|
||||
readonly uiCaseSensitive: StringCollator;
|
||||
|
||||
/**
|
||||
* Gets a string collator for case-insensitive comparisons of strings in an invariant locale.
|
||||
*
|
||||
* Invariant comparisons are based on the sort order of an invariant locale ('en-US').
|
||||
* They provide predictable ordering, placing "a" before "B". Characters with multiple
|
||||
* unicode representations may be considered equal. Invariant comparisons are best used
|
||||
* when interacting with the file system.
|
||||
*
|
||||
* Case-insensitive comparisons compare strings that differ in only base characters or
|
||||
* accents/diacritic marks as unequal.
|
||||
*/
|
||||
readonly invariantCaseInsensitive: StringCollator;
|
||||
|
||||
/**
|
||||
* Gets a string collator for case-sensitive comparisons of strings in an invariant locale.
|
||||
*
|
||||
* Invariant comparisons are based on the sort order of an invariant locale ('en-US').
|
||||
* They provide predictable ordering, placing "a" before "B". Characters with multiple
|
||||
* unicode representations may be considered equal. Invariant comparisons are best used
|
||||
* when interacting with the file system.
|
||||
*/
|
||||
readonly invariantCaseSensitive: StringCollator;
|
||||
|
||||
/**
|
||||
* Gets or sets the locale for UI collators
|
||||
*/
|
||||
uiLocale: string | undefined;
|
||||
|
||||
/**
|
||||
* Creates a `StringCollator` for a specific locale and case sensitivity.
|
||||
*/
|
||||
create(locale: string | undefined, caseSensitive: boolean): StringCollator;
|
||||
|
||||
/**
|
||||
* Gets the ordinal `StringCollator` for the provided case sensitivity.
|
||||
*/
|
||||
getOrdinalCollator(caseSensitive: boolean): StringCollator;
|
||||
|
||||
/**
|
||||
* Gets the UI `StringCollator` for the provided case sensitivity.
|
||||
*/
|
||||
getUICollator(caseSensitive: boolean): StringCollator;
|
||||
|
||||
/**
|
||||
* Gets the invariant `StringCollator` for the provided case sensitivity.
|
||||
*/
|
||||
getInvariantCollator(caseSensitive: boolean): StringCollator;
|
||||
|
||||
/**
|
||||
* Gets a `StringCollator` for comparing code fragments for code generation.
|
||||
*/
|
||||
getCodeCollator(caseSensitive: boolean): StringCollator;
|
||||
|
||||
/**
|
||||
* Gets a `StringCollator` for comparing paths.
|
||||
*/
|
||||
getPathCollator(caseSensitive: boolean): StringCollator;
|
||||
}
|
||||
|
||||
export const StringCollator: StringCollators = (function () {
|
||||
const invariantLocaleName = "en-US"; // we use en-US for the invariant locale
|
||||
const create = getStringCollatorFactory();
|
||||
const ordinalCS: StringCollator = {
|
||||
compare: compareValues,
|
||||
equate: equateValues
|
||||
};
|
||||
const ordinalCI: StringCollator = {
|
||||
compare: (a, b) => compareValues(toUpperCase(a), toUpperCase(b)),
|
||||
equate: (a, b) => toUpperCase(a) === toUpperCase(b)
|
||||
};
|
||||
let invariantCI: StringCollator | undefined;
|
||||
let invariantCS: StringCollator | undefined;
|
||||
let uiCI: StringCollator | undefined;
|
||||
let uiCS: StringCollator | undefined;
|
||||
let uiLocale: string | undefined;
|
||||
|
||||
return {
|
||||
get ordinalCaseInsensitive() { return ordinalCI; },
|
||||
get ordinalCaseSensitive() { return ordinalCS; },
|
||||
get uiCaseInsensitive() { return uiCI || (uiCI = create(uiLocale, /*caseInsensitive*/ true)); },
|
||||
get uiCaseSensitive() { return uiCS || (uiCS = create(uiLocale, /*caseInsensitive*/ false)); },
|
||||
get invariantCaseInsensitive() { return invariantCI || (invariantCI = create(invariantLocaleName, /*caseInsensitive*/ true)); },
|
||||
get invariantCaseSensitive() { return invariantCS || (invariantCS = create(invariantLocaleName, /*caseInsensitive*/ false)); },
|
||||
get uiLocale() { return uiLocale; },
|
||||
set uiLocale(value) {
|
||||
if (uiLocale !== value) {
|
||||
uiLocale = value;
|
||||
uiCI = undefined;
|
||||
uiCS = undefined;
|
||||
}
|
||||
},
|
||||
create,
|
||||
getOrdinalCollator,
|
||||
getUICollator,
|
||||
getInvariantCollator,
|
||||
getCodeCollator: getInvariantCollator,
|
||||
getPathCollator: getInvariantCollator
|
||||
};
|
||||
|
||||
function getOrdinalCollator(caseInsensitive: boolean) {
|
||||
return caseInsensitive ? StringCollator.ordinalCaseInsensitive : StringCollator.ordinalCaseSensitive;
|
||||
}
|
||||
|
||||
function getUICollator(caseInsensitive: boolean) {
|
||||
return caseInsensitive ? StringCollator.uiCaseInsensitive : StringCollator.uiCaseSensitive;
|
||||
}
|
||||
|
||||
function getInvariantCollator(caseInsensitive: boolean) {
|
||||
return caseInsensitive ? StringCollator.invariantCaseInsensitive : StringCollator.invariantCaseSensitive;
|
||||
}
|
||||
|
||||
function toUpperCase(value: string | undefined): string | undefined {
|
||||
return value === undefined ? undefined : value.toUpperCase();
|
||||
}
|
||||
|
||||
function compareDefined(a: string, b: string) {
|
||||
return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo;
|
||||
}
|
||||
|
||||
function compareWithCallback(a: string | undefined, b: string | undefined, comparer: (a: string, b: string) => number) {
|
||||
return a === b ? Comparison.EqualTo :
|
||||
a === undefined ? Comparison.LessThan :
|
||||
b === undefined ? Comparison.GreaterThan :
|
||||
toComparison(comparer(a, b));
|
||||
}
|
||||
|
||||
function toComparison(value: number) {
|
||||
return value < 0 ? Comparison.LessThan : value > 0 ? Comparison.GreaterThan : Comparison.EqualTo;
|
||||
}
|
||||
|
||||
function createIntlStringCollator(locale: string | undefined, caseInsensitive: boolean): StringCollator {
|
||||
// Initialize the sort collator on first use
|
||||
let sortComparer: Comparer<string> = (a, b) => {
|
||||
// Intl.Collator.prototype.compare is bound to the collator. See NOTE in
|
||||
// http://www.ecma-international.org/ecma-402/2.0/#sec-Intl.Collator.prototype.compare
|
||||
compare: sortCollator.compare,
|
||||
equate: (a, b) => searchCollator.compare(a, b) === 0
|
||||
sortComparer = new Intl.Collator(locale, { usage: "sort", sensitivity: caseInsensitive ? "accent" : "variant" }).compare;
|
||||
return sortComparer(a, b);
|
||||
};
|
||||
|
||||
// Initialize the search collator on first use
|
||||
let searchComparer: Comparer<string> = (a, b) => {
|
||||
// Intl.Collator.prototype.compare is bound to the collator. See NOTE in
|
||||
// http://www.ecma-international.org/ecma-402/2.0/#sec-Intl.Collator.prototype.compare
|
||||
searchComparer = new Intl.Collator(locale, { usage: "search", sensitivity: caseInsensitive ? "accent" : "variant" }).compare;
|
||||
return searchComparer(a, b);
|
||||
};
|
||||
}
|
||||
|
||||
function createLocaleCompareStringCollator(): StringCollator {
|
||||
// for case-insensitive comparisons we always map both strings to their
|
||||
// upper-case form as some unicode characters do not properly round-trip to
|
||||
// lowercase (such as ẞ).
|
||||
return {
|
||||
compare: (a, b) => a.toLocaleUpperCase().localeCompare(b.toLocaleUpperCase()),
|
||||
equate: (a, b) => a.toLocaleUpperCase() === b.toLocaleUpperCase()
|
||||
compare: (a, b) => compareWithCallback(a, b, sortComparer),
|
||||
equate: (a, b) => compareWithCallback(a, b, searchComparer) === 0
|
||||
};
|
||||
}
|
||||
|
||||
function createOrdinalStringCollator(): StringCollator {
|
||||
// for case-insensitive comparisons we always map both strings to their
|
||||
// upper-case form as some unicode characters do not properly round-trip to
|
||||
// lowercase (such as ẞ).
|
||||
function createLocaleCompareStringCollator(locale: string | undefined, caseInsensitive: boolean): StringCollator {
|
||||
if (locale !== undefined) return getFallbackStringCollator(/*locale*/ undefined, caseInsensitive);
|
||||
if (caseInsensitive) {
|
||||
// for case-insensitive comparisons we always map both strings to their
|
||||
// upper-case form as some unicode characters do not properly round-trip to
|
||||
// lowercase (such as `ẞ` (German sharp capital s)).
|
||||
return {
|
||||
compare: (a, b) => compareWithCallback(a, b, localeCompareCaseInsensitive),
|
||||
equate: (a, b) => compareWithCallback(a, b, localeCompareCaseInsensitive) === 0
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
compare: (a, b) => compareWithCallback(a, b, localeCompare),
|
||||
equate: (a, b) => compareWithCallback(a, b, localeCompare) === 0
|
||||
};
|
||||
}
|
||||
|
||||
function localeCompareCaseInsensitive(a: string, b: string) {
|
||||
return a.toLocaleUpperCase().localeCompare(b.toLocaleUpperCase());
|
||||
}
|
||||
|
||||
function localeCompare(a: string, b: string) {
|
||||
return a.localeCompare(b);
|
||||
}
|
||||
}
|
||||
|
||||
function getFallbackStringCollator(_locale: string | undefined, caseInsensitive: boolean): StringCollator {
|
||||
if (caseInsensitive) return ordinalCI;
|
||||
|
||||
function compareLowerCaseFirst(a: string, b: string) {
|
||||
// An ordinal comparison puts "A" after "b", but for the UI we want "A" before "b".
|
||||
// We first sort case insensitively. So "Aaa" will come before "baa".
|
||||
// Then we sort case sensitively, so "aaa" will come before "Aaa".
|
||||
return compareDefined(a.toUpperCase(), b.toUpperCase()) || compareDefined(a, b);
|
||||
}
|
||||
|
||||
return {
|
||||
compare: (a, b) => compareWithCallback(a, b, compareLowerCaseFirst),
|
||||
equate: ordinalCS.equate
|
||||
};
|
||||
}
|
||||
|
||||
function getStringCollatorFactory() {
|
||||
// If the host supports Intl (ECMA-402), we use Intl for comparisons using the default
|
||||
// locale:
|
||||
if (typeof Intl === "object" && typeof Intl.Collator === "function") {
|
||||
return createIntlStringCollator;
|
||||
}
|
||||
|
||||
// If the host does not support Intl, we fall back to localeCompare:
|
||||
//
|
||||
// The ordinal comparison cannot properly handle comparison of the Turkish
|
||||
// (dotted) i and (dotless) ı to the uppercase forms of (dotted) İ and (dotless) I.
|
||||
// This is best handled by Intl and not supported in the fallback case.
|
||||
return {
|
||||
compare: (a, b) => {
|
||||
const upperA = a.toUpperCase();
|
||||
const upperB = b.toUpperCase();
|
||||
return upperA < upperB ? Comparison.LessThan :
|
||||
upperA > upperB ? Comparison.GreaterThan :
|
||||
Comparison.EqualTo;
|
||||
},
|
||||
equate: (a, b) => a.toUpperCase() === b.toUpperCase()
|
||||
};
|
||||
// Node v0.10 provides incorrect results for comparisons using localeCompare, so we must
|
||||
// verify the implementation.
|
||||
if (typeof String.prototype.localeCompare === "function" &&
|
||||
typeof String.prototype.toLocaleUpperCase === "function" &&
|
||||
"a".localeCompare("B") < 0) {
|
||||
return createLocaleCompareStringCollator;
|
||||
}
|
||||
|
||||
// Otherwise, fall back to ordinal comparison:
|
||||
return getFallbackStringCollator;
|
||||
}
|
||||
|
||||
// If the host supports Intl (ECMA-402), we use Intl for comparisons using the default
|
||||
// locale:
|
||||
if (typeof Intl === "object" && typeof Intl.Collator === "function") {
|
||||
return createIntlStringCollator();
|
||||
}
|
||||
|
||||
// If the host does not support Intl, we fall back to localeCompare:
|
||||
//
|
||||
// Node v0.10 provides incorrect results for comparisons using localeCompare, so we must
|
||||
// verify the implementation.
|
||||
if (typeof String.prototype.localeCompare === "function" &&
|
||||
typeof String.prototype.toLocaleUpperCase === "function" &&
|
||||
"a".localeCompare("B") < 0) {
|
||||
return createLocaleCompareStringCollator();
|
||||
}
|
||||
|
||||
// Otherwise, fall back to ordinal comparison:
|
||||
return createOrdinalStringCollator();
|
||||
}
|
||||
|
||||
const caseInsensitiveCollator = createCaseInsensitiveStringCollator();
|
||||
|
||||
/**
|
||||
* Performs a case-insensitive comparison between two strings.
|
||||
*
|
||||
* If supported by the host, the default locale is used for comparisons. Otherwise, an ordinal
|
||||
* comparison is used.
|
||||
*/
|
||||
export function compareStringsCaseInsensitive(a: string | undefined, b: string | undefined) {
|
||||
if (a === b) return Comparison.EqualTo;
|
||||
if (a === undefined) return Comparison.LessThan;
|
||||
if (b === undefined) return Comparison.GreaterThan;
|
||||
const result = caseInsensitiveCollator.compare(a, b);
|
||||
return result < 0 ? Comparison.LessThan : result > 0 ? Comparison.GreaterThan : Comparison.EqualTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a case-sensitive comparison between two strings.
|
||||
*/
|
||||
export function compareStringsCaseSensitive(a: string | undefined, b: string | undefined) {
|
||||
return compareValues(a, b);
|
||||
}
|
||||
|
||||
export function compareStrings(a: string | undefined, b: string | undefined, ignoreCase?: boolean): Comparison {
|
||||
return ignoreCase ? compareStringsCaseInsensitive(a, b) : compareStringsCaseSensitive(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a case-insensitive equality comparison between two strings.
|
||||
*
|
||||
* If supported by the host, the default locale is used for comparisons. Otherwise, an ordinal
|
||||
* comparison is used.
|
||||
*/
|
||||
export function equateStringsCaseInsensitive(a: string | undefined, b: string | undefined) {
|
||||
return a === b
|
||||
|| a !== undefined
|
||||
&& b !== undefined
|
||||
&& caseInsensitiveCollator.equate(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a case-sensitive equality comparison between two strings.
|
||||
*/
|
||||
export function equateStringsCaseSensitive(a: string | undefined, b: string | undefined) {
|
||||
return equateValues(a, b);
|
||||
}
|
||||
|
||||
export function equateStrings(a: string | undefined, b: string | undefined, ignoreCase?: boolean) {
|
||||
return ignoreCase ? equateStringsCaseInsensitive(a, b) : equateStringsCaseSensitive(a, b);
|
||||
}
|
||||
})();
|
||||
|
||||
function getDiagnosticFileName(diagnostic: Diagnostic): string {
|
||||
return diagnostic.file ? diagnostic.file.fileName : undefined;
|
||||
@@ -2003,9 +2151,9 @@ namespace ts {
|
||||
const aComponents = getNormalizedPathComponents(a, currentDirectory);
|
||||
const bComponents = getNormalizedPathComponents(b, currentDirectory);
|
||||
const sharedLength = Math.min(aComponents.length, bComponents.length);
|
||||
const comparer = ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive;
|
||||
const collator = StringCollator.getPathCollator(ignoreCase);
|
||||
for (let i = 0; i < sharedLength; i++) {
|
||||
const result = comparer(aComponents[i], bComponents[i]);
|
||||
const result = collator.compare(aComponents[i], bComponents[i]);
|
||||
if (result !== Comparison.EqualTo) {
|
||||
return result;
|
||||
}
|
||||
@@ -2026,9 +2174,10 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
const equalityComparer = ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive;
|
||||
// File-system comparisons should use predictable ordering
|
||||
const collator = StringCollator.getPathCollator(ignoreCase);
|
||||
for (let i = 0; i < parentComponents.length; i++) {
|
||||
if (!equalityComparer(parentComponents[i], childComponents[i])) {
|
||||
if (!collator.equate(parentComponents[i], childComponents[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2284,7 +2433,7 @@ namespace ts {
|
||||
// If there are no "includes", then just put everything in results[0].
|
||||
const results: string[][] = includeFileRegexes ? includeFileRegexes.map(() => []) : [[]];
|
||||
|
||||
const comparer = useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive;
|
||||
const collator = StringCollator.getPathCollator(!useCaseSensitiveFileNames);
|
||||
for (const basePath of patterns.basePaths) {
|
||||
visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth);
|
||||
}
|
||||
@@ -2293,7 +2442,7 @@ namespace ts {
|
||||
|
||||
function visitDirectory(path: string, absolutePath: string, depth: number | undefined) {
|
||||
let { files, directories } = getFileSystemEntries(path);
|
||||
files = files.slice().sort(comparer);
|
||||
files = files.slice().sort(collator.compare);
|
||||
|
||||
for (const current of files) {
|
||||
const name = combinePaths(path, current);
|
||||
@@ -2318,7 +2467,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
directories = directories.slice().sort(comparer);
|
||||
directories = directories.slice().sort(collator.compare);
|
||||
for (const current of directories) {
|
||||
const name = combinePaths(path, current);
|
||||
const absoluteName = combinePaths(absolutePath, current);
|
||||
@@ -2349,7 +2498,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Sort the offsets array using either the literal or canonical path representations.
|
||||
includeBasePaths.sort(useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive);
|
||||
const collator = StringCollator.getPathCollator(!useCaseSensitiveFileNames);
|
||||
includeBasePaths.sort(collator.compare);
|
||||
|
||||
// Iterate over each include base path and include unique base paths that are not a
|
||||
// subpath of an existing base path
|
||||
|
||||
@@ -6069,7 +6069,7 @@ namespace ts {
|
||||
const checkJsDirectiveMatchResult = checkJsDirectiveRegEx.exec(comment);
|
||||
if (checkJsDirectiveMatchResult) {
|
||||
checkJsDirective = {
|
||||
enabled: equateStrings(checkJsDirectiveMatchResult[1], "@ts-check", /*ignoreCase*/ true),
|
||||
enabled: StringCollator.ordinalCaseInsensitive.equate(checkJsDirectiveMatchResult[1], "@ts-check"),
|
||||
end: range.end,
|
||||
pos: range.pos
|
||||
};
|
||||
|
||||
@@ -1101,12 +1101,14 @@ namespace ts {
|
||||
|
||||
// If '--lib' is not specified, include default library file according to '--target'
|
||||
// otherwise, using options specified in '--lib' instead of '--target' default library file
|
||||
|
||||
// File-system ordering should use a predictable order
|
||||
const collator = StringCollator.getPathCollator(!host.useCaseSensitiveFileNames());
|
||||
if (!options.lib) {
|
||||
return equateStrings(file.fileName, getDefaultLibraryFileName(), /*ignoreCase*/ !host.useCaseSensitiveFileNames());
|
||||
return collator.equate(file.fileName, getDefaultLibraryFileName());
|
||||
}
|
||||
else {
|
||||
const equalityComparer = host.useCaseSensitiveFileNames() ? equateStringsCaseSensitive : equateStringsCaseInsensitive;
|
||||
return forEach(options.lib, libFileName => equalityComparer(file.fileName, combinePaths(defaultLibraryPath, libFileName)));
|
||||
return forEach(options.lib, libFileName => collator.equate(file.fileName, combinePaths(defaultLibraryPath, libFileName)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3949,6 +3949,9 @@ namespace ts {
|
||||
trySetLanguageAndTerritory(language, /*territory*/ undefined, errors);
|
||||
}
|
||||
|
||||
// Set the locale for UI collation
|
||||
StringCollator.uiLocale = locale;
|
||||
|
||||
function trySetLanguageAndTerritory(language: string, territory: string, errors?: Push<Diagnostic>): boolean {
|
||||
const compilerFilePath = normalizePath(sys.getExecutingFilePath());
|
||||
const containingDirectoryPath = getDirectoryPath(compilerFilePath);
|
||||
|
||||
@@ -1698,7 +1698,9 @@ namespace Harness {
|
||||
|
||||
export function *iterateOutputs(outputFiles: Harness.Compiler.GeneratedFile[]): IterableIterator<[string, string]> {
|
||||
// Collect, test, and sort the fileNames
|
||||
outputFiles.sort((a, b) => ts.compareStrings(cleanName(a.fileName), cleanName(b.fileName)));
|
||||
// As this uses the file system, use a predictable order
|
||||
const collator = ts.StringCollator.getPathCollator(/*ignoreCase*/ false);
|
||||
outputFiles.sort((a, b) => collator.compare(cleanName(a.fileName), cleanName(b.fileName)));
|
||||
const dupeCase = ts.createMap<number>();
|
||||
// Yield them
|
||||
for (const outputFile of outputFiles) {
|
||||
|
||||
@@ -13,8 +13,10 @@ namespace ts.projectSystem {
|
||||
describe("CompileOnSave affected list", () => {
|
||||
function sendAffectedFileRequestAndCheckResult(session: server.Session, request: server.protocol.Request, expectedFileList: { projectFileName: string, files: FileOrFolder[] }[]) {
|
||||
const response = session.executeCommand(request).response as server.protocol.CompileOnSaveAffectedFileListSingleProject[];
|
||||
const actualResult = response.sort((list1, list2) => compareStrings(list1.projectFileName, list2.projectFileName));
|
||||
expectedFileList = expectedFileList.sort((list1, list2) => compareStrings(list1.projectFileName, list2.projectFileName));
|
||||
// File-system ordering should use a predictable order
|
||||
const collator = StringCollator.getPathCollator(/*ignoreCase*/ false);
|
||||
const actualResult = response.sort((list1, list2) => collator.compare(list1.projectFileName, list2.projectFileName));
|
||||
expectedFileList = expectedFileList.sort((list1, list2) => collator.compare(list1.projectFileName, list2.projectFileName));
|
||||
|
||||
assert.equal(actualResult.length, expectedFileList.length, `Actual result project number is different from the expected project number`);
|
||||
|
||||
|
||||
@@ -1186,6 +1186,7 @@ namespace ts.server {
|
||||
|
||||
const completions = project.getLanguageService().getCompletionsAtPosition(file, position);
|
||||
if (simplifiedResult) {
|
||||
const comparer = StringCollator.uiCaseSensitive.compare;
|
||||
return mapDefined<CompletionEntry, protocol.CompletionEntry>(completions && completions.entries, entry => {
|
||||
if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) {
|
||||
const { name, kind, kindModifiers, sortText, replacementSpan, hasAction } = entry;
|
||||
@@ -1193,7 +1194,7 @@ namespace ts.server {
|
||||
// Use `hasAction || undefined` to avoid serializing `false`.
|
||||
return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan, hasAction: hasAction || undefined };
|
||||
}
|
||||
}).sort((a, b) => compareStrings(a.name, b.name));
|
||||
}).sort((a, b) => comparer(a.name, b.name));
|
||||
}
|
||||
else {
|
||||
return completions;
|
||||
|
||||
@@ -175,12 +175,8 @@ namespace ts.NavigateTo {
|
||||
|
||||
function compareNavigateToItems(i1: RawNavigateToItem, i2: RawNavigateToItem): number {
|
||||
// TODO(cyrusn): get the gamut of comparisons that VS already uses here.
|
||||
// Right now we just sort by kind first, and then by name of the item.
|
||||
// We first sort case insensitively. So "Aaa" will come before "bar".
|
||||
// Then we sort case sensitively, so "aaa" will come before "Aaa".
|
||||
return i1.matchKind - i2.matchKind ||
|
||||
compareStringsCaseInsensitive(i1.name, i2.name) ||
|
||||
compareStringsCaseSensitive(i1.name, i2.name);
|
||||
StringCollator.uiCaseSensitive.compare(i1.name, i2.name);
|
||||
}
|
||||
|
||||
function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
|
||||
|
||||
@@ -368,13 +368,8 @@ namespace ts.NavigationBar {
|
||||
|
||||
function compareChildren(child1: NavigationBarNode, child2: NavigationBarNode): number {
|
||||
const name1 = tryGetName(child1.node), name2 = tryGetName(child2.node);
|
||||
if (name1 && name2) {
|
||||
const cmp = ts.compareStringsCaseInsensitive(name1, name2);
|
||||
return cmp !== 0 ? cmp : navigationBarNodeKind(child1) - navigationBarNodeKind(child2);
|
||||
}
|
||||
else {
|
||||
return name1 ? 1 : name2 ? -1 : navigationBarNodeKind(child1) - navigationBarNodeKind(child2);
|
||||
}
|
||||
return StringCollator.uiCaseInsensitive.compare(name1, name2)
|
||||
|| navigationBarNodeKind(child1) - navigationBarNodeKind(child2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1154,7 +1154,9 @@ namespace ts.refactor.extractSymbol {
|
||||
|
||||
const name1 = type1.symbol ? type1.symbol.getName() : "";
|
||||
const name2 = type2.symbol ? type2.symbol.getName() : "";
|
||||
const nameDiff = compareStrings(name1, name2);
|
||||
|
||||
// This is for code generation, use a predictable comparer.
|
||||
const nameDiff = StringCollator.invariantCaseSensitive.compare(name1, name2);
|
||||
if (nameDiff !== 0) {
|
||||
return nameDiff;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user