From dc2edd299e26cd8b5c1c82822d4f3ffcbcff46d5 Mon Sep 17 00:00:00 2001 From: aymeric bouzy <8168981+aymericbouzy@users.noreply.github.com> Date: Mon, 21 Mar 2022 23:02:55 +0100 Subject: [PATCH] (wip) feat: fuzzy search --- package.json | 1 + .../__snapshots__/plugin.test.ts.snap | 46 ++++++ src/file_name_plugin/__tests__/plugin.test.ts | 20 ++- src/file_name_plugin/prompt.ts | 15 +- src/fuzzysearch.d.ts | 6 + yarn.lock | 145 +++++++++--------- 6 files changed, 152 insertions(+), 81 deletions(-) create mode 100644 src/fuzzysearch.d.ts diff --git a/package.json b/package.json index 85fb5e8..00c928f 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "dependencies": { "ansi-escapes": "^4.3.1", "chalk": "^4.0.0", + "fuzzysearch": "^1.0.3", "jest-regex-util": "^27.0.0", "jest-watcher": "^27.0.0", "slash": "^4.0.0", diff --git a/src/file_name_plugin/__tests__/__snapshots__/plugin.test.ts.snap b/src/file_name_plugin/__tests__/__snapshots__/plugin.test.ts.snap index ffdf3c2..44b9829 100644 --- a/src/file_name_plugin/__tests__/__snapshots__/plugin.test.ts.snap +++ b/src/file_name_plugin/__tests__/__snapshots__/plugin.test.ts.snap @@ -1,5 +1,51 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`can fuzzy search 1`] = ` + +[MOCK - cursorLeft] + + pattern › f +[MOCK - cursorSavePosition] +[MOCK - cursorLeft] + + + Pattern matches 3 files + + › src/foo.js + + › src/file-1.js + + › src/file-2.js +[MOCK - cursorTo(12, 5)] +[MOCK - cursorRestorePosition] + +[MOCK - cursorLeft] + + pattern › f1 +[MOCK - cursorSavePosition] +[MOCK - cursorLeft] + + + Pattern matches 1 file + + › src/file-1.js +[MOCK - cursorTo(13, 5)] +[MOCK - cursorRestorePosition] + +[MOCK - cursorLeft] + + pattern › f1 +[MOCK - cursorSavePosition] +[MOCK - cursorLeft] + + + Pattern matches 1 file + + › src/file-1.js +[MOCK - cursorTo(13, 5)] +[MOCK - cursorRestorePosition] +`; + exports[`can select a pattern that matches multiple files 1`] = ` [MOCK - cursorLeft] diff --git a/src/file_name_plugin/__tests__/plugin.test.ts b/src/file_name_plugin/__tests__/plugin.test.ts index 550eb31..03feb0e 100644 --- a/src/file_name_plugin/__tests__/plugin.test.ts +++ b/src/file_name_plugin/__tests__/plugin.test.ts @@ -119,7 +119,7 @@ it('can select a pattern that matches multiple files', async () => { expect(updateConfigAndRun).toHaveBeenCalledWith({ mode: 'watch', - testPathPattern: 'fi', + testPathPattern: 'f.*i', }); }); @@ -179,3 +179,21 @@ it("selected file doesn't include trimming dots", async () => { testPathPattern: 'ong_name_gonna_need_trimming\\.js', }); }); + +it('can fuzzy search', async () => { + const { stdout, hookEmitter, updateConfigAndRun, plugin, type } = + pluginTester(FileNamePlugin); + + hookEmitter.onFileChange({ projects }); + const runPromise = plugin.run({}, updateConfigAndRun); + stdout.write.mockReset(); + type('f', '1', KEYS.ARROW_DOWN, KEYS.ENTER); + expect(stdout.write.mock.calls.join('\n')).toMatchSnapshot(); + + await runPromise; + + expect(updateConfigAndRun).toHaveBeenCalledWith({ + mode: 'watch', + testPathPattern: 'src/file-1\\.js', + }); +}); diff --git a/src/file_name_plugin/prompt.ts b/src/file_name_plugin/prompt.ts index 908acd0..7d7174c 100644 --- a/src/file_name_plugin/prompt.ts +++ b/src/file_name_plugin/prompt.ts @@ -8,6 +8,7 @@ import { printRestoredPatternCaret, } from 'jest-watcher'; import { escapeStrForRegex } from 'jest-regex-util'; +import fuzzysearch from 'fuzzysearch'; import type { Config } from '@jest/types'; import { highlight, @@ -91,14 +92,6 @@ export default class FileNamePatternPrompt extends PatternPrompt { path: string; context: { config: Config.ProjectConfig }; }> { - let regex: RegExp; - - try { - regex = new RegExp(pattern, 'i'); - } catch (e) { - return []; - } - return this._searchSources.reduce< Array<{ path: string; @@ -107,7 +100,9 @@ export default class FileNamePatternPrompt extends PatternPrompt { >((tests, { testPaths, config }) => { return tests.concat( testPaths - .filter((testPath) => regex.test(testPath)) + .filter((testPath) => + fuzzysearch(pattern.toLowerCase(), testPath.toLowerCase()), + ) .map((path) => ({ path, context: { config }, @@ -130,7 +125,7 @@ export default class FileNamePatternPrompt extends PatternPrompt { super.run( (value) => { onSuccess( - removeTrimmingDots(value).split('/').map(escapeStrForRegex).join('/'), + removeTrimmingDots(value).split('').map(escapeStrForRegex).join('.*'), ); }, onCancel, diff --git a/src/fuzzysearch.d.ts b/src/fuzzysearch.d.ts new file mode 100644 index 0000000..34e41b0 --- /dev/null +++ b/src/fuzzysearch.d.ts @@ -0,0 +1,6 @@ +/** Declaration file generated by dts-gen */ +declare module 'fuzzysearch' { + function fuzzysearch(needle: string, haystack: string): boolean; + + export = fuzzysearch; +} diff --git a/yarn.lock b/yarn.lock index 304e322..5bb546b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3310,6 +3310,11 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +fuzzysearch@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fuzzysearch/-/fuzzysearch-1.0.3.tgz#dffc80f6d6b04223f2226aa79dd194231096d008" + integrity sha1-3/yA9tawQiPyImqnndGUIxCW0Ag= + gauge@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.3.tgz#286cf105c1962c659f0963058fb05116c1b82d3f" @@ -5164,76 +5169,76 @@ npm@^7.0.0: resolved "https://registry.yarnpkg.com/npm/-/npm-7.24.2.tgz#861117af8241bea592289f22407230e5300e59ca" integrity sha512-120p116CE8VMMZ+hk8IAb1inCPk4Dj3VZw29/n2g6UI77urJKVYb7FZUDW8hY+EBnfsjI/2yrobBgFyzo7YpVQ== dependencies: - "@isaacs/string-locale-compare" "*" - "@npmcli/arborist" "*" - "@npmcli/ci-detect" "*" - "@npmcli/config" "*" - "@npmcli/map-workspaces" "*" - "@npmcli/package-json" "*" - "@npmcli/run-script" "*" - abbrev "*" - ansicolors "*" - ansistyles "*" - archy "*" - cacache "*" - chalk "*" - chownr "*" - cli-columns "*" - cli-table3 "*" - columnify "*" - fastest-levenshtein "*" - glob "*" - graceful-fs "*" - hosted-git-info "*" - ini "*" - init-package-json "*" - is-cidr "*" - json-parse-even-better-errors "*" - libnpmaccess "*" - libnpmdiff "*" - libnpmexec "*" - libnpmfund "*" - libnpmhook "*" - libnpmorg "*" - libnpmpack "*" - libnpmpublish "*" - libnpmsearch "*" - libnpmteam "*" - libnpmversion "*" - make-fetch-happen "*" - minipass "*" - minipass-pipeline "*" - mkdirp "*" - mkdirp-infer-owner "*" - ms "*" - node-gyp "*" - nopt "*" - npm-audit-report "*" - npm-install-checks "*" - npm-package-arg "*" - npm-pick-manifest "*" - npm-profile "*" - npm-registry-fetch "*" - npm-user-validate "*" - npmlog "*" - opener "*" - pacote "*" - parse-conflict-json "*" - qrcode-terminal "*" - read "*" - read-package-json "*" - read-package-json-fast "*" - readdir-scoped-modules "*" - rimraf "*" - semver "*" - ssri "*" - tar "*" - text-table "*" - tiny-relative-date "*" - treeverse "*" - validate-npm-package-name "*" - which "*" - write-file-atomic "*" + "@isaacs/string-locale-compare" "^1.1.0" + "@npmcli/arborist" "^2.9.0" + "@npmcli/ci-detect" "^1.2.0" + "@npmcli/config" "^2.3.0" + "@npmcli/map-workspaces" "^1.0.4" + "@npmcli/package-json" "^1.0.1" + "@npmcli/run-script" "^1.8.6" + abbrev "~1.1.1" + ansicolors "~0.3.2" + ansistyles "~0.1.3" + archy "~1.0.0" + cacache "^15.3.0" + chalk "^4.1.2" + chownr "^2.0.0" + cli-columns "^3.1.2" + cli-table3 "^0.6.0" + columnify "~1.5.4" + fastest-levenshtein "^1.0.12" + glob "^7.2.0" + graceful-fs "^4.2.8" + hosted-git-info "^4.0.2" + ini "^2.0.0" + init-package-json "^2.0.5" + is-cidr "^4.0.2" + json-parse-even-better-errors "^2.3.1" + libnpmaccess "^4.0.2" + libnpmdiff "^2.0.4" + libnpmexec "^2.0.1" + libnpmfund "^1.1.0" + libnpmhook "^6.0.2" + libnpmorg "^2.0.2" + libnpmpack "^2.0.1" + libnpmpublish "^4.0.1" + libnpmsearch "^3.1.1" + libnpmteam "^2.0.3" + libnpmversion "^1.2.1" + make-fetch-happen "^9.1.0" + minipass "^3.1.3" + minipass-pipeline "^1.2.4" + mkdirp "^1.0.4" + mkdirp-infer-owner "^2.0.0" + ms "^2.1.2" + node-gyp "^7.1.2" + nopt "^5.0.0" + npm-audit-report "^2.1.5" + npm-install-checks "^4.0.0" + npm-package-arg "^8.1.5" + npm-pick-manifest "^6.1.1" + npm-profile "^5.0.3" + npm-registry-fetch "^11.0.0" + npm-user-validate "^1.0.1" + npmlog "^5.0.1" + opener "^1.5.2" + pacote "^11.3.5" + parse-conflict-json "^1.1.1" + qrcode-terminal "^0.12.0" + read "~1.0.7" + read-package-json "^4.1.1" + read-package-json-fast "^2.0.3" + readdir-scoped-modules "^1.1.0" + rimraf "^3.0.2" + semver "^7.3.5" + ssri "^8.0.1" + tar "^6.1.11" + text-table "~0.2.0" + tiny-relative-date "^1.3.0" + treeverse "^1.0.4" + validate-npm-package-name "~3.0.0" + which "^2.0.2" + write-file-atomic "^3.0.3" npmlog@*, npmlog@^6.0.0, npmlog@^6.0.1: version "6.0.1"