mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Merge branch 'sxyazi:main' into main
This commit is contained in:
commit
10690317cf
13 changed files with 123 additions and 33 deletions
|
|
@ -61,7 +61,7 @@ impl Executor {
|
|||
|
||||
// Navigation
|
||||
"arrow" => {
|
||||
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_default();
|
||||
cx.manager.active_mut().arrow(step)
|
||||
}
|
||||
"peek" => {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@ keymap = [
|
|||
{ on = [ "K" ], exec = "arrow -5", desc = "Move cursor up 5 lines" },
|
||||
{ on = [ "J" ], exec = "arrow 5", desc = "Move cursor down 5 lines" },
|
||||
|
||||
{ on = [ "<C-u>" ], exec = "arrow -50%", desc = "Move cursor up half page" },
|
||||
{ on = [ "<C-d>" ], exec = "arrow 50%", desc = "Move cursor down half page" },
|
||||
{ on = [ "<C-b>" ], exec = "arrow -100%", desc = "Move cursor up one page" },
|
||||
{ on = [ "<C-f>" ], exec = "arrow 100%", desc = "Move cursor down one page" },
|
||||
|
||||
{ on = [ "h" ], exec = "leave", desc = "Go back to the parent directory" },
|
||||
{ on = [ "l" ], exec = "enter", desc = "Enter the child directory" },
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use serde::{Deserialize, Deserializer};
|
|||
pub struct Opener {
|
||||
pub exec: String,
|
||||
pub block: bool,
|
||||
pub orphan: bool,
|
||||
pub display_name: String,
|
||||
pub spread: bool,
|
||||
}
|
||||
|
|
@ -18,6 +19,8 @@ impl<'de> Deserialize<'de> for Opener {
|
|||
pub exec: String,
|
||||
#[serde(default)]
|
||||
pub block: bool,
|
||||
#[serde(default)]
|
||||
pub orphan: bool,
|
||||
pub display_name: Option<String>,
|
||||
}
|
||||
|
||||
|
|
@ -32,6 +35,6 @@ impl<'de> Deserialize<'de> for Opener {
|
|||
.unwrap_or_else(|| shadow.exec.split_whitespace().next().unwrap().to_string());
|
||||
|
||||
let spread = shadow.exec.contains("$*") || shadow.exec.contains("$@");
|
||||
Ok(Self { exec: shadow.exec, block: shadow.block, display_name, spread })
|
||||
Ok(Self { exec: shadow.exec, block: shadow.block, orphan: shadow.orphan, display_name, spread })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2
core/src/external/fd.rs
vendored
2
core/src/external/fd.rs
vendored
|
|
@ -17,7 +17,7 @@ pub fn fd(opt: FdOpt) -> Result<UnboundedReceiver<File>> {
|
|||
let mut child = Command::new("fd")
|
||||
.arg("--base-directory")
|
||||
.arg(&opt.cwd)
|
||||
.arg(if opt.hidden { "--hidden" } else { "--no-hidden" })
|
||||
.args(if opt.hidden { ["--hidden", "--no-ignore"] } else { ["--no-hidden", "--ignore"] })
|
||||
.arg(if opt.glob { "--glob" } else { "--regex" })
|
||||
.arg(&opt.subject)
|
||||
.kill_on_drop(true)
|
||||
|
|
|
|||
2
core/src/external/rg.rs
vendored
2
core/src/external/rg.rs
vendored
|
|
@ -16,7 +16,7 @@ pub fn rg(opt: RgOpt) -> Result<UnboundedReceiver<File>> {
|
|||
let mut child = Command::new("rg")
|
||||
.current_dir(&opt.cwd)
|
||||
.args(["--color=never", "--files-with-matches", "--smart-case"])
|
||||
.arg(if opt.hidden { "--hidden" } else { "--no-hidden" })
|
||||
.args(if opt.hidden { ["--hidden", "--no-ignore"] } else { ["--no-hidden", "--ignore"] })
|
||||
.arg(&opt.subject)
|
||||
.kill_on_drop(true)
|
||||
.stdout(Stdio::piped())
|
||||
|
|
|
|||
16
core/src/external/shell.rs
vendored
16
core/src/external/shell.rs
vendored
|
|
@ -4,9 +4,17 @@ use anyhow::Result;
|
|||
use tokio::process::{Child, Command};
|
||||
|
||||
pub struct ShellOpt {
|
||||
pub cmd: OsString,
|
||||
pub args: Vec<OsString>,
|
||||
pub piped: bool,
|
||||
pub cmd: OsString,
|
||||
pub args: Vec<OsString>,
|
||||
pub piped: bool,
|
||||
pub orphan: bool,
|
||||
}
|
||||
|
||||
impl ShellOpt {
|
||||
pub fn with_piped(mut self) -> Self {
|
||||
self.piped = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shell(opt: ShellOpt) -> Result<Child> {
|
||||
|
|
@ -21,7 +29,7 @@ pub fn shell(opt: ShellOpt) -> Result<Child> {
|
|||
.stdin(if opt.piped { Stdio::piped() } else { Stdio::inherit() })
|
||||
.stdout(if opt.piped { Stdio::piped() } else { Stdio::inherit() })
|
||||
.stderr(if opt.piped { Stdio::piped() } else { Stdio::inherit() })
|
||||
.kill_on_drop(true)
|
||||
.kill_on_drop(!opt.orphan)
|
||||
.spawn()?,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ pub mod help;
|
|||
mod highlighter;
|
||||
pub mod input;
|
||||
pub mod manager;
|
||||
pub mod position;
|
||||
mod position;
|
||||
pub mod select;
|
||||
mod step;
|
||||
pub mod tasks;
|
||||
pub mod which;
|
||||
|
||||
|
|
@ -23,5 +24,6 @@ pub use blocker::*;
|
|||
pub use event::*;
|
||||
pub use highlighter::*;
|
||||
pub use position::*;
|
||||
pub use step::*;
|
||||
|
||||
pub fn init() { init_blocker(); }
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use config::MANAGER;
|
|||
use ratatui::layout::Rect;
|
||||
use shared::Url;
|
||||
|
||||
use crate::{emit, files::{File, Files, FilesOp}};
|
||||
use crate::{emit, files::{File, Files, FilesOp}, Step};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Folder {
|
||||
|
|
@ -58,18 +58,18 @@ impl Folder {
|
|||
true
|
||||
}
|
||||
|
||||
pub fn next(&mut self, step: usize) -> bool {
|
||||
pub fn next(&mut self, step: Step) -> bool {
|
||||
let len = self.files.len();
|
||||
if len == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let old = self.cursor;
|
||||
self.cursor = (self.cursor + step).min(len - 1);
|
||||
let limit = MANAGER.layout.folder_height();
|
||||
self.cursor = step.add(self.cursor, || limit).min(len - 1);
|
||||
self.hovered = self.files.duplicate(self.cursor);
|
||||
self.set_page(false);
|
||||
|
||||
let limit = MANAGER.layout.folder_height();
|
||||
if self.cursor >= (self.offset + limit).min(len).saturating_sub(5) {
|
||||
self.offset = len.saturating_sub(limit).min(self.offset + self.cursor - old);
|
||||
}
|
||||
|
|
@ -77,9 +77,9 @@ impl Folder {
|
|||
old != self.cursor
|
||||
}
|
||||
|
||||
pub fn prev(&mut self, step: usize) -> bool {
|
||||
pub fn prev(&mut self, step: Step) -> bool {
|
||||
let old = self.cursor;
|
||||
self.cursor = self.cursor.saturating_sub(step);
|
||||
self.cursor = step.add(self.cursor, || MANAGER.layout.folder_height());
|
||||
self.hovered = self.files.duplicate(self.cursor);
|
||||
self.set_page(false);
|
||||
|
||||
|
|
@ -105,7 +105,11 @@ impl Folder {
|
|||
|
||||
pub fn hover(&mut self, url: &Url) -> bool {
|
||||
let new = self.files.position(url).unwrap_or(self.cursor);
|
||||
if new > self.cursor { self.next(new - self.cursor) } else { self.prev(self.cursor - new) }
|
||||
if new > self.cursor {
|
||||
self.next(Step::from(new - self.cursor))
|
||||
} else {
|
||||
self.prev(Step::from(self.cursor - new))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -259,9 +259,10 @@ impl Manager {
|
|||
emit!(Stop(true)).await;
|
||||
|
||||
let mut child = external::shell(ShellOpt {
|
||||
cmd: (*opener.exec).into(),
|
||||
args: vec![tmp.to_owned().into()],
|
||||
piped: false,
|
||||
cmd: (*opener.exec).into(),
|
||||
args: vec![tmp.to_owned().into()],
|
||||
piped: false,
|
||||
orphan: false,
|
||||
})?;
|
||||
child.wait().await?;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use tokio::{pin, task::JoinHandle};
|
|||
use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt};
|
||||
|
||||
use super::{Finder, Folder, Mode, Preview, PreviewLock};
|
||||
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, FilesOp, FilesSorter}, input::InputOpt, Event, BLOCKER};
|
||||
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, FilesOp, FilesSorter}, input::InputOpt, Event, Step, BLOCKER};
|
||||
|
||||
pub struct Tab {
|
||||
pub(super) mode: Mode,
|
||||
|
|
@ -67,12 +67,8 @@ impl Tab {
|
|||
self.search_stop()
|
||||
}
|
||||
|
||||
pub fn arrow(&mut self, step: isize) -> bool {
|
||||
let ok = if step > 0 {
|
||||
self.current.next(step as usize)
|
||||
} else {
|
||||
self.current.prev(step.unsigned_abs())
|
||||
};
|
||||
pub fn arrow(&mut self, step: Step) -> bool {
|
||||
let ok = if step.is_positive() { self.current.next(step) } else { self.current.prev(step) };
|
||||
if !ok {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -248,7 +244,7 @@ impl Tab {
|
|||
};
|
||||
|
||||
if let Some(step) = finder.ring(&self.current.files, self.current.cursor(), prev) {
|
||||
self.arrow(step);
|
||||
self.arrow(step.into());
|
||||
}
|
||||
|
||||
self.finder = Some(finder);
|
||||
|
|
@ -280,7 +276,7 @@ impl Tab {
|
|||
|
||||
let mut b = finder.catchup(&self.current.files);
|
||||
if let Some(step) = finder.arrow(&self.current.files, self.current.cursor(), prev) {
|
||||
b |= self.arrow(step);
|
||||
b |= self.arrow(step.into());
|
||||
}
|
||||
|
||||
b
|
||||
|
|
@ -379,7 +375,7 @@ impl Tab {
|
|||
|
||||
emit!(Open(
|
||||
selected,
|
||||
Some(Opener { exec, block, display_name: Default::default(), spread: true })
|
||||
Some(Opener { exec, block, orphan: false, display_name: Default::default(), spread: true })
|
||||
));
|
||||
});
|
||||
|
||||
|
|
|
|||
55
core/src/step.rs
Normal file
55
core/src/step.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
use std::{num::ParseIntError, str::FromStr};
|
||||
|
||||
pub enum Step {
|
||||
Fixed(isize),
|
||||
Percent(i8),
|
||||
}
|
||||
|
||||
impl Default for Step {
|
||||
fn default() -> Self { Self::Fixed(0) }
|
||||
}
|
||||
|
||||
impl FromStr for Step {
|
||||
type Err = ParseIntError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(if let Some(s) = s.strip_suffix('%') {
|
||||
Self::Percent(s.parse()?)
|
||||
} else {
|
||||
Self::Fixed(s.parse()?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<isize> for Step {
|
||||
fn from(n: isize) -> Self { Self::Fixed(n) }
|
||||
}
|
||||
|
||||
impl From<usize> for Step {
|
||||
fn from(n: usize) -> Self { Self::Fixed(n as isize) }
|
||||
}
|
||||
|
||||
impl Step {
|
||||
#[inline]
|
||||
fn fixed<F: FnOnce() -> usize>(self, f: F) -> isize {
|
||||
match self {
|
||||
Self::Fixed(n) => n,
|
||||
Self::Percent(0) => 0,
|
||||
Self::Percent(n) => n as isize * f() as isize / 100,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add<F: FnOnce() -> usize>(self, pos: usize, f: F) -> usize {
|
||||
let fixed = self.fixed(f);
|
||||
if fixed > 0 { pos + fixed as usize } else { pos.saturating_sub(fixed.unsigned_abs()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_positive(&self) -> bool {
|
||||
match *self {
|
||||
Self::Fixed(n) => n > 0,
|
||||
Self::Percent(n) => n > 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -310,6 +310,7 @@ impl Scheduler {
|
|||
cmd: opener.exec.into(),
|
||||
args,
|
||||
block: opener.block,
|
||||
orphan: opener.orphan,
|
||||
cancel: cancel_tx,
|
||||
})
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::ffi::OsString;
|
||||
use std::{ffi::OsString, mem};
|
||||
|
||||
use anyhow::Result;
|
||||
use tokio::{io::{AsyncBufReadExt, BufReader}, select, sync::{mpsc, oneshot}};
|
||||
|
|
@ -16,9 +16,21 @@ pub(crate) struct ProcessOpOpen {
|
|||
pub cmd: OsString,
|
||||
pub args: Vec<OsString>,
|
||||
pub block: bool,
|
||||
pub orphan: bool,
|
||||
pub cancel: oneshot::Sender<()>,
|
||||
}
|
||||
|
||||
impl From<&mut ProcessOpOpen> for ShellOpt {
|
||||
fn from(value: &mut ProcessOpOpen) -> Self {
|
||||
Self {
|
||||
cmd: mem::take(&mut value.cmd),
|
||||
args: mem::take(&mut value.args),
|
||||
piped: false,
|
||||
orphan: value.orphan,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Process {
|
||||
pub(crate) fn new(sch: mpsc::UnboundedSender<TaskOp>) -> Self { Self { sch } }
|
||||
|
||||
|
|
@ -33,7 +45,7 @@ impl Process {
|
|||
let _guard = BLOCKER.acquire().await.unwrap();
|
||||
emit!(Stop(true)).await;
|
||||
|
||||
match external::shell(ShellOpt { cmd: task.cmd, args: task.args, piped: false }) {
|
||||
match external::shell(ShellOpt::from(&mut task)) {
|
||||
Ok(mut child) => {
|
||||
child.wait().await.ok();
|
||||
}
|
||||
|
|
@ -48,13 +60,16 @@ impl Process {
|
|||
}
|
||||
|
||||
self.sch.send(TaskOp::New(task.id, 0))?;
|
||||
let mut child = external::shell(ShellOpt { cmd: task.cmd, args: task.args, piped: true })?;
|
||||
let mut child = external::shell(ShellOpt::from(&mut task).with_piped())?;
|
||||
|
||||
let mut stdout = BufReader::new(child.stdout.take().unwrap()).lines();
|
||||
let mut stderr = BufReader::new(child.stderr.take().unwrap()).lines();
|
||||
loop {
|
||||
select! {
|
||||
_ = task.cancel.closed() => break,
|
||||
_ = task.cancel.closed() => {
|
||||
child.start_kill().ok();
|
||||
break;
|
||||
}
|
||||
Ok(Some(line)) = stdout.next_line() => {
|
||||
self.log(task.id, line)?;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue