Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/ui/picker/handlers.rs')
| -rw-r--r-- | helix-term/src/ui/picker/handlers.rs | 190 |
1 files changed, 0 insertions, 190 deletions
diff --git a/helix-term/src/ui/picker/handlers.rs b/helix-term/src/ui/picker/handlers.rs deleted file mode 100644 index eabfac0c..00000000 --- a/helix-term/src/ui/picker/handlers.rs +++ /dev/null @@ -1,190 +0,0 @@ -use std::{ - path::Path, - sync::{atomic, Arc}, - time::Duration, -}; - -use helix_event::AsyncHook; -use tokio::time::Instant; - -use crate::{job, ui::overlay::Overlay}; - -use super::{CachedPreview, DynQueryCallback, Picker}; - -pub(super) struct PreviewHighlightHandler<T: 'static + Send + Sync, D: 'static + Send + Sync> { - trigger: Option<Arc<Path>>, - phantom_data: std::marker::PhantomData<(T, D)>, -} - -impl<T: 'static + Send + Sync, D: 'static + Send + Sync> Default for PreviewHighlightHandler<T, D> { - fn default() -> Self { - Self { - trigger: None, - phantom_data: Default::default(), - } - } -} - -impl<T: 'static + Send + Sync, D: 'static + Send + Sync> AsyncHook - for PreviewHighlightHandler<T, D> -{ - type Event = Arc<Path>; - - fn handle_event( - &mut self, - path: Self::Event, - timeout: Option<tokio::time::Instant>, - ) -> Option<tokio::time::Instant> { - if self - .trigger - .as_ref() - .is_some_and(|trigger| trigger == &path) - { - // If the path hasn't changed, don't reset the debounce - timeout - } else { - self.trigger = Some(path); - Some(Instant::now() + Duration::from_millis(150)) - } - } - - fn finish_debounce(&mut self) { - let Some(path) = self.trigger.take() else { - return; - }; - - job::dispatch_blocking(move |editor, compositor| { - let Some(Overlay { - content: picker, .. - }) = compositor.find::<Overlay<Picker<T, D>>>() - else { - return; - }; - - let Some(CachedPreview::Document(ref mut doc)) = picker.preview_cache.get_mut(&path) - else { - return; - }; - - if doc.syntax().is_some() { - return; - } - - let Some(language) = doc.language_config().map(|config| config.language()) else { - return; - }; - - let loader = editor.syn_loader.load(); - let text = doc.text().clone(); - - tokio::task::spawn_blocking(move || { - let syntax = match helix_core::Syntax::new(text.slice(..), language, &loader) { - Ok(syntax) => syntax, - Err(err) => { - log::info!("highlighting picker preview failed: {err}"); - return; - } - }; - - job::dispatch_blocking(move |editor, compositor| { - let Some(Overlay { - content: picker, .. - }) = compositor.find::<Overlay<Picker<T, D>>>() - else { - log::info!("picker closed before syntax highlighting finished"); - return; - }; - let Some(CachedPreview::Document(ref mut doc)) = - picker.preview_cache.get_mut(&path) - else { - return; - }; - let diagnostics = helix_view::Editor::doc_diagnostics( - &editor.language_servers, - &editor.diagnostics, - doc, - ); - doc.replace_diagnostics(diagnostics, &[], None); - doc.syntax = Some(syntax); - }); - }); - }); - } -} - -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. - // Defaults to 100ms if not provided via `Picker::with_dynamic_query`. Callers may want to set - // this higher if the dynamic query is expensive - for example global search. - debounce: Duration, - last_query: Arc<str>, - query: Option<Arc<str>>, -} - -impl<T: 'static + Send + Sync, D: 'static + Send + Sync> DynamicQueryHandler<T, D> { - pub(super) fn new(callback: DynQueryCallback<T, D>, duration_ms: Option<u64>) -> Self { - Self { - callback: Arc::new(callback), - debounce: Duration::from_millis(duration_ms.unwrap_or(100)), - last_query: "".into(), - query: None, - } - } -} - -impl<T: 'static + Send + Sync, D: 'static + Send + Sync> AsyncHook for DynamicQueryHandler<T, D> { - type Event = DynamicQueryChange; - - 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); - if is_paste { - self.finish_debounce(); - None - } else { - Some(Instant::now() + self.debounce) - } - } - } - - fn finish_debounce(&mut self) { - let Some(query) = self.query.take() else { - return; - }; - self.last_query = query.clone(); - let callback = self.callback.clone(); - - job::dispatch_blocking(move |editor, compositor| { - let Some(Overlay { - content: picker, .. - }) = compositor.find::<Overlay<Picker<T, D>>>() - else { - return; - }; - // Increment the version number to cancel any ongoing requests. - picker.version.fetch_add(1, atomic::Ordering::Relaxed); - picker.matcher.restart(false); - let injector = picker.injector(); - let get_options = (callback)(&query, editor, picker.editor_data.clone(), &injector); - tokio::spawn(async move { - if let Err(err) = get_options.await { - log::info!("Dynamic request failed: {err}"); - } - // NOTE: the Drop implementation of Injector will request a redraw when the - // injector falls out of scope here, clearing the "running" indicator. - }); - }) - } -} |