-
Notifications
You must be signed in to change notification settings - Fork 285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Three bugs in the QSSense scoring algorithm #2450
Comments
I also tried to replicate the results of the |
Deliberately introduce bugs in quick-score.js to replicate the scores produced by QSSense.m, as described here: quicksilver/Quicksilver#2450 Add hitmask test that replicates the results from TestQSSense.m.
Gross. 😉 (I’ve done something similar.) That’s the one area of the code that I’ve never really looked at, but I’m sure you’re probably right about the bugs. I’m looking forward to digging into it. (Looks like you’ve done most of the hard work. Thanks.) Maybe this is a good time to incorporate the TextStart logic into the main app and make it the default (which I’ve wanted to do for a long time). |
I've got a JS version of this algorithm in my QuicKey Chrome extension, which lets you switch to a tab via a Quicksilver-style search on the title or URL. I realized it needed some tuning to better sort webpage titles or URLs while the user types, since they're much longer than typical OS filenames and often contain GUIDs or tokens with lots of capital letters. So I dove into to trying to better understand the algorithm and added some debugging info that tried to visualize how the scoring worked. Recently I started extracting that algorithm into an independent library, and tried to match the scores in The current, very early version of that library is here (on the Running
Anyway, it might be useful for your investigations, depending on how gross you find JavaScript. :) I'll have to look into that TextStart algo. Does it give better matches? |
It probably depends on how your brain works, but I think so. It gives more weight to letters at the beginning of words and uppercase letters, so searching for “aft” would show “A Frozen Treat.jpg” before showing “Aftermath.txt”. |
Doesn't the original algorithm already do that? I get 0.92308 for |
Well yeah, the built-in ranker will score “A Frozen Treat” higher than "A rofzen reat”, but not as high as “Aftermath”. I want contiguous letters to result in a lower score than letters at word boundaries. |
Squashed commit of the following: commit 9210921 Author: John Dunning <fw@johndunning.com> Date: Thu Sep 6 00:15:12 2018 -0700 Add QuickScore class to replace createScorer() Turn off linebreak-style rule Refactor QuickScore Change QuickScore.score() to search(). Add setKeys() and default params to QuickScore. In createConfig(), don't create a new QuickScoreConfig if the param is an instance of Config, so that BaseConfig isn't affected. Make createConfig() available on quickScore(). Add more QuickScore tests. Add setup.js and utils.js. Get rid of default searchRange in getRangeOfSubstring(). Clean up package.json scripts. Add license to README.md. Add badges to README.md Add npm install to .travis.yml Move scorer to QuickScore to avoid circular import Add .travis.yml. Add codecov.io support. Move scorer to config Add QuickScore class Add QuickScore.test.js. Add babel key back to package.json so that the tests work again. commit c0b8591 Author: John Dunning <fw@johndunning.com> Date: Wed Sep 5 23:11:40 2018 -0700 Tweak .eslintrc.js commit 1e68c38 Author: John Dunning <fw@johndunning.com> Date: Mon Sep 3 12:03:16 2018 -0700 Add a comment commit 9493e23 Author: John Dunning <fw@johndunning.com> Date: Sat Sep 1 20:23:05 2018 -0700 Refactor QuickScoreConfig to extend BaseConfig Add useSkipReduction() method and emptyQueryScore to the config. Move constants into config object so they can be overridden. Use emptyQueryScore = 0 with the default config. Rename config() to createConfig(). Create a config from the configOptions in createScorer(). Fix zero score tests. Remove babel key from package.json. Add babel-plugin-external-helpers to avoid duplicate class call checks. Update eslintrc. commit b2f91be Author: John Dunning <fw@johndunning.com> Date: Sat Sep 1 16:04:03 2018 -0700 Return ignoredScore for empty queries Add a test for an empty query that returns 0.9. commit 3642560 Author: John Dunning <fw@johndunning.com> Date: Fri Aug 31 19:53:26 2018 -0700 Update quick-score.js with code from quickey-quick-score.js Clean up order of params in adjustRemainingScore(). Include exports from config in index.js. Update scoreArray() to not default the config to null, so that DefaultConfig gets used. Remove quickey-quick-score.js. Fix index.test.js with new arity for quickScore(). Update .eslintrc. commit 0a6f925 Author: John Dunning <fw@johndunning.com> Date: Fri Aug 31 18:20:25 2018 -0700 Refactor quickScore() to use a config object for calculating scores Lowercase the string and query once rather than on every substring search. Add config.js. Add QuicksilverConfig to score strings like the default Quicksilver algo. commit c89e364 Author: John Dunning <fw@johndunning.com> Date: Sun Aug 26 20:17:06 2018 -0700 Add configOptions to scoreArray() commit 930f0f7 Author: John Dunning <fw@johndunning.com> Date: Tue Aug 21 23:19:11 2018 -0700 Add version that returns the same scores as the QuicKey algo commit 0b510c1 Author: John Dunning <fw@johndunning.com> Date: Sun Aug 19 23:31:37 2018 -0700 Switch to returning ranges in matches array Remove addIndexesToRange() function. Fix quick-score.test.js. and score-array.test.js. Fix array-element-newline eslint rule. commit 8dcdd55 Author: John Dunning <fw@johndunning.com> Date: Sun Aug 19 16:25:53 2018 -0700 Rename hits to matches commit 78838d4 Author: John Dunning <fw@johndunning.com> Date: Sat Aug 18 18:09:15 2018 -0700 Check max() only once in addIndexesInRange() commit bdc206f Author: John Dunning <fw@johndunning.com> Date: Mon Aug 13 23:59:36 2018 -0700 Rename some parameters commit 4ec9436 Author: John Dunning <fw@johndunning.com> Date: Mon Aug 13 16:40:28 2018 -0700 Remove default exports Update tests for index.js. commit 8d246cd Author: John Dunning <fw@johndunning.com> Date: Sun Aug 12 19:51:14 2018 -0700 Add scoreKey to indicate which key had the high score commit ada7d2b Author: John Dunning <fw@johndunning.com> Date: Sun Aug 12 17:41:44 2018 -0700 Add test for configuring individual scorers per key Add minimum node version to package.json. Collect coverage only from src/. commit 82a31aa Author: John Dunning <fw@johndunning.com> Date: Sat Aug 11 19:11:28 2018 -0700 Make the scorer function default to quickScore commit ff88b45 Author: John Dunning <fw@johndunning.com> Date: Sat Aug 11 11:49:42 2018 -0700 Add default scoreArray() export to score-array.js Add a "qk" score test. Add beginning of some examples to README.md. commit 82b6859 Author: John Dunning <fw@johndunning.com> Date: Fri Aug 10 17:56:34 2018 -0700 Use for-loop instead of reduce() when scoring arrays Add another Tabs scoring test and refactor it. commit 67f19a8 Author: John Dunning <fw@johndunning.com> Date: Thu Aug 9 23:31:47 2018 -0700 Add score-array.js and test Add tabs.js for example data. Tweak eslint rules and change to tab-delimited. commit b0c32b8 Author: John Dunning <fw@johndunning.com> Date: Sun Aug 5 17:03:36 2018 -0700 0.1.0 commit 31d3e5a Author: John Dunning <fw@johndunning.com> Date: Sat Aug 4 19:08:43 2018 -0700 Add eslint Add pretest script. Export named quickScore and default. commit 76f106e Author: John Dunning <fw@johndunning.com> Date: Wed Aug 1 19:48:18 2018 -0700 Add test for index.js Include dist and lib in the npm package. Indent package.json with spaces. commit fcbff51 Author: John Dunning <fw@johndunning.com> Date: Tue Jul 31 00:10:33 2018 -0700 Change lib UMD output to index.js commit 0ada1a5 Author: John Dunning <fw@johndunning.com> Date: Tue Jul 31 00:02:25 2018 -0700 Fix prepare in package.json commit 71cf5bf Author: John Dunning <fw@johndunning.com> Date: Mon Jul 30 23:56:58 2018 -0700 Simplify exports from index.js Add prepare script and files array to package.json. Added some info to README.md. commit f39959f Author: John Dunning <fw@johndunning.com> Date: Mon Jul 30 10:17:28 2018 -0700 Add minified output to dist Change filenames in dist to quick-score. Add rollup-plugin-babel-minify. commit dc8b100 Author: John Dunning <fw@johndunning.com> Date: Sun Jul 29 20:10:28 2018 -0700 Add rollup to build the library Switch to using import and export instead of require() in src files. Build files to lib/ and dist/. Add index.js to combine the files into a single export. Add babel config to package.json to transform modules when testing with jest, but not when building with rollup. Remove webpack. Switch to let and const in debug-harness.js. commit 19d1761 Author: John Dunning <fw@johndunning.com> Date: Sat Jul 28 14:59:09 2018 -0700 Convert some lets to consts Convert substr() calls to substring(). Add --runInBand and --env node to jest calls to try to speed it up. Add test-watch script. commit aaf2def Author: John Dunning <fw@johndunning.com> Date: Thu Jul 26 23:19:03 2018 -0700 Rename debug files Fix path to src/quick-score.js from debug-harness.js. Add debug npm script and repository link. commit 9941ce7 Author: John Dunning <fw@johndunning.com> Date: Thu Jul 26 19:40:37 2018 -0700 Change how debug statements are inserted by quick-score-debug.js Search for strings in quick-score.js and insert the debug statements before or after them. Convert some lets to consts in quick-score.js. commit 29c83e1 Author: John Dunning <fw@johndunning.com> Date: Thu Jul 26 00:56:22 2018 -0700 Convert to one var declaration per line Convert var to let declarations, and use default params. Add loop to quick-score-test.js that demonstrates increasing the search range. Don't show the discount score values that aren't being used currently. Change indent so it starts at a 1-based column 10. commit 9bd598f Author: John Dunning <fw@johndunning.com> Date: Wed Jul 25 18:55:18 2018 -0700 Remove buggy lines from quick-score.js Update tests in quick-score.test.js with scores from the non-buggy code. Add zero scores and search ranges test suites. Add engines in package.json to require at least node 8.0, so that the eval() in the quick-score-debug.js works. commit 5d300d8 Author: John Dunning <fw@johndunning.com> Date: Wed Jul 25 00:06:37 2018 -0700 Replicate bugs in QSSense.m Deliberately introduce bugs in quick-score.js to replicate the scores produced by QSSense.m, as described here: quicksilver/Quicksilver#2450 Add hitmask test that replicates the results from TestQSSense.m. commit 7cfd712 Author: John Dunning <fw@johndunning.com> Date: Sun Jul 15 19:36:52 2018 -0700 Add uppercase and word separator tests to improve coverage Add some tests to quick-score-test.js. commit aa8706d Author: John Dunning <fw@johndunning.com> Date: Sun Jul 15 18:17:18 2018 -0700 Add debug harness Remove debug statements from quick-score.js and move them to debug-statements.js. quick-score-debug.js reinserts the debug statements after reading in the source. Add a maxDifference param to scoreNearly(). commit 9d50828 Author: John Dunning <fw@johndunning.com> Date: Sat Jul 14 15:14:17 2018 -0700 Update license field commit e05d2f6 Author: John Dunning <fw@johndunning.com> Date: Sat Jul 14 15:12:00 2018 -0700 Add MIT license commit c0dc4d6 Author: John Dunning <fw@johndunning.com> Date: Sat Jul 14 14:39:41 2018 -0700 Add README.md Ignore coverage directory. commit 69c66b2 Author: John Dunning <fw@johndunning.com> Date: Sat Jul 14 14:32:13 2018 -0700 Convert Range to ES6 class Remove unused toValue() method. commit 43a1bca Author: John Dunning <fw@johndunning.com> Date: Mon Jul 9 13:29:58 2018 -0700 Add coverage command to package.json commit 5b23e8d Author: John Dunning <fw@johndunning.com> Date: Sun Jul 8 19:53:49 2018 -0700 Fix require paths from tests commit 827e68d Author: John Dunning <fw@johndunning.com> Date: Sun Jul 8 19:34:46 2018 -0700 Move tests to /test folder commit 714716b Author: John Dunning <fw@johndunning.com> Date: Sun Jul 8 19:30:02 2018 -0700 Add hit index tests Add toBeNearly() matcher. Rename some constants. Clean up some old code.
Squashed commit of the following: commit 1441e92 Author: John Dunning <fw@johndunning.com> Date: Sat Sep 15 12:29:50 2018 -0700 Create QuickScore class to replace createScorer() Squashed commit of the following: commit 99c2915 Author: John Dunning <fw@johndunning.com> Date: Sat Sep 8 21:31:56 2018 -0700 Turn off linebreak-style rule commit 6709cd6 Author: John Dunning <fw@johndunning.com> Date: Sat Sep 8 21:21:33 2018 -0700 Refactor QuickScore Change QuickScore.score() to search(). Add setKeys() and default params to QuickScore. In createConfig(), don't create a new QuickScoreConfig if the param is an instance of Config, so that BaseConfig isn't affected. Make createConfig() available on quickScore(). Add more QuickScore tests. Add setup.js and utils.js. Get rid of default searchRange in getRangeOfSubstring(). Clean up package.json scripts. Add license to README.md. commit 9797460 Author: John Dunning <fw@johndunning.com> Date: Fri Sep 7 19:18:41 2018 -0700 Add badges to README.md commit e4ea4d1 Author: John Dunning <fw@johndunning.com> Date: Fri Sep 7 19:11:52 2018 -0700 Add npm install to .travis.yml commit db891c1 Author: John Dunning <john_dunning@yahoo.com> Date: Fri Sep 7 17:37:14 2018 -0700 Move scorer to QuickScore to avoid circular import Add .travis.yml. Add codecov.io support. commit 9e2bdaa Author: John Dunning <fw@johndunning.com> Date: Thu Sep 6 15:50:35 2018 -0700 Move scorer to config commit 5d99f3a Author: John Dunning <fw@johndunning.com> Date: Thu Sep 6 00:15:12 2018 -0700 Add QuickScore class Add QuickScore.test.js. Add babel key back to package.json so that the tests work again. commit adfe6b9 Author: John Dunning <fw@johndunning.com> Date: Wed Sep 5 23:11:40 2018 -0700 Tweak .eslintrc.js commit 03c0efe Author: John Dunning <fw@johndunning.com> Date: Mon Sep 3 12:03:16 2018 -0700 Add a comment commit 074b364 Author: John Dunning <fw@johndunning.com> Date: Sat Sep 1 20:23:05 2018 -0700 Refactor QuickScoreConfig to extend BaseConfig Add useSkipReduction() method and emptyQueryScore to the config. Move constants into config object so they can be overridden. Use emptyQueryScore = 0 with the default config. Rename config() to createConfig(). Create a config from the configOptions in createScorer(). Fix zero score tests. Remove babel key from package.json. Add babel-plugin-external-helpers to avoid duplicate class call checks. Update eslintrc. commit 8343fe9 Author: John Dunning <fw@johndunning.com> Date: Sat Sep 1 16:04:03 2018 -0700 Return ignoredScore for empty queries Add a test for an empty query that returns 0.9. commit 51e9a1a Author: John Dunning <fw@johndunning.com> Date: Fri Aug 31 19:53:26 2018 -0700 Update quick-score.js with code from quickey-quick-score.js Clean up order of params in adjustRemainingScore(). Include exports from config in index.js. Update scoreArray() to not default the config to null, so that DefaultConfig gets used. Remove quickey-quick-score.js. Fix index.test.js with new arity for quickScore(). Update .eslintrc. commit 95b6d50 Author: John Dunning <fw@johndunning.com> Date: Fri Aug 31 18:20:25 2018 -0700 Refactor quickScore() to use a config object for calculating scores Lowercase the string and query once rather than on every substring search. Add config.js. Add QuicksilverConfig to score strings like the default Quicksilver algo. commit 446e8d2 Author: John Dunning <fw@johndunning.com> Date: Sun Aug 26 20:17:06 2018 -0700 Add configOptions to scoreArray() commit cc00f6d Author: John Dunning <fw@johndunning.com> Date: Tue Aug 21 23:19:11 2018 -0700 Add version that returns the same scores as the QuicKey algo commit ffe6926 Author: John Dunning <fw@johndunning.com> Date: Sun Aug 19 23:31:37 2018 -0700 Switch to returning ranges in matches array Remove addIndexesToRange() function. Fix quick-score.test.js. and score-array.test.js. Fix array-element-newline eslint rule. commit c74b645 Author: John Dunning <fw@johndunning.com> Date: Sun Aug 19 16:25:53 2018 -0700 Rename hits to matches commit caf2948 Author: John Dunning <fw@johndunning.com> Date: Sat Aug 18 18:09:15 2018 -0700 Check max() only once in addIndexesInRange() commit 6936ba7 Author: John Dunning <fw@johndunning.com> Date: Mon Aug 13 23:59:36 2018 -0700 Rename some parameters commit aa09f3e Author: John Dunning <fw@johndunning.com> Date: Mon Aug 13 16:40:28 2018 -0700 Remove default exports Update tests for index.js. commit c3946f4 Author: John Dunning <fw@johndunning.com> Date: Sun Aug 12 19:51:14 2018 -0700 Add scoreKey to indicate which key had the high score commit 718e073 Author: John Dunning <fw@johndunning.com> Date: Sun Aug 12 17:41:44 2018 -0700 Add test for configuring individual scorers per key Add minimum node version to package.json. Collect coverage only from src/. commit b2d1597 Author: John Dunning <fw@johndunning.com> Date: Sat Aug 11 19:11:28 2018 -0700 Make the scorer function default to quickScore commit 5947103 Author: John Dunning <fw@johndunning.com> Date: Sat Aug 11 11:49:42 2018 -0700 Add default scoreArray() export to score-array.js Add a "qk" score test. Add beginning of some examples to README.md. commit f5c1df3 Author: John Dunning <fw@johndunning.com> Date: Fri Aug 10 17:56:34 2018 -0700 Use for-loop instead of reduce() when scoring arrays Add another Tabs scoring test and refactor it. commit d79a9c5 Author: John Dunning <fw@johndunning.com> Date: Thu Aug 9 23:31:47 2018 -0700 Add score-array.js and test Add tabs.js for example data. Tweak eslint rules and change to tab-delimited. commit 544f612 Author: John Dunning <fw@johndunning.com> Date: Sun Aug 5 17:03:36 2018 -0700 0.1.0 commit 8cb962f Author: John Dunning <fw@johndunning.com> Date: Sat Aug 4 19:08:43 2018 -0700 Add eslint Add pretest script. Export named quickScore and default. commit be2e947 Author: John Dunning <fw@johndunning.com> Date: Wed Aug 1 19:48:18 2018 -0700 Add test for index.js Include dist and lib in the npm package. Indent package.json with spaces. commit 0783861 Author: John Dunning <fw@johndunning.com> Date: Tue Jul 31 00:10:33 2018 -0700 Change lib UMD output to index.js commit 2392998 Author: John Dunning <fw@johndunning.com> Date: Tue Jul 31 00:02:25 2018 -0700 Fix prepare in package.json commit 0fc811a Author: John Dunning <fw@johndunning.com> Date: Mon Jul 30 23:56:58 2018 -0700 Simplify exports from index.js Add prepare script and files array to package.json. Added some info to README.md. commit 7af842c Author: John Dunning <fw@johndunning.com> Date: Mon Jul 30 10:17:28 2018 -0700 Add minified output to dist Change filenames in dist to quick-score. Add rollup-plugin-babel-minify. commit 9e071a8 Author: John Dunning <fw@johndunning.com> Date: Sun Jul 29 20:10:28 2018 -0700 Add rollup to build the library Switch to using import and export instead of require() in src files. Build files to lib/ and dist/. Add index.js to combine the files into a single export. Add babel config to package.json to transform modules when testing with jest, but not when building with rollup. Remove webpack. Switch to let and const in debug-harness.js. commit 3e82c74 Author: John Dunning <fw@johndunning.com> Date: Sat Jul 28 14:59:09 2018 -0700 Convert some lets to consts Convert substr() calls to substring(). Add --runInBand and --env node to jest calls to try to speed it up. Add test-watch script. commit 49cb7dd Author: John Dunning <fw@johndunning.com> Date: Thu Jul 26 23:19:03 2018 -0700 Rename debug files Fix path to src/quick-score.js from debug-harness.js. Add debug npm script and repository link. commit 99bcfab Author: John Dunning <fw@johndunning.com> Date: Thu Jul 26 19:40:37 2018 -0700 Change how debug statements are inserted by quick-score-debug.js Search for strings in quick-score.js and insert the debug statements before or after them. Convert some lets to consts in quick-score.js. commit c514469 Author: John Dunning <fw@johndunning.com> Date: Thu Jul 26 00:56:22 2018 -0700 Convert to one var declaration per line Convert var to let declarations, and use default params. Add loop to quick-score-test.js that demonstrates increasing the search range. Don't show the discount score values that aren't being used currently. Change indent so it starts at a 1-based column 10. commit 397fa19 Author: John Dunning <fw@johndunning.com> Date: Wed Jul 25 18:55:18 2018 -0700 Remove buggy lines from quick-score.js Update tests in quick-score.test.js with scores from the non-buggy code. Add zero scores and search ranges test suites. Add engines in package.json to require at least node 8.0, so that the eval() in the quick-score-debug.js works. commit 645a69e Author: John Dunning <fw@johndunning.com> Date: Wed Jul 25 00:06:37 2018 -0700 Replicate bugs in QSSense.m Deliberately introduce bugs in quick-score.js to replicate the scores produced by QSSense.m, as described here: quicksilver/Quicksilver#2450 Add hitmask test that replicates the results from TestQSSense.m. commit 6a1b820 Author: John Dunning <fw@johndunning.com> Date: Sun Jul 15 19:36:52 2018 -0700 Add uppercase and word separator tests to improve coverage Add some tests to quick-score-test.js. commit 6b6bd15 Author: John Dunning <fw@johndunning.com> Date: Sun Jul 15 18:17:18 2018 -0700 Add debug harness Remove debug statements from quick-score.js and move them to debug-statements.js. quick-score-debug.js reinserts the debug statements after reading in the source. Add a maxDifference param to scoreNearly().
* Add debug harness Remove debug statements from quick-score.js and move them to debug-statements.js. quick-score-debug.js reinserts the debug statements after reading in the source. Add a maxDifference param to scoreNearly(). * Add uppercase and word separator tests to improve coverage Add some tests to quick-score-test.js. * Replicate bugs in QSSense.m Deliberately introduce bugs in quick-score.js to replicate the scores produced by QSSense.m, as described here: quicksilver/Quicksilver#2450 Add hitmask test that replicates the results from TestQSSense.m. * Remove buggy lines from quick-score.js Update tests in quick-score.test.js with scores from the non-buggy code. Add zero scores and search ranges test suites. Add engines in package.json to require at least node 8.0, so that the eval() in the quick-score-debug.js works. * Convert to one var declaration per line Convert var to let declarations, and use default params. Add loop to quick-score-test.js that demonstrates increasing the search range. Don't show the discount score values that aren't being used currently. Change indent so it starts at a 1-based column 10. * Change how debug statements are inserted by quick-score-debug.js Search for strings in quick-score.js and insert the debug statements before or after them. Convert some lets to consts in quick-score.js. * Rename debug files Fix path to src/quick-score.js from debug-harness.js. Add debug npm script and repository link. * Convert some lets to consts Convert substr() calls to substring(). Add --runInBand and --env node to jest calls to try to speed it up. Add test-watch script. * Add rollup to build the library Switch to using import and export instead of require() in src files. Build files to lib/ and dist/. Add index.js to combine the files into a single export. Add babel config to package.json to transform modules when testing with jest, but not when building with rollup. Remove webpack. Switch to let and const in debug-harness.js. * Add minified output to dist Change filenames in dist to quick-score. Add rollup-plugin-babel-minify. * Simplify exports from index.js Add prepare script and files array to package.json. Added some info to README.md. * Fix prepare in package.json * Change lib UMD output to index.js * Add test for index.js Include dist and lib in the npm package. Indent package.json with spaces. * Add eslint Add pretest script. Export named quickScore and default. * 0.1.0 * Add score-array.js and test Add tabs.js for example data. Tweak eslint rules and change to tab-delimited. * Use for-loop instead of reduce() when scoring arrays Add another Tabs scoring test and refactor it. * Add default scoreArray() export to score-array.js Add a "qk" score test. Add beginning of some examples to README.md. * Make the scorer function default to quickScore * Add test for configuring individual scorers per key Add minimum node version to package.json. Collect coverage only from src/. * Add scoreKey to indicate which key had the high score * Remove default exports Update tests for index.js. * Rename some parameters * Check max() only once in addIndexesInRange() * Rename hits to matches * Switch to returning ranges in matches array Remove addIndexesToRange() function. Fix quick-score.test.js. and score-array.test.js. Fix array-element-newline eslint rule. * Add version that returns the same scores as the QuicKey algo * Add configOptions to scoreArray() * Refactor quickScore() to use a config object for calculating scores Lowercase the string and query once rather than on every substring search. Add config.js. Add QuicksilverConfig to score strings like the default Quicksilver algo. * Update quick-score.js with code from quickey-quick-score.js Clean up order of params in adjustRemainingScore(). Include exports from config in index.js. Update scoreArray() to not default the config to null, so that DefaultConfig gets used. Remove quickey-quick-score.js. Fix index.test.js with new arity for quickScore(). Update .eslintrc. * Return ignoredScore for empty queries Add a test for an empty query that returns 0.9. * Refactor QuickScoreConfig to extend BaseConfig Add useSkipReduction() method and emptyQueryScore to the config. Move constants into config object so they can be overridden. Use emptyQueryScore = 0 with the default config. Rename config() to createConfig(). Create a config from the configOptions in createScorer(). Fix zero score tests. Remove babel key from package.json. Add babel-plugin-external-helpers to avoid duplicate class call checks. Update eslintrc. * Add a comment * Tweak .eslintrc.js * Create QuickScore class to replace createScorer() Squashed commit of the following: commit 99c2915 Author: John Dunning <fw@johndunning.com> Date: Sat Sep 8 21:31:56 2018 -0700 Turn off linebreak-style rule commit 6709cd6 Author: John Dunning <fw@johndunning.com> Date: Sat Sep 8 21:21:33 2018 -0700 Refactor QuickScore Change QuickScore.score() to search(). Add setKeys() and default params to QuickScore. In createConfig(), don't create a new QuickScoreConfig if the param is an instance of Config, so that BaseConfig isn't affected. Make createConfig() available on quickScore(). Add more QuickScore tests. Add setup.js and utils.js. Get rid of default searchRange in getRangeOfSubstring(). Clean up package.json scripts. Add license to README.md. commit 9797460 Author: John Dunning <fw@johndunning.com> Date: Fri Sep 7 19:18:41 2018 -0700 Add badges to README.md commit e4ea4d1 Author: John Dunning <fw@johndunning.com> Date: Fri Sep 7 19:11:52 2018 -0700 Add npm install to .travis.yml commit db891c1 Author: John Dunning <john_dunning@yahoo.com> Date: Fri Sep 7 17:37:14 2018 -0700 Move scorer to QuickScore to avoid circular import Add .travis.yml. Add codecov.io support. commit 9e2bdaa Author: John Dunning <fw@johndunning.com> Date: Thu Sep 6 15:50:35 2018 -0700 Move scorer to config commit 5d99f3a Author: John Dunning <fw@johndunning.com> Date: Thu Sep 6 00:15:12 2018 -0700 Add QuickScore class Add QuickScore.test.js. Add babel key back to package.json so that the tests work again.
This is awesome! Thanks @fwextensions for going through the painful task of debugging, and thanks @skurfer for implementing. The Thanks again! |
I've converted the Quicksilver scoring algorithm to JavaScript for various projects, and I believe I've found a few bugs in
QSSense.m
. (I've only learned as much Objective-C as I needed to translate the algorithm, but I'm fairly sure these bugs are real.)Several years ago I started with this old version of the code, which used
NSString
. That worked well, though I didn't really have a way to verify that the JS version was returning the same scores as the Objective-C version.Recently, I found this repo and the
TestQSSense.m
file. The scores intestScoreSimpleNS()
from the initial commit matched the scores from my JS version, but I couldn't get it to consistently match the new values, even when I added the/2
toscore -= (matchedRange.location-strRange.location)/2;
(which wasn't in the original code above). Some of the tests produced the same scores, but others didn't. As far as I could tell by inspection, they all should've been the same, so it was very confusing.I finally just gave in and ran the ObjC code in Xcode to see what was going on. After stepping through it (and cursing the absurd complexity of simply dumping strings to the console in Cocoa/ObjC), I believe there are three issues:
The line where the
/2
was added to increase the scores for longer strings throws away the fractional part of the result, since it's not dividing by2.0
. I suppose that could be intentional, like callingfloor()
on the result, but it seemed odd sincescore
is a float. Making the divisor a float brought more of the scores from my code into alignment with the test file.The inline buffer is filled with only part of the string, rather than all of it. This throws off the lookup of the current and previous chars, since the lookup is done from
matchedRange.location
, which is in relation to the start of the entire string. ButinlineBuffer
is filled fromstrRange
, which moves to the right with each recursion. So the check of whether the previous char was a word separator or the current char is uppercase is looking at the wrong letters, which then causes the score to be adjusted incorrectly. For instance, an abbreviation matching the initial letters of words will have a lower score than it should, since the previous char in the buffer won't be a space, while it is in the original string. Fixing this in the ObjC code brought the remainder of the scores into alignment.The mask of matching letters can contain more matches than there are letters in the abbreviation, which I mentioned recently in a comment on this old PR (someplace probably no one would see it).
Consider matching
"test"
against the string"t e s t tess!"
. The full abbreviation doesn't appear as a substring in the first loop, so it's reduced to"tes"
and the function recurses. That substring is found near the end of the string, and those indices ([8, 9, 10]
, I think) get set in the mask. Then it looks for the remaining"t"
in the rest of the string, but only finds"s!"
, so it has to start over and shrink the abbreviation again. Eventually, it finds the individual letters separated by spaces and returns a score. But the mask still contains those first hits that it gave up on.My solution was to clear the mask just before the
return 0;
at the end of the function. I believe that's correct, as the 0 score means the algorithm will start over with a shorter abbreviation, and that shorter bit might hit in a different place, rendering the previous hits obsolete. If the UI isn't underlining all those letters, then something else must be cleaning up the mask. (I tried clamping it to the length of the abbreviation, but I don't think that gives the correct results in all cases.)It looks like the inline buffer was added 9 years ago, so maybe it doesn't make sense to fix the behavior now. But it should be noticeable if you have two somewhat similar strings and an abbreviation that matches the start of words or uppercase letters in one, and random letters near the beginning of the other. The first match might score lower than the second when it shouldn't.
Anyway, I'm just happy to solve the mystery of why my code wasn't returning the same scores when it looked like it should. My rough test version of
QSSense.m
that I was using to step through the code and print out debug info is in this gist.The text was updated successfully, but these errors were encountered: