mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: make emacs readline keybindings configurable (#382)
This commit is contained in:
parent
ac039c628e
commit
d57a9642db
10 changed files with 204 additions and 177 deletions
|
|
@ -171,40 +171,56 @@ keymap = [
|
||||||
[input]
|
[input]
|
||||||
|
|
||||||
keymap = [
|
keymap = [
|
||||||
{ on = [ "<C-q>" ], exec = "close", desc = "Cancel input" },
|
{ on = [ "<C-q>" ], exec = "close", desc = "Cancel input" },
|
||||||
{ on = [ "<Enter>" ], exec = "close --submit", desc = "Submit the input" },
|
{ on = [ "<Enter>" ], exec = "close --submit", desc = "Submit the input" },
|
||||||
{ on = [ "<Esc>" ], exec = "escape", desc = "Go back the normal mode, or cancel input" },
|
{ on = [ "<Esc>" ], exec = "escape", desc = "Go back the normal mode, or cancel input" },
|
||||||
|
|
||||||
# Mode
|
# Mode
|
||||||
{ on = [ "i" ], exec = "insert", desc = "Enter insert mode" },
|
{ on = [ "i" ], exec = "insert", desc = "Enter insert mode" },
|
||||||
{ on = [ "a" ], exec = "insert --append", desc = "Enter append mode" },
|
{ on = [ "a" ], exec = "insert --append", desc = "Enter append mode" },
|
||||||
|
{ on = [ "I" ], exec = [ "move -999", "insert" ], desc = "Move to the BOL, and enter insert mode" },
|
||||||
|
{ on = [ "A" ], exec = [ "move 999", "insert --append" ], desc = "Move to the EOL, and enter append mode" },
|
||||||
{ on = [ "v" ], exec = "visual", desc = "Enter visual mode" },
|
{ on = [ "v" ], exec = "visual", desc = "Enter visual mode" },
|
||||||
{ on = [ "V" ], exec = [ "move -999", "visual", "move 999" ], desc = "Enter visual mode and select all" },
|
{ on = [ "V" ], exec = [ "move -999", "visual", "move 999" ], desc = "Enter visual mode and select all" },
|
||||||
|
|
||||||
# Navigation
|
# Character-wise movement
|
||||||
{ on = [ "h" ], exec = "move -1", desc = "Move cursor left" },
|
{ on = [ "h" ], exec = "move -1", desc = "Move back a character" },
|
||||||
{ on = [ "l" ], exec = "move 1", desc = "Move cursor right" },
|
{ on = [ "l" ], exec = "move 1", desc = "Move forward a character" },
|
||||||
|
{ on = [ "<Left>" ], exec = "move -1", desc = "Move back a character" },
|
||||||
|
{ on = [ "<Right>" ], exec = "move 1", desc = "Move forward a character" },
|
||||||
|
{ on = [ "<C-b>" ], exec = "move -1", desc = "Move back a character" },
|
||||||
|
{ on = [ "<C-f>" ], exec = "move 1", desc = "Move forward a character" },
|
||||||
|
|
||||||
{ on = [ "0" ], exec = "move -999", desc = "Move to the BOL" },
|
# Word-wise movement
|
||||||
{ on = [ "$" ], exec = "move 999", desc = "Move to the EOL" },
|
{ on = [ "b" ], exec = "backward", desc = "Move back to the start of the current or previous word" },
|
||||||
{ on = [ "I" ], exec = [ "move -999", "insert" ], desc = "Move to the BOL, and enter insert mode" },
|
{ on = [ "w" ], exec = "forward", desc = "Move forward to the start of the next word" },
|
||||||
{ on = [ "A" ], exec = [ "move 999", "insert --append" ], desc = "Move to the EOL, and enter append mode" },
|
{ on = [ "e" ], exec = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
|
||||||
|
{ on = [ "<A-b>" ], exec = "backward", desc = "Move back to the start of the current or previous word" },
|
||||||
|
{ on = [ "<A-f>" ], exec = "forward --end-of-word", desc = "Move forward to the end of the current or next word" },
|
||||||
|
|
||||||
{ on = [ "<Left>" ], exec = "move -1", desc = "Move cursor left" },
|
# Line-wise movement
|
||||||
{ on = [ "<Right>" ], exec = "move 1", desc = "Move cursor right" },
|
{ on = [ "0" ], exec = "move -999", desc = "Move to the BOL" },
|
||||||
|
{ on = [ "$" ], exec = "move 999", desc = "Move to the EOL" },
|
||||||
|
{ on = [ "<C-a>" ], exec = "move -999", desc = "Move to the BOL" },
|
||||||
|
{ on = [ "<C-e>" ], exec = "move 999", desc = "Move to the EOL" },
|
||||||
|
|
||||||
{ on = [ "b" ], exec = "backward", desc = "Move to the beginning of the previous word" },
|
# Delete
|
||||||
{ on = [ "w" ], exec = "forward", desc = "Move to the beginning of the next word" },
|
{ on = [ "<Backspace>" ], exec = "backspace", desc = "Delete the character before the cursor" },
|
||||||
{ on = [ "e" ], exec = "forward --end-of-word", desc = "Move to the end of the next word" },
|
{ on = [ "<C-h>" ], exec = "backspace", desc = "Delete the character before the cursor" },
|
||||||
|
{ on = [ "<C-d>" ], exec = "backspace --under", desc = "Delete the character under the cursor" },
|
||||||
|
|
||||||
# Deletion
|
# Kill
|
||||||
|
{ on = [ "<C-u>" ], exec = "kill bol", desc = "Kill backwards to the BOL" },
|
||||||
|
{ on = [ "<C-k>" ], exec = "kill eol", desc = "Kill forwards to the EOL" },
|
||||||
|
{ on = [ "<C-w>" ], exec = "kill backward", desc = "Kill backwards to the start of the current word" },
|
||||||
|
{ on = [ "<A-d>" ], exec = "kill forward", desc = "Kill forwards to the end of the current word" },
|
||||||
|
|
||||||
|
# Cut/Yank/Paste
|
||||||
{ on = [ "d" ], exec = "delete --cut", desc = "Cut the selected characters" },
|
{ on = [ "d" ], exec = "delete --cut", desc = "Cut the selected characters" },
|
||||||
{ on = [ "D" ], exec = [ "delete --cut", "move 999" ], desc = "Cut until the EOL" },
|
{ on = [ "D" ], exec = [ "delete --cut", "move 999" ], desc = "Cut until the EOL" },
|
||||||
{ on = [ "c" ], exec = "delete --cut --insert", desc = "Cut the selected characters, and enter insert mode" },
|
{ on = [ "c" ], exec = "delete --cut --insert", desc = "Cut the selected characters, and enter insert mode" },
|
||||||
{ on = [ "C" ], exec = [ "delete --cut --insert", "move 999" ], desc = "Cut until the EOL, and enter insert mode" },
|
{ on = [ "C" ], exec = [ "delete --cut --insert", "move 999" ], desc = "Cut until the EOL, and enter insert mode" },
|
||||||
{ on = [ "x" ], exec = [ "delete --cut", "move 1 --in-operating" ], desc = "Cut the current character" },
|
{ on = [ "x" ], exec = [ "delete --cut", "move 1 --in-operating" ], desc = "Cut the current character" },
|
||||||
|
|
||||||
# Yank/Paste
|
|
||||||
{ on = [ "y" ], exec = "yank", desc = "Copy the selected characters" },
|
{ on = [ "y" ], exec = "yank", desc = "Copy the selected characters" },
|
||||||
{ on = [ "p" ], exec = "paste", desc = "Paste the copied characters after the cursor" },
|
{ on = [ "p" ], exec = "paste", desc = "Paste the copied characters after the cursor" },
|
||||||
{ on = [ "P" ], exec = "paste --before", desc = "Paste the copied characters before the cursor" },
|
{ on = [ "P" ], exec = "paste --before", desc = "Paste the copied characters before the cursor" },
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,8 @@ impl TryFrom<Vec<i16>> for Offset {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Offset {
|
||||||
|
#[inline]
|
||||||
|
pub fn line() -> Self { Self { x: 0, y: 0, width: u16::MAX, height: 1 } }
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use yazi_config::keymap::Exec;
|
use yazi_config::{keymap::Exec, popup::{Offset, Origin, Position}};
|
||||||
|
|
||||||
use crate::help::Help;
|
use crate::{help::Help, input::Input};
|
||||||
|
|
||||||
pub struct Opt;
|
pub struct Opt;
|
||||||
|
|
||||||
|
|
@ -10,7 +10,10 @@ impl From<&Exec> for Opt {
|
||||||
|
|
||||||
impl Help {
|
impl Help {
|
||||||
pub fn filter(&mut self, _: impl Into<Opt>) -> bool {
|
pub fn filter(&mut self, _: impl Into<Opt>) -> bool {
|
||||||
self.in_filter = Some(Default::default());
|
let mut input = Input::default();
|
||||||
|
input.position = Position::new(Origin::BottomLeft, Offset::line());
|
||||||
|
|
||||||
|
self.in_filter = Some(input);
|
||||||
self.filter_apply();
|
self.filter_apply();
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crossterm::event::KeyCode;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
use yazi_config::{keymap::{Control, Key, KeymapLayer}, KEYMAP};
|
use yazi_config::{keymap::{Control, Key, KeymapLayer}, KEYMAP};
|
||||||
use yazi_shared::Term;
|
use yazi_shared::Term;
|
||||||
|
|
@ -65,11 +66,14 @@ impl Help {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.type_(key) {
|
let b = match &key {
|
||||||
return self.filter_apply();
|
Key { code: KeyCode::Backspace, shift: false, ctrl: false, alt: false } => {
|
||||||
}
|
input.backspace(false)
|
||||||
|
}
|
||||||
|
_ => input.type_(key),
|
||||||
|
};
|
||||||
|
|
||||||
false
|
if b { self.filter_apply() } else { false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
38
yazi-core/src/input/commands/backspace.rs
Normal file
38
yazi-core/src/input/commands/backspace.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
use yazi_config::keymap::Exec;
|
||||||
|
|
||||||
|
use crate::input::Input;
|
||||||
|
|
||||||
|
pub struct Opt {
|
||||||
|
under: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Exec> for Opt {
|
||||||
|
fn from(e: &Exec) -> Self { Self { under: e.named.contains_key("under") } }
|
||||||
|
}
|
||||||
|
impl From<bool> for Opt {
|
||||||
|
fn from(under: bool) -> Self { Self { under } }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Input {
|
||||||
|
pub fn backspace(&mut self, opt: impl Into<Opt>) -> bool {
|
||||||
|
let opt = opt.into() as Opt;
|
||||||
|
let snap = self.snaps.current_mut();
|
||||||
|
|
||||||
|
if !opt.under && snap.cursor < 1 {
|
||||||
|
return false;
|
||||||
|
} else if opt.under && snap.cursor >= snap.value.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.under {
|
||||||
|
snap.value.remove(snap.idx(snap.cursor).unwrap());
|
||||||
|
self.move_(0);
|
||||||
|
} else {
|
||||||
|
snap.value.remove(snap.idx(snap.cursor - 1).unwrap());
|
||||||
|
self.move_(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.flush_value();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,14 +14,13 @@ impl From<&Exec> for Opt {
|
||||||
impl Input {
|
impl Input {
|
||||||
pub fn forward(&mut self, opt: impl Into<Opt>) -> bool {
|
pub fn forward(&mut self, opt: impl Into<Opt>) -> bool {
|
||||||
let opt = opt.into() as Opt;
|
let opt = opt.into() as Opt;
|
||||||
|
|
||||||
let snap = self.snap();
|
let snap = self.snap();
|
||||||
if snap.value.is_empty() {
|
|
||||||
return self.move_(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut it = snap.value.chars().skip(snap.cursor).enumerate();
|
let mut it = snap.value.chars().skip(snap.cursor).enumerate();
|
||||||
let mut prev = CharKind::new(it.next().unwrap().1);
|
let Some(mut prev) = it.next().map(|(_, c)| CharKind::new(c)) else {
|
||||||
|
return self.move_(0);
|
||||||
|
};
|
||||||
|
|
||||||
for (i, c) in it {
|
for (i, c) in it {
|
||||||
let c = CharKind::new(c);
|
let c = CharKind::new(c);
|
||||||
let b = if opt.end_of_word {
|
let b = if opt.end_of_word {
|
||||||
|
|
|
||||||
96
yazi-core/src/input/commands/kill.rs
Normal file
96
yazi-core/src/input/commands/kill.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
use std::ops::RangeBounds;
|
||||||
|
|
||||||
|
use yazi_config::keymap::Exec;
|
||||||
|
use yazi_shared::CharKind;
|
||||||
|
|
||||||
|
use crate::input::Input;
|
||||||
|
|
||||||
|
pub struct Opt<'a> {
|
||||||
|
kind: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Exec> for Opt<'a> {
|
||||||
|
fn from(e: &'a Exec) -> Self {
|
||||||
|
Self { kind: e.args.first().map(|s| s.as_str()).unwrap_or_default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Input {
|
||||||
|
fn kill_range(&mut self, range: impl RangeBounds<usize>) -> bool {
|
||||||
|
let snap = self.snap_mut();
|
||||||
|
snap.cursor = match range.start_bound() {
|
||||||
|
std::ops::Bound::Included(i) => *i,
|
||||||
|
std::ops::Bound::Excluded(_) => unreachable!(),
|
||||||
|
std::ops::Bound::Unbounded => 0,
|
||||||
|
};
|
||||||
|
if snap.value.drain(range).next().is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.move_(0);
|
||||||
|
self.flush_value();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Searches for a word boundary and returns the movement in the cursor
|
||||||
|
/// position.
|
||||||
|
///
|
||||||
|
/// A word boundary is where the [`CharKind`] changes.
|
||||||
|
///
|
||||||
|
/// If `skip_whitespace_first` is true, we skip initial whitespace.
|
||||||
|
/// Otherwise, we skip whitespace after reaching a word boundary.
|
||||||
|
///
|
||||||
|
/// If `stop_before_boundary` is true, returns how many characters the cursor
|
||||||
|
/// needs to move to be at the character *BEFORE* the word boundary, or until
|
||||||
|
/// the end of the iterator.
|
||||||
|
///
|
||||||
|
/// Otherwise, returns how many characters to move to reach right *AFTER* the
|
||||||
|
/// word boundary, or the end of the iterator.
|
||||||
|
fn find_word_boundary(input: impl Iterator<Item = char> + Clone) -> usize {
|
||||||
|
fn count_spaces(input: impl Iterator<Item = char>) -> usize {
|
||||||
|
// Move until we don't see any more whitespace.
|
||||||
|
input.take_while(|&c| CharKind::new(c) == CharKind::Space).count()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count_characters(mut input: impl Iterator<Item = char>) -> usize {
|
||||||
|
// Determine the current character class.
|
||||||
|
let first = match input.next() {
|
||||||
|
Some(c) => CharKind::new(c),
|
||||||
|
None => return 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Move until we see a different character class or the end of the iterator.
|
||||||
|
input.take_while(|&c| CharKind::new(c) == first).count() + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let spaces = count_spaces(input.clone());
|
||||||
|
spaces + count_characters(input.skip(spaces))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kill<'a>(&mut self, opt: impl Into<Opt<'a>>) -> bool {
|
||||||
|
let opt = opt.into() as Opt;
|
||||||
|
let snap = self.snap_mut();
|
||||||
|
|
||||||
|
match opt.kind.as_bytes() {
|
||||||
|
b"bol" => {
|
||||||
|
let end = snap.idx(snap.cursor).unwrap_or(snap.len());
|
||||||
|
self.kill_range(..end)
|
||||||
|
}
|
||||||
|
b"eol" => {
|
||||||
|
let start = snap.idx(snap.cursor).unwrap_or(snap.len());
|
||||||
|
self.kill_range(start..)
|
||||||
|
}
|
||||||
|
b"backward" => {
|
||||||
|
let end = snap.idx(snap.cursor).unwrap_or(snap.len());
|
||||||
|
let start = end - Self::find_word_boundary(snap.value[..end].chars().rev());
|
||||||
|
self.kill_range(start..end)
|
||||||
|
}
|
||||||
|
b"forward" => {
|
||||||
|
let start = snap.idx(snap.cursor).unwrap_or(snap.len());
|
||||||
|
let end = start + Self::find_word_boundary(snap.value[start..].chars());
|
||||||
|
self.kill_range(start..end)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod backspace;
|
||||||
mod backward;
|
mod backward;
|
||||||
mod close;
|
mod close;
|
||||||
mod complete;
|
mod complete;
|
||||||
|
|
@ -5,6 +6,7 @@ mod delete;
|
||||||
mod escape;
|
mod escape;
|
||||||
mod forward;
|
mod forward;
|
||||||
mod insert;
|
mod insert;
|
||||||
|
mod kill;
|
||||||
mod move_;
|
mod move_;
|
||||||
mod paste;
|
mod paste;
|
||||||
mod redo;
|
mod redo;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
use std::ops::RangeBounds;
|
|
||||||
|
|
||||||
use crossterm::event::KeyCode;
|
|
||||||
use yazi_config::keymap::{Exec, Key};
|
use yazi_config::keymap::{Exec, Key};
|
||||||
use yazi_shared::CharKind;
|
|
||||||
|
|
||||||
use crate::input::{Input, InputMode};
|
use crate::input::{Input, InputMode};
|
||||||
|
|
||||||
|
|
@ -13,77 +9,6 @@ impl From<&Exec> for Opt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
/// Searches for a word boundary and returns the movement in the cursor
|
|
||||||
/// position.
|
|
||||||
///
|
|
||||||
/// A word boundary is where the [`CharKind`] changes.
|
|
||||||
///
|
|
||||||
/// If `skip_whitespace_first` is true, we skip initial whitespace.
|
|
||||||
/// Otherwise, we skip whitespace after reaching a word boundary.
|
|
||||||
///
|
|
||||||
/// If `stop_before_boundary` is true, returns how many characters the cursor
|
|
||||||
/// needs to move to be at the character *BEFORE* the word boundary, or until
|
|
||||||
/// the end of the iterator.
|
|
||||||
///
|
|
||||||
/// Otherwise, returns how many characters to move to reach right *AFTER* the
|
|
||||||
/// word boundary, or the end of the iterator.
|
|
||||||
fn find_word_boundary(input: impl Iterator<Item = char> + Clone) -> usize {
|
|
||||||
fn count_spaces(input: impl Iterator<Item = char>) -> usize {
|
|
||||||
// Move until we don't see any more whitespace.
|
|
||||||
input.take_while(|&c| CharKind::new(c) == CharKind::Space).count()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn count_characters(mut input: impl Iterator<Item = char>) -> usize {
|
|
||||||
// Determine the current character class.
|
|
||||||
let first = match input.next() {
|
|
||||||
Some(c) => CharKind::new(c),
|
|
||||||
None => return 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Move until we see a different character class or the end of the iterator.
|
|
||||||
input.take_while(|&c| CharKind::new(c) == first).count() + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let spaces = count_spaces(input.clone());
|
|
||||||
spaces + count_characters(input.skip(spaces))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn delete_range(&mut self, range: impl RangeBounds<usize>) -> bool {
|
|
||||||
let snap = self.snap_mut();
|
|
||||||
snap.cursor = match range.start_bound() {
|
|
||||||
std::ops::Bound::Included(i) => *i,
|
|
||||||
std::ops::Bound::Excluded(_) => unreachable!(),
|
|
||||||
std::ops::Bound::Unbounded => 0,
|
|
||||||
};
|
|
||||||
if snap.value.drain(range).next().is_none() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.move_(0);
|
|
||||||
self.flush_value();
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn backspace(&mut self, under: bool) -> bool {
|
|
||||||
let snap = self.snaps.current_mut();
|
|
||||||
if !under && snap.cursor < 1 {
|
|
||||||
return false;
|
|
||||||
} else if under && snap.cursor >= snap.value.len() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if under {
|
|
||||||
snap.value.remove(snap.idx(snap.cursor).unwrap());
|
|
||||||
self.move_(0);
|
|
||||||
} else {
|
|
||||||
snap.value.remove(snap.idx(snap.cursor - 1).unwrap());
|
|
||||||
self.move_(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.flush_value();
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_(&mut self, key: &Key) -> bool {
|
pub fn type_(&mut self, key: &Key) -> bool {
|
||||||
if self.mode() != InputMode::Insert {
|
if self.mode() != InputMode::Insert {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -93,71 +18,6 @@ impl Input {
|
||||||
let mut bits = [0; 4];
|
let mut bits = [0; 4];
|
||||||
return self.type_str(c.encode_utf8(&mut bits));
|
return self.type_str(c.encode_utf8(&mut bits));
|
||||||
}
|
}
|
||||||
|
false
|
||||||
use KeyCode::{Backspace, Char as C};
|
|
||||||
|
|
||||||
match key {
|
|
||||||
// Move to the start of the line
|
|
||||||
Key { code: C('a'), shift: false, ctrl: true, alt: false } => self.move_(isize::MIN),
|
|
||||||
// Move to the end of the line
|
|
||||||
Key { code: C('e'), shift: false, ctrl: true, alt: false } => self.move_(isize::MAX),
|
|
||||||
|
|
||||||
// Move back a character
|
|
||||||
Key { code: C('b'), shift: false, ctrl: true, alt: false } => self.move_(-1),
|
|
||||||
// Move forward a character
|
|
||||||
Key { code: C('f'), shift: false, ctrl: true, alt: false } => self.move_(1),
|
|
||||||
|
|
||||||
// Delete the character before the cursor
|
|
||||||
Key { code: Backspace, shift: false, ctrl: false, alt: false } => self.backspace(false),
|
|
||||||
Key { code: C('h'), shift: false, ctrl: true, alt: false } => self.backspace(false),
|
|
||||||
// Delete the character under the cursor
|
|
||||||
Key { code: C('d'), shift: false, ctrl: true, alt: false } => self.backspace(true),
|
|
||||||
|
|
||||||
// Move back to the start of the current or previous word
|
|
||||||
Key { code: C('b'), shift: false, ctrl: false, alt: true } => {
|
|
||||||
let snap = self.snap();
|
|
||||||
let idx = snap.idx(snap.cursor).unwrap_or(snap.len());
|
|
||||||
|
|
||||||
let step = Self::find_word_boundary(snap.value[..idx].chars().rev());
|
|
||||||
self.move_(-(step as isize))
|
|
||||||
}
|
|
||||||
// Move forward to the end of the next word
|
|
||||||
Key { code: C('f'), shift: false, ctrl: false, alt: true } => {
|
|
||||||
let snap = self.snap();
|
|
||||||
let step = Self::find_word_boundary(snap.value.chars().skip(snap.cursor));
|
|
||||||
self.move_(step as isize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kill backwards to the start of the line
|
|
||||||
Key { code: C('u'), shift: false, ctrl: true, alt: false } => {
|
|
||||||
let snap = self.snap_mut();
|
|
||||||
let end = snap.idx(snap.cursor).unwrap_or(snap.len());
|
|
||||||
self.delete_range(..end)
|
|
||||||
}
|
|
||||||
// Kill forwards to the end of the line
|
|
||||||
Key { code: C('k'), shift: false, ctrl: true, alt: false } => {
|
|
||||||
let snap = self.snap_mut();
|
|
||||||
let start = snap.idx(snap.cursor).unwrap_or(snap.len());
|
|
||||||
self.delete_range(start..)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kill backwards to the start of the current word
|
|
||||||
Key { code: C('w'), shift: false, ctrl: true, alt: false }
|
|
||||||
| Key { code: Backspace, shift: false, ctrl: false, alt: true } => {
|
|
||||||
let snap = self.snap_mut();
|
|
||||||
let end = snap.idx(snap.cursor).unwrap_or(snap.len());
|
|
||||||
let start = end - Self::find_word_boundary(snap.value[..end].chars().rev());
|
|
||||||
self.delete_range(start..end)
|
|
||||||
}
|
|
||||||
// Kill forwards to the end of the current word
|
|
||||||
Key { code: C('d'), shift: false, ctrl: false, alt: true } => {
|
|
||||||
let snap = self.snap_mut();
|
|
||||||
let start = snap.idx(snap.cursor).unwrap_or(snap.len());
|
|
||||||
let end = start + Self::find_word_boundary(snap.value[start..].chars());
|
|
||||||
self.delete_range(start..end)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ impl<'a> Executor<'a> {
|
||||||
if self.cx.which.visible {
|
if self.cx.which.visible {
|
||||||
return self.cx.which.press(key);
|
return self.cx.which.press(key);
|
||||||
}
|
}
|
||||||
if self.cx.input.visible && self.cx.input.type_(&key) {
|
if self.cx.help.visible && self.cx.help.type_(&key) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if self.cx.help.visible && self.cx.help.type_(&key) {
|
if self.cx.input.visible && self.cx.input.type_(&key) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,6 +212,8 @@ impl<'a> Executor<'a> {
|
||||||
on!(close);
|
on!(close);
|
||||||
on!(escape);
|
on!(escape);
|
||||||
on!(move_, "move");
|
on!(move_, "move");
|
||||||
|
on!(backward);
|
||||||
|
on!(forward);
|
||||||
|
|
||||||
if exec.cmd.as_str() == "complete" {
|
if exec.cmd.as_str() == "complete" {
|
||||||
return if exec.named.contains_key("trigger") {
|
return if exec.named.contains_key("trigger") {
|
||||||
|
|
@ -226,10 +228,7 @@ impl<'a> Executor<'a> {
|
||||||
on!(insert);
|
on!(insert);
|
||||||
on!(visual);
|
on!(visual);
|
||||||
|
|
||||||
on!(backward);
|
|
||||||
on!(forward);
|
|
||||||
on!(delete);
|
on!(delete);
|
||||||
|
|
||||||
on!(yank);
|
on!(yank);
|
||||||
on!(paste);
|
on!(paste);
|
||||||
|
|
||||||
|
|
@ -241,7 +240,12 @@ impl<'a> Executor<'a> {
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InputMode::Insert => false,
|
InputMode::Insert => {
|
||||||
|
on!(backspace);
|
||||||
|
on!(kill);
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue