diff --git a/src/components/searchModes/fuzzyMode.test.ts b/src/components/searchModes/fuzzyMode.test.ts new file mode 100644 index 0000000..5a82b6c --- /dev/null +++ b/src/components/searchModes/fuzzyMode.test.ts @@ -0,0 +1,70 @@ +import { fuzzyMode } from './fuzzyMode' + +type TreeNodeSource = { + [key: string]: true | TreeNodeSource +} + +function createTreeNode(source: TreeNodeSource, name: string = '', paths: string[] = []): TreeNode { + const subPaths = paths.concat(name) + return { + name, + path: subPaths.join('/'), + type: 'tree', + contents: Object.entries(source).map(([key, value]) => { + return value === true + ? { + name: key, + path: subPaths.concat(key).join('/'), + type: 'blob', + } + : createTreeNode(value, key, subPaths) + }), + } +} + +const node = createTreeNode({ + a: { + b: { + ['c.json']: true, + }, + }, +}) + +it(`finds the items that matches all search key chars`, () => { + // a/b/c.json + // "b" => + // ✅a/b + // ❌a + expect(fuzzyMode.getSearchParams('b')?.matchNode(node.contents?.[0].contents?.[0]!)).toBe(true) + expect(fuzzyMode.getSearchParams('b')?.matchNode(node.contents?.[0]!)).toBe(false) +}) + +it(`excludes files under the dir that matches last search key char`, () => { + // a/b/c.json + // "b" => + // ❌a/b/c.json + expect( + fuzzyMode.getSearchParams('b')?.matchNode(node.contents?.[0].contents?.[0].contents?.[0]!), + ).toBe(false) + expect( + fuzzyMode.getSearchParams('tooltip')?.matchNode({ + name: '', + type: 'tree', + path: '/components/tooltip', + }), + ).toBe(true) + expect( + fuzzyMode.getSearchParams('tooltip')?.matchNode({ + name: '', + type: 'blob', + path: '/components/tooltip/x', + }), + ).toBe(false) + expect( + fuzzyMode.getSearchParams('tooltip/')?.matchNode({ + name: '', + type: 'blob', + path: '/components/tooltip/x', + }), + ).toBe(true) +}) diff --git a/src/components/searchModes/fuzzyMode.tsx b/src/components/searchModes/fuzzyMode.tsx index b2e495b..ad71595 100644 --- a/src/components/searchModes/fuzzyMode.tsx +++ b/src/components/searchModes/fuzzyMode.tsx @@ -8,8 +8,11 @@ export const fuzzyMode: ModeShape = { getSearchParams(searchKey) { if (!searchKey) return null - const matchNode = (node: TreeNode) => - fuzzyMatch(searchKey, hasUpperCase(searchKey) ? node.path : node.path.toLowerCase()) + const matchNode = (node: TreeNode) => { + const path = hasUpperCase(searchKey) ? node.path : node.path.toLowerCase() + const { match, lastIndex } = fuzzyMatch(searchKey, path) + return match && (searchKey[searchKey.length - 1] === '/' || lastIndex > path.lastIndexOf('/')) + } return { matchNode, } @@ -32,7 +35,10 @@ export const fuzzyMode: ModeShape = { progress += chunk.length + 1 // not neat side effect in map function return ( - + ) }) @@ -45,7 +51,10 @@ function fuzzyMatch(input: string, sample: string) { while (i < input.length && j < sample.length) { if (input[i] === sample[j++]) i++ } - return i === input.length + return { + lastIndex: j - 1, + match: i === input.length, + } } function fuzzyMatchIndexes(input: string, sample: string, shift: number = 0) {