@@ -1927,11 +1927,9 @@ namespace ts {
1927
1927
1928
1928
/**
1929
1929
* 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.
1931
1931
*
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,
1935
1933
* except for candidates:
1936
1934
* * With no name
1937
1935
* * Whose length differs from the target name by more than 0.34 of the length of the name.
@@ -1941,41 +1939,28 @@ namespace ts {
1941
1939
*/
1942
1940
export function getSpellingSuggestion < T > ( name : string , candidates : T [ ] , getName : ( candidate : T ) => string | undefined ) : T | undefined {
1943
1941
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.
1945
1943
let bestCandidate : T | undefined ;
1946
- let justCheckExactMatches = false ;
1947
- const nameLowerCase = name . toLowerCase ( ) ;
1948
1944
for ( const candidate of candidates ) {
1949
1945
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 ) {
1959
1948
continue ;
1960
1949
}
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 ( ) ) {
1963
1953
continue ;
1964
1954
}
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) ;
1967
1957
if ( distance === undefined ) {
1968
1958
continue ;
1969
1959
}
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 ;
1979
1964
}
1980
1965
}
1981
1966
return bestCandidate ;
@@ -1985,26 +1970,30 @@ namespace ts {
1985
1970
let previous = new Array ( s2 . length + 1 ) ;
1986
1971
let current = new Array ( s2 . length + 1 ) ;
1987
1972
/** Represents any value > max. We don't care about the particular value. */
1988
- const big = max + 1 ;
1973
+ const big = max + 0.01 ;
1989
1974
1990
1975
for ( let i = 0 ; i <= s2 . length ; i ++ ) {
1991
1976
previous [ i ] = i ;
1992
1977
}
1993
1978
1994
1979
for ( let i = 1 ; i <= s1 . length ; i ++ ) {
1995
1980
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 ) ;
1998
1983
current [ 0 ] = i ;
1999
1984
/** Smallest value of the matrix in the ith column. */
2000
1985
let colMin = i ;
2001
1986
for ( let j = 1 ; j < minJ ; j ++ ) {
2002
1987
current [ j ] = big ;
2003
1988
}
2004
1989
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 ) ;
2005
1994
const dist = c1 === s2 . charCodeAt ( j - 1 )
2006
1995
? 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 ) ;
2008
1997
current [ j ] = dist ;
2009
1998
colMin = Math . min ( colMin , dist ) ;
2010
1999
}
0 commit comments