summaryrefslogtreecommitdiff
path: root/helix-term/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term/src/ui')
-rw-r--r--helix-term/src/ui/completion.rs6
-rw-r--r--helix-term/src/ui/editor.rs54
-rw-r--r--helix-term/src/ui/mod.rs28
-rw-r--r--helix-term/src/ui/prompt.rs19
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);
}