Unnamed repository; edit this file 'description' to name the repository.
| -rw-r--r-- | helix-term/src/commands.rs | 3 | ||||
| -rw-r--r-- | helix-term/src/keymap.rs | 28 | ||||
| -rw-r--r-- | helix-term/src/ui/mod.rs | 4 | ||||
| -rw-r--r-- | helix-term/src/ui/picker.rs | 30 |
4 files changed, 55 insertions, 10 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index f77e27a2..f829e4d5 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2591,7 +2591,8 @@ fn buffer_picker(cx: &mut Context) { .primary() .cursor_line(doc.text().slice(..)); Some((meta.id.into(), Some((line, line)))) - }); + }) + .with_id("buffer-picker"); cx.push_layer(Box::new(overlaid(picker))); } diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 55c107f3..aae78933 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -279,6 +279,34 @@ pub enum Domain { Component(&'static str), } +const REMAPPABLE_COMPONENTS: [&'static str; 3] = [ + crate::ui::DYNAMIC_PICKER_ID, + crate::ui::PICKER_ID, + // TODO: make it a constant + "buffer-picker", +]; + +impl<'de> Deserialize<'de> for Domain { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + if let Ok(mode) = s.parse::<Mode>() { + return Ok(Domain::Mode(mode)); + } else if let Some(name) = REMAPPABLE_COMPONENTS + .iter() + .find(|name| **name == s.as_str()) + { + Ok(Domain::Component(name)) + } else { + Err(serde::de::Error::custom(format!( + "Unknown keymap domain {s}. Expected a mode or component name" + ))) + } + } +} + pub struct Keymaps { pub map: Box<dyn DynAccess<HashMap<Domain, KeyTrie>>>, /// Stores pending keys waiting for the next key. This is relative to a diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 155f2435..150c2975 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -7,7 +7,7 @@ pub mod lsp; mod markdown; pub mod menu; pub mod overlay; -mod picker; +pub mod picker; pub mod popup; mod prompt; mod spinner; @@ -21,7 +21,7 @@ pub use completion::{Completion, CompletionItem}; pub use editor::EditorView; pub use markdown::Markdown; pub use menu::Menu; -pub use picker::{DynamicPicker, FileLocation, Picker}; +pub use picker::{DynamicPicker, FileLocation, Picker, DYNAMIC_PICKER_ID, PICKER_ID}; pub use popup::Popup; pub use prompt::{Prompt, PromptEvent}; pub use spinner::{ProgressSpinners, Spinner}; diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 13746cfc..192a03a6 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -114,6 +114,8 @@ impl Preview<'_, '_> { } } +pub const PICKER_ID: &'static str = "picker"; + pub struct Picker<T: Item> { options: Vec<T>, editor_data: T::Data, @@ -141,6 +143,9 @@ pub struct Picker<T: Item> { read_buffer: Vec<u8>, /// Given an item in the picker, return the file path and line number to display. file_fn: Option<FileCallback<T>>, + + /// A unique identifier for the picker as a Component + id: &'static str, } impl<T: Item + 'static> Picker<T> { @@ -172,6 +177,7 @@ impl<T: Item + 'static> Picker<T> { preview_cache: HashMap::new(), read_buffer: Vec::with_capacity(1024), file_fn: None, + id: PICKER_ID, }; picker.calculate_column_widths(); @@ -205,6 +211,11 @@ impl<T: Item + 'static> Picker<T> { self } + pub fn with_id(mut self, id: &'static str) -> Self { + self.id = id; + self + } + pub fn set_options(&mut self, new_options: Vec<T>) { self.options = new_options; self.cursor = 0; @@ -871,6 +882,10 @@ impl<T: Item + 'static> Component for Picker<T> { self.completion_height = height.saturating_sub(4); Some((width, height)) } + + fn id(&self) -> Option<&'static str> { + Some(self.id) + } } #[derive(PartialEq, Eq, Debug)] @@ -905,6 +920,8 @@ type PickerCallback<T> = Box<dyn Fn(&mut Context, &T, Action)>; pub type DynQueryCallback<T> = Box<dyn Fn(String, &mut Editor) -> BoxFuture<'static, anyhow::Result<Vec<T>>>>; +pub const DYNAMIC_PICKER_ID: &'static str = "dynamic-picker"; + /// A picker that updates its contents via a callback whenever the /// query string changes. Useful for live grep, workspace symbols, etc. pub struct DynamicPicker<T: ui::menu::Item + Send> { @@ -914,8 +931,6 @@ pub struct DynamicPicker<T: ui::menu::Item + Send> { } impl<T: ui::menu::Item + Send> DynamicPicker<T> { - pub const ID: &'static str = "dynamic-picker"; - pub fn new(file_picker: Picker<T>, query_callback: DynQueryCallback<T>) -> Self { Self { file_picker, @@ -947,10 +962,11 @@ impl<T: Item + Send + 'static> Component for DynamicPicker<T> { let callback = Callback::EditorCompositor(Box::new(move |editor, compositor| { // Wrapping of pickers in overlay is done outside the picker code, // so this is fragile and will break if wrapped in some other widget. - let picker = match compositor.find_id::<Overlay<DynamicPicker<T>>>(Self::ID) { - Some(overlay) => &mut overlay.content.file_picker, - None => return, - }; + let picker = + match compositor.find_id::<Overlay<DynamicPicker<T>>>(DYNAMIC_PICKER_ID) { + Some(overlay) => &mut overlay.content.file_picker, + None => return, + }; picker.set_options(new_options); editor.reset_idle_timer(); })); @@ -968,6 +984,6 @@ impl<T: Item + Send + 'static> Component for DynamicPicker<T> { } fn id(&self) -> Option<&'static str> { - Some(Self::ID) + Some(DYNAMIC_PICKER_ID) } } |