diff --git a/Cargo.lock b/Cargo.lock index c9d1bf43..fcfebb84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1511,18 +1511,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.171" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "e91f70896d6720bc714a4a57d22fc91f1db634680e65c8efe13323f1fa38d53f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "a6250dde8342e0232232be9ca3db7aa40aceb5a3e5dd9bddbc00d99a007cde49" dependencies = [ "proc-macro2", "quote", @@ -1560,9 +1560,9 @@ dependencies = [ [[package]] name = "signal-hook" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b824b6e687aff278cdbf3b36f07aa52d4bd4099699324d5da86a2ebce3aa00b3" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", diff --git a/config/keymap.toml b/config/keymap.toml index 4f164326..d7014ea5 100644 --- a/config/keymap.toml +++ b/config/keymap.toml @@ -130,5 +130,5 @@ keymap = [ { on = [ "d" ], exec = "delete" }, { on = [ "c" ], exec = "delete --insert" }, - { on = [ "x" ], exec = [ "delete", "move 1" ] } + { on = [ "x" ], exec = [ "delete", "move 1 --in-operating" ] } ] diff --git a/docs/keymap.md b/docs/keymap.md index e3941553..bebcce58 100644 --- a/docs/keymap.md +++ b/docs/keymap.md @@ -114,22 +114,23 @@ ## input -### Normal mode - - close: Cancel input. - `--submit`: Submit the input. - escape: Cancel visual mode and enter normal mode. +- move: Move the cursor left or right. + + - `n`: Move the cursor n characters left or right. Negative value for left, positive value for right. + - `--in-operating`: Move the cursor only if its currently waiting for an operation. + +### Normal mode + - insert: Enter insert mode. - `--append`: Insert after the cursor. - visual: Enter visual mode. -- move: Move the cursor left or right. - - - `n`: Move the cursor n characters left or right. Negative value for left, positive value for right. - - backward: Move to the beginning of the previous word. - forward: Move to the beginning of the next word. diff --git a/docs/yazi.md b/docs/yazi.md index 53af3fdc..d46e7289 100644 --- a/docs/yazi.md +++ b/docs/yazi.md @@ -44,8 +44,9 @@ Available parameters are as follows: - cmd: The program to open the selected files - args: Arguments to be passed - - `$n`: The n-th selected item - - `$*`: All selected files + - `"$n"`: The N-th selected file + - `"$*"`: All selected files + - `"foo"`: Literal string to be passed - block: Open in a blocking manner. After setting this, Yazi will hide into a secondary screen and display the program on the main screen until it exits. During this time, it can receive I/O signals, which is useful for interactive programs. ## open diff --git a/src/config/keymap/key.rs b/src/config/keymap/key.rs index 48b0f977..5263126e 100644 --- a/src/config/keymap/key.rs +++ b/src/config/keymap/key.rs @@ -11,6 +11,16 @@ pub struct Key { pub alt: bool, } +impl Key { + #[inline] + pub fn plain(&self) -> Option { + match self.code { + KeyCode::Char(c) if !self.ctrl && !self.alt => Some(c), + _ => None, + } + } +} + impl Default for Key { fn default() -> Self { Self { code: KeyCode::Null, shift: false, ctrl: false, alt: false } } } diff --git a/src/core/input/input.rs b/src/core/input/input.rs index 90ff1c09..76f335a4 100644 --- a/src/core/input/input.rs +++ b/src/core/input/input.rs @@ -65,6 +65,9 @@ impl Input { let _ = cb.send(if submit { Ok(self.value.clone()) } else { Err(anyhow!("canceled")) }); } + self.op = InputOp::None; + self.range = None; + self.mode = InputMode::Insert; self.visible = false; true @@ -131,6 +134,11 @@ impl Input { self.handle_op(include) || self.cursor != old } + #[inline] + pub fn move_in_operating(&mut self, step: isize) -> bool { + if self.op == InputOp::None { false } else { self.move_(step) } + } + pub fn backward(&mut self) -> bool { if self.cursor == 0 { return self.handle_op(false); @@ -282,6 +290,37 @@ impl Input { (area.x + width + 1, area.y + 1) } + pub fn range(&self) -> Option { + if let Some((start, end)) = self.range { + let end = self + .value + .chars() + .skip(start) + .enumerate() + .take_while(|(i, _)| *i < end) + .map(|(_, c)| c) + .collect::() + .width() as u16; + + let start = self + .value + .chars() + .enumerate() + .take_while(|(i, _)| *i < start) + .map(|(_, c)| c) + .collect::() + .width() as u16; + + return Some(Rect { + x: self.position.0 + 1 + start, + y: self.position.1 + 3, + width: end, + height: 1, + }); + } + None + } + #[inline] fn count(&self) -> usize { self.value.chars().count() } diff --git a/src/main.rs b/src/main.rs index ed61b898..405bbc1e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![feature(if_let_guard)] + use ui::App; mod config; diff --git a/src/ui/dispatcher.rs b/src/ui/dispatcher.rs index 2f4b0a79..d4bda72e 100644 --- a/src/ui/dispatcher.rs +++ b/src/ui/dispatcher.rs @@ -35,13 +35,13 @@ impl Executor { } else if layer == 2 { render = Self::select(cx, e) || render; } else if layer == 3 { - render = Self::input(cx, Some(e), key.code) || render; + render = Self::input(cx, Some(e), &key) || render; } } } if layer == 3 && !matched { - render = Self::input(cx, None, key.code); + render = Self::input(cx, None, &key); } render } @@ -166,27 +166,34 @@ impl Executor { } } - fn input(cx: &mut Ctx, exec: Option<&Exec>, code: KeyCode) -> bool { + fn input(cx: &mut Ctx, exec: Option<&Exec>, key: &Key) -> bool { let exec = if let Some(e) = exec { e } else { if cx.input.mode() == InputMode::Insert { - if let KeyCode::Char(c) = code { + if let KeyCode::Char(c) = key.code { return cx.input.type_(c); } } return false; }; - match exec.cmd.as_str() { - "close" => return cx.input.close(exec.named.contains_key("submit")), - "escape" => return cx.input.escape(), + if cx.input.mode() != InputMode::Insert || key.plain().is_none() { + match exec.cmd.as_str() { + "close" => return cx.input.close(exec.named.contains_key("submit")), + "escape" => return cx.input.escape(), - "move" => { - let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0); - return cx.input.move_(step); + "move" => { + let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0); + let in_operating = exec.named.contains_key("in-operating"); + return if in_operating { + cx.input.move_in_operating(step) + } else { + cx.input.move_(step) + }; + } + _ => {} } - _ => {} } match cx.input.mode() { @@ -200,13 +207,10 @@ impl Executor { _ => false, }, InputMode::Insert => match exec.cmd.as_str() { + _ if let Some(c) = key.plain() => cx.input.type_(c), + "backspace" => cx.input.backspace(), - _ => { - if let KeyCode::Char(c) = code { - return cx.input.type_(c); - } - false - } + _ => false }, } } diff --git a/src/ui/input.rs b/src/ui/input.rs index ceb3c4df..275604dd 100644 --- a/src/ui/input.rs +++ b/src/ui/input.rs @@ -32,6 +32,10 @@ impl<'a> Widget for Input<'a> { .style(Style::default().fg(Color::White)) .render(area, buf); + if let Some(range) = input.range() { + buf.set_style(range, Style::default().bg(Color::Rgb(72, 77, 102))) + } + let _ = match input.mode() { InputMode::Insert => Term::set_cursor_bar(), _ => Term::set_cursor_block(),