feat: single-character replace keybind for inputs

This commit is contained in:
darcy 2025-01-02 14:54:36 +11:00 committed by sxyazi
parent 2eed881f12
commit 873f47d214
No known key found for this signature in database
11 changed files with 65 additions and 1 deletions

View file

@ -238,6 +238,7 @@ keymap = [
{ on = "A", run = [ "move 999", "insert --append" ], desc = "Move to the EOL, and enter append mode" },
{ on = "v", run = "visual", desc = "Enter visual mode" },
{ on = "V", run = [ "move -999", "visual", "move 999" ], desc = "Enter visual mode and select all" },
{ on = "r", run = "replace", desc = "Enter replace mode" },
# Character-wise movement
{ on = "h", run = "move -1", desc = "Move back a character" },

View file

@ -32,6 +32,9 @@ impl Input {
CompletionProxy::close();
}
}
InputMode::Replace => {
snap.mode = InputMode::Normal;
}
}
self.snaps.tag(self.limit());

View file

@ -1,4 +1,4 @@
yazi_macro::mod_flat!(
backspace backward close complete delete escape forward insert kill move_ paste redo
show type_ undo visual yank
replace show type_ undo visual yank
);

View file

@ -0,0 +1,20 @@
use yazi_macro::render;
use yazi_shared::event::CmdCow;
use crate::input::{Input, InputMode, op::InputOp};
impl Input {
#[yazi_codegen::command]
pub fn replace(&mut self, _: CmdCow) {
let snap = self.snap_mut();
if snap.mode == InputMode::Normal {
snap.op = InputOp::None;
snap.mode = InputMode::Replace;
} else {
return;
}
render!();
}
}

View file

@ -11,6 +11,18 @@ impl From<CmdCow> for Opt {
impl Input {
pub fn type_(&mut self, key: &Key) -> bool {
if self.mode() == InputMode::Replace {
let Some(c) = key.plain() else {
return false;
};
let snap = self.snaps.current_mut();
snap.mode = InputMode::Normal;
self.replace_str(c.encode_utf8(&mut [0; 4]));
return true;
}
if self.mode() != InputMode::Insert {
return false;
}

View file

@ -46,6 +46,17 @@ impl Input {
render!();
}
pub fn replace_str(&mut self, s: &str) {
let snap = self.snaps.current_mut();
let start = snap.idx(snap.cursor).unwrap();
let end = snap.idx(snap.cursor + 1).unwrap();
snap.value.replace_range(start..end, s);
self.flush_value();
render!();
}
pub(super) fn handle_op(&mut self, cursor: usize, include: bool) -> bool {
let old = self.snap().clone();
let snap = self.snaps.current_mut();

View file

@ -3,6 +3,7 @@ pub enum InputMode {
Normal,
#[default]
Insert,
Replace,
}
impl InputMode {

View file

@ -93,6 +93,9 @@ impl App {
if input.mode() == InputMode::Insert {
input.type_str(&str);
}
if input.mode() == InputMode::Replace {
input.replace_str(&str);
}
}
}
}

View file

@ -259,6 +259,7 @@ impl<'a> Executor<'a> {
InputMode::Normal => {
on!(insert);
on!(visual);
on!(replace);
on!(delete);
on!(yank);
@ -279,6 +280,7 @@ impl<'a> Executor<'a> {
on!(backspace);
on!(kill);
}
InputMode::Replace => {}
}
}

View file

@ -59,6 +59,7 @@ impl Widget for Input<'_> {
_ = match input.mode() {
InputMode::Insert => Term::set_cursor_bar(),
InputMode::Replace => Term::set_cursor_underscore(),
_ => Term::set_cursor_block(),
};
}

View file

@ -159,6 +159,16 @@ impl Term {
queue!(stderr(), SetCursorStyle::SteadyBar)?
})
}
#[inline]
pub(super) fn set_cursor_underscore() -> Result<()> {
use crossterm::cursor::SetCursorStyle;
Ok(if INPUT.cursor_blink {
queue!(stderr(), SetCursorStyle::BlinkingUnderScore)?
} else {
queue!(stderr(), SetCursorStyle::SteadyUnderScore)?
})
}
}
impl Drop for Term {