Skip to content
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

Closed
fwextensions opened this issue Jul 24, 2018 · 7 comments · Fixed by #2452
Closed

Three bugs in the QSSense scoring algorithm #2450

fwextensions opened this issue Jul 24, 2018 · 7 comments · Fixed by #2452
Assignees

Comments

@fwextensions
Copy link

@fwextensions fwextensions commented Jul 24, 2018

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 in testScoreSimpleNS() 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 to score -= (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:

  1. 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 by 2.0. I suppose that could be intentional, like calling floor() on the result, but it seemed odd since score is a float. Making the divisor a float brought more of the scores from my code into alignment with the test file.

  2. 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. But inlineBuffer is filled from strRange, 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.

  3. 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.

@fwextensions
Copy link
Author

@fwextensions fwextensions commented Jul 25, 2018

I also tried to replicate the results of the testLongString() test in TestQSSense.m with my JS version. Part of the reason the indexes set kept growing as strRange.length kept increasing was bug 3 above, but the other reason is that the index set is not cleared or reintialized between each test. That means it ends up with every hit from all the calls to QSScoreForAbbreviationWithRanges(), whereas you'd normally call it with a fresh set each time.

fwextensions added a commit to fwextensions/quick-score that referenced this issue Jul 25, 2018
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.
@skurfer
Copy link
Member

@skurfer skurfer commented Jul 25, 2018

I've converted the Quicksilver scoring algorithm to JavaScript

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).

@skurfer skurfer self-assigned this Jul 25, 2018
@fwextensions
Copy link
Author

@fwextensions fwextensions commented Jul 27, 2018

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 TestQSSense.m test. That's when I realized there were some discrepancies with the official Quicksilver source.

The current, very early version of that library is here (on the dev branch): https://github.com/fwextensions/quick-score/tree/dev

Running npm run debug will execute the debug/debug-quick-score.js file, which runs through some tests similar to the range test in TestQSSense.m and outputs a sort of ASCII visualization of the algorithm as it loops through the abbreviation looking for matches:

=====================================================================
         This excellent string tells us an interesting story
test
tes
te
         (                     **                      )
st
                               **(               **    )
remainingScore: 0.90000
         ------------------------++++++++++++++++++
score: 18
                                 ------------------|||||
score: 10.000 remaining: 0.90000 [42,47)
score: 14.500
0.63043
remainingScore: 0.63043
         ++++++++++++++++++++++++
score: 24
         ----w---------w---------|||||||||||||||||||||||
score: 19.150 remaining: 0.63043 [24,47)
score: 33.650
0.71596

0.7159574468085113
=====================================================================

( ) is the strRange of that call. * shows hits. + indicates the beginning of the current strRange to the end of the match. | indicates what's left in remainingStrRange from the match to the end. w shows word separators and u uppercase letters before the current match. If you look at the output in an IDE, the cursor's column - 10 is the 0-based index of that char in the string.

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?

@skurfer
Copy link
Member

@skurfer skurfer commented Jul 30, 2018

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”.

@fwextensions
Copy link
Author

@fwextensions fwextensions commented Jul 31, 2018

Doesn't the original algorithm already do that? I get 0.92308 for Aftermath.txt and 0.91389 for A Frozen Treat.jpg, compared with 0.68235 for A rofzen reat.jpg, where the matches aren't at the beginning of words.

@skurfer
Copy link
Member

@skurfer skurfer commented Jul 31, 2018

I get 0.92308 for Aftermath.txt and 0.91389 for A Frozen Treat.jpg

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.

skurfer added a commit that referenced this issue Aug 1, 2018
skurfer added a commit that referenced this issue Aug 1, 2018
fwextensions added a commit to fwextensions/quick-score that referenced this issue Sep 15, 2018
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.
fwextensions added a commit to fwextensions/quick-score that referenced this issue Sep 15, 2018
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().
fwextensions added a commit to fwextensions/quick-score that referenced this issue Sep 15, 2018
* 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.
@pjrobertson
Copy link
Member

@pjrobertson pjrobertson commented Feb 1, 2019

This is awesome! Thanks @fwextensions for going through the painful task of debugging, and thanks @skurfer for implementing. The /2.0 vs /2 thing is definitely a bad oversight... I thought I'd chime in to apologise for my awful code writing almost 6 years ago

Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants