From 24437774a8dfdffe7fd49c064e491acd99ace38c Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 26 Oct 2017 11:56:29 -0700 Subject: [PATCH] Further simplification --- src/compiler/core.ts | 123 +++++++++++++----------- src/compiler/program.ts | 2 +- src/compiler/tsc.ts | 2 +- src/harness/fourslash.ts | 6 ++ src/harness/harness.ts | 3 +- src/harness/unittests/compileOnSave.ts | 5 +- src/services/refactors/extractSymbol.ts | 2 +- 7 files changed, 77 insertions(+), 66 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 3457af9d174..64ab5af67c2 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1483,23 +1483,18 @@ namespace ts { return headChain; } - /** - * Compare two values for their equality. - */ - export function equateValues(a: T, b: T) { + function equateValues(a: T, b: T) { return a === b; } /** - * Compare equality between two strings using an ordinal comparison. + * Compare the equality of two strings using a case-sensitive ordinal comparison. * - * Case-insensitive comparisons compare both strings after applying `toUpperCase` to - * each string. + * Case-sensitive comparisons compare both strings one code-point at a time using the integer + * value of each code-point after applying `toUpperCase` to each string. 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)). */ - export function equateStrings(a: string, b: string, ignoreCase: boolean) { - return ignoreCase ? equateStringsCaseInsensitive(a, b) : equateStringsCaseSensitive(a, b); - } - export function equateStringsCaseInsensitive(a: string, b: string) { return a === b || a !== undefined @@ -1507,14 +1502,16 @@ namespace ts { && a.toUpperCase() === b.toUpperCase(); } + /** + * Compare the equality of two strings using a case-sensitive ordinal comparison. + * + * Case-sensitive comparisons compare both strings one code-point at a time using the + * integer value of each code-point. + */ export function equateStringsCaseSensitive(a: string, b: string) { return equateValues(a, b); } - export function getStringEqualityComparer(ignoreCase: boolean) { - return ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive; - } - /** * Compare two values for their order relative to each other. */ @@ -1527,19 +1524,17 @@ namespace ts { } /** - * Compare two strings using an ordinal comparison. + * Compare two strings using a case-insensitive 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". + * 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. + * Case-insensitive comparisons compare both strings one code-point at a time using the integer + * value of each code-point after applying `toUpperCase` to each string. 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)). */ - export function compareStrings(a: string, b: string, ignoreCase: boolean) { - return ignoreCase ? compareStringsCaseInsensitive(a, b) : compareStringsCaseSensitive(a, b); - } - export function compareStringsCaseInsensitive(a: string, b: string) { if (a === b) return Comparison.EqualTo; if (a === undefined) return Comparison.LessThan; @@ -1549,28 +1544,20 @@ namespace ts { return a < b ? Comparison.LessThan : a > b ? Comparison.GreaterThan : Comparison.EqualTo; } + /** + * Compare two strings using a case-sensitive 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-sensitive comparisons compare both strings one code-point at a time using the integer + * value of each code-point. + */ export function compareStringsCaseSensitive(a: string, b: string) { return compareValues(a, b); } - export function getStringComparer(ignoreCase: boolean) { - return ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive; - } - - /** - * 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); - } - /** * Creates a string comparer for use with string collation in the UI. */ @@ -1656,6 +1643,10 @@ namespace ts { let uiCI: Comparer | undefined; let uiLocale: string | undefined; + export function getUILocale() { + return uiLocale; + } + export function setUILocale(value: string) { if (uiLocale !== value) { uiLocale = value; @@ -1664,25 +1655,41 @@ namespace ts { } } + /** + * Compare two strings using the case-insensitive 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 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); } - export function getStringComparerUI(ignoreCase: boolean) { - return ignoreCase ? compareStringsCaseInsensitiveUI : compareStringsCaseSensitiveUI; + /** + * Compare two strings in a using the case-sensitive 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-sensitive comparisons compare strings that differ in base characters, or + * accents/diacritic marks, or case as unequal. + */ + export function compareStringsCaseSensitiveUI(a: string, b: string) { + const comparer = uiCS || (uiCS = createStringComparer(uiLocale, /*caseInsensitive*/ false)); + return comparer(a, b); } - export function compareProperties(a: T, b: T, key: keyof T) { + export function compareProperties(a: T, b: T, key: K, comparer: Comparer) { return a === b ? Comparison.EqualTo : a === undefined ? Comparison.LessThan : b === undefined ? Comparison.GreaterThan : - compareValues(a[key], b[key]); + comparer(a[key], b[key]); } function getDiagnosticFileName(diagnostic: Diagnostic): string { @@ -1690,7 +1697,7 @@ namespace ts { } export function compareDiagnostics(d1: Diagnostic, d2: Diagnostic): Comparison { - return compareValues(getDiagnosticFileName(d1), getDiagnosticFileName(d2)) || + return compareStringsCaseSensitive(getDiagnosticFileName(d1), getDiagnosticFileName(d2)) || compareValues(d1.start, d2.start) || compareValues(d1.length, d2.length) || compareValues(d1.code, d2.code) || @@ -1704,7 +1711,7 @@ namespace ts { const string1 = isString(text1) ? text1 : text1.messageText; const string2 = isString(text2) ? text2 : text2.messageText; - const res = compareValues(string1, string2); + const res = compareStringsCaseSensitive(string1, string2); if (res) { return res; } @@ -2067,7 +2074,7 @@ namespace ts { const aComponents = getNormalizedPathComponents(a, currentDirectory); const bComponents = getNormalizedPathComponents(b, currentDirectory); const sharedLength = Math.min(aComponents.length, bComponents.length); - const comparer = getStringComparer(ignoreCase); + const comparer = ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive; for (let i = 0; i < sharedLength; i++) { const result = comparer(aComponents[i], bComponents[i]); if (result !== Comparison.EqualTo) { @@ -2091,7 +2098,7 @@ namespace ts { } // File-system comparisons should use predictable ordering - const equalityComparer = getStringEqualityComparer(ignoreCase); + const equalityComparer = ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive; for (let i = 0; i < parentComponents.length; i++) { if (!equalityComparer(parentComponents[i], childComponents[i])) { return false; @@ -2338,6 +2345,7 @@ namespace ts { path = normalizePath(path); currentDirectory = normalizePath(currentDirectory); + const comparer = useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive; const patterns = getFileMatcherPatterns(path, excludes, includes, useCaseSensitiveFileNames, currentDirectory); const regexFlag = useCaseSensitiveFileNames ? "" : "i"; @@ -2349,7 +2357,6 @@ namespace ts { // If there are no "includes", then just put everything in results[0]. const results: string[][] = includeFileRegexes ? includeFileRegexes.map(() => []) : [[]]; - const comparer = getStringComparer(!useCaseSensitiveFileNames); for (const basePath of patterns.basePaths) { visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth); } @@ -2414,7 +2421,7 @@ namespace ts { } // Sort the offsets array using either the literal or canonical path representations. - includeBasePaths.sort(getStringComparer(!useCaseSensitiveFileNames)); + includeBasePaths.sort(useCaseSensitiveFileNames ? compareStringsCaseSensitive : compareStringsCaseInsensitive); // 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/program.ts b/src/compiler/program.ts index 6a60327371c..eb2ac9babe5 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1101,7 +1101,7 @@ 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 - const equalityComparer = getStringEqualityComparer(!host.useCaseSensitiveFileNames()); + const equalityComparer = host.useCaseSensitiveFileNames() ? equateStringsCaseSensitive : equateStringsCaseInsensitive; if (!options.lib) { return equalityComparer(file.fileName, getDefaultLibraryFileName()); } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 680daeeb485..cc3c256827a 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -306,7 +306,7 @@ namespace ts { // Sort our options by their names, (e.g. "--noImplicitAny" comes before "--watch") const optsList = showAllOptions ? - optionDeclarations.slice().sort((a, b) => compareValues(a.name.toLowerCase(), b.name.toLowerCase())) : + optionDeclarations.slice().sort((a, b) => compareStringsCaseInsensitive(a.name, b.name)) : filter(optionDeclarations.slice(), v => v.showInSimplifiedHelpView); // We want our descriptions to align at the same column in our output, diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index d88f0e0854e..105501196b0 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -3128,7 +3128,10 @@ Actual: ${stringify(fullActual)}`); `(function(test, goTo, verify, edit, debug, format, cancellation, classification, verifyOperationIsCancelled) { ${code} })`; + const savedUILocale = ts.getUILocale(); + ts.setUILocale("en-US"); // run tests in en-US by default. try { + const test = new FourSlashInterface.Test(state); const goTo = new FourSlashInterface.GoTo(state); const verify = new FourSlashInterface.Verify(state); @@ -3142,6 +3145,9 @@ ${code} catch (err) { throw err; } + finally { + ts.setUILocale(savedUILocale); + } } function chompLeadingSpace(content: string) { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index d8e12c8e8f7..728d18c59cc 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1698,8 +1698,7 @@ namespace Harness { export function *iterateOutputs(outputFiles: Harness.Compiler.GeneratedFile[]): IterableIterator<[string, string]> { // Collect, test, and sort the fileNames - const comparer = ts.getStringComparer(/*ignoreCase*/ false); - outputFiles.sort((a, b) => comparer(cleanName(a.fileName), cleanName(b.fileName))); + outputFiles.sort((a, b) => ts.compareStringsCaseSensitive(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 d6d22b162bf..540c94b9f6d 100644 --- a/src/harness/unittests/compileOnSave.ts +++ b/src/harness/unittests/compileOnSave.ts @@ -13,9 +13,8 @@ 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 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)); + const actualResult = response.sort((list1, list2) => ts.compareStringsCaseSensitive(list1.projectFileName, list2.projectFileName)); + expectedFileList = expectedFileList.sort((list1, list2) => ts.compareStringsCaseSensitive(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/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index e26cdd0f74f..a790db44ea3 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -1137,7 +1137,7 @@ namespace ts.refactor.extractSymbol { {type: type1, declaration: declaration1}: {type: Type, declaration?: Declaration}, {type: type2, declaration: declaration2}: {type: Type, declaration?: Declaration}) { - return compareProperties(declaration1, declaration2, "pos") + return compareProperties(declaration1, declaration2, "pos", compareValues) || compareStringsCaseSensitive( type1.symbol ? type1.symbol.getName() : "", type2.symbol ? type2.symbol.getName() : "")