From 3d3295bb759994d4fbf328c126678f63e0c38d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Mon, 21 Dec 2020 16:23:05 +0900 Subject: [PATCH] ui: buffer picker on ctrl-b --- helix-term/src/commands.rs | 85 +++++++++++++++++++++--------------- helix-term/src/compositor.rs | 4 +- helix-term/src/keymap.rs | 1 + helix-term/src/ui/mod.rs | 28 +++++++++++- helix-term/src/ui/picker.rs | 10 +++-- helix-term/src/ui/prompt.rs | 10 +++-- 6 files changed, 91 insertions(+), 47 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 7722eca5..b4b64249 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -297,9 +297,11 @@ pub fn split_selection(cx: &mut Context) { }, ); - cx.callback = Some(Box::new(move |compositor: &mut Compositor| { - compositor.push(Box::new(prompt)); - })); + cx.callback = Some(Box::new( + move |compositor: &mut Compositor, editor: &mut Editor| { + compositor.push(Box::new(prompt)); + }, + )); } pub fn split_selection_on_newline(cx: &mut Context) { @@ -399,12 +401,13 @@ pub fn append_mode(cx: &mut Context) { // TODO: I, A, o and O can share a lot of the primitives. pub fn command_mode(cx: &mut Context) { - cx.callback = Some(Box::new(|compositor: &mut Compositor| { - let prompt = Prompt::new( - ":".to_owned(), - |_input: &str| { - // TODO: i need this duplicate list right now to avoid borrow checker issues - let command_list = vec![ + cx.callback = Some(Box::new( + |compositor: &mut Compositor, editor: &mut Editor| { + let prompt = Prompt::new( + ":".to_owned(), + |_input: &str| { + // TODO: i need this duplicate list right now to avoid borrow checker issues + let command_list = vec![ String::from("q"), String::from("aaa"), String::from("bbb"), @@ -437,37 +440,49 @@ pub fn command_mode(cx: &mut Context) { String::from("ddd"), String::from("eee"), ]; - command_list - .into_iter() - .filter(|command| command.contains(_input)) - .collect() - }, // completion - |editor: &mut Editor, input: &str, event: PromptEvent| { - if event != PromptEvent::Validate { - return; - } + command_list + .into_iter() + .filter(|command| command.contains(_input)) + .collect() + }, // completion + |editor: &mut Editor, input: &str, event: PromptEvent| { + if event != PromptEvent::Validate { + return; + } - let parts = input.split_ascii_whitespace().collect::>(); + let parts = input.split_ascii_whitespace().collect::>(); - match parts.as_slice() { - &["q"] => editor.should_close = true, - &["o", path] => { - // TODO: make view()/view_mut() always contain a view. - let size = editor.view().unwrap().size; - editor.open(path.into(), size); + match parts.as_slice() { + &["q"] => editor.should_close = true, + &["o", path] => { + // TODO: make view()/view_mut() always contain a view. + let size = editor.view().unwrap().size; + editor.open(path.into(), size); + } + _ => (), } - _ => (), - } - }, - ); - compositor.push(Box::new(prompt)); - })); + }, + ); + compositor.push(Box::new(prompt)); + }, + )); } pub fn file_picker(cx: &mut Context) { - cx.callback = Some(Box::new(|compositor: &mut Compositor| { - let picker = ui::file_picker("./"); - compositor.push(Box::new(picker)); - })); + cx.callback = Some(Box::new( + |compositor: &mut Compositor, editor: &mut Editor| { + let picker = ui::file_picker("./"); + compositor.push(Box::new(picker)); + }, + )); +} + +pub fn buffer_picker(cx: &mut Context) { + cx.callback = Some(Box::new( + |compositor: &mut Compositor, editor: &mut Editor| { + let picker = ui::buffer_picker(&editor.views, editor.focus); + compositor.push(Box::new(picker)); + }, + )); } // calculate line numbers for each selection range diff --git a/helix-term/src/compositor.rs b/helix-term/src/compositor.rs index f0d94dbc..b1b92a71 100644 --- a/helix-term/src/compositor.rs +++ b/helix-term/src/compositor.rs @@ -19,7 +19,7 @@ use smol::Executor; use tui::buffer::Buffer as Surface; use tui::layout::Rect; -pub type Callback = Box; +pub type Callback = Box; // --> EventResult should have a callback that takes a context with methods like .popup(), // .prompt() etc. That way we can abstract it from the renderer. @@ -128,7 +128,7 @@ impl Compositor { if let Some(layer) = self.layers.last_mut() { return match layer.handle_event(event, cx) { EventResult::Consumed(Some(callback)) => { - callback(self); + callback(self, cx.editor); true } EventResult::Consumed(None) => true, diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index a31676e4..c1677847 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -185,6 +185,7 @@ pub fn default() -> Keymaps { vec![ctrl!('d')] => commands::half_page_down, vec![ctrl!('p')] => commands::file_picker, + vec![ctrl!('b')] => commands::buffer_picker, ), Mode::Insert => hashmap!( vec![Key { diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index b778f531..f9480dd4 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -15,7 +15,7 @@ pub fn text_color() -> Style { Style::default().fg(Color::Rgb(219, 191, 239)) // lilac } -use std::path::PathBuf; +use std::path::{Path, PathBuf}; pub fn file_picker(root: &str) -> Picker { use ignore::Walk; // TODO: determine root based on git root @@ -38,7 +38,7 @@ pub fn file_picker(root: &str) -> Picker { files.take(MAX).collect(), |path: &PathBuf| { // format_fn - path.strip_prefix("./").unwrap().to_str().unwrap() // TODO: render paths without ./ + path.strip_prefix("./").unwrap().to_str().unwrap() }, |editor: &mut Editor, path: &PathBuf| { let size = editor.view().unwrap().size; @@ -46,3 +46,27 @@ pub fn file_picker(root: &str) -> Picker { }, ) } + +use helix_view::View; +pub fn buffer_picker(views: &[View], current: usize) -> Picker<(Option, usize)> { + use helix_view::Editor; + Picker::new( + views + .iter() + .enumerate() + .map(|(i, view)| (view.doc.relative_path().map(Path::to_path_buf), i)) + .collect(), + |(path, index): &(Option, usize)| { + // format_fn + match path { + Some(path) => path.to_str().unwrap(), + None => "[NEW]", + } + }, + |editor: &mut Editor, &(_, index): &(Option, usize)| { + if index < editor.views.len() { + editor.focus = index; + } + }, + ) +} diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 0a12cff9..94eec2eb 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -123,10 +123,12 @@ impl Component for Picker { _ => return EventResult::Ignored, }; - let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor| { - // remove the layer - compositor.pop(); - }))); + let close_fn = EventResult::Consumed(Some(Box::new( + |compositor: &mut Compositor, editor: &mut Editor| { + // remove the layer + compositor.pop(); + }, + ))); match key_event { // KeyEvent { diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index 58efd560..7abc08c2 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -167,10 +167,12 @@ impl Component for Prompt { _ => return EventResult::Ignored, }; - let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor| { - // remove the layer - compositor.pop(); - }))); + let close_fn = EventResult::Consumed(Some(Box::new( + |compositor: &mut Compositor, editor: &mut Editor| { + // remove the layer + compositor.pop(); + }, + ))); match event { KeyEvent {