mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Merge d17fa043b7 into bfa1e53584
This commit is contained in:
commit
86bb635667
9 changed files with 137 additions and 16 deletions
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use yazi_macro::succ;
|
||||
use yazi_parser::mgr::PeekOpt;
|
||||
use yazi_proxy::HIDER;
|
||||
use yazi_shared::data::Data;
|
||||
use yazi_shared::{data::Data, url::AsUrl};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
@ -25,7 +25,8 @@ impl Actor for Peek {
|
|||
let folder = cx.tab().hovered_folder().map(|f| (f.offset, f.cha));
|
||||
|
||||
if !cx.tab().preview.same_url(&hovered.url) {
|
||||
cx.tab_mut().preview.skip = folder.map(|f| f.0).unwrap_or_default();
|
||||
// set matched line on the top of preview window
|
||||
cx.tab_mut().preview.skip = folder.map(|f| f.0).unwrap_or_else(|| hovered.url.as_url().line().map_or(0, |l| l.saturating_sub(1)));
|
||||
}
|
||||
if !cx.tab().preview.same_file(&hovered, &mime) {
|
||||
cx.tab_mut().preview.reset();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use yazi_fs::{File, FilesOp};
|
||||
use yazi_macro::{act, render, succ};
|
||||
use yazi_parser::mgr::RevealOpt;
|
||||
use yazi_shared::{data::Data, url::UrlLike};
|
||||
use yazi_shared::{data::Data, url::{AsUrl, UrlLike}};
|
||||
|
||||
use crate::{Actor, Ctx};
|
||||
|
||||
|
|
@ -14,6 +14,15 @@ impl Actor for Reveal {
|
|||
const NAME: &str = "reveal";
|
||||
|
||||
fn act(cx: &mut Ctx, opt: Self::Options) -> Result<Data> {
|
||||
match cx.cwd().is_search() {
|
||||
true => Self::reveal_search(cx, opt),
|
||||
false => Self::reveal_regular(cx, opt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Reveal {
|
||||
fn reveal_regular(cx: &mut Ctx, opt: RevealOpt) -> Result<Data> {
|
||||
let Some((parent, child)) = opt.target.pair() else { succ!() };
|
||||
|
||||
// Cd to the parent directory
|
||||
|
|
@ -37,4 +46,28 @@ impl Actor for Reveal {
|
|||
act!(mgr:watch, cx)?;
|
||||
succ!();
|
||||
}
|
||||
|
||||
fn reveal_search(cx: &mut Ctx, opt: RevealOpt) -> Result<Data> {
|
||||
let cwd = cx.cwd().clone();
|
||||
let tab = cx.tab_mut();
|
||||
let pos = tab.current.files.iter().position(|f| f.url == opt.target);
|
||||
|
||||
if let Some(pos) = pos {
|
||||
let delta = pos as isize - tab.current.cursor as isize;
|
||||
tab.current.arrow(delta);
|
||||
} else if !opt.no_dummy {
|
||||
let op = FilesOp::Creating(cwd, vec![File::from_dummy(&opt.target, None)]);
|
||||
tab.current.update_pub(tab.id, op);
|
||||
|
||||
if let Some(pos) = tab.current.files.iter().position(|f| f.url == opt.target) {
|
||||
let delta = pos as isize - tab.current.cursor as isize;
|
||||
tab.current.arrow(delta);
|
||||
}
|
||||
}
|
||||
|
||||
act!(mgr:hover, cx, None)?;
|
||||
act!(mgr:peek, cx)?;
|
||||
act!(mgr:watch, cx)?;
|
||||
succ!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -273,5 +273,10 @@ macro_rules! impl_file_methods {
|
|||
// TODO: use a cache
|
||||
Ok(yazi_config::THEME.icon.matches(me).map(Icon::from))
|
||||
});
|
||||
|
||||
$methods.add_method("line", |_, me, ()| {
|
||||
use yazi_shared::url::AsUrl;
|
||||
Ok(me.url.as_url().line().map(|v| v as i64))
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
use std::os::unix::ffi::{OsStrExt, OsStringExt};
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::ffi::{OsStrExt, OsStringExt};
|
||||
use std::{cell::Cell, ffi::{OsStr, OsString}, iter::{self, Peekable}, mem};
|
||||
use std::{cell::Cell, ffi::{OsStr, OsString}, fs::OpenOptions, io::Write, iter::{self, Peekable}, mem};
|
||||
|
||||
use yazi_shared::url::{AsUrl, Url, UrlCow};
|
||||
|
||||
|
|
@ -81,6 +81,8 @@ where
|
|||
Some('d') | Some('D') => self.visit_dirname(it, buf),
|
||||
Some('t') | Some('T') => self.visit_tab(it, buf),
|
||||
Some('y') | Some('Y') => self.visit_yanked(it, buf),
|
||||
Some('l') | Some('L') => self.visit_line_number(it, buf),
|
||||
Some('c') | Some('C') => self.visit_column_number(it, buf),
|
||||
Some('%') => self.visit_escape(it, buf),
|
||||
Some('*') => self.visit_selected(it, buf), // TODO: remove this
|
||||
Some(c) if c.is_ascii_digit() => self.visit_digit(it, buf),
|
||||
|
|
@ -192,6 +194,36 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_line_number(&mut self, it: &mut Iter, buf: &mut Buf) {
|
||||
it.next();
|
||||
let idx = self.consume_digit(it);
|
||||
|
||||
let line_str = self
|
||||
.src
|
||||
.selected(self.tab, idx)
|
||||
.next()
|
||||
.and_then(|url| url.line())
|
||||
.map(|line| line.to_string())
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
|
||||
cue(buf, OsStr::new(&line_str));
|
||||
}
|
||||
|
||||
fn visit_column_number(&mut self, it: &mut Iter, buf: &mut Buf) {
|
||||
it.next();
|
||||
let idx = self.consume_digit(it);
|
||||
|
||||
let col_str = self
|
||||
.src
|
||||
.selected(self.tab, idx)
|
||||
.next()
|
||||
.and_then(|url| url.column())
|
||||
.map(|col| col.to_string())
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
|
||||
cue(buf, OsStr::new(&col_str));
|
||||
}
|
||||
|
||||
fn visit_escape(&mut self, it: &mut Iter, buf: &mut Buf) { buf.push(it.next().unwrap()); }
|
||||
|
||||
fn visit_unknown(&mut self, it: &mut Iter, buf: &mut Buf) {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,13 @@ end
|
|||
|
||||
function Entity:highlights()
|
||||
local name, p = self._file.name, ui.printable
|
||||
|
||||
local ok, line = pcall(function() return self._file:line() end)
|
||||
if ok and line then
|
||||
local path_str = tostring(self._file.path)
|
||||
name = name .. ":" .. line .. " (" .. path_str .. ")"
|
||||
end
|
||||
|
||||
local highlights = self._file:highlights()
|
||||
if not highlights or #highlights == 0 then
|
||||
return p(name)
|
||||
|
|
|
|||
30
yazi-plugin/src/external/rg.rs
vendored
30
yazi-plugin/src/external/rg.rs
vendored
|
|
@ -2,8 +2,8 @@ use std::process::Stdio;
|
|||
|
||||
use anyhow::Result;
|
||||
use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}};
|
||||
use yazi_fs::{File, FsUrl};
|
||||
use yazi_shared::url::{AsUrl, UrlBuf, UrlLike};
|
||||
use yazi_fs::{File};
|
||||
use yazi_shared::url::{UrlBuf, UrlLike};
|
||||
use yazi_vfs::VfsFile;
|
||||
|
||||
pub struct RgOpt {
|
||||
|
|
@ -13,13 +13,24 @@ pub struct RgOpt {
|
|||
pub args: Vec<String>,
|
||||
}
|
||||
|
||||
fn parse_rg_line_column(line: &str) -> Option<(&str, usize, usize)> {
|
||||
let mut parts = line.split(':');
|
||||
|
||||
let (f, l, c) = (parts.next()?, parts.next()?, parts.next()?);
|
||||
if f.is_empty() { return None; }
|
||||
|
||||
Some((f, l.parse().ok()?, c.parse().ok()?))
|
||||
}
|
||||
|
||||
pub fn rg(opt: RgOpt) -> Result<UnboundedReceiver<File>> {
|
||||
let subject = opt.subject.clone();
|
||||
let cwd = opt.cwd.clone();
|
||||
|
||||
let mut child = Command::new("rg")
|
||||
.args(["--color=never", "--files-with-matches", "--smart-case"])
|
||||
.args(["--color=never", "--line-number", "--no-heading", "--smart-case", "--column"])
|
||||
.arg(if opt.hidden { "--hidden" } else { "--no-hidden" })
|
||||
.args(opt.args)
|
||||
.arg(opt.subject)
|
||||
.arg(&*opt.cwd.as_url().unified_path())
|
||||
.kill_on_drop(true)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::null())
|
||||
|
|
@ -30,9 +41,14 @@ pub fn rg(opt: RgOpt) -> Result<UnboundedReceiver<File>> {
|
|||
|
||||
tokio::spawn(async move {
|
||||
while let Ok(Some(line)) = it.next_line().await {
|
||||
let Ok(url) = opt.cwd.try_join(line) else {
|
||||
continue;
|
||||
};
|
||||
let Some((fp, l, c)) = parse_rg_line_column(&line) else { continue };
|
||||
|
||||
let url = cwd.try_join(fp)
|
||||
.ok()
|
||||
.and_then(|u| u.to_search(&format!("{}#{}:{}", subject, l, c)).ok());
|
||||
|
||||
let Some(url) = url else { continue };
|
||||
|
||||
if let Ok(file) = File::new(url).await {
|
||||
tx.send(file).ok();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,7 @@ impl Utils {
|
|||
let path = lock.url.as_url().unified_path();
|
||||
let inner = match Highlighter::new(path).highlight(lock.skip, area.size()).await {
|
||||
Ok(text) => text,
|
||||
Err(e @ PeekError::Exceed(max)) => return (e.to_string(), max).into_lua_multi(&lua),
|
||||
Err(e @ PeekError::Unexpected(_)) => {
|
||||
return e.to_string().into_lua_multi(&lua);
|
||||
}
|
||||
Err(e) => return e.to_string().into_lua_multi(&lua),
|
||||
};
|
||||
|
||||
lock.data = vec![Renderable::Text(Text {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ impl<'a> From<crate::url::Encode<'a>> for Encode<'a> {
|
|||
impl<'a> Encode<'a> {
|
||||
#[inline]
|
||||
pub fn domain<'s>(s: &'s str) -> PercentEncode<'s> {
|
||||
const SET: &AsciiSet = &CONTROLS.add(b'/').add(b':');
|
||||
const SET: &AsciiSet = &CONTROLS.add(b'/').add(b':').add(b'#');
|
||||
percent_encode(s.as_bytes(), SET)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,6 +86,36 @@ impl<'a> Url<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn line(self) -> Option<usize> {
|
||||
let Self::Search { domain, .. } = self else { return None };
|
||||
let Some(hash_pos) = domain.find('#') else {
|
||||
return None
|
||||
};
|
||||
|
||||
let frag = &domain[hash_pos + 1..];
|
||||
if let Some(colon_pos) = frag.find(':') {
|
||||
frag[..colon_pos].parse::<usize>().ok()
|
||||
} else {
|
||||
frag.parse::<usize>().ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn column(self) -> Option<usize> {
|
||||
let Self::Search { domain, .. } = self else { return None };
|
||||
let Some(hash_pos) = domain.find('#') else {
|
||||
return None
|
||||
};
|
||||
|
||||
let frag = &domain[hash_pos + 1..];
|
||||
if let Some(colon_pos) = frag.find(':') {
|
||||
frag[colon_pos + 1..].parse::<usize>().ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_base(self) -> bool {
|
||||
match self {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue