mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: render selected range
This commit is contained in:
parent
b8d9014d83
commit
9f93c6f34e
9 changed files with 93 additions and 32 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
|
@ -1511,18 +1511,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.171"
|
version = "1.0.173"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9"
|
checksum = "e91f70896d6720bc714a4a57d22fc91f1db634680e65c8efe13323f1fa38d53f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.171"
|
version = "1.0.173"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
|
checksum = "a6250dde8342e0232232be9ca3db7aa40aceb5a3e5dd9bddbc00d99a007cde49"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -1560,9 +1560,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "signal-hook"
|
||||||
version = "0.3.16"
|
version = "0.3.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b824b6e687aff278cdbf3b36f07aa52d4bd4099699324d5da86a2ebce3aa00b3"
|
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
|
|
|
||||||
|
|
@ -130,5 +130,5 @@ keymap = [
|
||||||
|
|
||||||
{ on = [ "d" ], exec = "delete" },
|
{ on = [ "d" ], exec = "delete" },
|
||||||
{ on = [ "c" ], exec = "delete --insert" },
|
{ on = [ "c" ], exec = "delete --insert" },
|
||||||
{ on = [ "x" ], exec = [ "delete", "move 1" ] }
|
{ on = [ "x" ], exec = [ "delete", "move 1 --in-operating" ] }
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -114,22 +114,23 @@
|
||||||
|
|
||||||
## input
|
## input
|
||||||
|
|
||||||
### Normal mode
|
|
||||||
|
|
||||||
- close: Cancel input.
|
- close: Cancel input.
|
||||||
|
|
||||||
- `--submit`: Submit the input.
|
- `--submit`: Submit the input.
|
||||||
|
|
||||||
- escape: Cancel visual mode and enter normal mode.
|
- 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.
|
- insert: Enter insert mode.
|
||||||
|
|
||||||
- `--append`: Insert after the cursor.
|
- `--append`: Insert after the cursor.
|
||||||
|
|
||||||
- visual: Enter visual mode.
|
- 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.
|
- backward: Move to the beginning of the previous word.
|
||||||
- forward: Move to the beginning of the next word.
|
- forward: Move to the beginning of the next word.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,9 @@ Available parameters are as follows:
|
||||||
|
|
||||||
- cmd: The program to open the selected files
|
- cmd: The program to open the selected files
|
||||||
- args: Arguments to be passed
|
- args: Arguments to be passed
|
||||||
- `$n`: The n-th selected item
|
- `"$n"`: The N-th selected file
|
||||||
- `$*`: All selected files
|
- `"$*"`: 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.
|
- 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
|
## open
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,16 @@ pub struct Key {
|
||||||
pub alt: bool,
|
pub alt: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Key {
|
||||||
|
#[inline]
|
||||||
|
pub fn plain(&self) -> Option<char> {
|
||||||
|
match self.code {
|
||||||
|
KeyCode::Char(c) if !self.ctrl && !self.alt => Some(c),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Key {
|
impl Default for Key {
|
||||||
fn default() -> Self { Self { code: KeyCode::Null, shift: false, ctrl: false, alt: false } }
|
fn default() -> Self { Self { code: KeyCode::Null, shift: false, ctrl: false, alt: false } }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,9 @@ impl Input {
|
||||||
let _ = cb.send(if submit { Ok(self.value.clone()) } else { Err(anyhow!("canceled")) });
|
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.mode = InputMode::Insert;
|
||||||
self.visible = false;
|
self.visible = false;
|
||||||
true
|
true
|
||||||
|
|
@ -131,6 +134,11 @@ impl Input {
|
||||||
self.handle_op(include) || self.cursor != old
|
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 {
|
pub fn backward(&mut self) -> bool {
|
||||||
if self.cursor == 0 {
|
if self.cursor == 0 {
|
||||||
return self.handle_op(false);
|
return self.handle_op(false);
|
||||||
|
|
@ -282,6 +290,37 @@ impl Input {
|
||||||
(area.x + width + 1, area.y + 1)
|
(area.x + width + 1, area.y + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn range(&self) -> Option<Rect> {
|
||||||
|
if let Some((start, end)) = self.range {
|
||||||
|
let end = self
|
||||||
|
.value
|
||||||
|
.chars()
|
||||||
|
.skip(start)
|
||||||
|
.enumerate()
|
||||||
|
.take_while(|(i, _)| *i < end)
|
||||||
|
.map(|(_, c)| c)
|
||||||
|
.collect::<String>()
|
||||||
|
.width() as u16;
|
||||||
|
|
||||||
|
let start = self
|
||||||
|
.value
|
||||||
|
.chars()
|
||||||
|
.enumerate()
|
||||||
|
.take_while(|(i, _)| *i < start)
|
||||||
|
.map(|(_, c)| c)
|
||||||
|
.collect::<String>()
|
||||||
|
.width() as u16;
|
||||||
|
|
||||||
|
return Some(Rect {
|
||||||
|
x: self.position.0 + 1 + start,
|
||||||
|
y: self.position.1 + 3,
|
||||||
|
width: end,
|
||||||
|
height: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn count(&self) -> usize { self.value.chars().count() }
|
fn count(&self) -> usize { self.value.chars().count() }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![feature(if_let_guard)]
|
||||||
|
|
||||||
use ui::App;
|
use ui::App;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,13 @@ impl Executor {
|
||||||
} else if layer == 2 {
|
} else if layer == 2 {
|
||||||
render = Self::select(cx, e) || render;
|
render = Self::select(cx, e) || render;
|
||||||
} else if layer == 3 {
|
} 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 {
|
if layer == 3 && !matched {
|
||||||
render = Self::input(cx, None, key.code);
|
render = Self::input(cx, None, &key);
|
||||||
}
|
}
|
||||||
render
|
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 {
|
let exec = if let Some(e) = exec {
|
||||||
e
|
e
|
||||||
} else {
|
} else {
|
||||||
if cx.input.mode() == InputMode::Insert {
|
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 cx.input.type_(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
match exec.cmd.as_str() {
|
if cx.input.mode() != InputMode::Insert || key.plain().is_none() {
|
||||||
"close" => return cx.input.close(exec.named.contains_key("submit")),
|
match exec.cmd.as_str() {
|
||||||
"escape" => return cx.input.escape(),
|
"close" => return cx.input.close(exec.named.contains_key("submit")),
|
||||||
|
"escape" => return cx.input.escape(),
|
||||||
|
|
||||||
"move" => {
|
"move" => {
|
||||||
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0);
|
let step = exec.args.get(0).and_then(|s| s.parse().ok()).unwrap_or(0);
|
||||||
return cx.input.move_(step);
|
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() {
|
match cx.input.mode() {
|
||||||
|
|
@ -200,13 +207,10 @@ impl Executor {
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
InputMode::Insert => match exec.cmd.as_str() {
|
InputMode::Insert => match exec.cmd.as_str() {
|
||||||
|
_ if let Some(c) = key.plain() => cx.input.type_(c),
|
||||||
|
|
||||||
"backspace" => cx.input.backspace(),
|
"backspace" => cx.input.backspace(),
|
||||||
_ => {
|
_ => false
|
||||||
if let KeyCode::Char(c) = code {
|
|
||||||
return cx.input.type_(c);
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ impl<'a> Widget for Input<'a> {
|
||||||
.style(Style::default().fg(Color::White))
|
.style(Style::default().fg(Color::White))
|
||||||
.render(area, buf);
|
.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() {
|
let _ = match input.mode() {
|
||||||
InputMode::Insert => Term::set_cursor_bar(),
|
InputMode::Insert => Term::set_cursor_bar(),
|
||||||
_ => Term::set_cursor_block(),
|
_ => Term::set_cursor_block(),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue