-
Notifications
You must be signed in to change notification settings - Fork 154
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
Add match_indices field to Suggestion #798
base: main
Are you sure you want to change the base?
Conversation
I think @maxomatic458 would be the ideal reviewer here, based on recent work in the completion logic and the discussion we had around the pitfalls with this highlighting. |
looks good so far. I think instead of defaulting to this it might be better to just ignore the styling (so basically fully leave it up to the completer). Then we could get rid of this trait function here (since we have the If you are ok with that i can make a PR for that. |
@maxomatic458 Interesting idea, I was going to say highlighting shouldn't be the completer's responsibility, but if it simplifies things, that'd be great. It's definitely worth a PR. @sholderbach Thoughts? |
How would that interact with other highlighting functionality in reedline? Eg. it takes care of highlighting the selected item, if the completer dealt with highlighting individual characters how would that mix?
Do you mean decoupling value from how it's displaying, eg. adding something like Slightly tangential, but there's plenty of places that initialize |
I think this might actually be better solution. So instead of adding more stuff to the Suggestion we just have one that can be modified by a seperate styling layer (that can be attached to a completer). But this might require a good amount of breaking changes, so maybe thats something for a future PR? |
Ah, you're right @qfel, highlighting in the completer wouldn't work.
@maxomatic458 As @qfel pointed out, we'd need two display values, one in case the suggestion is selected and one in case it isn't, right? That feels like too much to me.
@maxomatic458 This sounds interesting, could you explain more? (having a hard time figuring out what a separate styling layer would look like) |
maybe this could be implemented as a trait pub trait SuggestionStyler {
fn style_selected(&self, suggestion: Suggestion, ...) -> String {
...
}
fn style_unselected(&self, suggestion: Suggestion, ...) -> String {
...
}
} then we could pass a SuggestionStyler struct down to the menu and inside the the functions of the so we would move the styling out of the menu and out of the completer and we can implement it using this trait Edit: |
@maxomatic458 Sorry, still not getting it. Do you mean that the |
@ysthakur that would potentially allow for more complex styling, without having to make any changes to the menu so something like this here enum MatchMode {
Fuzzy,
Prefix,
}
struct UnderlineStyler {
color: ...,
mode: MatchMode
}
impl SuggestionStyler for UnderlineStyler {
fn style_selected(&mut self, suggestion: Suggestion, cursor_pos: Option<(u16, u16)>, text_typed_by_user: String, terminal_dims: Option<(u16, u16)>, ...) -> Suggestion {
// Do the styling here
...
return suggestion
}
fn style_unselected(...) -> Suggestion {
...
}
} then we could add this and then call the trait functions in menu when we are generating the strings for the suggestions Thats just an idea how it could be implemented if we want to make the suggestion-styling that extensible. Im also ok with just adding the |
IIUC that would either mean we keep Or it would mean inside the styler we have to recompute the matching indices, which sounds a bit unpleasant. Unless we allow to attach arbitrary data with suggestions - then completers could generate suggestions with completer-specific hints, and provide stylers that consume those hints to producer stylized output. Though that feels like a fairly big change? |
That was my idea (if you are concerned about the recomputing, i believe in both cases we would need to recompute the indices whenever the user types something). We would attach a Styler to a completer, and that would handle the styling of the suggestions. so the Suggestions struct would not have to be modified again, when we want to introduce a new way of styling suggestions. |
@maxomatic458 Oh, I see now. I like the idea of avoiding adding fields to That said, I agree with you that performance might not be a huge problem. Only a few suggestions are displayed on the screen at any point, even if there are hundreds of suggestions in total. If we only recompute the match indices for each suggestion when it shows up on the screen, the user may not see a noticeable delay. I'm not an expert here, though. Anyone have any idea what impact this would have on performance?
@maxomatic458 Did you have any possible future ways of styling suggestions in mind? Do you mean we might want to highlight suggestions in three different colors (rather than the current two)? |
That aside, potentially having to run the same matching code in two different places in the Completer is an unpleasant API. Not a huge issue I suppose, but makes it a little harder to get things going (and might have some weird implications for non-deterministic/pure matching, maybe if somebody decided to do some server-side completions).
I noticed one of the examples above had this - what would it be used for? Hm let me expand a bit on the styler idea that I think could work well (if we really want to go there):
pub struct Suggestion<T> {
/// String replacement that will be introduced to the the buffer
pub value: String,
/// Replacement span in the buffer
pub span: Span,
/// Whether to append a space after selecting this suggestion.
/// This helps to avoid that a completer repeats the complete suggestion.
pub append_whitespace: bool,
/// Completer-associated context, passed back to Styler.
pub context: T,
}
pub struct DisplaySuggestion {
/// The value to display, often the same as coming from Suggestion but will have ANSI escapes.
pub value: String,
/// Optional description for the replacement, can contain ANSI escapes.
pub description: Option<String>,
/// Optional vector of strings in the suggestion. These can be used to
/// represent examples coming from a suggestion. Can contain ANSI escapes.
pub extra: Option<Vec<String>>,
}
// Probably needs more thought about the naming.
pub trait Styler<T> {
fn display(&self, suggestion: Suggestion<T>, is_selected: bool, some_type_of_user_config: _, ...) -> DisplaySuggestion;
// Also batch_display with some default implementation. May be useful for some completers to parallelize.
}
pub trait Completer {
type Context;
fn complete(&mut self, line: &str, pos: usize) -> Vec<Suggestion<Self::Context>>;
// We *probably* want to instead have it be a separate field on ReedlineMenu::WithCompleter,
// so that any compatible Styler can be used. But that's maybe something to think on later.
//
// This could return one of predefined simple static stylers, or something custom for the completer.
fn styler<'a>(&'a self) -> &'a dyn Styler<Self::Context>;
}
Regarding the |
Yea not really sure what that could be used for either, just came into my mind when thinking of things that might be useful to style a suggestion. I think it would also be useful to store the string the completer used to suggest that suggestion in the Suggestion struct. |
# Description Take advantage of the `Default` implementation of `Suggestion`. This in particular should make code compatible forward-compatible with nushell/reedline#798. # User-Facing Changes None # Tests + Formatting Existing coverage.
@maxomatic458 Would this be used by the
That's a good point, @qfel, running matching twice wouldn't be good for non-deterministic/time-consuming suggestions. That said, @maxomatic458's |
@ysthakur that could be also be used outside of styling. Currently we have the helper function "complete_with_base_ranges", that basically does the same thing already, but instead of collecting the actual strings, we just get the ranges in the buffer (i think). So an extra field that holds the "completion base value" could be used to store that instead. Lines 80 to 87 in ad56a23
|
Description
This PR adds a
match_indices: Vec<usize>
field to theSuggestion
struct representing which indices of the Suggestion'svalue
field were matched.Motivation
Currently, in Nushell, if you have fuzzy matching on, the matches are still highlighted with the assumption that you're using prefix matching:
This PR doesn't fix that, but adding
match_indices
toSuggestion
does make it so that completers using fuzzy matching can include the indices of the matches in their returned suggestions (in the future). Here's thefuzzy_completions
example included in this PR, which uses a columnar menu (ignore how bad the fuzzy completion algorithm is, I didn't bother using skim or Nucleo):And modified to use an IDE menu with fake descriptions:
Tests
I only added a test for the
style_suggestion
helper that I added for highlighting the matched portions of a suggestion. Other than that, I just manually tried the menus out a bit. If there are any edge cases anyone can think of to test (either through unit tests or manually), I'd appreciate that.I can remove the
fuzzy_completions
example before merging, it's mostly just there to help reviewers check whether there's anything wrong. In its current form, it's not super useful to actual Reedline users.After submitting
This will break Nushell, so I made nushell/nushell#13111, to be merged after this is merged into Reedline.