diff options
Diffstat (limited to 'helix-term/src/ui')
-rw-r--r-- | helix-term/src/ui/completion.rs | 6 | ||||
-rw-r--r-- | helix-term/src/ui/editor.rs | 54 | ||||
-rw-r--r-- | helix-term/src/ui/mod.rs | 28 | ||||
-rw-r--r-- | helix-term/src/ui/prompt.rs | 19 |
4 files changed, 65 insertions, 42 deletions
diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index 00ecce03..06ed966d 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -89,7 +89,7 @@ impl Completion { // doc.state = snapshot.clone(); } PromptEvent::Validate => { - let (view, doc) = editor.current(); + let (view, doc) = current!(editor); // revert state to what it was before the last update // doc.state = snapshot.clone(); @@ -169,7 +169,7 @@ impl Completion { pub fn update(&mut self, cx: &mut commands::Context) { // recompute menu based on matches let menu = self.popup.contents_mut(); - let (view, doc) = cx.editor.current(); + let (view, doc) = current!(cx.editor); // cx.hooks() // cx.add_hook(enum type, ||) @@ -233,7 +233,7 @@ impl Component for Completion { // --- // option.documentation - let (view, doc) = cx.editor.current(); + let (view, doc) = current!(cx.editor); let language = doc .language() .and_then(|scope| scope.strip_prefix("source.")) diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index d6010e6c..42bb3ba8 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -11,15 +11,13 @@ use helix_core::{ syntax::{self, HighlightEvent}, Position, Range, }; -use helix_view::{ - document::{IndentStyle, Mode}, - Document, Editor, Theme, View, -}; +use helix_view::input::{KeyCode, KeyEvent, KeyModifiers}; +use helix_view::{document::Mode, Document, Editor, Theme, View}; use std::borrow::Cow; use crossterm::{ cursor, - event::{read, Event, EventStream, KeyCode, KeyEvent, KeyModifiers}, + event::{read, Event, EventStream}, }; use tui::{ backend::CrosstermBackend, @@ -30,7 +28,7 @@ use tui::{ }; pub struct EditorView { - keymap: Keymaps, + keymaps: Keymaps, on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>, last_insert: (commands::Command, Vec<KeyEvent>), completion: Option<Completion>, @@ -40,16 +38,16 @@ const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter impl Default for EditorView { fn default() -> Self { - Self::new() + Self::new(Keymaps::default()) } } impl EditorView { - pub fn new() -> Self { + pub fn new(keymaps: Keymaps) -> Self { Self { - keymap: keymap::default(), + keymaps, on_next_key: None, - last_insert: (commands::normal_mode, Vec::new()), + last_insert: (commands::Command::normal_mode, Vec::new()), completion: None, } } @@ -479,18 +477,15 @@ impl EditorView { Mode::Select => "SEL", Mode::Normal => "NOR", }; - let text_color = if is_focused { - theme.get("ui.text.focus") + let style = if is_focused { + theme.get("ui.statusline") } else { - theme.get("ui.text") + theme.get("ui.statusline.inactive") }; // statusline - surface.set_style( - Rect::new(viewport.x, viewport.y, viewport.width, 1), - theme.get("ui.statusline"), - ); + surface.set_style(Rect::new(viewport.x, viewport.y, viewport.width, 1), style); if is_focused { - surface.set_string(viewport.x + 1, viewport.y, mode, text_color); + surface.set_string(viewport.x + 1, viewport.y, mode, style); } if let Some(path) = doc.relative_path() { @@ -502,7 +497,7 @@ impl EditorView { viewport.y, title, viewport.width.saturating_sub(6) as usize, - text_color, + style, ); } @@ -541,13 +536,13 @@ impl EditorView { viewport.x + viewport.width.saturating_sub(text_len), viewport.y, right_side_text, - text_color, + style, ); } fn insert_mode(&self, cx: &mut commands::Context, event: KeyEvent) { - if let Some(command) = self.keymap[&Mode::Insert].get(&event) { - command(cx); + if let Some(command) = self.keymaps[&Mode::Insert].get(&event) { + command.execute(cx); } else if let KeyEvent { code: KeyCode::Char(ch), .. @@ -568,7 +563,7 @@ impl EditorView { // special handling for repeat operator key!('.') => { // first execute whatever put us into insert mode - (self.last_insert.0)(cxt); + self.last_insert.0.execute(cxt); // then replay the inputs for key in &self.last_insert.1 { self.insert_mode(cxt, *key) @@ -584,8 +579,8 @@ impl EditorView { // set the register cxt.selected_register = cxt.editor.selected_register.take(); - if let Some(command) = self.keymap[&mode].get(&event) { - command(cxt); + if let Some(command) = self.keymaps[&mode].get(&event) { + command.execute(cxt); } } } @@ -613,12 +608,13 @@ impl Component for EditorView { cx.editor.resize(Rect::new(0, 0, width, height - 1)); EventResult::Consumed(None) } - Event::Key(mut key) => { + Event::Key(key) => { + let mut key = KeyEvent::from(key); canonicalize_key(&mut key); // clear status cx.editor.status_msg = None; - let (view, doc) = cx.editor.current(); + let (view, doc) = current!(cx.editor); let mode = doc.mode(); let mut cxt = commands::Context { @@ -687,7 +683,7 @@ impl Component for EditorView { return EventResult::Ignored; } - let (view, doc) = cx.editor.current(); + let (view, doc) = current!(cx.editor); view.ensure_cursor_in_view(doc); // mode transitions @@ -699,7 +695,7 @@ impl Component for EditorView { // how we entered insert mode is important, and we should track that so // we can repeat the side effect. - self.last_insert.0 = self.keymap[&mode][&key]; + self.last_insert.0 = self.keymaps[&mode][&key]; self.last_insert.1.clear(); } (Mode::Insert, Mode::Normal) => { diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 77e53690..39e11cd6 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -30,8 +30,9 @@ pub fn regex_prompt( prompt: String, fun: impl Fn(&mut View, &mut Document, &mut Registers, Regex) + 'static, ) -> Prompt { - let view_id = cx.view().id; - let snapshot = cx.doc().selection(view_id).clone(); + let (view, doc) = current!(cx.editor); + let view_id = view.id; + let snapshot = doc.selection(view_id).clone(); Prompt::new( prompt, @@ -40,7 +41,7 @@ pub fn regex_prompt( match event { PromptEvent::Abort => { // TODO: also revert text - let (view, doc) = editor.current(); + let (view, doc) = current!(editor); doc.set_selection(view.id, snapshot.clone()); } PromptEvent::Validate => { @@ -54,7 +55,8 @@ pub fn regex_prompt( match Regex::new(input) { Ok(regex) => { - let (view, doc, registers) = editor.current_with_registers(); + let (view, doc) = current!(editor); + let registers = &mut editor.registers; // revert state to what it was before the last update // TODO: also revert text @@ -124,10 +126,11 @@ pub mod completers { use ignore::WalkBuilder; use std::path::{Path, PathBuf}; - let path = Path::new(input); + let is_tilde = input.starts_with('~') && input.len() == 1; + let path = helix_view::document::expand_tilde(Path::new(input)); let (dir, file_name) = if input.ends_with('/') { - (path.into(), None) + (path, None) } else { let file_name = path .file_name() @@ -152,7 +155,16 @@ pub mod completers { let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir()); let path = entry.path(); - let mut path = path.strip_prefix(&dir).unwrap_or(path).to_path_buf(); + let mut path = if is_tilde { + // if it's a single tilde an absolute path is displayed so that when `TAB` is pressed on + // one of the directories the tilde will be replaced with a valid path not with a relative + // home directory name. + // ~ -> <TAB> -> /home/user + // ~/ -> <TAB> -> ~/first_entry + path.to_path_buf() + } else { + path.strip_prefix(&dir).unwrap_or(path).to_path_buf() + }; if is_dir { path.push(""); @@ -182,7 +194,7 @@ pub mod completers { }) .collect(); - let range = ((input.len() - file_name.len())..); + let range = ((input.len().saturating_sub(file_name.len()))..); matches.sort_unstable_by_key(|(_file, score)| Reverse(*score)); files = matches diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index 7b8af820..991b328d 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -53,7 +53,16 @@ impl Prompt { } pub fn insert_char(&mut self, c: char) { - self.line.insert(self.cursor, c); + let pos = if self.line.is_empty() { + 0 + } else { + self.line + .char_indices() + .nth(self.cursor) + .map(|(pos, _)| pos) + .unwrap_or_else(|| self.line.len()) + }; + self.line.insert(pos, c); self.cursor += 1; self.completion = (self.completion_fn)(&self.line); self.exit_selection(); @@ -79,7 +88,13 @@ impl Prompt { pub fn delete_char_backwards(&mut self) { if self.cursor > 0 { - self.line.remove(self.cursor - 1); + let pos = self + .line + .char_indices() + .nth(self.cursor - 1) + .map(|(pos, _)| pos) + .expect("line is not empty"); + self.line.remove(pos); self.cursor -= 1; self.completion = (self.completion_fn)(&self.line); } |