mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Spelling correction fixes should not be case-agnostic for two equally weighed options (#39060)
* Spelling correction fixes should not be case-agnostic when two equally weighed options occur. fixes #17219 * update tests * Update src/compiler/core.ts Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com> * init bestCaseSensitiveDistance lazily * Add a test case with a class named the same as an instance, except for case * make the core levenshtein distance check case-aware * Update package-lock.json * use fractional Levenshtein distance * fix weight of Levenshtein distance * Update package-lock.json * Update package-lock.json * Update package-lock.json * Update package-lock.json * Update package-lock.json * Update package-lock.json * Update package-lock.json * Update package-lock.json * Update package-lock.json * Update package-lock.json * Update package-lock.json * Update package-lock.json * Update package-lock.json * Update package-lock.json * Update src/compiler/core.ts Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> * refactor * Update package-lock.json * revert unnecessary changes * Update package-lock.json * increase bestDistance * increase bestDistance again * make changes minimal * Update package-lock.json * Update package-lock.json * Update package-lock.json Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com> Co-authored-by: TypeScript Bot <typescriptbot@microsoft.com> Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
co-authored by
Daniel Rosenwasser
Nathan Shively-Sanders
TypeScript Bot
parent
4a9e2be386
commit
422fd1962e
+22
-33
@@ -1927,11 +1927,9 @@ namespace ts {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Names less than length 3 only check for case-insensitive equality, not Levenshtein distance.
|
||||
* Names less than length 3 only check for case-insensitive equality.
|
||||
*
|
||||
* If there is a candidate that's the same except for case, return that.
|
||||
* If there is a candidate that's within one edit of the name, return that.
|
||||
* Otherwise, return the candidate with the smallest Levenshtein distance,
|
||||
* find the candidate with the smallest Levenshtein distance,
|
||||
* except for candidates:
|
||||
* * With no name
|
||||
* * Whose length differs from the target name by more than 0.34 of the length of the name.
|
||||
@@ -1941,41 +1939,28 @@ namespace ts {
|
||||
*/
|
||||
export function getSpellingSuggestion<T>(name: string, candidates: T[], getName: (candidate: T) => string | undefined): T | undefined {
|
||||
const maximumLengthDifference = Math.min(2, Math.floor(name.length * 0.34));
|
||||
let bestDistance = Math.floor(name.length * 0.4) + 1; // If the best result isn't better than this, don't bother.
|
||||
let bestDistance = Math.floor(name.length * 0.4) + 1; // If the best result is worse than this, don't bother.
|
||||
let bestCandidate: T | undefined;
|
||||
let justCheckExactMatches = false;
|
||||
const nameLowerCase = name.toLowerCase();
|
||||
for (const candidate of candidates) {
|
||||
const candidateName = getName(candidate);
|
||||
if (candidateName !== undefined && Math.abs(candidateName.length - nameLowerCase.length) <= maximumLengthDifference) {
|
||||
const candidateNameLowerCase = candidateName.toLowerCase();
|
||||
if (candidateNameLowerCase === nameLowerCase) {
|
||||
if (candidateName === name) {
|
||||
continue;
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
if (justCheckExactMatches) {
|
||||
if (candidateName !== undefined && Math.abs(candidateName.length - name.length) <= maximumLengthDifference) {
|
||||
if (candidateName === name) {
|
||||
continue;
|
||||
}
|
||||
if (candidateName.length < 3) {
|
||||
// Don't bother, user would have noticed a 2-character name having an extra character
|
||||
// Only consider candidates less than 3 characters long when they differ by case.
|
||||
// Otherwise, don't bother, since a user would usually notice differences of a 2-character name.
|
||||
if (candidateName.length < 3 && candidateName.toLowerCase() !== name.toLowerCase()) {
|
||||
continue;
|
||||
}
|
||||
// Only care about a result better than the best so far.
|
||||
const distance = levenshteinWithMax(nameLowerCase, candidateNameLowerCase, bestDistance - 1);
|
||||
|
||||
const distance = levenshteinWithMax(name, candidateName, bestDistance - 0.1);
|
||||
if (distance === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (distance < 3) {
|
||||
justCheckExactMatches = true;
|
||||
bestCandidate = candidate;
|
||||
}
|
||||
else {
|
||||
Debug.assert(distance < bestDistance); // Else `levenshteinWithMax` should return undefined
|
||||
bestDistance = distance;
|
||||
bestCandidate = candidate;
|
||||
}
|
||||
|
||||
Debug.assert(distance < bestDistance); // Else `levenshteinWithMax` should return undefined
|
||||
bestDistance = distance;
|
||||
bestCandidate = candidate;
|
||||
}
|
||||
}
|
||||
return bestCandidate;
|
||||
@@ -1985,7 +1970,7 @@ namespace ts {
|
||||
let previous = new Array(s2.length + 1);
|
||||
let current = new Array(s2.length + 1);
|
||||
/** Represents any value > max. We don't care about the particular value. */
|
||||
const big = max + 1;
|
||||
const big = max + 0.01;
|
||||
|
||||
for (let i = 0; i <= s2.length; i++) {
|
||||
previous[i] = i;
|
||||
@@ -1993,8 +1978,8 @@ namespace ts {
|
||||
|
||||
for (let i = 1; i <= s1.length; i++) {
|
||||
const c1 = s1.charCodeAt(i - 1);
|
||||
const minJ = i > max ? i - max : 1;
|
||||
const maxJ = s2.length > max + i ? max + i : s2.length;
|
||||
const minJ = Math.ceil(i > max ? i - max : 1);
|
||||
const maxJ = Math.floor(s2.length > max + i ? max + i : s2.length);
|
||||
current[0] = i;
|
||||
/** Smallest value of the matrix in the ith column. */
|
||||
let colMin = i;
|
||||
@@ -2002,9 +1987,13 @@ namespace ts {
|
||||
current[j] = big;
|
||||
}
|
||||
for (let j = minJ; j <= maxJ; j++) {
|
||||
// case difference should be significantly cheaper than other differences
|
||||
const substitutionDistance = s1[i - 1].toLowerCase() === s2[j-1].toLowerCase()
|
||||
? (previous[j - 1] + 0.1)
|
||||
: (previous[j - 1] + 2);
|
||||
const dist = c1 === s2.charCodeAt(j - 1)
|
||||
? previous[j - 1]
|
||||
: Math.min(/*delete*/ previous[j] + 1, /*insert*/ current[j - 1] + 1, /*substitute*/ previous[j - 1] + 2);
|
||||
: Math.min(/*delete*/ previous[j] + 1, /*insert*/ current[j - 1] + 1, /*substitute*/ substitutionDistance);
|
||||
current[j] = dist;
|
||||
colMin = Math.min(colMin, dist);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////export let Console = 1;
|
||||
////export let console = 1;
|
||||
////[|conole|] = 1;
|
||||
|
||||
verify.rangeAfterCodeFix('console');
|
||||
@@ -0,0 +1,7 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////export let console = 1;
|
||||
////export let Console = 1;
|
||||
////[|conole|] = 1;
|
||||
|
||||
verify.rangeAfterCodeFix('console');
|
||||
@@ -0,0 +1,7 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////class Node {}
|
||||
////let node = new Node();
|
||||
////[|nodes|]
|
||||
|
||||
verify.rangeAfterCodeFix('node');
|
||||
@@ -0,0 +1,7 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////let ABCDEFGHIJKLMNOPQR = 1;
|
||||
////let abcdefghijklmnopqrs = 1;
|
||||
////[|abcdefghijklmnopqr|]
|
||||
|
||||
verify.rangeAfterCodeFix('abcdefghijklmnopqrs');
|
||||
@@ -0,0 +1,7 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////let ABCDEFGHI = 1;
|
||||
////let abcdefghij = 1;
|
||||
////[|abcdefghi|]
|
||||
|
||||
verify.rangeAfterCodeFix('ABCDEFGHI');
|
||||
@@ -0,0 +1,6 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////export let ab = 1;
|
||||
////[|aB|] = 1;
|
||||
|
||||
verify.rangeAfterCodeFix('ab');
|
||||
@@ -0,0 +1,6 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////export let ab = 1;
|
||||
////abc;
|
||||
|
||||
verify.not.codeFixAvailable();
|
||||
Reference in New Issue
Block a user