From 8f0b28aeb872797e4be3f07575e628f5f93e74e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Tue, 15 Dec 2020 19:29:56 +0900 Subject: [PATCH] Make the select prompt interactive. --- helix-term/src/commands.rs | 47 ++++++++++++++++++++++++++++--------- helix-term/src/ui/mod.rs | 2 +- helix-term/src/ui/prompt.rs | 27 +++++++++++++++++---- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index e49c780ad..462d2a209 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -10,7 +10,7 @@ use helix_core::{ use once_cell::sync::Lazy; use crate::compositor::Compositor; -use crate::ui::Prompt; +use crate::ui::{Prompt, PromptEvent}; use helix_view::{ document::Mode, @@ -262,18 +262,37 @@ pub fn split_selection(cx: &mut Context) { // # update state // } + let snapshot = cx.view.doc.state.clone(); + let prompt = Prompt::new( "split:".to_string(), |input: &str| Vec::new(), // this is fine because Vec::new() doesn't allocate - |editor: &mut Editor, input: &str| { - match Regex::new(input) { - Ok(regex) => { + move |editor: &mut Editor, input: &str, event: PromptEvent| { + match event { + PromptEvent::Abort => { + // revert state let view = editor.view_mut().unwrap(); - let text = &view.doc.text().slice(..); - let selection = selection::split_on_matches(text, view.doc.selection(), ®ex); - view.doc.set_selection(selection); + view.doc.state = snapshot.clone(); + } + PromptEvent::Validate => { + // + } + PromptEvent::Update => { + match Regex::new(input) { + Ok(regex) => { + let view = editor.view_mut().unwrap(); + + // revert state to what it was before the last update + view.doc.state = snapshot.clone(); + + let text = &view.doc.text().slice(..); + let selection = + selection::split_on_matches(text, view.doc.selection(), ®ex); + view.doc.set_selection(selection); + } + Err(_) => (), // TODO: mark command line as error + } } - Err(_) => (), // TODO: mark command line as error } }, ); @@ -416,9 +435,15 @@ pub fn command_mode(cx: &mut Context) { .filter(|command| command.contains(_input)) .collect() }, // completion - |editor: &mut Editor, input: &str| match input { - "q" => editor.should_close = true, - _ => (), + |editor: &mut Editor, input: &str, event: PromptEvent| { + if event != PromptEvent::Validate { + return; + } + + match input { + "q" => editor.should_close = true, + _ => (), + } }, ); compositor.push(Box::new(prompt)); diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index bc79e09cf..9a70d1bd7 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -2,7 +2,7 @@ mod editor; mod prompt; pub use editor::EditorView; -pub use prompt::Prompt; +pub use prompt::{Prompt, PromptEvent}; pub use tui::layout::Rect; pub use tui::style::{Color, Modifier, Style}; diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index f5ef94779..07c0f9170 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -12,14 +12,24 @@ pub struct Prompt { pub completion: Vec, pub completion_selection_index: Option, completion_fn: Box Vec>, - callback_fn: Box, + callback_fn: Box, +} + +#[derive(PartialEq)] +pub enum PromptEvent { + /// The prompt input has been updated. + Update, + /// Validate and finalize the change. + Validate, + /// Abort the change, reverting to the initial state. + Abort, } impl Prompt { pub fn new( prompt: String, mut completion_fn: impl FnMut(&str) -> Vec + 'static, - callback_fn: impl FnMut(&mut Editor, &str) + 'static, + callback_fn: impl FnMut(&mut Editor, &str, PromptEvent) + 'static, ) -> Prompt { Prompt { prompt, @@ -160,10 +170,14 @@ impl Component for Prompt { KeyEvent { code: KeyCode::Char(c), modifiers: KeyModifiers::NONE, - } => self.insert_char(c), + } => { + self.insert_char(c); + (self.callback_fn)(cx.editor, &self.line, PromptEvent::Update); + } KeyEvent { code: KeyCode::Esc, .. } => { + (self.callback_fn)(cx.editor, &self.line, PromptEvent::Abort); return close_fn; } KeyEvent { @@ -185,12 +199,15 @@ impl Component for Prompt { KeyEvent { code: KeyCode::Backspace, modifiers: KeyModifiers::NONE, - } => self.delete_char_backwards(), + } => { + self.delete_char_backwards(); + (self.callback_fn)(cx.editor, &self.line, PromptEvent::Update); + } KeyEvent { code: KeyCode::Enter, .. } => { - (self.callback_fn)(cx.editor, &self.line); + (self.callback_fn)(cx.editor, &self.line, PromptEvent::Validate); return close_fn; } KeyEvent {