diff --git a/helix-core/src/state.rs b/helix-core/src/state.rs index bd14850f..1b0a67ae 100644 --- a/helix-core/src/state.rs +++ b/helix-core/src/state.rs @@ -10,7 +10,6 @@ pub enum Mode { Normal, Insert, Goto, - Command, } /// A state represents the current editor state of a single buffer. diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 51df234a..87d03c72 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -225,7 +225,6 @@ impl Renderer { Mode::Insert => "INS", Mode::Normal => "NOR", Mode::Goto => "GOTO", - Mode::Command => "COM", // command? }; // statusline self.surface.set_style( @@ -237,13 +236,15 @@ impl Renderer { } pub fn render_prompt(&mut self, prompt: &Prompt) { - use tui::backend::Backend; // render buffer text self.surface - .set_string(1, self.size.1 - 1, String::from(":"), self.text_color); + .set_string(1, self.size.1 - 1, &prompt.prompt, self.text_color); self.surface - .set_string(2, self.size.1 - 1, &prompt.buffer, self.text_color); + .set_string(2, self.size.1 - 1, &prompt.line, self.text_color); + } + pub fn draw(&mut self) { + use tui::backend::Backend; // TODO: theres probably a better place for this self.terminal .backend_mut() @@ -259,7 +260,7 @@ impl Renderer { mode => write!(stdout, "\x1B[2 q"), }; let pos = if let Some(prompt) = prompt { - Position::new(self.size.0 as usize, 2 + prompt.cursor_loc) + Position::new(self.size.0 as usize, 2 + prompt.cursor) } else { if let Some(path) = view.state.path() { self.surface.set_string( @@ -303,15 +304,6 @@ impl Application { Ok(app) } - pub fn set_prompt(&mut self) { - let commands = |editor: &mut Editor, input: &str| match input { - "q" => editor.should_close = true, - _ => (), - }; - let prompt = Prompt::new(|input| None, commands); - self.prompt = Some(prompt); - } - fn render(&mut self) { let viewport = Rect::new(OFFSET, 0, self.terminal.size.0, self.terminal.size.1 - 2); // - 2 for statusline and prompt @@ -323,6 +315,8 @@ impl Application { self.terminal.render_prompt(prompt); } + self.terminal.draw(); + // TODO: drop unwrap self.terminal.render_cursor( self.editor.view.as_ref().unwrap(), @@ -335,7 +329,6 @@ impl Application { let mut reader = EventStream::new(); let keymap = keymap::default(); - self.set_prompt(); self.render(); loop { @@ -363,6 +356,8 @@ impl Application { .as_mut() .unwrap() .handle_input(event, &mut self.editor); + + self.render(); } else if let Some(view) = &mut self.editor.view { let keys = vec![event]; // TODO: sequences (`gg`) @@ -380,7 +375,31 @@ impl Application { } view.ensure_cursor_in_view(); } - Mode::Command => unreachable!(), + Mode::Normal => { + if let &[KeyEvent { + code: KeyCode::Char(':'), + .. + }] = keys.as_slice() + { + let prompt = Prompt::new( + ":".to_owned(), + |_input: &str| None, // completion + |editor: &mut Editor, input: &str| match input { + "q" => editor.should_close = true, + _ => (), + }, + ); + + self.prompt = Some(prompt); + + // HAXX: special casing for command mode + } else if let Some(command) = keymap[&Mode::Normal].get(&keys) { + command(view, 1); + + // TODO: simplistic ensure cursor in view for now + view.ensure_cursor_in_view(); + } + } mode => { if let Some(command) = keymap[&mode].get(&keys) { command(view, 1); diff --git a/helix-view/src/commands.rs b/helix-view/src/commands.rs index 6efbf98d..33fc06e4 100644 --- a/helix-view/src/commands.rs +++ b/helix-view/src/commands.rs @@ -307,8 +307,19 @@ pub fn append_mode(view: &mut View, _count: usize) { }) } -pub fn command_mode(view: &mut View, _count: usize) { - view.state.mode = Mode::Command; +pub fn command_mode(_view: &mut View, _count: usize) { + use crate::Editor; + + let prompt = Prompt::new( + ":".to_owned(), + |_input: &str| None, // completion + |editor: &mut Editor, input: &str| match input { + "q" => editor.should_close = true, + _ => (), + }, + ); + + // set_prompt(prompt) } // TODO: I, A, o and O can share a lot of the primitives. @@ -627,7 +638,7 @@ pub fn unindent(view: &mut View, _count: usize) { append_changes_to_history(view); } -pub fn indent_selection(view: &mut View, _count: usize) { +pub fn indent_selection(_view: &mut View, _count: usize) { // loop over each line and recompute proper indentation unimplemented!() } diff --git a/helix-view/src/prompt.rs b/helix-view/src/prompt.rs index f0ae44c9..a3feaeec 100644 --- a/helix-view/src/prompt.rs +++ b/helix-view/src/prompt.rs @@ -4,54 +4,57 @@ use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use std::string::String; pub struct Prompt { - pub buffer: String, - pub cursor_loc: usize, + pub prompt: String, + pub line: String, + pub cursor: usize, completion_fn: Box Option>>, callback_fn: Box, } impl Prompt { pub fn new( + prompt: String, completion_fn: impl FnMut(&str) -> Option> + 'static, callback_fn: impl FnMut(&mut Editor, &str) + 'static, ) -> Prompt { Prompt { - buffer: String::from(""), - cursor_loc: 0, + prompt, + line: String::new(), + cursor: 0, completion_fn: Box::new(completion_fn), callback_fn: Box::new(callback_fn), } } pub fn insert_char(&mut self, c: char) { - self.buffer.insert(self.cursor_loc, c); - self.cursor_loc += 1; + self.line.insert(self.cursor, c); + self.cursor += 1; } pub fn move_char_left(&mut self) { - if self.cursor_loc > 1 { - self.cursor_loc -= 1; + if self.cursor > 1 { + self.cursor -= 1; } } pub fn move_char_right(&mut self) { - if self.cursor_loc < self.buffer.len() { - self.cursor_loc += 1; + if self.cursor < self.line.len() { + self.cursor += 1; } } pub fn move_start(&mut self) { - self.cursor_loc = 0; + self.cursor = 0; } pub fn move_end(&mut self) { - self.cursor_loc = self.buffer.len(); + self.cursor = self.line.len(); } pub fn delete_char_backwards(&mut self) { - if self.cursor_loc > 0 { - self.buffer.remove(self.cursor_loc - 1); - self.cursor_loc -= 1; + if self.cursor > 0 { + self.line.remove(self.cursor - 1); + self.cursor -= 1; } } @@ -87,7 +90,7 @@ impl Prompt { KeyEvent { code: KeyCode::Enter, .. - } => (self.callback_fn)(editor, &self.buffer), + } => (self.callback_fn)(editor, &self.line), _ => (), } }