diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index f829e4d52..daf961d12 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -50,7 +50,7 @@ use movement::Movement; use crate::{ args, - compositor::{self, Component, Compositor}, + compositor::{self, Component, Compositor, EventResult}, filter_picker_entry, job::Callback, keymap::{Keymaps, ReverseKeymap}, @@ -172,6 +172,11 @@ pub enum MappableCommand { fun: fn(cx: &mut Context), doc: &'static str, }, + Component { + name: &'static str, + fun: fn(&mut dyn crate::compositor::Component, &mut compositor::Context) -> EventResult, + doc: &'static str, + }, } macro_rules! static_commands { @@ -209,6 +214,7 @@ impl MappableCommand { } } Self::Static { fun, .. } => (fun)(cx), + Self::Component { .. } => unimplemented!(), } } @@ -216,6 +222,7 @@ impl MappableCommand { match &self { Self::Typable { name, .. } => name, Self::Static { name, .. } => name, + Self::Component { .. } => unimplemented!(), } } @@ -223,9 +230,18 @@ impl MappableCommand { match &self { Self::Typable { doc, .. } => doc, Self::Static { doc, .. } => doc, + Self::Component { .. } => unimplemented!(), } } + // TODO: macro for this... + #[allow(non_upper_case_globals)] + pub const close_buffer_in_buffer_picker: Self = Self::Component { + name: "close_buffer_in_buffer_picker", + fun: crate::ui::picker::close_buffer_in_buffer_picker, + doc: "Closes the currently focused buffer", + }; + #[rustfmt::skip] static_commands!( no_op, "Do nothing", @@ -503,6 +519,7 @@ impl fmt::Debug for MappableCommand { .field(name) .field(args) .finish(), + Self::Component { .. } => unimplemented!(), } } } @@ -2526,17 +2543,19 @@ fn file_picker_in_current_directory(cx: &mut Context) { cx.push_layer(Box::new(overlaid(picker))); } +pub struct BufferMeta { + pub id: DocumentId, + path: Option, + is_modified: bool, + is_current: bool, + focused_at: std::time::Instant, +} + +pub type BufferPicker = Picker; + fn buffer_picker(cx: &mut Context) { let current = view!(cx.editor).doc; - struct BufferMeta { - id: DocumentId, - path: Option, - is_modified: bool, - is_current: bool, - focused_at: std::time::Instant, - } - impl ui::menu::Item for BufferMeta { type Data = (); @@ -2710,6 +2729,7 @@ impl ui::menu::Item for MappableCommand { Some(bindings) => format!("{} ({}) [{}]", doc, fmt_binding(bindings), name).into(), None => format!("{} [{}]", doc, name).into(), }, + MappableCommand::Component { .. } => unimplemented!(), } } } diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index fd92c3615..7bd5146fe 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -379,9 +379,15 @@ pub fn default() -> HashMap { "home" => goto_line_start, "end" => goto_line_end_newline, }); + + let buffer_picker = keymap!({ "Buffer picker" + "C-x" => close_buffer_in_buffer_picker, + }); + hashmap!( Domain::Mode(Mode::Normal) => normal, Domain::Mode(Mode::Select) => select, Domain::Mode(Mode::Insert) => insert, + Domain::Component("buffer-picker") => buffer_picker, ) } diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 192a03a65..dea2e905c 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -802,6 +802,18 @@ impl Component for Picker { _ => return EventResult::Ignored(None), }; + match ctx.keymaps.get_by_component_id(self.id, key_event) { + crate::keymap::KeymapResult::Matched(crate::keymap::MappableCommand::Component { + fun, + .. + }) => { + if let EventResult::Consumed(callback) = fun(self, ctx) { + return EventResult::Consumed(callback); + } + } + _ => (), + } + let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _ctx| { // remove the layer @@ -987,3 +999,39 @@ impl Component for DynamicPicker { Some(DYNAMIC_PICKER_ID) } } + +pub fn close_buffer_in_buffer_picker( + component: &mut dyn Component, + cx: &mut compositor::Context, +) -> EventResult { + let Some(picker) = component + .as_any_mut() + .downcast_mut::() + else { + return EventResult::Ignored(None); + }; + let Some(id) = picker.selection().map(|meta| meta.id) else { + return EventResult::Ignored(None); + }; + match cx.editor.close_document(id, false) { + Ok(_) => { + picker.options.retain(|item| item.id != id); + if picker.options.is_empty() { + return close_fn(); + } + picker.cursor = picker.cursor.saturating_sub(1); + picker.force_score(); + } + // TODO: impl From for anyhow::Error + Err(_err) => cx.editor.set_error("Failed to close buffer"), + } + + EventResult::Consumed(None) +} + +fn close_fn() -> EventResult { + EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _ctx| { + // remove the layer + compositor.last_picker = compositor.pop(); + }))) +}