Skip to content

Commit

Permalink
Picker: Skip dynamic query debounce for pastes (#11211)
Browse files Browse the repository at this point in the history
Pastes are probably the last edit one means to make before the query
should run so it doesn't need to be debounced.
This makes global search much snappier for example when accepting the
history suggestion from the '/' register or pasting a pattern from the
clipboard or a register.
  • Loading branch information
the-mikedavis authored Jul 19, 2024
1 parent 748a9cf commit dbaa636
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 10 deletions.
24 changes: 17 additions & 7 deletions helix-term/src/ui/picker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ use helix_view::{
Document, DocumentId, Editor,
};

use self::handlers::{DynamicQueryHandler, PreviewHighlightHandler};
use self::handlers::{DynamicQueryChange, DynamicQueryHandler, PreviewHighlightHandler};

pub const ID: &str = "picker";

Expand Down Expand Up @@ -272,7 +272,7 @@ pub struct Picker<T: 'static + Send + Sync, D: 'static> {
file_fn: Option<FileCallback<T>>,
/// An event handler for syntax highlighting the currently previewed file.
preview_highlight_handler: Sender<Arc<Path>>,
dynamic_query_handler: Option<Sender<Arc<str>>>,
dynamic_query_handler: Option<Sender<DynamicQueryChange>>,
}

impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
Expand Down Expand Up @@ -435,7 +435,12 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
debounce_ms: Option<u64>,
) -> Self {
let handler = DynamicQueryHandler::new(callback, debounce_ms).spawn();
helix_event::send_blocking(&handler, self.primary_query());
let event = DynamicQueryChange {
query: self.primary_query(),
// Treat the initial query as a paste.
is_paste: true,
};
helix_event::send_blocking(&handler, event);
self.dynamic_query_handler = Some(handler);
self
}
Expand Down Expand Up @@ -511,12 +516,12 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {

fn prompt_handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) {
self.handle_prompt_change();
self.handle_prompt_change(matches!(event, Event::Paste(_)));
}
EventResult::Consumed(None)
}

fn handle_prompt_change(&mut self) {
fn handle_prompt_change(&mut self, is_paste: bool) {
// TODO: better track how the pattern has changed
let line = self.prompt.line();
let old_query = self.query.parse(line);
Expand Down Expand Up @@ -557,7 +562,11 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Picker<T, D> {
// If this is a dynamic picker, notify the query hook that the primary
// query might have been updated.
if let Some(handler) = &self.dynamic_query_handler {
helix_event::send_blocking(handler, self.primary_query());
let event = DynamicQueryChange {
query: self.primary_query(),
is_paste,
};
helix_event::send_blocking(handler, event);
}
}

Expand Down Expand Up @@ -1028,7 +1037,8 @@ impl<I: 'static + Send + Sync, D: 'static + Send + Sync> Component for Picker<I,
.filter(|_| self.prompt.line().is_empty())
{
self.prompt.set_line(completion.to_string(), ctx.editor);
self.handle_prompt_change();
// Inserting from the history register is a paste.
self.handle_prompt_change(true);
} else {
if let Some(option) = self.selection() {
(self.callback_fn)(ctx, option, Action::Replace);
Expand Down
17 changes: 14 additions & 3 deletions helix-term/src/ui/picker/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> AsyncHook
}
}

pub(super) struct DynamicQueryChange {
pub query: Arc<str>,
pub is_paste: bool,
}

pub(super) struct DynamicQueryHandler<T: 'static + Send + Sync, D: 'static + Send + Sync> {
callback: Arc<DynQueryCallback<T, D>>,
// Duration used as a debounce.
Expand All @@ -137,17 +142,23 @@ impl<T: 'static + Send + Sync, D: 'static + Send + Sync> DynamicQueryHandler<T,
}

impl<T: 'static + Send + Sync, D: 'static + Send + Sync> AsyncHook for DynamicQueryHandler<T, D> {
type Event = Arc<str>;
type Event = DynamicQueryChange;

fn handle_event(&mut self, query: Self::Event, _timeout: Option<Instant>) -> Option<Instant> {
fn handle_event(&mut self, change: Self::Event, _timeout: Option<Instant>) -> Option<Instant> {
let DynamicQueryChange { query, is_paste } = change;
if query == self.last_query {
// If the search query reverts to the last one we requested, no need to
// make a new request.
self.query = None;
None
} else {
self.query = Some(query);
Some(Instant::now() + self.debounce)
if is_paste {
self.finish_debounce();
None
} else {
Some(Instant::now() + self.debounce)
}
}
}

Expand Down

0 comments on commit dbaa636

Please sign in to comment.