on master help sign in
Make the select prompt interactive.
8f0b28aBlaž Hrastnik
helix-term/src/commands.rs+36−11
@@ -10,7 +10,7 @@ use helix_core::{
10use once_cell::sync::Lazy;
10use once_cell::sync::Lazy;
11
11
12use crate::compositor::Compositor;
12use crate::compositor::Compositor;
13−use crate::ui::Prompt;
13+use crate::ui::{Prompt, PromptEvent};
14
14
15use helix_view::{
15use helix_view::{
16 document::Mode,
16 document::Mode,
@@ -262,18 +262,37 @@ pub fn split_selection(cx: &mut Context) {
262 // # update state
262 // # update state
263 // }
263 // }
264
264
265+ let snapshot = cx.view.doc.state.clone();
266+
265 let prompt = Prompt::new(
267 let prompt = Prompt::new(
266 "split:".to_string(),
268 "split:".to_string(),
267 |input: &str| Vec::new(), // this is fine because Vec::new() doesn't allocate
269 |input: &str| Vec::new(), // this is fine because Vec::new() doesn't allocate
268− |editor: &mut Editor, input: &str| {
270+ move |editor: &mut Editor, input: &str, event: PromptEvent| {
269− match Regex::new(input) {
271+ match event {
270− Ok(regex) => {
272+ PromptEvent::Abort => {
273+ // revert state
271 let view = editor.view_mut().unwrap();
274 let view = editor.view_mut().unwrap();
275+ view.doc.state = snapshot.clone();
276+ }
277+ PromptEvent::Validate => {
278+ //
279+ }
280+ PromptEvent::Update => {
281+ match Regex::new(input) {
282+ Ok(regex) => {
283+ let view = editor.view_mut().unwrap();
284+
285+ // revert state to what it was before the last update
286+ view.doc.state = snapshot.clone();
287+
272− let text = &view.doc.text().slice(..);
288+ let text = &view.doc.text().slice(..);
289+ let selection =
273− let selection = selection::split_on_matches(text, view.doc.selection(), &regex);
290+ selection::split_on_matches(text, view.doc.selection(), &regex);
274− view.doc.set_selection(selection);
291+ view.doc.set_selection(selection);
292+ }
293+ Err(_) => (), // TODO: mark command line as error
294+ }
275 }
295 }
276− Err(_) => (), // TODO: mark command line as error
277 }
296 }
278 },
297 },
279 );
298 );
@@ -416,9 +435,15 @@ pub fn command_mode(cx: &mut Context) {
416 .filter(|command| command.contains(_input))
435 .filter(|command| command.contains(_input))
417 .collect()
436 .collect()
418 }, // completion
437 }, // completion
419− |editor: &mut Editor, input: &str| match input {
438+ |editor: &mut Editor, input: &str, event: PromptEvent| {
439+ if event != PromptEvent::Validate {
440+ return;
441+ }
442+
443+ match input {
420− "q" => editor.should_close = true,
444+ "q" => editor.should_close = true,
421− _ => (),
445+ _ => (),
446+ }
422 },
447 },
423 );
448 );
424 compositor.push(Box::new(prompt));
449 compositor.push(Box::new(prompt));
helix-term/src/ui/mod.rs+1−1
@@ -2,7 +2,7 @@ mod editor;
2mod prompt;
2mod prompt;
3
3
4pub use editor::EditorView;
4pub use editor::EditorView;
5−pub use prompt::Prompt;
5+pub use prompt::{Prompt, PromptEvent};
6
6
7pub use tui::layout::Rect;
7pub use tui::layout::Rect;
8pub use tui::style::{Color, Modifier, Style};
8pub use tui::style::{Color, Modifier, Style};
helix-term/src/ui/prompt.rs+22−5
@@ -12,14 +12,24 @@ pub struct Prompt {
12 pub completion: Vec<String>,
12 pub completion: Vec<String>,
13 pub completion_selection_index: Option<usize>,
13 pub completion_selection_index: Option<usize>,
14 completion_fn: Box<dyn FnMut(&str) -> Vec<String>>,
14 completion_fn: Box<dyn FnMut(&str) -> Vec<String>>,
15− callback_fn: Box<dyn FnMut(&mut Editor, &str)>,
15+ callback_fn: Box<dyn FnMut(&mut Editor, &str, PromptEvent)>,
16+}
17+
18+#[derive(PartialEq)]
19+pub enum PromptEvent {
20+ /// The prompt input has been updated.
21+ Update,
22+ /// Validate and finalize the change.
23+ Validate,
24+ /// Abort the change, reverting to the initial state.
25+ Abort,
16}
26}
17
27
18impl Prompt {
28impl Prompt {
19 pub fn new(
29 pub fn new(
20 prompt: String,
30 prompt: String,
21 mut completion_fn: impl FnMut(&str) -> Vec<String> + 'static,
31 mut completion_fn: impl FnMut(&str) -> Vec<String> + 'static,
22− callback_fn: impl FnMut(&mut Editor, &str) + 'static,
32+ callback_fn: impl FnMut(&mut Editor, &str, PromptEvent) + 'static,
23 ) -> Prompt {
33 ) -> Prompt {
24 Prompt {
34 Prompt {
25 prompt,
35 prompt,
@@ -160,10 +170,14 @@ impl Component for Prompt {
160 KeyEvent {
170 KeyEvent {
161 code: KeyCode::Char(c),
171 code: KeyCode::Char(c),
162 modifiers: KeyModifiers::NONE,
172 modifiers: KeyModifiers::NONE,
173+ } => {
163− } => self.insert_char(c),
174+ self.insert_char(c);
175+ (self.callback_fn)(cx.editor, &self.line, PromptEvent::Update);
176+ }
164 KeyEvent {
177 KeyEvent {
165 code: KeyCode::Esc, ..
178 code: KeyCode::Esc, ..
166 } => {
179 } => {
180+ (self.callback_fn)(cx.editor, &self.line, PromptEvent::Abort);
167 return close_fn;
181 return close_fn;
168 }
182 }
169 KeyEvent {
183 KeyEvent {
@@ -185,12 +199,15 @@ impl Component for Prompt {
185 KeyEvent {
199 KeyEvent {
186 code: KeyCode::Backspace,
200 code: KeyCode::Backspace,
187 modifiers: KeyModifiers::NONE,
201 modifiers: KeyModifiers::NONE,
202+ } => {
188− } => self.delete_char_backwards(),
203+ self.delete_char_backwards();
204+ (self.callback_fn)(cx.editor, &self.line, PromptEvent::Update);
205+ }
189 KeyEvent {
206 KeyEvent {
190 code: KeyCode::Enter,
207 code: KeyCode::Enter,
191 ..
208 ..
192 } => {
209 } => {
193− (self.callback_fn)(cx.editor, &self.line);
210+ (self.callback_fn)(cx.editor, &self.line, PromptEvent::Validate);
194 return close_fn;
211 return close_fn;
195 }
212 }
196 KeyEvent {
213 KeyEvent {