feat: render selected range

This commit is contained in:
sxyazi 2023-07-20 19:48:01 +08:00
parent b8d9014d83
commit 9f93c6f34e
No known key found for this signature in database
9 changed files with 93 additions and 32 deletions

12
Cargo.lock generated
View file

@ -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",

View file

@ -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" ] }
]

View file

@ -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.

View file

@ -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

View file

@ -11,6 +11,16 @@ pub struct Key {
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 {
fn default() -> Self { Self { code: KeyCode::Null, shift: false, ctrl: false, alt: false } }
}

View file

@ -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<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]
fn count(&self) -> usize { self.value.chars().count() }

View file

@ -1,3 +1,5 @@
#![feature(if_let_guard)]
use ui::App;
mod config;

View file

@ -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
},
}
}

View file

@ -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(),