@@ -15748,61 +15748,95 @@ namespace ts {
1574815748 * except for candidates:
1574915749 * * With no name
1575015750 * * Whose meaning doesn't match the `meaning` parameter.
15751- * * Whose length differs from the target name by more than 0.3 of the length of the name.
15751+ * * Whose length differs from the target name by more than 0.34 of the length of the name.
1575215752 * * Whose levenshtein distance is more than 0.4 of the length of the name
1575315753 * (0.4 allows 1 substitution/transposition for every 5 characters,
1575415754 * and 1 insertion/deletion at 3 characters)
15755- * Names longer than 30 characters don't get suggestions because Levenshtein distance is an n**2 algorithm.
1575615755 */
1575715756 function getSpellingSuggestionForName(name: string, symbols: Symbol[], meaning: SymbolFlags): Symbol | undefined {
15758- const worstDistance = name.length * 0.4;
15759- const maximumLengthDifference = Math.min(3, name.length * 0.34);
15760- let bestDistance = Number.MAX_VALUE;
15761- let bestCandidate = undefined;
15757+ const maximumLengthDifference = Math.min(2, Math.floor(name.length * 0.34));
15758+ let bestDistance = Math.floor(name.length * 0.4) + 1; // If the best result isn't better than this, don't bother.
15759+ let bestCandidate: Symbol | undefined;
1576215760 let justCheckExactMatches = false;
15763- if (name.length > 30) {
15764- return undefined;
15765- }
15766- name = name.toLowerCase();
15761+ const nameLowerCase = name.toLowerCase();
1576715762 for (const candidate of symbols) {
15768- let candidateName = symbolName(candidate);
15769- if (candidate.flags & meaning &&
15770- candidateName &&
15771- Math.abs(candidateName.length - name.length) < maximumLengthDifference) {
15772- candidateName = candidateName.toLowerCase();
15773- if (candidateName === name) {
15774- return candidate;
15775- }
15776- if (justCheckExactMatches) {
15777- continue;
15778- }
15779- if (candidateName.length < 3 ||
15780- name.length < 3 ||
15781- candidateName === "eval" ||
15782- candidateName === "intl" ||
15783- candidateName === "undefined" ||
15784- candidateName === "map" ||
15785- candidateName === "nan" ||
15786- candidateName === "set") {
15787- continue;
15788- }
15789- const distance = levenshtein(name, candidateName);
15790- if (distance > worstDistance) {
15791- continue;
15792- }
15793- if (distance < 3) {
15794- justCheckExactMatches = true;
15795- bestCandidate = candidate;
15796- }
15797- else if (distance < bestDistance) {
15798- bestDistance = distance;
15799- bestCandidate = candidate;
15800- }
15763+ const candidateName = symbolName(candidate);
15764+ if (!(candidate.flags & meaning && Math.abs(candidateName.length - nameLowerCase.length) <= maximumLengthDifference)) {
15765+ continue;
15766+ }
15767+ const candidateNameLowerCase = candidateName.toLowerCase();
15768+ if (candidateNameLowerCase === nameLowerCase) {
15769+ return candidate;
15770+ }
15771+ if (justCheckExactMatches) {
15772+ continue;
15773+ }
15774+ if (candidateName.length < 3) {
15775+ // Don't bother, user would have noticed a 2-character name having an extra character
15776+ continue;
15777+ }
15778+ // Only care about a result better than the best so far.
15779+ const distance = levenshteinWithMax(nameLowerCase, candidateNameLowerCase, bestDistance - 1);
15780+ if (distance === undefined) {
15781+ continue;
15782+ }
15783+ if (distance < 3) {
15784+ justCheckExactMatches = true;
15785+ bestCandidate = candidate;
15786+ }
15787+ else {
15788+ Debug.assert(distance < bestDistance); // Else `levenshteinWithMax` should return undefined
15789+ bestDistance = distance;
15790+ bestCandidate = candidate;
1580115791 }
1580215792 }
1580315793 return bestCandidate;
1580415794 }
1580515795
15796+ function levenshteinWithMax(s1: string, s2: string, max: number): number | undefined {
15797+ let previous = new Array(s2.length + 1);
15798+ let current = new Array(s2.length + 1);
15799+ /** Represents any value > max. We don't care about the particular value. */
15800+ const big = max + 1;
15801+
15802+ for (let i = 0; i <= s2.length; i++) {
15803+ previous[i] = i;
15804+ }
15805+
15806+ for (let i = 1; i <= s1.length; i++) {
15807+ const c1 = s1.charCodeAt(i - 1);
15808+ const minJ = i > max ? i - max : 1;
15809+ const maxJ = s2.length > max + i ? max + i : s2.length;
15810+ current[0] = i;
15811+ /** Smallest value of the matrix in the ith column. */
15812+ let colMin = i;
15813+ for (let j = 1; j < minJ; j++) {
15814+ current[j] = big;
15815+ }
15816+ for (let j = minJ; j <= maxJ; j++) {
15817+ const dist = c1 === s2.charCodeAt(j - 1)
15818+ ? previous[j - 1]
15819+ : Math.min(/*delete*/ previous[j] + 1, /*insert*/ current[j - 1] + 1, /*substitute*/ previous[j - 1] + 2);
15820+ current[j] = dist;
15821+ colMin = Math.min(colMin, dist);
15822+ }
15823+ for (let j = maxJ + 1; j <= s2.length; j++) {
15824+ current[j] = big;
15825+ }
15826+ if (colMin > max) {
15827+ // Give up -- everything in this column is > max and it can't get better in future columns.
15828+ return undefined;
15829+ }
15830+
15831+ const temp = previous;
15832+ previous = current;
15833+ current = temp;
15834+ }
15835+
15836+ const res = previous[s2.length];
15837+ return res > max ? undefined : res;
15838+ }
15839+
1580615840 function markPropertyAsReferenced(prop: Symbol, nodeForCheckWriteOnly: Node | undefined, isThisAccess: boolean) {
1580715841 if (prop &&
1580815842 noUnusedIdentifiers &&
0 commit comments