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

Better sorting in picker in case of ties #5169

Merged
merged 1 commit into from
Dec 17, 2022

Conversation

matklad
Copy link
Contributor

@matklad matklad commented Dec 15, 2022

Before:
old

After:
new

Wouldn't be opposed to writing a test here, but, given that this doesn't fail any existing test, also happy to let it pass :P

@kirawi kirawi added A-helix-term Area: Helix term improvements S-waiting-on-review Status: Awaiting review from a maintainer. labels Dec 15, 2022
@pascalkuthe
Copy link
Member

pascalkuthe commented Dec 15, 2022

Previous work for context: #4698

Thanks for taking the time to contribute! I like the general idea of sorting file picker entries alphabetically.
However there are many pickers where the initial sorting makes much more sense compared to alphabetical sorting. For example the references in the goto_reference LSP picker items are usually sorted by their occurrence in the file (you know that better then me tough :D) .
Furthermore this particular implementation is not that efficient as filter_text actually allocates twice internally (and would be called for each picker item, on each keypress). The overhead is not huge but it would be nice to avoid nonetheless as this particular action blocks the main UI thread.

I think the same result can be better achieved as follows:
Change the Ord implementation of PickerItem to also consider index in case of a tie (right now it compares by fuzzy score and then by char length). Maybe a stable sort also works but I am not sure about that since a previous query could have changed the sorting due to a difference in fuzzy score. It might be alright depending on the particulars of how the fuzzy score works but I would rather not rely on that (and I believe an unstable sort by index is actually faster anyway).

When creating the picker we could then sort all items before passing them to Picker::new.
This has the advantage of allowing a per-picker tie breaker that is simply based on how the original picker items are sorted.

Furthermore this keeps the picker order a bit more consistent.
When a picker has not query it's items are simply displayed in original order.
When you then filter that picker with a query items would not be shuffled around randomly if they have the same matching score and instead keep their original order.

@matklad
Copy link
Contributor Author

matklad commented Dec 15, 2022

makes makes sense, ptal!

@matklad matklad force-pushed the sort-tie branch 2 times, most recently from b397f18 to 9267e3d Compare December 15, 2022 18:05
len: usize,
}

impl PickerMatch {
fn key(&self) -> impl Ord {
(cmp::Reverse(self.score), self.index, self.len)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, this also fixes the Eq impl -- previously the derived impl would disagree with the Ord

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose that was a small oversight in the previous implementation. We probably should have returned None in case of equality to be consistent with PartialEq so this is definitly more consistent 👍

len: usize,
}

impl PickerMatch {
fn key(&self) -> impl Ord {
(cmp::Reverse(self.score), self.index, self.len)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(cmp::Reverse(self.score), self.index, self.len)
(cmp::Reverse(self.score), self.len, self.index)

I believe len and index must be swapped here.
Otherwise the length tiebreaker would have no effect (as the index is always unique)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 do we need a len at all then? as in, I'd expect

aaaaac
bc

order perhaps?

Or maybe not -- I can see the appeal of starting with shorter entitiees... Let's go for a minimal diff for the start

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed (good thing that rust-analyzer has "flip comma" code action, hehe)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorting by length is useful because it usually shows you what you want first.
For example foo/bar matches foo/bar.rs before foo/bar/baz.rs this way.
See #4698 and #4688 for context. Fzf shares the same behavior by default and I tested this a bit and it's quite useful in practice.

Note that we only apply this sorting once something is typed into the query.
I think the following example illustrates the intended behaviour.

Consider the initial list of files (sorted alphabetically):

aaaaac
ac
bc

Once you type ac the following is shown:

ac
aaaaac

@matklad
Copy link
Contributor Author

matklad commented Dec 15, 2022

Should be good now! I briefly entertained thee idea of adding Picker::new where T: Ord and Picker::new_presorted, but think it's better to say that all call-sites are responsible for comming up with a reasonable order.

Copy link
Member

@pascalkuthe pascalkuthe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, I think this is pretty uncontroversial because it only acts as a tiebreaker (for what was previously an unstable sort).

Copy link
Member

@the-mikedavis the-mikedavis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

TIL about "flip comma", that's handy 😄

@the-mikedavis the-mikedavis changed the title Bette sorting in picker in case of ties Better sorting in picker in case of ties Dec 17, 2022
@the-mikedavis the-mikedavis merged commit e6a2df8 into helix-editor:master Dec 17, 2022
gibbz00 pushed a commit to gibbz00/helix that referenced this pull request Dec 17, 2022
@matklad matklad deleted the sort-tie branch December 20, 2022 17:23
@matklad
Copy link
Contributor Author

matklad commented Dec 20, 2022

TIL about "flip comma", that's handy smile

users not knowing what assists are available is a hard problem. I myself only remeber things I've added personally :D Some thoughts on how to address that: #2011 (comment)

hadronized pushed a commit to hadronized/helix that referenced this pull request Jan 4, 2023
freqmod pushed a commit to freqmod/helix that referenced this pull request Feb 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-helix-term Area: Helix term improvements S-waiting-on-review Status: Awaiting review from a maintainer.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants