@@ -1490,17 +1490,60 @@ namespace ts {
14901490 return a < b ? Comparison . LessThan : Comparison . GreaterThan ;
14911491 }
14921492
1493+ interface StringCollator {
1494+ compare ( a : string , b : string ) : number ;
1495+ equals ( a : string , b : string ) : boolean ;
1496+ }
1497+
14931498 // Gets string comparers compatible with the current host
1494- function createCaseInsensitiveStringComparer ( ) : ( a : string , b : string ) => number {
1495- // If the host supports Intl (ECMA-402), we use Intl for comparisons using the default
1496- // locale:
1497- if ( typeof Intl === "object" && typeof Intl . Collator === "function" ) {
1499+ function createCaseInsensitiveStringComparers ( ) {
1500+ function createIntlStringCollator ( ) : StringCollator {
14981501 // Strings that differ in base or accents/diacritic marks compare as unequal.
14991502 // An `undefined` locale uses the default locale of the host.
1503+ const sortCollator = new Intl . Collator ( /*locales*/ undefined , { usage : "sort" , sensitivity : "accent" } ) ;
1504+ const searchCollator = new Intl . Collator ( /*locales*/ undefined , { usage : "search" , sensitivity : "accent" } ) ;
1505+ return {
1506+ // Intl.Collator.prototype.compare is bound to the collator. See NOTE in
1507+ // http://www.ecma-international.org/ecma-402/2.0/#sec-Intl.Collator.prototype.compare
1508+ compare : sortCollator . compare ,
1509+ equals : ( a , b ) => searchCollator . compare ( a , b ) === 0
1510+ } ;
1511+ }
1512+
1513+ function createLocaleCompareStringCollator ( ) : StringCollator {
1514+ // for case-insensitive comparisons we always map both strings to their
1515+ // upper-case form as some unicode characters do not properly round-trip to
1516+ // lowercase (such as ẞ).
1517+ return {
1518+ compare : ( a , b ) => a . toLocaleUpperCase ( ) . localeCompare ( b . toLocaleUpperCase ( ) ) ,
1519+ equals : ( a , b ) => a . toLocaleUpperCase ( ) === b . toLocaleUpperCase ( )
1520+ } ;
1521+ }
1522+
1523+ function createOrdinalStringCollator ( ) : StringCollator {
1524+ // for case-insensitive comparisons we always map both strings to their
1525+ // upper-case form as some unicode characters do not properly round-trip to
1526+ // lowercase (such as ẞ).
15001527 //
1501- // Intl.Collator.prototype.compare is bound to the collator. See NOTE in
1502- // http://www.ecma-international.org/ecma-402/2.0/#sec-Intl.Collator.prototype.compare
1503- return new Intl . Collator ( /*locales*/ undefined , { usage : "sort" , sensitivity : "accent" } ) . compare ;
1528+ // The ordinal comparison cannot properly handle comparison of the Turkish
1529+ // (dotted) i and (dotless) ı to the uppercase forms of (dotted) İ and (dotless) I.
1530+ // This is best handled by Intl and not supported in the fallback case.
1531+ return {
1532+ compare : ( a , b ) => {
1533+ const upperA = a . toUpperCase ( ) ;
1534+ const upperB = b . toUpperCase ( ) ;
1535+ return upperA < upperB ? Comparison . LessThan :
1536+ upperA > upperB ? Comparison . GreaterThan :
1537+ Comparison . EqualTo ;
1538+ } ,
1539+ equals : ( a , b ) => a . toUpperCase ( ) === b . toUpperCase ( )
1540+ } ;
1541+ }
1542+
1543+ // If the host supports Intl (ECMA-402), we use Intl for comparisons using the default
1544+ // locale:
1545+ if ( typeof Intl === "object" && typeof Intl . Collator === "function" ) {
1546+ return createIntlStringCollator ( ) ;
15041547 }
15051548
15061549 // If the host does not support Intl, we fall back to localeCompare:
@@ -1510,31 +1553,14 @@ namespace ts {
15101553 if ( typeof String . prototype . localeCompare === "function" &&
15111554 typeof String . prototype . toLocaleUpperCase === "function" &&
15121555 "a" . localeCompare ( "B" ) < 0 ) {
1513- // for case-insensitive comparisons we always map both strings to their
1514- // upper-case form as some unicode characters do not properly round-trip to
1515- // lowercase (such as ẞ).
1516- return ( a , b ) => a . toLocaleUpperCase ( ) . localeCompare ( b . toLocaleUpperCase ( ) ) ;
1556+ return createLocaleCompareStringCollator ( ) ;
15171557 }
15181558
15191559 // Otherwise, fall back to ordinal comparison:
1520- //
1521- // for case-insensitive comparisons we always map both strings to their
1522- // upper-case form as some unicode characters do not properly round-trip to
1523- // lowercase (such as ẞ).
1524- //
1525- // The ordinal comparison cannot properly handle comparison of the Turkish
1526- // (dotted) i and (dotless) ı to the uppercase forms of (dotted) İ and (dotless) I.
1527- // This is best handled by Intl and not supported in the fallback case.
1528- return ( a , b ) => {
1529- const upperA = a . toUpperCase ( ) ;
1530- const upperB = b . toUpperCase ( ) ;
1531- return upperA < upperB ? Comparison . LessThan :
1532- upperA > upperB ? Comparison . GreaterThan :
1533- Comparison . EqualTo ;
1534- } ;
1560+ return createOrdinalStringCollator ( ) ;
15351561 }
15361562
1537- const caseInsensitiveComparer = createCaseInsensitiveStringComparer ( ) ;
1563+ const caseInsensitiveCollator = createCaseInsensitiveStringComparers ( ) ;
15381564
15391565 /**
15401566 * Performs a case-insensitive comparison between two strings.
@@ -1546,7 +1572,7 @@ namespace ts {
15461572 if ( a === b ) return Comparison . EqualTo ;
15471573 if ( a === undefined ) return Comparison . LessThan ;
15481574 if ( b === undefined ) return Comparison . GreaterThan ;
1549- const result = caseInsensitiveComparer ( a , b ) ;
1575+ const result = caseInsensitiveCollator . compare ( a , b ) ;
15501576 return result < 0 ? Comparison . LessThan : result > 0 ? Comparison . GreaterThan : Comparison . EqualTo ;
15511577 }
15521578
@@ -1561,6 +1587,30 @@ namespace ts {
15611587 return ignoreCase ? compareStringsCaseInsensitive ( a , b ) : compareStringsCaseSensitive ( a , b ) ;
15621588 }
15631589
1590+ /**
1591+ * Performs a case-insensitive equality comparison between two strings.
1592+ *
1593+ * If supported by the host, the default locale is used for comparisons. Otherwise, an ordinal
1594+ * comparison is used.
1595+ */
1596+ export function equateStringsCaseInsensitive ( a : string | undefined , b : string | undefined ) {
1597+ return a === b
1598+ || a !== undefined
1599+ && b !== undefined
1600+ && caseInsensitiveCollator . equals ( a , b ) ;
1601+ }
1602+
1603+ /**
1604+ * Performs a case-sensitive equality comparison between two strings.
1605+ */
1606+ export function equateStringsCaseSensitive ( a : string | undefined , b : string | undefined ) {
1607+ return a === b ;
1608+ }
1609+
1610+ export function equateStrings ( a : string | undefined , b : string | undefined , ignoreCase ?: boolean ) {
1611+ return ignoreCase ? equateStringsCaseInsensitive ( a , b ) : equateStringsCaseSensitive ( a , b ) ;
1612+ }
1613+
15641614 function getDiagnosticFileName ( diagnostic : Diagnostic ) : string {
15651615 return diagnostic . file ? diagnostic . file . fileName : undefined ;
15661616 }
@@ -1966,10 +2016,9 @@ namespace ts {
19662016 return false ;
19672017 }
19682018
1969- const stringComparer = ignoreCase ? compareStringsCaseInsensitive : compareStringsCaseSensitive ;
2019+ const stringEqualityComparer = ignoreCase ? equateStringsCaseInsensitive : equateStringsCaseSensitive ;
19702020 for ( let i = 0 ; i < parentComponents . length ; i ++ ) {
1971- const result = stringComparer ( parentComponents [ i ] , childComponents [ i ] ) ;
1972- if ( result !== Comparison . EqualTo ) {
2021+ if ( ! stringEqualityComparer ( parentComponents [ i ] , childComponents [ i ] ) ) {
19732022 return false ;
19742023 }
19752024 }
0 commit comments