diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 3a706a9119d..94ab45a6f6a 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1483,6 +1483,38 @@ namespace ts { return headChain; } + /** + * Compare two values for their equality. + */ + export function equateValues(a: T, b: T) { + return a === b; + } + + export function equateStringsCaseInsensitive(a: string, b: string) { + return a === b + || a !== undefined + && b !== undefined + && a.toUpperCase() === b.toUpperCase(); + } + + export function equateStringsCaseSensitive(a: string, b: string) { + return equateValues(a, b); + } + + /** + * Compare equality between two strings using an ordinal comparison. + * + * Case-insensitive comparisons compare both strings after applying `toUpperCase` to + * each string. + */ + export function equateStrings(a: string, b: string, ignoreCase: boolean) { + return ignoreCase ? equateStringsCaseInsensitive(a, b) : equateStringsCaseSensitive(a, b); + } + + export function getStringEqualityComparer(ignoreCase: boolean) { + return ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive; + } + /** * Compare two values for their order relative to each other. */ @@ -1495,280 +1527,157 @@ namespace ts { } /** - * Compare two values for their equality. + * Compare two strings using an ordinal comparison. + * + * Ordinal comparisons are based on the difference between the unicode code points of + * both strings. Characters with multiple unicode representations are considered + * unequal. Ordinal comparisons provide predictable ordering, but place "a" after "B". + * + * Case-insensitive comparisons compare both strings after applying `toUpperCase` to + * each string. */ - export function equateValues(a: T, b: T) { - return a === b; + export function compareStrings(a: string, b: string, ignoreCase: boolean) { + return ignoreCase ? compareStringsCaseInsensitive(a, b) : compareStringsCaseSensitive(a, b); } - export interface StringCollator { - compare(a: string | undefined, b: string | undefined): number; - equate(a: string | undefined, b: string | undefined): boolean; + export function compareStringsCaseInsensitive(a: string, b: string) { + if (a === b) return Comparison.EqualTo; + if (a === undefined) return Comparison.LessThan; + if (b === undefined) return Comparison.GreaterThan; + a = a.toUpperCase(); + b = b.toUpperCase(); + return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo; } - 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 function compareStringsCaseSensitive(a: string, b: string) { + return compareValues(a, b); } - 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; + export function getStringComparer(ignoreCase: boolean) { + return ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive; + } - 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; + /** + * Creates a string comparer for use with string collation in the UI. + */ + const createStringComparer = (function () { + // If the host supports Intl, we use it for comparisons using the default locale. + if (typeof Intl === "object" && typeof Intl.Collator === "function") { + return createIntlCollatorStringComparer; } - function getUICollator(caseInsensitive: boolean) { - return caseInsensitive ? StringCollator.uiCaseInsensitive : StringCollator.uiCaseSensitive; + // If the host does not support Intl, we fall back to localeCompare. + // localeCompare in Node v0.10 is just an ordinal comparison, so don't use it. + if (typeof String.prototype.localeCompare === "function" && + typeof String.prototype.toLocaleUpperCase === "function" && + "a".localeCompare("B") < 0) { + return createLocaleCompareStringComparer; } - 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; - } + // Otherwise, fall back to ordinal comparison: + return createFallbackStringComparer; 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) { + if (a === b) return Comparison.EqualTo; + if (a === undefined) return Comparison.LessThan; + if (b === undefined) return Comparison.GreaterThan; + const value = comparer(a, b); return value < 0 ? Comparison.LessThan : value > 0 ? Comparison.GreaterThan : Comparison.EqualTo; } - function createIntlStringCollator(locale: string | undefined, caseInsensitive: boolean): StringCollator { + function createIntlCollatorStringComparer(locale: string | undefined, caseInsensitive: boolean): Comparer { // Initialize the sort collator on first use - let sortComparer: Comparer = (a, b) => { + let comparer: Comparer = (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 - 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 = (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); - }; - - return { - compare: (a, b) => compareWithCallback(a, b, sortComparer), - equate: (a, b) => compareWithCallback(a, b, searchComparer) === 0 + comparer = new Intl.Collator(locale, { usage: "sort", sensitivity: caseInsensitive ? "accent" : "variant" }).compare; + return comparer(a, b); }; + return (a, b) => compareWithCallback(a, b, comparer); } - function createLocaleCompareStringCollator(locale: string | undefined, caseInsensitive: boolean): StringCollator { - if (locale !== undefined) return getFallbackStringCollator(/*locale*/ undefined, caseInsensitive); - if (caseInsensitive) { + function createLocaleCompareStringComparer(locale: string | undefined, caseInsensitive: boolean): Comparer { + // if the locale is not the default locale (`undefined`), use the fallback comparer. + return locale !== undefined ? createFallbackStringComparer(locale, caseInsensitive) : + caseInsensitive ? (a, b) => compareWithCallback(a, b, compareCaseInsensitive) : + (a, b) => compareWithCallback(a, b, compareCaseSensitive); + + function compareCaseInsensitive(a: string, b: string) { // 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 - }; + return compareCaseSensitive(a.toLocaleUpperCase(), b.toLocaleUpperCase()); } - function localeCompareCaseInsensitive(a: string, b: string) { - return a.toLocaleUpperCase().localeCompare(b.toLocaleUpperCase()); - } - - function localeCompare(a: string, b: string) { + function compareCaseSensitive(a: string, b: string) { return a.localeCompare(b); } } - function getFallbackStringCollator(_locale: string | undefined, caseInsensitive: boolean): StringCollator { - if (caseInsensitive) return ordinalCI; + function createFallbackStringComparer(_locale: string | undefined, caseInsensitive: boolean): Comparer { + return caseInsensitive ? (a, b) => compareWithCallback(a, b, compareCaseInsensitive) : + (a, b) => compareWithCallback(a, b, compareCaseSensitiveDictionaryOrder); - function compareLowerCaseFirst(a: string, b: string) { + function compareCaseInsensitive(a: string, b: string) { + // 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 compareCaseSensitive(a.toUpperCase(), b.toUpperCase()); + } + + function compareCaseSensitive(a: string, b: string) { + return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo; + } + + function compareCaseSensitiveDictionaryOrder(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 compareCaseInsensitive(a, b) || compareCaseSensitive(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: - // - // 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; } })(); + let uiCS: Comparer | undefined; + let uiCI: Comparer | undefined; + let uiLocale: string | undefined; + + export function setUILocale(value: string) { + if (uiLocale !== value) { + uiLocale = value; + uiCS = undefined; + uiCI = undefined; + } + } + + export function compareStringsCaseInsensitiveUI(a: string, b: string) { + const comparer = uiCS || (uiCS = createStringComparer(uiLocale, /*caseInsensitive*/ false)); + return comparer(a, b); + } + + export function compareStringsCaseSensitiveUI(a: string, b: string) { + const comparer = uiCI || (uiCI = createStringComparer(uiLocale, /*caseInsensitive*/ true)); + return comparer(a, b); + } + + /** + * Compare two strings using the sort behavior of the UI 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. + */ + export function compareStringsUI(a: string, b: string, ignoreCase: boolean) { + return ignoreCase ? compareStringsCaseInsensitiveUI(a, b) : compareStringsCaseSensitiveUI(a, b); + } + + export function getStringComparerUI(ignoreCase: boolean) { + return ignoreCase ? compareStringsCaseInsensitiveUI : compareStringsCaseSensitiveUI; + } + function getDiagnosticFileName(diagnostic: Diagnostic): string { return diagnostic.file ? diagnostic.file.fileName : undefined; } @@ -2151,9 +2060,9 @@ namespace ts { const aComponents = getNormalizedPathComponents(a, currentDirectory); const bComponents = getNormalizedPathComponents(b, currentDirectory); const sharedLength = Math.min(aComponents.length, bComponents.length); - const collator = StringCollator.getPathCollator(ignoreCase); + const comparer = getStringComparer(ignoreCase); for (let i = 0; i < sharedLength; i++) { - const result = collator.compare(aComponents[i], bComponents[i]); + const result = comparer(aComponents[i], bComponents[i]); if (result !== Comparison.EqualTo) { return result; } @@ -2175,9 +2084,9 @@ namespace ts { } // File-system comparisons should use predictable ordering - const collator = StringCollator.getPathCollator(ignoreCase); + const equalityComparer = getStringEqualityComparer(ignoreCase); for (let i = 0; i < parentComponents.length; i++) { - if (!collator.equate(parentComponents[i], childComponents[i])) { + if (!equalityComparer(parentComponents[i], childComponents[i])) { return false; } } @@ -2433,7 +2342,7 @@ namespace ts { // If there are no "includes", then just put everything in results[0]. const results: string[][] = includeFileRegexes ? includeFileRegexes.map(() => []) : [[]]; - const collator = StringCollator.getPathCollator(!useCaseSensitiveFileNames); + const comparer = getStringComparer(!useCaseSensitiveFileNames); for (const basePath of patterns.basePaths) { visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth); } @@ -2442,7 +2351,7 @@ namespace ts { function visitDirectory(path: string, absolutePath: string, depth: number | undefined) { let { files, directories } = getFileSystemEntries(path); - files = files.slice().sort(collator.compare); + files = files.slice().sort(comparer); for (const current of files) { const name = combinePaths(path, current); @@ -2467,7 +2376,7 @@ namespace ts { } } - directories = directories.slice().sort(collator.compare); + directories = directories.slice().sort(comparer); for (const current of directories) { const name = combinePaths(path, current); const absoluteName = combinePaths(absolutePath, current); @@ -2498,8 +2407,7 @@ namespace ts { } // Sort the offsets array using either the literal or canonical path representations. - const collator = StringCollator.getPathCollator(!useCaseSensitiveFileNames); - includeBasePaths.sort(collator.compare); + includeBasePaths.sort(getStringComparer(!useCaseSensitiveFileNames)); // Iterate over each include base path and include unique base paths that are not a // subpath of an existing base path diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index bda31d83d86..11eabb32b43 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6069,7 +6069,7 @@ namespace ts { const checkJsDirectiveMatchResult = checkJsDirectiveRegEx.exec(comment); if (checkJsDirectiveMatchResult) { checkJsDirective = { - enabled: StringCollator.ordinalCaseInsensitive.equate(checkJsDirectiveMatchResult[1], "@ts-check"), + enabled: equateStringsCaseInsensitive(checkJsDirectiveMatchResult[1], "@ts-check"), end: range.end, pos: range.pos }; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index ff3143f3d96..76d3ce4f58d 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1103,12 +1103,12 @@ namespace ts { // 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()); + const equalityComparer = getStringEqualityComparer(!host.useCaseSensitiveFileNames()); if (!options.lib) { - return collator.equate(file.fileName, getDefaultLibraryFileName()); + return equalityComparer(file.fileName, getDefaultLibraryFileName()); } else { - return forEach(options.lib, libFileName => collator.equate(file.fileName, combinePaths(defaultLibraryPath, libFileName))); + return forEach(options.lib, libFileName => equalityComparer(file.fileName, combinePaths(defaultLibraryPath, libFileName))); } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 7ae7e84ad18..4c75215acb5 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3950,7 +3950,7 @@ namespace ts { } // Set the locale for UI collation - StringCollator.uiLocale = locale; + setUILocale(locale); function trySetLanguageAndTerritory(language: string, territory: string, errors?: Push): boolean { const compilerFilePath = normalizePath(sys.getExecutingFilePath()); diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 8029c3672af..1529b19b47a 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1699,8 +1699,8 @@ namespace Harness { export function *iterateOutputs(outputFiles: Harness.Compiler.GeneratedFile[]): IterableIterator<[string, string]> { // Collect, test, and sort the fileNames // 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 comparer = ts.getStringComparer(/*ignoreCase*/ false); + outputFiles.sort((a, b) => comparer(cleanName(a.fileName), cleanName(b.fileName))); const dupeCase = ts.createMap(); // Yield them for (const outputFile of outputFiles) { diff --git a/src/harness/unittests/compileOnSave.ts b/src/harness/unittests/compileOnSave.ts index 8c7ae507991..43becc3fdb1 100644 --- a/src/harness/unittests/compileOnSave.ts +++ b/src/harness/unittests/compileOnSave.ts @@ -14,9 +14,9 @@ namespace ts.projectSystem { function sendAffectedFileRequestAndCheckResult(session: server.Session, request: server.protocol.Request, expectedFileList: { projectFileName: string, files: FileOrFolder[] }[]) { const response = session.executeCommand(request).response as server.protocol.CompileOnSaveAffectedFileListSingleProject[]; // 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)); + const comparer = getStringComparer(/*ignoreCase*/ false); + const actualResult = response.sort((list1, list2) => comparer(list1.projectFileName, list2.projectFileName)); + expectedFileList = expectedFileList.sort((list1, list2) => comparer(list1.projectFileName, list2.projectFileName)); assert.equal(actualResult.length, expectedFileList.length, `Actual result project number is different from the expected project number`); diff --git a/src/server/session.ts b/src/server/session.ts index ea09a9720f8..d40becbbd16 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1186,7 +1186,6 @@ namespace ts.server { const completions = project.getLanguageService().getCompletionsAtPosition(file, position); if (simplifiedResult) { - const comparer = StringCollator.uiCaseSensitive.compare; return mapDefined(completions && completions.entries, entry => { if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) { const { name, kind, kindModifiers, sortText, replacementSpan, hasAction } = entry; @@ -1194,7 +1193,7 @@ namespace ts.server { // Use `hasAction || undefined` to avoid serializing `false`. return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan, hasAction: hasAction || undefined }; } - }).sort((a, b) => comparer(a.name, b.name)); + }).sort((a, b) => compareStringsCaseSensitiveUI(a.name, b.name)); } else { return completions; diff --git a/src/services/navigateTo.ts b/src/services/navigateTo.ts index 3988a88fe78..603342df7a8 100644 --- a/src/services/navigateTo.ts +++ b/src/services/navigateTo.ts @@ -176,7 +176,7 @@ namespace ts.NavigateTo { function compareNavigateToItems(i1: RawNavigateToItem, i2: RawNavigateToItem): number { // TODO(cyrusn): get the gamut of comparisons that VS already uses here. return i1.matchKind - i2.matchKind || - StringCollator.uiCaseSensitive.compare(i1.name, i2.name); + compareStringsCaseSensitiveUI(i1.name, i2.name); } function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem { diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 91b7ec78a89..9f4d37407eb 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -368,7 +368,7 @@ namespace ts.NavigationBar { function compareChildren(child1: NavigationBarNode, child2: NavigationBarNode): number { const name1 = tryGetName(child1.node), name2 = tryGetName(child2.node); - return StringCollator.uiCaseInsensitive.compare(name1, name2) + return compareStringsCaseInsensitiveUI(name1, name2) || navigationBarNodeKind(child1) - navigationBarNodeKind(child2); } diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 2a5ea4ec53b..41b922c2003 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -1156,7 +1156,7 @@ namespace ts.refactor.extractSymbol { const name2 = type2.symbol ? type2.symbol.getName() : ""; // This is for code generation, use a predictable comparer. - const nameDiff = StringCollator.invariantCaseSensitive.compare(name1, name2); + const nameDiff = compareStringsCaseSensitive(name1, name2); if (nameDiff !== 0) { return nameDiff; }