From 06c5cd1e6bb7058ff1936b2502da9b30cfbe2566 Mon Sep 17 00:00:00 2001 From: Eric Correia Date: Sat, 29 Oct 2022 17:45:07 -0400 Subject: [PATCH 1/9] working implementation of closing a buffer from file picker --- helix-term/src/commands.rs | 65 +++++++++++++++++++++++++++------- helix-term/src/commands/dap.rs | 3 ++ helix-term/src/commands/lsp.rs | 3 ++ helix-term/src/ui/mod.rs | 3 +- helix-term/src/ui/picker.rs | 31 +++++++++++++++- helix-view/src/editor.rs | 4 +++ 6 files changed, 94 insertions(+), 15 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 69870a279f3c..98ede89f4b13 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2,6 +2,7 @@ pub(crate) mod dap; pub(crate) mod lsp; pub(crate) mod typed; +use crate::ctrl; pub use dap::*; pub use lsp::*; use tui::text::Spans; @@ -49,7 +50,7 @@ use crate::{ compositor::{self, Component, Compositor}, job::Callback, keymap::ReverseKeymap, - ui::{self, overlay::overlayed, FilePicker, Picker, Popup, Prompt, PromptEvent}, + ui::{self, overlay::overlayed, FilePicker, Picker, PickerAction, Popup, Prompt, PromptEvent}, }; use crate::job::{self, Jobs}; @@ -1962,6 +1963,7 @@ fn global_search(cx: &mut Context) { |_editor, FileResult { path, line_num }| { Some((path.clone(), Some((*line_num, *line_num)))) }, + |_, _, _| None, ); compositor.push(Box::new(overlayed(picker))); }, @@ -2301,7 +2303,7 @@ fn buffer_picker(cx: &mut Context) { } } - let new_meta = |doc: &Document| BufferMeta { + let new_meta = move |doc: &Document| BufferMeta { id: doc.id(), path: doc.path().cloned(), is_modified: doc.is_modified(), @@ -2327,6 +2329,37 @@ fn buffer_picker(cx: &mut Context) { .cursor_line(doc.text().slice(..)); Some((meta.path.clone()?, Some((line, line)))) }, + move |cx, meta, key_event| match key_event { + ctrl!('x') => { + if cx.editor.close_document(meta.id, false).is_err() { + cx.editor.set_error("Cannot close buffer"); + None + } else { + let updated_options = cx + .editor + .documents + .iter() + .map(|(_, doc)| new_meta(doc)) + .collect(); + Some(PickerAction::UpdateOptions(updated_options)) + } + } + ctrl!('X') => { + if cx.editor.close_document(meta.id, true).is_err() { + cx.editor.set_error("Cannot force close buffer"); + None + } else { + let updated_options = cx + .editor + .documents + .iter() + .map(|(_, doc)| new_meta(doc)) + .collect(); + Some(PickerAction::UpdateOptions(updated_options)) + } + } + _ => None, + }, ); cx.push_layer(Box::new(overlayed(picker))); } @@ -2408,6 +2441,7 @@ fn jumplist_picker(cx: &mut Context) { let line = meta.selection.primary().cursor_line(doc.text().slice(..)); Some((meta.path.clone()?, Some((line, line)))) }, + |_, _, _| None, ); cx.push_layer(Box::new(overlayed(picker))); } @@ -2459,17 +2493,22 @@ pub fn command_palette(cx: &mut Context) { } })); - let picker = Picker::new(commands, keymap, move |cx, command, _action| { - let mut ctx = Context { - register: None, - count: std::num::NonZeroUsize::new(1), - editor: cx.editor, - callback: None, - on_next_key_callback: None, - jobs: cx.jobs, - }; - command.execute(&mut ctx); - }); + let picker = Picker::new( + commands, + keymap, + move |cx, command, _action| { + let mut ctx = Context { + register: None, + count: std::num::NonZeroUsize::new(1), + editor: cx.editor, + callback: None, + on_next_key_callback: None, + jobs: cx.jobs, + }; + command.execute(&mut ctx); + }, + |_, _, _| None, + ); compositor.push(Box::new(overlayed(picker))); }, )); diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs index c27417e397b1..626ba867d517 100644 --- a/helix-term/src/commands/dap.rs +++ b/helix-term/src/commands/dap.rs @@ -87,6 +87,7 @@ fn thread_picker( )); Some((path, pos)) }, + |_, _, _| None, ); compositor.push(Box::new(picker)); }, @@ -286,6 +287,7 @@ pub fn dap_launch(cx: &mut Context) { }); cx.jobs.callback(callback); }, + |_, _, _| None, )))); } @@ -714,6 +716,7 @@ pub fn dap_switch_stack_frame(cx: &mut Context) { ) }) }, + |_, _, _| None, ); cx.push_layer(Box::new(picker)) } diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index 3c72cd2a5442..52c424d3964a 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -241,6 +241,7 @@ fn sym_picker( } }, move |_editor, symbol| Some(location_to_file_location(&symbol.location)), + |_, _, _| None, ) .truncate_start(false) } @@ -304,6 +305,7 @@ fn diag_picker( let location = lsp::Location::new(url.clone(), diag.range); Some(location_to_file_location(&location)) }, + |_, _, _| None, ) .truncate_start(false) } @@ -817,6 +819,7 @@ fn goto_impl( jump_to_location(cx.editor, location, offset_encoding, action) }, move |_editor, location| Some(location_to_file_location(location)), + |_, _, _| None, ); compositor.push(Box::new(overlayed(picker))); } diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index f99dea0b8dc3..90191f93b5f7 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -19,7 +19,7 @@ pub use completion::Completion; pub use editor::EditorView; pub use markdown::Markdown; pub use menu::Menu; -pub use picker::{FileLocation, FilePicker, Picker}; +pub use picker::{FileLocation, FilePicker, Picker, PickerAction}; pub use popup::Popup; pub use prompt::{Prompt, PromptEvent}; pub use spinner::{ProgressSpinners, Spinner}; @@ -231,6 +231,7 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> FilePi } }, |_editor, path| Some((path.clone(), None)), + |_, _, _| None, ) } diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 2505f21972e4..58d0c8455e38 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -24,6 +24,7 @@ use helix_core::{movement::Direction, Position}; use helix_view::{ editor::Action, graphics::{CursorKind, Margin, Modifier, Rect}, + input::KeyEvent, Document, Editor, }; @@ -89,9 +90,10 @@ impl FilePicker { editor_data: T::Data, callback_fn: impl Fn(&mut Context, &T, Action) + 'static, preview_fn: impl Fn(&Editor, &T) -> Option + 'static, + key_event_callback_fn: impl Fn(&mut Context, &T, &KeyEvent) -> Option> + 'static, ) -> Self { let truncate_start = true; - let mut picker = Picker::new(options, editor_data, callback_fn); + let mut picker = Picker::new(options, editor_data, callback_fn, key_event_callback_fn); picker.truncate_start = truncate_start; Self { @@ -309,6 +311,10 @@ impl Component for FilePicker { } } +pub enum PickerAction { + UpdateOptions(Vec), +} + pub struct Picker { options: Vec, editor_data: T::Data, @@ -330,6 +336,7 @@ pub struct Picker { show_preview: bool, callback_fn: Box, + key_event_callback_fn: Box Option>>, } impl Picker { @@ -337,6 +344,7 @@ impl Picker { options: Vec, editor_data: T::Data, callback_fn: impl Fn(&mut Context, &T, Action) + 'static, + key_event_callback_fn: impl Fn(&mut Context, &T, &KeyEvent) -> Option> + 'static, ) -> Self { let prompt = Prompt::new( "".into(), @@ -357,6 +365,7 @@ impl Picker { show_preview: true, callback_fn: Box::new(callback_fn), completion_height: 0, + key_event_callback_fn: Box::new(key_event_callback_fn), }; // scoring on empty input: @@ -485,6 +494,17 @@ impl Picker { self.show_preview = !self.show_preview; } + pub fn set_options(&mut self, options: Vec) { + self.options = options; + self.matches.clear(); + self.matches.extend( + self.options + .iter() + .enumerate() + .map(|(index, _option)| (index, 0)), + ); + } + fn prompt_handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult { if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) { // TODO: recalculate only if pattern changed @@ -569,6 +589,15 @@ impl Component for Picker { } } + // handle any external key_events + match self + .selection() + .and_then(|option| (self.key_event_callback_fn)(cx, option, &key_event)) + { + Some(PickerAction::UpdateOptions(options)) => self.set_options(options), + None => {} + } + EventResult::Consumed(None) } diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index af69ceeae19a..551f74695a53 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -734,6 +734,8 @@ pub enum Action { Replace, HorizontalSplit, VerticalSplit, + BufferClose, + BufferCloseForce, } /// Error thrown on failed document closed @@ -1047,6 +1049,8 @@ impl Editor { let doc = doc_mut!(self, &id); doc.ensure_view_init(view_id); } + Action::BufferClose => {} + Action::BufferCloseForce => {} } self._refresh(); From 00eaa22c9fb2e546f6bb62ea3bacd1e40aa54b47 Mon Sep 17 00:00:00 2001 From: Eric Correia Date: Sat, 29 Oct 2022 17:57:40 -0400 Subject: [PATCH 2/9] reset cursor position on deletion --- helix-term/src/ui/picker.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 58d0c8455e38..bc58a15014fe 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -503,6 +503,8 @@ impl Picker { .enumerate() .map(|(index, _option)| (index, 0)), ); + self.score(); + self.move_by(1, Direction::Backward); } fn prompt_handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult { @@ -585,19 +587,19 @@ impl Component for Picker { self.toggle_preview(); } _ => { - self.prompt_handle_event(event, cx); + // handle any external key_events + match self + .selection() + .and_then(|option| (self.key_event_callback_fn)(cx, option, &key_event)) + { + Some(PickerAction::UpdateOptions(options)) => self.set_options(options), + None => { + self.prompt_handle_event(event, cx); + } + } } } - // handle any external key_events - match self - .selection() - .and_then(|option| (self.key_event_callback_fn)(cx, option, &key_event)) - { - Some(PickerAction::UpdateOptions(options)) => self.set_options(options), - None => {} - } - EventResult::Consumed(None) } From e21ed37c91260236ed32a74d7e44d1d96d3affa6 Mon Sep 17 00:00:00 2001 From: Eric Correia Date: Sat, 29 Oct 2022 18:01:14 -0400 Subject: [PATCH 3/9] remove unused variants --- helix-view/src/editor.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index 551f74695a53..af69ceeae19a 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -734,8 +734,6 @@ pub enum Action { Replace, HorizontalSplit, VerticalSplit, - BufferClose, - BufferCloseForce, } /// Error thrown on failed document closed @@ -1049,8 +1047,6 @@ impl Editor { let doc = doc_mut!(self, &id); doc.ensure_view_init(view_id); } - Action::BufferClose => {} - Action::BufferCloseForce => {} } self._refresh(); From 462cb4be9e09df625593a274ae7a291d66d9d4d8 Mon Sep 17 00:00:00 2001 From: Eric Correia Date: Sun, 30 Oct 2022 13:32:53 -0400 Subject: [PATCH 4/9] fix clippy warning --- helix-term/src/ui/picker.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index bc58a15014fe..e6a2e14c9e27 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -315,6 +315,8 @@ pub enum PickerAction { UpdateOptions(Vec), } +type KeyEventCallback = Box Option>>; + pub struct Picker { options: Vec, editor_data: T::Data, @@ -336,7 +338,7 @@ pub struct Picker { show_preview: bool, callback_fn: Box, - key_event_callback_fn: Box Option>>, + key_event_callback_fn: KeyEventCallback, } impl Picker { From 04b4a600258c9b493c2654505f04d970e0734267 Mon Sep 17 00:00:00 2001 From: Eric Correia Date: Thu, 3 Nov 2022 08:46:19 -0400 Subject: [PATCH 5/9] make key event callback optional on picker --- helix-term/src/ui/picker.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index e6a2e14c9e27..dcf63fc317dc 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -338,7 +338,7 @@ pub struct Picker { show_preview: bool, callback_fn: Box, - key_event_callback_fn: KeyEventCallback, + key_event_callback: Option>, } impl Picker { @@ -367,7 +367,7 @@ impl Picker { show_preview: true, callback_fn: Box::new(callback_fn), completion_height: 0, - key_event_callback_fn: Box::new(key_event_callback_fn), + key_event_callback: Some(Box::new(key_event_callback_fn)), }; // scoring on empty input: @@ -590,10 +590,11 @@ impl Component for Picker { } _ => { // handle any external key_events - match self - .selection() - .and_then(|option| (self.key_event_callback_fn)(cx, option, &key_event)) - { + match self.selection().and_then(|option| { + self.key_event_callback + .as_ref() + .and_then(|callback| callback(cx, option, &key_event)) + }) { Some(PickerAction::UpdateOptions(options)) => self.set_options(options), None => { self.prompt_handle_event(event, cx); From 7e3223ee09d10fa08ebe4b5f23830a3a37577a40 Mon Sep 17 00:00:00 2001 From: Eric Correia Date: Sat, 4 Feb 2023 23:27:38 -0500 Subject: [PATCH 6/9] fix settign new options --- helix-term/src/commands/typed.rs | 11 ++++++++--- helix-term/src/ui/picker.rs | 15 +++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 1fd11b65de5e..b2b31eaa0213 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1287,9 +1287,14 @@ fn lsp_workspace_command( let callback = async move { let call: job::Callback = Callback::EditorCompositor(Box::new( move |_editor: &mut Editor, compositor: &mut Compositor| { - let picker = ui::Picker::new(commands, (), |cx, command, _action| { - execute_lsp_command(cx.editor, command.clone()); - }); + let picker = ui::Picker::new( + commands, + (), + |cx, command, _action| { + execute_lsp_command(cx.editor, command.clone()); + }, + |_, _, _| None, + ); compositor.push(Box::new(overlayed(picker))) }, )); diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 5156b5cd8d48..0e201beccb90 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -623,12 +623,15 @@ impl Picker { pub fn set_options(&mut self, options: Vec) { self.options = options; self.matches.clear(); - self.matches.extend( - self.options - .iter() - .enumerate() - .map(|(index, _option)| (index, 0)), - ); + self.matches + .extend(self.options.iter().enumerate().map(|(index, option)| { + let text = option.filter_text(&self.editor_data); + PickerMatch { + index, + score: 0, + len: text.chars().count(), + } + })); self.score(); self.move_by(1, Direction::Backward); } From e5a50ad1eacc9e38f0b8f8bda4ff18bf51ef99d6 Mon Sep 17 00:00:00 2001 From: Eric Correia Date: Sat, 3 Jun 2023 21:10:43 -0400 Subject: [PATCH 7/9] implementation without callback --- helix-term/src/commands.rs | 83 ++++++++---------------------- helix-term/src/commands/dap.rs | 3 -- helix-term/src/commands/lsp.rs | 3 -- helix-term/src/commands/typed.rs | 11 ++-- helix-term/src/ui/mod.rs | 3 +- helix-term/src/ui/picker.rs | 88 ++++++++++++++++++-------------- 6 files changed, 75 insertions(+), 116 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 7e26bd9dc3f8..26b4d6ac4038 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2,7 +2,6 @@ pub(crate) mod dap; pub(crate) mod lsp; pub(crate) mod typed; -use crate::ctrl; pub use dap::*; use helix_vcs::Hunk; pub use lsp::*; @@ -57,7 +56,7 @@ use crate::{ keymap::ReverseKeymap, ui::{ self, editor::InsertEvent, lsp::SignatureHelp, overlay::overlaid, CompletionItem, - FilePicker, Picker, PickerAction, Popup, Prompt, PromptEvent, + FilePicker, Picker, Popup, Prompt, PromptEvent, }, }; @@ -2177,7 +2176,6 @@ fn global_search(cx: &mut Context) { |_editor, FileResult { path, line_num }| { Some((path.clone().into(), Some((*line_num, *line_num)))) }, - |_, _, _| None, ); compositor.push(Box::new(overlaid(picker))); }, @@ -2584,37 +2582,6 @@ fn buffer_picker(cx: &mut Context) { .cursor_line(doc.text().slice(..)); Some((meta.id.into(), Some((line, line)))) }, - move |cx, meta, key_event| match key_event { - ctrl!('x') => { - if cx.editor.close_document(meta.id, false).is_err() { - cx.editor.set_error("Cannot close buffer"); - None - } else { - let updated_options = cx - .editor - .documents - .iter() - .map(|(_, doc)| new_meta(doc)) - .collect(); - Some(PickerAction::UpdateOptions(updated_options)) - } - } - ctrl!('X') => { - if cx.editor.close_document(meta.id, true).is_err() { - cx.editor.set_error("Cannot force close buffer"); - None - } else { - let updated_options = cx - .editor - .documents - .iter() - .map(|(_, doc)| new_meta(doc)) - .collect(); - Some(PickerAction::UpdateOptions(updated_options)) - } - } - _ => None, - }, ); cx.push_layer(Box::new(overlaid(picker))); } @@ -2704,7 +2671,6 @@ fn jumplist_picker(cx: &mut Context) { let line = meta.selection.primary().cursor_line(doc.text().slice(..)); Some((meta.path.clone()?.into(), Some((line, line)))) }, - |_, _, _| None, ); cx.push_layer(Box::new(overlaid(picker))); } @@ -2754,37 +2720,32 @@ pub fn command_palette(cx: &mut Context) { } })); - let picker = Picker::new( - commands, - keymap, - move |cx, command, _action| { - let mut ctx = Context { - register: None, - count: std::num::NonZeroUsize::new(1), - editor: cx.editor, - callback: None, - on_next_key_callback: None, - jobs: cx.jobs, - }; - let focus = view!(ctx.editor).id; + let picker = Picker::new(commands, keymap, move |cx, command, _action| { + let mut ctx = Context { + register: None, + count: std::num::NonZeroUsize::new(1), + editor: cx.editor, + callback: None, + on_next_key_callback: None, + jobs: cx.jobs, + }; + let focus = view!(ctx.editor).id; - command.execute(&mut ctx); + command.execute(&mut ctx); - if ctx.editor.tree.contains(focus) { - let config = ctx.editor.config(); - let mode = ctx.editor.mode(); - let view = view_mut!(ctx.editor, focus); - let doc = doc_mut!(ctx.editor, &view.doc); + if ctx.editor.tree.contains(focus) { + let config = ctx.editor.config(); + let mode = ctx.editor.mode(); + let view = view_mut!(ctx.editor, focus); + let doc = doc_mut!(ctx.editor, &view.doc); - view.ensure_cursor_in_view(doc, config.scrolloff); + view.ensure_cursor_in_view(doc, config.scrolloff); - if mode != Mode::Insert { - doc.append_changes_to_history(view); - } + if mode != Mode::Insert { + doc.append_changes_to_history(view); } - }, - |_, _, _| None, - ); + } + }); compositor.push(Box::new(overlaid(picker))); }, )); diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs index bf5299e1171a..84794bedfce9 100644 --- a/helix-term/src/commands/dap.rs +++ b/helix-term/src/commands/dap.rs @@ -87,7 +87,6 @@ fn thread_picker( )); Some((path.into(), pos)) }, - |_, _, _| None, ); compositor.push(Box::new(picker)); }, @@ -287,7 +286,6 @@ pub fn dap_launch(cx: &mut Context) { }); cx.jobs.callback(callback); }, - |_, _, _| None, )))); } @@ -763,7 +761,6 @@ pub fn dap_switch_stack_frame(cx: &mut Context) { ) }) }, - |_, _, _| None, ); cx.push_layer(Box::new(picker)) } diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs index d0b57fe27ff6..3596df45bd7c 100644 --- a/helix-term/src/commands/lsp.rs +++ b/helix-term/src/commands/lsp.rs @@ -277,7 +277,6 @@ fn sym_picker(symbols: Vec, current_path: Option FilePi } }, |_editor, path| Some((path.clone().into(), None)), - |_, _, _| None, ) } diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 31d741e92907..1e5c4678a708 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -33,7 +33,6 @@ use helix_core::{ use helix_view::{ editor::Action, graphics::{CursorKind, Margin, Modifier, Rect}, - input::KeyEvent, theme::Style, view::ViewPosition, Document, DocumentId, Editor, @@ -131,10 +130,9 @@ impl FilePicker { editor_data: T::Data, callback_fn: impl Fn(&mut Context, &T, Action) + 'static, preview_fn: impl Fn(&Editor, &T) -> Option + 'static, - key_event_callback_fn: impl Fn(&mut Context, &T, &KeyEvent) -> Option> + 'static, ) -> Self { let truncate_start = true; - let mut picker = Picker::new(options, editor_data, callback_fn, key_event_callback_fn); + let mut picker = Picker::new(options, editor_data, callback_fn); picker.truncate_start = truncate_start; Self { @@ -395,6 +393,53 @@ impl Component for FilePicker { if let Event::IdleTimeout = event { return self.handle_idle_timeout(ctx); } + + let Some((current_file, _)) = self.current_file(ctx.editor) else { + return EventResult::Consumed(None) + }; + + // Try to find a document in the cache + let doc = match ¤t_file { + PathOrId::Id(doc_id) => doc_mut!(ctx.editor, doc_id), + PathOrId::Path(path) => match self.preview_cache.get_mut(path) { + Some(CachedPreview::Document(ref mut doc)) => doc, + _ => return EventResult::Consumed(None), + }, + }; + let doc_id = doc.id(); + + if let Event::Key(key_event) = *event { + match key_event { + ctrl!('x') => { + if ctx.editor.close_document(doc_id, false).is_err() { + ctx.editor.set_error("Cannot close buffer"); + } else { + ctx.editor.documents.remove(&doc_id); + // .filter(|(d_id, _)| d_id() != doc_id) + // .collect(); + // let updated_documents = ctx.editor.documents; + // let updated_options = + + // self.picker.set_options(updated_documents); + } + } + ctrl!('X') => { + if ctx.editor.close_document(doc_id, true).is_err() { + ctx.editor.set_error("Cannot force close buffer"); + } else { + ctx.editor.documents.remove(&doc_id); + // let updated_options = ctx + // .editor + // .documents + // .iter() + // .filter(|(_, d)| d.id() != doc_id) + // .collect(); + // self.picker.set_options(updated_options); + } + } + _ => {} + } + }; // TODO: keybinds for scrolling preview self.picker.handle_event(event, ctx) } @@ -418,12 +463,6 @@ impl Component for FilePicker { } } -pub enum PickerAction { - UpdateOptions(Vec), -} - -type KeyEventCallback = Box Option>>; - #[derive(PartialEq, Eq, Debug)] struct PickerMatch { score: i64, @@ -472,7 +511,6 @@ pub struct Picker { /// Constraints for tabular formatting widths: Vec, callback_fn: PickerCallback, - key_event_callback: Option>, } impl Picker { @@ -480,7 +518,6 @@ impl Picker { options: Vec, editor_data: T::Data, callback_fn: impl Fn(&mut Context, &T, Action) + 'static, - key_event_callback_fn: impl Fn(&mut Context, &T, &KeyEvent) -> Option> + 'static, ) -> Self { let prompt = Prompt::new( "".into(), @@ -501,7 +538,6 @@ impl Picker { show_preview: true, callback_fn: Box::new(callback_fn), completion_height: 0, - key_event_callback: Some(Box::new(key_event_callback_fn)), widths: Vec::new(), }; @@ -682,22 +718,6 @@ impl Picker { self.show_preview = !self.show_preview; } - pub fn set_options(&mut self, options: Vec) { - self.options = options; - self.matches.clear(); - self.matches - .extend(self.options.iter().enumerate().map(|(index, option)| { - let text = option.filter_text(&self.editor_data); - PickerMatch { - index, - score: 0, - len: text.chars().count(), - } - })); - self.score(); - self.move_by(1, Direction::Backward); - } - fn prompt_handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult { if let EventResult::Consumed(_) = self.prompt.handle_event(event, cx) { // TODO: recalculate only if pattern changed @@ -783,17 +803,7 @@ impl Component for Picker { self.toggle_preview(); } _ => { - // handle any external key_events - match self.selection().and_then(|option| { - self.key_event_callback - .as_ref() - .and_then(|callback| callback(cx, option, &key_event)) - }) { - Some(PickerAction::UpdateOptions(options)) => self.set_options(options), - None => { - self.prompt_handle_event(event, cx); - } - } + self.prompt_handle_event(event, cx); } } From e2b1ca18706433df5758689900190f541fb15e13 Mon Sep 17 00:00:00 2001 From: Eric Correia Date: Sun, 4 Jun 2023 21:19:14 -0400 Subject: [PATCH 8/9] simplify key event callback --- helix-term/src/commands.rs | 38 +++++++++++++++-- helix-term/src/ui/mod.rs | 2 +- helix-term/src/ui/picker.rs | 82 +++++++++++++++---------------------- 3 files changed, 70 insertions(+), 52 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 26b4d6ac4038..3368d636a555 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -51,12 +51,12 @@ use movement::Movement; use crate::{ args, compositor::{self, Component, Compositor}, - filter_picker_entry, + ctrl, filter_picker_entry, job::Callback, keymap::ReverseKeymap, ui::{ self, editor::InsertEvent, lsp::SignatureHelp, overlay::overlaid, CompletionItem, - FilePicker, Picker, Popup, Prompt, PromptEvent, + FilePicker, Picker, PickerAction, Popup, Prompt, PromptEvent, }, }; @@ -2567,7 +2567,7 @@ fn buffer_picker(cx: &mut Context) { // mru items.sort_unstable_by_key(|item| std::cmp::Reverse(item.focused_at)); - let picker = FilePicker::new( + let mut picker = FilePicker::new( items, (), |cx, meta, action| { @@ -2583,6 +2583,38 @@ fn buffer_picker(cx: &mut Context) { Some((meta.id.into(), Some((line, line)))) }, ); + picker.on_key_event(move |cx, meta, key_event| match key_event { + ctrl!('x') => { + if cx.editor.close_document(meta.id, false).is_err() { + cx.editor.set_error("Cannot close buffer"); + None + } else { + let updated_options = cx + .editor + .documents + .iter() + .map(|(_, doc)| new_meta(doc)) + .collect(); + Some(PickerAction::UpdateOptions(updated_options)) + } + } + ctrl!('X') => { + if cx.editor.close_document(meta.id, true).is_err() { + cx.editor.set_error("Cannot force close buffer"); + None + } else { + let updated_options = cx + .editor + .documents + .iter() + .map(|(_, doc)| new_meta(doc)) + .collect(); + Some(PickerAction::UpdateOptions(updated_options)) + } + } + _ => None, + }); + cx.push_layer(Box::new(overlaid(picker))); } diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index ec328ec55cea..e1b4f5a3bec4 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -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, FilePicker, Picker}; +pub use picker::{DynamicPicker, FileLocation, FilePicker, Picker, PickerAction}; 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 1e5c4678a708..04a0a3504504 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -33,6 +33,7 @@ use helix_core::{ use helix_view::{ editor::Action, graphics::{CursorKind, Margin, Modifier, Rect}, + input::KeyEvent, theme::Style, view::ViewPosition, Document, DocumentId, Editor, @@ -74,6 +75,12 @@ impl From for PathOrId { type FileCallback = Box Option>; +pub enum PickerAction { + UpdateOptions(Vec), +} + +type KeyEventCallback = Box Option>>; + /// File path and range of lines (used to align and highlight lines) pub type FileLocation = (PathOrId, Option<(usize, usize)>); @@ -85,6 +92,7 @@ pub struct FilePicker { read_buffer: Vec, /// Given an item in the picker, return the file path and line number to display. file_fn: FileCallback, + key_event_callback: Option>, } pub enum CachedPreview { @@ -141,9 +149,17 @@ impl FilePicker { preview_cache: HashMap::new(), read_buffer: Vec::with_capacity(1024), file_fn: Box::new(preview_fn), + key_event_callback: None, } } + pub fn on_key_event( + &mut self, + callback: impl Fn(&mut Context, &T, &KeyEvent) -> Option> + 'static, + ) { + self.key_event_callback = Some(Box::new(callback)); + } + pub fn truncate_start(mut self, truncate_start: bool) -> Self { self.truncate_start = truncate_start; self.picker.truncate_start = truncate_start; @@ -390,58 +406,28 @@ impl Component for FilePicker { } fn handle_event(&mut self, event: &Event, ctx: &mut Context) -> EventResult { - if let Event::IdleTimeout = event { - return self.handle_idle_timeout(ctx); - } - - let Some((current_file, _)) = self.current_file(ctx.editor) else { - return EventResult::Consumed(None) + let key_event = match event { + Event::Key(event) => *event, + Event::IdleTimeout => return self.handle_idle_timeout(ctx), + _ => { + // TODO: keybinds for scrolling preview + return self.picker.handle_event(event, ctx); + } }; - // Try to find a document in the cache - let doc = match ¤t_file { - PathOrId::Id(doc_id) => doc_mut!(ctx.editor, doc_id), - PathOrId::Path(path) => match self.preview_cache.get_mut(path) { - Some(CachedPreview::Document(ref mut doc)) => doc, - _ => return EventResult::Consumed(None), - }, - }; - let doc_id = doc.id(); - - if let Event::Key(key_event) = *event { - match key_event { - ctrl!('x') => { - if ctx.editor.close_document(doc_id, false).is_err() { - ctx.editor.set_error("Cannot close buffer"); - } else { - ctx.editor.documents.remove(&doc_id); - // .filter(|(d_id, _)| d_id() != doc_id) - // .collect(); - // let updated_documents = ctx.editor.documents; - // let updated_options = - - // self.picker.set_options(updated_documents); - } - } - ctrl!('X') => { - if ctx.editor.close_document(doc_id, true).is_err() { - ctx.editor.set_error("Cannot force close buffer"); - } else { - ctx.editor.documents.remove(&doc_id); - // let updated_options = ctx - // .editor - // .documents - // .iter() - // .filter(|(_, d)| d.id() != doc_id) - // .collect(); - // self.picker.set_options(updated_options); - } - } - _ => {} + // handle any external key_events + match self.picker.selection().and_then(|option| { + self.key_event_callback + .as_ref() + .and_then(|cb| cb(ctx, option, &key_event)) + }) { + Some(PickerAction::UpdateOptions(options)) => self.picker.set_options(options), + None => { + return self.picker.handle_event(event, ctx); } }; - // TODO: keybinds for scrolling preview - self.picker.handle_event(event, ctx) + + EventResult::Consumed(None) } fn cursor(&self, area: Rect, ctx: &Editor) -> (Option, CursorKind) { From 88a1b5920f43ddd4ceb5eed62f2bc9039772aeb4 Mon Sep 17 00:00:00 2001 From: Eric Correia Date: Thu, 22 Jun 2023 20:31:28 -0400 Subject: [PATCH 9/9] update to implement on new picker --- helix-term/src/commands.rs | 33 ++++++++++++++++++++++++++++++++- helix-term/src/ui/mod.rs | 2 +- helix-term/src/ui/picker.rs | 21 ++++++++++++++++++++- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 38099f5bd09e..ce4918acc6fc 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -56,7 +56,7 @@ use crate::{ keymap::ReverseKeymap, ui::{ self, editor::InsertEvent, lsp::SignatureHelp, overlay::overlaid, CompletionItem, Picker, - Popup, Prompt, PromptEvent, + PickerAction, Popup, Prompt, PromptEvent, }, }; @@ -2588,6 +2588,37 @@ fn buffer_picker(cx: &mut Context) { .primary() .cursor_line(doc.text().slice(..)); Some((meta.id.into(), Some((line, line)))) + }) + .on_key_event(move |cx, meta, key_event| match key_event { + ctrl!('x') => { + if cx.editor.close_document(meta.id, false).is_err() { + cx.editor.set_error("Cannot close buffer"); + None + } else { + let updated_options = cx + .editor + .documents + .iter() + .map(|(_, doc)| new_meta(doc)) + .collect(); + Some(PickerAction::UpdateOptions(updated_options)) + } + } + ctrl!('X') => { + if cx.editor.close_document(meta.id, true).is_err() { + cx.editor.set_error("Cannot force close buffer"); + None + } else { + let updated_options = cx + .editor + .documents + .iter() + .map(|(_, doc)| new_meta(doc)) + .collect(); + Some(PickerAction::UpdateOptions(updated_options)) + } + } + _ => None, }); cx.push_layer(Box::new(overlaid(picker))); } diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 155f24356187..8962f6064a0d 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -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, PickerAction}; 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 79a84eb4e1f3..f6f2bb86ee17 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -147,6 +147,7 @@ pub struct Picker { read_buffer: Vec, /// Given an item in the picker, return the file path and line number to display. file_fn: Option>, + key_event_callback: Option>, } impl Picker { @@ -178,6 +179,7 @@ impl Picker { preview_cache: HashMap::new(), read_buffer: Vec::with_capacity(1024), file_fn: None, + key_event_callback: None, }; picker.calculate_column_widths(); @@ -211,6 +213,14 @@ impl Picker { self } + pub fn on_key_event( + mut self, + callback: impl Fn(&mut Context, &T, &KeyEvent) -> Option> + 'static, + ) -> Self { + self.key_event_callback = Some(Box::new(callback)); + self + } + pub fn set_options(&mut self, new_options: Vec) { self.options = new_options; self.cursor = 0; @@ -855,7 +865,16 @@ impl Component for Picker { self.toggle_preview(); } _ => { - self.prompt_handle_event(event, ctx); + match self.selection().and_then(|option| { + self.key_event_callback + .as_ref() + .and_then(|cb| cb(ctx, option, &key_event)) + }) { + Some(PickerAction::UpdateOptions(options)) => self.set_options(options), + None => { + self.prompt_handle_event(event, ctx); + } + }; } }