aboutsummaryrefslogtreecommitdiff
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/editor.rs76
-rw-r--r--helix-term/src/ui/info.rs2
-rw-r--r--helix-term/src/ui/mod.rs1
-rw-r--r--helix-term/src/ui/picker.rs1
-rw-r--r--helix-term/src/ui/prompt.rs58
5 files changed, 118 insertions, 20 deletions
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index 482a4117..3e131bf1 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -2,7 +2,7 @@ use crate::{
commands,
compositor::{Component, Context, EventResult},
key,
- keymap::Keymaps,
+ keymap::{KeymapResult, Keymaps},
ui::{Completion, ProgressSpinners},
};
@@ -15,6 +15,7 @@ use helix_core::{
use helix_view::{
document::Mode,
graphics::{CursorKind, Modifier, Rect, Style},
+ info::Info,
input::KeyEvent,
keyboard::{KeyCode, KeyModifiers},
Document, Editor, Theme, View,
@@ -30,6 +31,7 @@ pub struct EditorView {
last_insert: (commands::Command, Vec<KeyEvent>),
completion: Option<Completion>,
spinners: ProgressSpinners,
+ pub autoinfo: Option<Info>,
}
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
@@ -48,6 +50,7 @@ impl EditorView {
last_insert: (commands::Command::normal_mode, Vec::new()),
completion: None,
spinners: ProgressSpinners::default(),
+ autoinfo: None,
}
}
@@ -594,19 +597,53 @@ impl EditorView {
);
}
- fn insert_mode(&self, cx: &mut commands::Context, event: KeyEvent) {
- if let Some(command) = self.keymaps[&Mode::Insert].get(&event) {
- command.execute(cx);
- } else if let KeyEvent {
- code: KeyCode::Char(ch),
- ..
- } = event
- {
- commands::insert::insert_char(cx, ch);
+ /// Handle events by looking them up in `self.keymaps`. Returns None
+ /// if event was handled (a command was executed or a subkeymap was
+ /// activated). Only KeymapResult::{NotFound, Cancelled} is returned
+ /// otherwise.
+ fn handle_keymap_event(
+ &mut self,
+ mode: Mode,
+ cxt: &mut commands::Context,
+ event: KeyEvent,
+ ) -> Option<KeymapResult> {
+ self.autoinfo = None;
+ match self.keymaps.get_mut(&mode).unwrap().get(event) {
+ KeymapResult::Matched(command) => command.execute(cxt),
+ KeymapResult::Pending(node) => self.autoinfo = Some(node.into()),
+ k @ KeymapResult::NotFound | k @ KeymapResult::Cancelled(_) => return Some(k),
}
+ None
}
- fn command_mode(&self, mode: Mode, cxt: &mut commands::Context, event: KeyEvent) {
+ fn insert_mode(&mut self, cx: &mut commands::Context, event: KeyEvent) {
+ if let Some(keyresult) = self.handle_keymap_event(Mode::Insert, cx, event) {
+ match keyresult {
+ KeymapResult::NotFound => {
+ if let Some(ch) = event.char() {
+ commands::insert::insert_char(cx, ch)
+ }
+ }
+ KeymapResult::Cancelled(pending) => {
+ for ev in pending {
+ match ev.char() {
+ Some(ch) => commands::insert::insert_char(cx, ch),
+ None => {
+ if let KeymapResult::Matched(command) =
+ self.keymaps.get_mut(&Mode::Insert).unwrap().get(ev)
+ {
+ command.execute(cx);
+ }
+ }
+ }
+ }
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ fn command_mode(&mut self, mode: Mode, cxt: &mut commands::Context, event: KeyEvent) {
match event {
// count handling
key!(i @ '0'..='9') => {
@@ -619,8 +656,8 @@ impl EditorView {
// first execute whatever put us into insert mode
self.last_insert.0.execute(cxt);
// then replay the inputs
- for key in &self.last_insert.1 {
- self.insert_mode(cxt, *key)
+ for &key in &self.last_insert.1.clone() {
+ self.insert_mode(cxt, key)
}
}
_ => {
@@ -633,9 +670,7 @@ impl EditorView {
// set the register
cxt.selected_register = cxt.editor.selected_register.take();
- if let Some(command) = self.keymaps[&mode].get(&event) {
- command.execute(cxt);
- }
+ self.handle_keymap_event(mode, cxt, event);
}
}
}
@@ -749,7 +784,11 @@ 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.keymaps[&mode][&key];
+ self.last_insert.0 = match self.keymaps.get_mut(&mode).unwrap().get(key) {
+ KeymapResult::Matched(command) => command,
+ // FIXME: insert mode can only be entered through single KeyCodes
+ _ => unimplemented!(),
+ };
self.last_insert.1.clear();
}
(Mode::Insert, Mode::Normal) => {
@@ -787,9 +826,8 @@ impl Component for EditorView {
);
}
- if let Some(info) = std::mem::take(&mut cx.editor.autoinfo) {
+ if let Some(ref info) = self.autoinfo {
info.render(area, surface, cx);
- cx.editor.autoinfo = Some(info);
}
// render status msg
diff --git a/helix-term/src/ui/info.rs b/helix-term/src/ui/info.rs
index e5f20562..36b096db 100644
--- a/helix-term/src/ui/info.rs
+++ b/helix-term/src/ui/info.rs
@@ -8,7 +8,7 @@ impl Component for Info {
fn render(&self, viewport: Rect, surface: &mut Surface, cx: &mut Context) {
let style = cx.editor.theme.get("ui.popup");
let block = Block::default()
- .title(self.title)
+ .title(self.title.as_str())
.borders(Borders::ALL)
.border_style(style);
let Info { width, height, .. } = self;
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index 288d3d2e..9e71cfe7 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -36,6 +36,7 @@ pub fn regex_prompt(
Prompt::new(
prompt,
+ None,
|_input: &str| Vec::new(), // this is fine because Vec::new() doesn't allocate
move |cx: &mut crate::compositor::Context, input: &str, event: PromptEvent| {
match event {
diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs
index 733be2fc..0b67cd9c 100644
--- a/helix-term/src/ui/picker.rs
+++ b/helix-term/src/ui/picker.rs
@@ -43,6 +43,7 @@ impl<T> Picker<T> {
) -> Self {
let prompt = Prompt::new(
"".to_string(),
+ None,
|_pattern: &str| Vec::new(),
|_editor: &mut Context, _pattern: &str, _event: PromptEvent| {
//
diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs
index 2df1e281..57daef3a 100644
--- a/helix-term/src/ui/prompt.rs
+++ b/helix-term/src/ui/prompt.rs
@@ -20,6 +20,8 @@ pub struct Prompt {
cursor: usize,
completion: Vec<Completion>,
selection: Option<usize>,
+ history_register: Option<char>,
+ history_pos: Option<usize>,
completion_fn: Box<dyn FnMut(&str) -> Vec<Completion>>,
callback_fn: Box<dyn FnMut(&mut Context, &str, PromptEvent)>,
pub doc_fn: Box<dyn Fn(&str) -> Option<&'static str>>,
@@ -54,6 +56,7 @@ pub enum Movement {
impl Prompt {
pub fn new(
prompt: String,
+ history_register: Option<char>,
mut completion_fn: impl FnMut(&str) -> Vec<Completion> + 'static,
callback_fn: impl FnMut(&mut Context, &str, PromptEvent) + 'static,
) -> Self {
@@ -63,6 +66,8 @@ impl Prompt {
cursor: 0,
completion: completion_fn(""),
selection: None,
+ history_register,
+ history_pos: None,
completion_fn: Box::new(completion_fn),
callback_fn: Box::new(callback_fn),
doc_fn: Box::new(|_| None),
@@ -226,6 +231,28 @@ impl Prompt {
self.exit_selection();
}
+ pub fn change_history(&mut self, register: &[String], direction: CompletionDirection) {
+ if register.is_empty() {
+ return;
+ }
+
+ let end = register.len().saturating_sub(1);
+
+ let index = match direction {
+ CompletionDirection::Forward => self.history_pos.map_or(0, |i| i + 1),
+ CompletionDirection::Backward => {
+ self.history_pos.unwrap_or(register.len()).saturating_sub(1)
+ }
+ }
+ .min(end);
+
+ self.line = register[index].clone();
+
+ self.history_pos = Some(index);
+
+ self.move_end();
+ }
+
pub fn change_completion_selection(&mut self, direction: CompletionDirection) {
if self.completion.is_empty() {
return;
@@ -468,10 +495,41 @@ impl Component for Prompt {
self.exit_selection();
} else {
(self.callback_fn)(cx, &self.line, PromptEvent::Validate);
+
+ if let Some(register) = self.history_register {
+ // store in history
+ let register = cx.editor.registers.get_mut(register);
+ register.push(self.line.clone());
+ }
return close_fn;
}
}
KeyEvent {
+ code: KeyCode::Char('p'),
+ modifiers: KeyModifiers::CONTROL,
+ }
+ | KeyEvent {
+ code: KeyCode::Up, ..
+ } => {
+ if let Some(register) = self.history_register {
+ let register = cx.editor.registers.get_mut(register);
+ self.change_history(register.read(), CompletionDirection::Backward);
+ }
+ }
+ KeyEvent {
+ code: KeyCode::Char('n'),
+ modifiers: KeyModifiers::CONTROL,
+ }
+ | KeyEvent {
+ code: KeyCode::Down,
+ ..
+ } => {
+ if let Some(register) = self.history_register {
+ let register = cx.editor.registers.get_mut(register);
+ self.change_history(register.read(), CompletionDirection::Forward);
+ }
+ }
+ KeyEvent {
code: KeyCode::Tab, ..
} => self.change_completion_selection(CompletionDirection::Forward),
KeyEvent {