Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--helix-term/src/commands.rs3
-rw-r--r--helix-term/src/keymap.rs28
-rw-r--r--helix-term/src/ui/mod.rs4
-rw-r--r--helix-term/src/ui/picker.rs30
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)
}
}