From d2c121b00f2e1eb2ea8cc3a23a5039b3a4425bea Mon Sep 17 00:00:00 2001 From: thrien <39382079+thrien@users.noreply.github.com> Date: Mon, 12 Jul 2021 21:25:08 +0200 Subject: [PATCH] feat: update Levenshtein to Damerau-Levenshtein (#1973) --- lib/utils/levenshtein.ts | 24 +++++++++++++++++------- test/yargs.cjs | 12 ++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/lib/utils/levenshtein.ts b/lib/utils/levenshtein.ts index aa4e9c94a..760c68c77 100644 --- a/lib/utils/levenshtein.ts +++ b/lib/utils/levenshtein.ts @@ -21,6 +21,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // levenshtein distance algorithm, pulled from Andrei Mackenzie's MIT licensed. // gist, which can be found here: https://gist.github.com/andrei-m/982927 +// extended to compute damerau-levenshtein distance // Compute the edit distance between the two given strings export function levenshtein(a: string, b: string) { @@ -47,13 +48,22 @@ export function levenshtein(a: string, b: string) { if (b.charAt(i - 1) === a.charAt(j - 1)) { matrix[i][j] = matrix[i - 1][j - 1]; } else { - matrix[i][j] = Math.min( - matrix[i - 1][j - 1] + 1, // substitution - Math.min( - matrix[i][j - 1] + 1, // insertion - matrix[i - 1][j] + 1 - ) - ); // deletion + if ( + i > 1 && + j > 1 && + b.charAt(i - 2) === a.charAt(j - 1) && + b.charAt(i - 1) === a.charAt(j - 2) + ) { + matrix[i][j] = matrix[i - 2][j - 2] + 1; // transposition + } else { + matrix[i][j] = Math.min( + matrix[i - 1][j - 1] + 1, // substitution + Math.min( + matrix[i][j - 1] + 1, // insertion + matrix[i - 1][j] + 1 + ) + ); // deletion + } } } } diff --git a/test/yargs.cjs b/test/yargs.cjs index ee6091e3e..e743a70da 100644 --- a/test/yargs.cjs +++ b/test/yargs.cjs @@ -423,6 +423,18 @@ describe('yargs dsl tests', () => { r.errors[2].should.match(/Did you mean goat/); }); + it('counts tranposition as one mistake', () => { + const r = checkOutput(() => { + yargs(['baot']) + .command('boat') + .command('bot') + .recommendCommands() + .parse(); + }); + + r.errors[2].should.match(/Did you mean boat/); + }); + // see: https://github.com/yargs/yargs/issues/822 it('does not print command recommendation if help message will be shown', done => { const parser = yargs().command('goat').help().recommendCommands();