@@ -1927,11 +1927,9 @@ namespace ts {
19271927
19281928 /**
19291929 * Given a name and a list of names that are *not* equal to the name, return a spelling suggestion if there is one that is close enough.
1930- * Names less than length 3 only check for case-insensitive equality, not Levenshtein distance .
1930+ * Names less than length 3 only check for case-insensitive equality.
19311931 *
1932- * If there is a candidate that's the same except for case, return that.
1933- * If there is a candidate that's within one edit of the name, return that.
1934- * Otherwise, return the candidate with the smallest Levenshtein distance,
1932+ * find the candidate with the smallest Levenshtein distance,
19351933 * except for candidates:
19361934 * * With no name
19371935 * * Whose length differs from the target name by more than 0.34 of the length of the name.
@@ -1941,41 +1939,28 @@ namespace ts {
19411939 */
19421940 export function getSpellingSuggestion < T > ( name : string , candidates : T [ ] , getName : ( candidate : T ) => string | undefined ) : T | undefined {
19431941 const maximumLengthDifference = Math . min ( 2 , Math . floor ( name . length * 0.34 ) ) ;
1944- let bestDistance = Math . floor ( name . length * 0.4 ) + 1 ; // If the best result isn't better than this, don't bother.
1942+ let bestDistance = Math . floor ( name . length * 0.4 ) + 1 ; // If the best result is worse than this, don't bother.
19451943 let bestCandidate : T | undefined ;
1946- let justCheckExactMatches = false ;
1947- const nameLowerCase = name . toLowerCase ( ) ;
19481944 for ( const candidate of candidates ) {
19491945 const candidateName = getName ( candidate ) ;
1950- if ( candidateName !== undefined && Math . abs ( candidateName . length - nameLowerCase . length ) <= maximumLengthDifference ) {
1951- const candidateNameLowerCase = candidateName . toLowerCase ( ) ;
1952- if ( candidateNameLowerCase === nameLowerCase ) {
1953- if ( candidateName === name ) {
1954- continue ;
1955- }
1956- return candidate ;
1957- }
1958- if ( justCheckExactMatches ) {
1946+ if ( candidateName !== undefined && Math . abs ( candidateName . length - name . length ) <= maximumLengthDifference ) {
1947+ if ( candidateName === name ) {
19591948 continue ;
19601949 }
1961- if ( candidateName . length < 3 ) {
1962- // Don't bother, user would have noticed a 2-character name having an extra character
1950+ // Only consider candidates less than 3 characters long when they differ by case.
1951+ // Otherwise, don't bother, since a user would usually notice differences of a 2-character name.
1952+ if ( candidateName . length < 3 && candidateName . toLowerCase ( ) !== name . toLowerCase ( ) ) {
19631953 continue ;
19641954 }
1965- // Only care about a result better than the best so far.
1966- const distance = levenshteinWithMax ( nameLowerCase , candidateNameLowerCase , bestDistance - 1 ) ;
1955+
1956+ const distance = levenshteinWithMax ( name , candidateName , bestDistance - 0. 1) ;
19671957 if ( distance === undefined ) {
19681958 continue ;
19691959 }
1970- if ( distance < 3 ) {
1971- justCheckExactMatches = true ;
1972- bestCandidate = candidate ;
1973- }
1974- else {
1975- Debug . assert ( distance < bestDistance ) ; // Else `levenshteinWithMax` should return undefined
1976- bestDistance = distance ;
1977- bestCandidate = candidate ;
1978- }
1960+
1961+ Debug . assert ( distance < bestDistance ) ; // Else `levenshteinWithMax` should return undefined
1962+ bestDistance = distance ;
1963+ bestCandidate = candidate ;
19791964 }
19801965 }
19811966 return bestCandidate ;
@@ -1985,26 +1970,30 @@ namespace ts {
19851970 let previous = new Array ( s2 . length + 1 ) ;
19861971 let current = new Array ( s2 . length + 1 ) ;
19871972 /** Represents any value > max. We don't care about the particular value. */
1988- const big = max + 1 ;
1973+ const big = max + 0.01 ;
19891974
19901975 for ( let i = 0 ; i <= s2 . length ; i ++ ) {
19911976 previous [ i ] = i ;
19921977 }
19931978
19941979 for ( let i = 1 ; i <= s1 . length ; i ++ ) {
19951980 const c1 = s1 . charCodeAt ( i - 1 ) ;
1996- const minJ = i > max ? i - max : 1 ;
1997- const maxJ = s2 . length > max + i ? max + i : s2 . length ;
1981+ const minJ = Math . ceil ( i > max ? i - max : 1 ) ;
1982+ const maxJ = Math . floor ( s2 . length > max + i ? max + i : s2 . length ) ;
19981983 current [ 0 ] = i ;
19991984 /** Smallest value of the matrix in the ith column. */
20001985 let colMin = i ;
20011986 for ( let j = 1 ; j < minJ ; j ++ ) {
20021987 current [ j ] = big ;
20031988 }
20041989 for ( let j = minJ ; j <= maxJ ; j ++ ) {
1990+ // case difference should be significantly cheaper than other differences
1991+ const substitutionDistance = s1 [ i - 1 ] . toLowerCase ( ) === s2 [ j - 1 ] . toLowerCase ( )
1992+ ? ( previous [ j - 1 ] + 0.1 )
1993+ : ( previous [ j - 1 ] + 2 ) ;
20051994 const dist = c1 === s2 . charCodeAt ( j - 1 )
20061995 ? previous [ j - 1 ]
2007- : Math . min ( /*delete*/ previous [ j ] + 1 , /*insert*/ current [ j - 1 ] + 1 , /*substitute*/ previous [ j - 1 ] + 2 ) ;
1996+ : Math . min ( /*delete*/ previous [ j ] + 1 , /*insert*/ current [ j - 1 ] + 1 , /*substitute*/ substitutionDistance ) ;
20081997 current [ j ] = dist ;
20091998 colMin = Math . min ( colMin , dist ) ;
20101999 }
0 commit comments