From c1f4fb5c3bdab769e7bf41a6b0ee961d2b0cdf9d Mon Sep 17 00:00:00 2001 From: Jed Date: Thu, 4 Jun 2026 19:49:37 +0200 Subject: [PATCH 01/15] feat: wip improve rg generated url to get all files occurences --- yazi-plugin/src/external/rg.rs | 76 +++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 14 deletions(-) diff --git a/yazi-plugin/src/external/rg.rs b/yazi-plugin/src/external/rg.rs index 5ab3a8ed..82f9cd22 100644 --- a/yazi-plugin/src/external/rg.rs +++ b/yazi-plugin/src/external/rg.rs @@ -1,21 +1,26 @@ -use std::{path::Path, process::Stdio}; +use std::process::Stdio; use anyhow::Result; -use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}}; +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_vfs::VfsFile; pub struct RgOpt { - pub cwd: UrlBuf, - pub hidden: bool, + pub cwd: UrlBuf, + pub hidden: bool, pub subject: String, - pub args: Vec, + pub args: Vec, } pub fn rg(opt: RgOpt) -> Result> { let mut child = Command::new("rg") - .args(["--color=never", "--files-with-matches", "--smart-case"]) + .args(["--color=never", "--no-heading", "--column", "--smart-case"]) + // .args(["--color=never", "--files-with-matches", "--smart-case"]) .arg(if opt.hidden { "--hidden" } else { "--no-hidden" }) .args(opt.args) .arg(opt.subject) @@ -29,18 +34,61 @@ pub fn rg(opt: RgOpt) -> Result> { let (tx, rx) = mpsc::unbounded_channel(); tokio::spawn(async move { - while let Ok(Some(line)) = it.next_line().await { - if Path::new(&line).is_absolute() { - continue; + // let mut occurrences_per_file: HashMap> = HashMap::new(); + // let mut current_file_path: String = String::new(); + let mut current_file: String = String::new(); + let mut current_occurrences: Vec<(u32, u32)> = vec![]; + + while let Ok(Some(search_line)) = it.next_line().await { + let Some((file_path, line, col)) = parse_rg_line(&search_line) else { continue }; + + if current_file != file_path { + let Some(url) = build_file_url(opt.cwd.clone(), ¤t_file, ¤t_occurrences) else { + continue; + }; + + if let Ok(file) = File::new(url).await { + tx.send(file).ok(); + } + current_file = file_path.clone(); + current_occurrences = vec![]; } - let Ok(url) = opt.cwd.try_join(line) else { - continue; - }; - if let Ok(file) = File::new(url).await { - tx.send(file).ok(); + + current_occurrences.push((line as u32, col as u32)); + } + + if current_occurrences.len() > 0 { + match build_file_url(opt.cwd, ¤t_file, ¤t_occurrences) { + Some(url) => { + if let Ok(file) = File::new(url).await { + tx.send(file).ok(); + } + } + None => {} } } + child.wait().await.ok(); }); + Ok(rx) } + +fn build_file_url(cwd: UrlBuf, file_path: &str, occurences: &Vec<(u32, u32)>) -> Option { + let occurrences_str = + occurences.iter().map(|(line, col)| format!("{line}-{col}")).collect::>().join(","); + let url = cwd.try_join(file_path).ok().and_then(|u| u.into_search(occurrences_str).ok()); + + url +} + +fn parse_rg_line(line: &str) -> Option<(String, usize, usize)> { + let mut parts = line.split(':'); + + let (file, line, col) = (parts.next()?, parts.next()?, parts.next()?); + if file.is_empty() { + return None; + } + + Some((file.to_owned(), line.parse().ok()?, col.parse().ok()?)) +} From ba43e04fb87394512a1a53a50cb63826ea7bea7b Mon Sep 17 00:00:00 2001 From: Jed Date: Thu, 4 Jun 2026 23:30:54 +0200 Subject: [PATCH 02/15] feat: rework code.lua to seek and peak to the right search occurrence --- yazi-plugin/preset/plugins/code.lua | 65 +++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/yazi-plugin/preset/plugins/code.lua b/yazi-plugin/preset/plugins/code.lua index a9747d0e..9b5e1392 100644 --- a/yazi-plugin/preset/plugins/code.lua +++ b/yazi-plugin/preset/plugins/code.lua @@ -1,8 +1,53 @@ local M = {} +local function get_search_occurrences(url) + if not url.is_search then + return + end + local occurrences = tostring(url):match("^search://(.-)//") + local lines = {} + for occurrence in occurrences:gmatch("[^,]+") do + local line, col = occurrence:match("(%d+)-(%d+)") + ya.dbg("line col:", line, col) + table.insert(lines, { tonumber(line), tonumber(col) }) + end + + if #lines == 0 then + return + end + + return lines +end + +local function get_next_occurrence(occurrences, current_line, direction) + local step = direction == "up" and -1 or 1 + local start = direction == "up" and #occurrences or 1 + local stop = direction == "up" and 1 or #occurrences + + for i = start, stop, step do + local line = occurrences[i] + if go_up and line >= current_line then + return line + end + if direction == "down" and line <= current_line then + return line + end + + -- TODO: Handle return to first element or last one + end +end + function M:peek(job) local err, bound = ya.preview_code(job) + ya.dbg("PEEKING broooo", job.file.url, bound) if bound then + local search_occurrences = get_search_occurrences(job.file.url) + if search_occurrences then + local line = search_occurrences[1][1] + ya.dbg("liiiiine: ", line) + ya.emit("peek", { math.max(0, search_occurrences[1][1] - 1), only_if = job.file.url }) + return + end ya.emit("peek", { bound, only_if = job.file.url, upper_bound = true }) elseif err and not err:find("cancelled", 1, true) then require("empty").msg(job, err) @@ -10,11 +55,31 @@ function M:peek(job) end function M:seek(job) + ya.dbg("UNITS IN SEEK ", job.units) + local direction = job.units > 0 and "down" or "up" end local h = cx.active.current.hovered if not h or h.url ~= job.file.url then return end + local search_occurrences = get_search_occurrences(job.file.url) + if search_occurrences then + local current_line = cx.active.preview.skip + 1 + + local next_occurrence = get_next_occurrence(search_occurrences, current_line, direction) + + for _, occurrence in ipairs(search_occurrences) do + local search_line = occurrence[1] + -- local col = occurrence[2] + + if search_line >= current_line then + ya.emit("peek", { math.max(0, search_line - 1), only_if = job.file.url }) + return + end + end + end + -- ya.emit("peek", { math.max(0, line - 1), only_if = job.file.url }) + local step = math.floor(job.units * job.area.h / 10) step = step == 0 and ya.clamp(-1, job.units, 1) or step From 947e7348609707dd8f50462923221c074214a81b Mon Sep 17 00:00:00 2001 From: Jed Date: Mon, 8 Jun 2026 23:42:15 +0200 Subject: [PATCH 03/15] feat: wip store search_idx in preview to be usable in code.lua --- yazi-actor/src/lives/preview.rs | 5 +- yazi-actor/src/mgr/peek.rs | 13 ++++- yazi-core/src/tab/preview.rs | 31 +++++++--- yazi-parser/src/mgr/peek.rs | 26 ++++++--- yazi-plugin/preset/plugins/code.lua | 89 ++++++++++++++++------------- yazi-plugin/src/utils/preview.rs | 55 +++++++++++++++++- 6 files changed, 156 insertions(+), 63 deletions(-) diff --git a/yazi-actor/src/lives/preview.rs b/yazi-actor/src/lives/preview.rs index 30a4a1c1..754e768a 100644 --- a/yazi-actor/src/lives/preview.rs +++ b/yazi-actor/src/lives/preview.rs @@ -15,7 +15,9 @@ pub(super) struct Preview { impl Deref for Preview { type Target = yazi_core::tab::Preview; - fn deref(&self) -> &Self::Target { &self.tab.preview } + fn deref(&self) -> &Self::Target { + &self.tab.preview + } } impl Preview { @@ -27,6 +29,7 @@ impl Preview { impl UserData for Preview { fn add_fields>(fields: &mut F) { fields.add_field_method_get("skip", |_, me| Ok(me.skip)); + fields.add_field_method_get("search_idx", |_, me| Ok(me.search_idx)); cached_field!(fields, folder, |_, me| { me.tab .hovered_folder() diff --git a/yazi-actor/src/mgr/peek.rs b/yazi-actor/src/mgr/peek.rs index c1d41d28..739ac081 100644 --- a/yazi-actor/src/mgr/peek.rs +++ b/yazi-actor/src/mgr/peek.rs @@ -1,7 +1,7 @@ use anyhow::Result; use yazi_macro::succ; use yazi_parser::mgr::PeekForm; -use yazi_shared::data::Data; +use yazi_shared::{data::Data, url::UrlLike}; use crate::{Actor, Ctx}; @@ -37,8 +37,8 @@ impl Actor for Peek { succ!(); } + let preview = &mut cx.tab_mut().preview; if let Some(skip) = form.skip { - let preview = &mut cx.tab_mut().preview; if form.upper_bound { preview.skip = preview.skip.min(skip); } else { @@ -46,6 +46,15 @@ impl Actor for Peek { } } + tracing::debug!("peeeek boy before set stuff"); + match form.search_idx { + Some(index) => { + tracing::debug!("search index setting {}", index); + preview.search_idx = Some(index); + } + None => {} + } + if hovered.is_dir() { cx.tab_mut().preview.go_folder(hovered, folder.map(|(_, cha)| cha), mime, form.force); } else { diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index 83052479..f8cd9d3d 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -6,20 +6,27 @@ use yazi_adapter::ADAPTOR; use yazi_config::{LAYOUT, YAZI}; use yazi_fs::{File, Files, FilesOp, cha::Cha}; use yazi_macro::render; -use yazi_runner::{RUNNER, previewer::{PeekError, PeekJob}}; -use yazi_shared::{pool::Symbol, url::{UrlBuf, UrlLike}}; +use yazi_runner::{ + RUNNER, + previewer::{PeekError, PeekJob}, +}; +use yazi_shared::{ + pool::Symbol, + url::{UrlBuf, UrlLike}, +}; use yazi_vfs::{VfsFiles, VfsFilesOp}; use crate::{AppProxy, Highlighter, MgrProxy, tab::PreviewLock}; -#[derive(Default)] +#[derive(Default, Debug)] pub struct Preview { pub lock: Option, pub skip: usize, + pub search_idx: Option, - handle: Option>, + handle: Option>, pub folder_lock: Option, - folder_loader: Option>, + folder_loader: Option>, } impl Preview { @@ -35,8 +42,12 @@ impl Preview { }; self.abort(); + + // let search_first_occurrence = self.get_first_search_occurrence(&file.url); let job = PeekJob { previewer, file, mime, skip: self.skip }; + tracing::debug!("preview {:?}", self); + self.handle = Some(tokio::spawn(async move { let mut rx = RUNNER.peek(&job).await; match rx.recv().await.unwrap_or(Err(PeekError::Cancelled)) { @@ -85,7 +96,9 @@ impl Preview { } pub fn reset(&mut self) { + self.search_idx = None; self.abort(); + ADAPTOR.get().image_hide().ok(); render!(self.lock.take().is_some()) } @@ -95,7 +108,9 @@ impl Preview { ADAPTOR.get().image_hide().ok(); } - pub fn same_url(&self, url: &UrlBuf) -> bool { matches!(&self.lock, Some(l) if l.url == *url) } + pub fn same_url(&self, url: &UrlBuf) -> bool { + matches!(&self.lock, Some(l) if l.url == *url) + } pub fn same_file(&self, file: &File, mime: &str) -> bool { self.same_url(&file.url) @@ -106,5 +121,7 @@ impl Preview { self.same_file(file, mime) && matches!(&self.lock, Some(l) if l.skip == self.skip) } - pub fn same_folder(&self, url: &UrlBuf) -> bool { self.folder_lock.as_ref() == Some(url) } + pub fn same_folder(&self, url: &UrlBuf) -> bool { + self.folder_lock.as_ref() == Some(url) + } } diff --git a/yazi-parser/src/mgr/peek.rs b/yazi-parser/src/mgr/peek.rs index 4279bc44..69a296b4 100644 --- a/yazi-parser/src/mgr/peek.rs +++ b/yazi-parser/src/mgr/peek.rs @@ -3,31 +3,39 @@ use yazi_shared::{event::ActionCow, url::UrlBuf}; #[derive(Debug, Default)] pub struct PeekForm { - pub skip: Option, - pub force: bool, - pub only_if: Option, + pub skip: Option, + pub force: bool, + pub only_if: Option, pub upper_bound: bool, + pub search_idx: Option, } impl From for PeekForm { fn from(mut a: ActionCow) -> Self { Self { - skip: a.first().ok(), - force: a.bool("force"), - only_if: a.take("only-if").ok(), + skip: a.first().ok(), + force: a.bool("force"), + only_if: a.take("only-if").ok(), upper_bound: a.bool("upper-bound"), + search_idx: a.take("search_idx").ok(), } } } impl From for PeekForm { - fn from(force: bool) -> Self { Self { force, ..Default::default() } } + fn from(force: bool) -> Self { + Self { force, ..Default::default() } + } } impl FromLua for PeekForm { - fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } + fn from_lua(_: Value, _: &Lua) -> mlua::Result { + Err("unsupported".into_lua_err()) + } } impl IntoLua for PeekForm { - fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } + fn into_lua(self, _: &Lua) -> mlua::Result { + Err("unsupported".into_lua_err()) + } } diff --git a/yazi-plugin/preset/plugins/code.lua b/yazi-plugin/preset/plugins/code.lua index 9b5e1392..0f239a80 100644 --- a/yazi-plugin/preset/plugins/code.lua +++ b/yazi-plugin/preset/plugins/code.lua @@ -8,7 +8,6 @@ local function get_search_occurrences(url) local lines = {} for occurrence in occurrences:gmatch("[^,]+") do local line, col = occurrence:match("(%d+)-(%d+)") - ya.dbg("line col:", line, col) table.insert(lines, { tonumber(line), tonumber(col) }) end @@ -19,35 +18,38 @@ local function get_search_occurrences(url) return lines end -local function get_next_occurrence(occurrences, current_line, direction) - local step = direction == "up" and -1 or 1 - local start = direction == "up" and #occurrences or 1 - local stop = direction == "up" and 1 or #occurrences - - for i = start, stop, step do - local line = occurrences[i] - if go_up and line >= current_line then - return line - end - if direction == "down" and line <= current_line then - return line - end - - -- TODO: Handle return to first element or last one - end -end +-- local function get_next_occurrence(occurrences, current_line, direction) +-- local step = direction == "up" and -1 or 1 +-- local start = direction == "up" and #occurrences or 1 +-- local stop = direction == "up" and 1 or #occurrences +-- +-- for i = start, stop, step do +-- local line = occurrences[i] +-- if go_up and line >= current_line then +-- return line +-- end +-- if direction == "down" and line <= current_line then +-- return line +-- end +-- +-- -- TODO: Handle return to first element or last one +-- end +-- end function M:peek(job) + local search_occurrences = get_search_occurrences(job.file.url) + local search_idx = cx.active.preview.search_idx + ya.dbg(" search index", search_idx) + local occurrence + if search_occurrences and search_idx then + occurrence = search_occurrences[search_idx] + end + + ya.dgb("Peek Occurrence:", occurrence) + + -- Todo: Pass the occurrence for highlighting local err, bound = ya.preview_code(job) - ya.dbg("PEEKING broooo", job.file.url, bound) if bound then - local search_occurrences = get_search_occurrences(job.file.url) - if search_occurrences then - local line = search_occurrences[1][1] - ya.dbg("liiiiine: ", line) - ya.emit("peek", { math.max(0, search_occurrences[1][1] - 1), only_if = job.file.url }) - return - end ya.emit("peek", { bound, only_if = job.file.url, upper_bound = true }) elseif err and not err:find("cancelled", 1, true) then require("empty").msg(job, err) @@ -55,8 +57,11 @@ function M:peek(job) end function M:seek(job) - ya.dbg("UNITS IN SEEK ", job.units) - local direction = job.units > 0 and "down" or "up" end + local direction = job.units > 0 and "down" or "up" + + local search_idx = cx.active.preview.search_idx + ya.dbg(" search index", search_idx) + local h = cx.active.current.hovered if not h or h.url ~= job.file.url then return @@ -64,20 +69,21 @@ function M:seek(job) local search_occurrences = get_search_occurrences(job.file.url) if search_occurrences then - local current_line = cx.active.preview.skip + 1 - - local next_occurrence = get_next_occurrence(search_occurrences, current_line, direction) - - for _, occurrence in ipairs(search_occurrences) do - local search_line = occurrence[1] - -- local col = occurrence[2] - - if search_line >= current_line then - ya.emit("peek", { math.max(0, search_line - 1), only_if = job.file.url }) - return - end - end + search_idx = (search_idx or 0) + 1 + -- local current_line = cx.active.preview.skip + 1 + -- ya.dbg("Current line:", current_line) + -- local next_occurrence = get_next_occurrence(search_occurrences, current_line, direction) + -- for _, occurrence in ipairs(search_occurrences) do + -- local search_line = occurrence[1] + -- -- local col = occurrence[2] + -- + -- if search_line >= current_line then + -- ya.emit("peek", { math.max(0, search_line - 1), only_if = job.file.url }) + -- return + -- end + -- end end + -- ya.emit("peek", { math.max(0, line - 1), only_if = job.file.url }) local step = math.floor(job.units * job.area.h / 10) @@ -86,6 +92,7 @@ function M:seek(job) ya.emit("peek", { math.max(0, cx.active.preview.skip + step), only_if = job.file.url, + search_idx, }) end diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 5c996f74..2a3fc8fe 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -1,5 +1,9 @@ use mlua::{ExternalError, Function, IntoLuaMulti, Lua, Table, Value}; -use yazi_binding::{Error, elements::{Area, Renderable, Text}}; +// use ratatui::style::Color; +use yazi_binding::{ + Error, + elements::{Area, HighlightPosition, Renderable, Text}, +}; use yazi_core::{Highlighter, MgrProxy, tab::PreviewLock}; use yazi_fs::FsUrl; use yazi_runner::previewer::PeekError; @@ -14,10 +18,17 @@ impl Utils { pub(super) fn preview_code(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, t: Table| async move { let area: Area = t.raw_get("area")?; - let mut lock = PreviewLock::try_from(t)?; + // let position: Option = t.raw_get("position").ok(); + let mut lock = PreviewLock::try_from(t)?; let path = lock.url.as_url().unified_path(); - let inner = match Highlighter::oneshot(path, lock.skip, area.size()).await { + + // let skip = position.map(|p| p.line).unwrap_or(lock.skip); + let size = area.size(); + + // tracing::debug!("skip size: {size}"); + + let inner = match Highlighter::oneshot(path, lock.skip, size).await { Ok(text) => text, Err(e @ PeekError::Exceeded(max)) => return (e, max).into_lua_multi(&lua), Err(e) => { @@ -25,6 +36,8 @@ impl Utils { } }; + // tracing::debug!("Inner: {inner}"); + lock.data = vec![Renderable::Text(Text { area, inner, ..Default::default() })]; MgrProxy::update_peeked(lock); @@ -32,6 +45,8 @@ impl Utils { }) } + // Note: You need to implement or update Highlighter::oneshot_with_highlight to accept line/column and highlight accordingly. + pub(super) fn preview_widget(lua: &Lua) -> mlua::Result { lua.create_async_function(|_, (t, value): (Table, Value)| async move { let mut lock = PreviewLock::try_from(t)?; @@ -59,3 +74,37 @@ impl Utils { }) } } + +// fn apply_highlight(text: &mut Text, column: usize, length: usize) { +// use ratatui::text::Span; +// if length == 0 { +// return; +// } +// let Some(line) = text.inner.lines.first_mut() else { return }; +// let mut new_spans = Vec::new(); +// let mut char_pos = 0usize; +// for span in std::mem::take(&mut line.spans) { +// let n = span.content.chars().count(); +// let range = char_pos..char_pos + n; +// if range.end <= column || range.start >= column + length || n == 0 { +// new_spans.push(span); +// } else { +// let chars: Vec = span.content.chars().collect(); +// let hl_start = column.saturating_sub(char_pos).min(n); +// let hl_end = (column + length).saturating_sub(char_pos).min(n); +// if hl_start > 0 { +// new_spans.push(Span { content: chars[..hl_start].iter().collect(), style: span.style }); +// } +// { +// let mut hl_style = span.style; +// hl_style.bg = hl_style.bg.or(Some(Color::Yellow)); +// new_spans.push(Span { content: chars[hl_start..hl_end].iter().collect(), style: hl_style }); +// } +// if hl_end < n { +// new_spans.push(Span { content: chars[hl_end..].iter().collect(), style: span.style }); +// } +// } +// char_pos = range.end; +// } +// line.spans = new_spans; +// } From a29e5c9296216d052c5500e265e10ac7e92b37a6 Mon Sep 17 00:00:00 2001 From: Jed Date: Mon, 8 Jun 2026 23:42:30 +0200 Subject: [PATCH 04/15] feat: wip highlight section of preview --- yazi-binding/src/elements/highlight.rs | 39 ++++++++++++++++++++++++++ yazi-binding/src/elements/mod.rs | 2 +- yazi-core/src/highlighter.rs | 36 ++++++++++++++++-------- 3 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 yazi-binding/src/elements/highlight.rs diff --git a/yazi-binding/src/elements/highlight.rs b/yazi-binding/src/elements/highlight.rs new file mode 100644 index 00000000..e94dc8da --- /dev/null +++ b/yazi-binding/src/elements/highlight.rs @@ -0,0 +1,39 @@ +use mlua::{ + ExternalError, FromLua, IntoLua, Lua, Result as LuaResult, Table, UserData, UserDataMethods, + Value, +}; + +const EXPECTED: &str = "expected a table containing a line and a length"; + +#[derive(Clone, Debug)] +pub struct HighlightPosition { + pub line: usize, + pub length: usize, +} + +impl HighlightPosition { + pub fn compose(lua: &Lua) -> LuaResult { + let new = lua.create_function(|_, (line, length): (usize, usize)| Ok(Self { line, length }))?; + + let tbl = lua.create_table_from([("new", new)])?; + tbl.into_lua(lua) + } +} + +impl FromLua for HighlightPosition { + fn from_lua(value: Value, _: &Lua) -> mlua::Result { + match value { + Value::Table(tbl) => Ok(Self { line: tbl.raw_get("line")?, length: tbl.raw_get("length")? }), + _ => Err(EXPECTED.into_lua_err()), + } + } +} + +impl IntoLua for HighlightPosition { + fn into_lua(self, lua: &Lua) -> LuaResult { + let tbl = lua.create_table()?; + tbl.set("line", self.line)?; + tbl.set("length", self.length)?; + Ok(Value::Table(tbl)) + } +} diff --git a/yazi-binding/src/elements/mod.rs b/yazi-binding/src/elements/mod.rs index 61dcd02f..6e1066f1 100644 --- a/yazi-binding/src/elements/mod.rs +++ b/yazi-binding/src/elements/mod.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(align area bar border cell clear color constraint edge elements gauge layout line list pad pos rect renderable row span table text wrap); +yazi_macro::mod_flat!(align area bar border cell clear color constraint edge highlight elements gauge layout line list pad pos rect renderable row span table text wrap); diff --git a/yazi-core/src/highlighter.rs b/yazi-core/src/highlighter.rs index 2b3f292b..046d3667 100644 --- a/yazi-core/src/highlighter.rs +++ b/yazi-core/src/highlighter.rs @@ -1,8 +1,20 @@ -use std::{io::{BufRead, BufReader, Cursor, Seek}, path::PathBuf, sync::OnceLock}; +use std::{ + io::{BufRead, BufReader, Cursor, Seek}, + path::PathBuf, + sync::OnceLock, +}; use anyhow::{Result, anyhow, bail}; -use ratatui::{layout::Size, text::{Line, Span, Text}}; -use syntect::{LoadingError, dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}}; +use ratatui::{ + layout::Size, + text::{Line, Span, Text}, +}; +use syntect::{ + LoadingError, dumps, + easy::HighlightLines, + highlighting::{self, Theme, ThemeSet}, + parsing::{SyntaxReference, SyntaxSet}, +}; use yazi_config::{THEME, YAZI}; use yazi_runner::previewer::PeekError; use yazi_shared::{Id, Ids, replace_to_printable}; @@ -11,17 +23,17 @@ use yazi_shim::ratatui::LineIter; static INCR: Ids = Ids::new(); pub struct Highlighter { - path: PathBuf, + path: PathBuf, reader: BufReader, - skip: usize, - size: Size, + skip: usize, + size: Size, ticket: Id, - theme: &'static Theme, + theme: &'static Theme, syntaxes: &'static SyntaxSet, - inner: Option>, - syntax: Option<&'static SyntaxReference>, + inner: Option>, + syntax: Option<&'static SyntaxReference>, } impl Highlighter { @@ -57,7 +69,9 @@ impl Highlighter { }) } - pub fn abort() { INCR.next(); } + pub fn abort() { + INCR.next(); + } fn highlight(mut self) -> Result, PeekError> { self.load_syntax()?; @@ -220,7 +234,7 @@ impl Highlighter { Span { content: s.into(), - style: ratatui::style::Style { + style: ratatui::style::Style { fg: Self::to_ansi_color(style.foreground), // bg: Self::to_ansi_color(style.background), add_modifier: modifier, From 520ff0adafd10a9c3e5b655a91afa88d6f55c218 Mon Sep 17 00:00:00 2001 From: Jed Date: Sun, 14 Jun 2026 15:45:55 +0200 Subject: [PATCH 05/15] feat: going to next or previous occurrence is working with J and K --- yazi-actor/src/lives/preview.rs | 2 + yazi-actor/src/mgr/peek.rs | 4 +- yazi-core/src/tab/preview.rs | 5 +-- yazi-parser/src/mgr/peek.rs | 2 +- yazi-plugin/preset/plugins/code.lua | 67 +++++++++++++---------------- yazi-runner/src/previewer/job.rs | 10 +++-- 6 files changed, 43 insertions(+), 47 deletions(-) diff --git a/yazi-actor/src/lives/preview.rs b/yazi-actor/src/lives/preview.rs index 754e768a..5390f48d 100644 --- a/yazi-actor/src/lives/preview.rs +++ b/yazi-actor/src/lives/preview.rs @@ -29,7 +29,9 @@ impl Preview { impl UserData for Preview { fn add_fields>(fields: &mut F) { fields.add_field_method_get("skip", |_, me| Ok(me.skip)); + // TODO: Make sure we need this ? fields.add_field_method_get("search_idx", |_, me| Ok(me.search_idx)); + cached_field!(fields, folder, |_, me| { me.tab .hovered_folder() diff --git a/yazi-actor/src/mgr/peek.rs b/yazi-actor/src/mgr/peek.rs index 739ac081..4399ed55 100644 --- a/yazi-actor/src/mgr/peek.rs +++ b/yazi-actor/src/mgr/peek.rs @@ -1,7 +1,7 @@ use anyhow::Result; use yazi_macro::succ; use yazi_parser::mgr::PeekForm; -use yazi_shared::{data::Data, url::UrlLike}; +use yazi_shared::data::Data; use crate::{Actor, Ctx}; @@ -46,10 +46,8 @@ impl Actor for Peek { } } - tracing::debug!("peeeek boy before set stuff"); match form.search_idx { Some(index) => { - tracing::debug!("search index setting {}", index); preview.search_idx = Some(index); } None => {} diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index f8cd9d3d..d1f9f4fe 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -43,10 +43,9 @@ impl Preview { self.abort(); - // let search_first_occurrence = self.get_first_search_occurrence(&file.url); - let job = PeekJob { previewer, file, mime, skip: self.skip }; + let job = PeekJob { previewer, file, mime, skip: self.skip, search_idx: self.search_idx }; - tracing::debug!("preview {:?}", self); + tracing::debug!("preview before to peek {:?}", self.search_idx); self.handle = Some(tokio::spawn(async move { let mut rx = RUNNER.peek(&job).await; diff --git a/yazi-parser/src/mgr/peek.rs b/yazi-parser/src/mgr/peek.rs index 69a296b4..bccdaf20 100644 --- a/yazi-parser/src/mgr/peek.rs +++ b/yazi-parser/src/mgr/peek.rs @@ -17,7 +17,7 @@ impl From for PeekForm { force: a.bool("force"), only_if: a.take("only-if").ok(), upper_bound: a.bool("upper-bound"), - search_idx: a.take("search_idx").ok(), + search_idx: a.take("search-idx").ok(), } } } diff --git a/yazi-plugin/preset/plugins/code.lua b/yazi-plugin/preset/plugins/code.lua index 0f239a80..6ad206ee 100644 --- a/yazi-plugin/preset/plugins/code.lua +++ b/yazi-plugin/preset/plugins/code.lua @@ -18,35 +18,36 @@ local function get_search_occurrences(url) return lines end --- local function get_next_occurrence(occurrences, current_line, direction) --- local step = direction == "up" and -1 or 1 --- local start = direction == "up" and #occurrences or 1 --- local stop = direction == "up" and 1 or #occurrences --- --- for i = start, stop, step do --- local line = occurrences[i] --- if go_up and line >= current_line then --- return line --- end --- if direction == "down" and line <= current_line then --- return line --- end --- --- -- TODO: Handle return to first element or last one --- end --- end +local function get_next_occurrence_idx(search_idx, direction, occurrences_len) + local index + + if direction == "up" then + index = (search_idx or 1) - 1 + if index < 1 then + index = occurrences_len + end + else + index = (search_idx or 1) + 1 + if index > occurrences_len then + index = 1 + end + end + + return index +end function M:peek(job) local search_occurrences = get_search_occurrences(job.file.url) - local search_idx = cx.active.preview.search_idx - ya.dbg(" search index", search_idx) + local search_idx = job.search_idx + + ya.dbg("code.lua: search_occurrences:", search_occurrences) + local occurrence if search_occurrences and search_idx then occurrence = search_occurrences[search_idx] end - ya.dgb("Peek Occurrence:", occurrence) - + ya.dbg("code.lua: Occurrence:", occurrence) -- Todo: Pass the occurrence for highlighting local err, bound = ya.preview_code(job) if bound then @@ -60,7 +61,7 @@ function M:seek(job) local direction = job.units > 0 and "down" or "up" local search_idx = cx.active.preview.search_idx - ya.dbg(" search index", search_idx) + ya.dbg("search index before to set it in seek", search_idx) local h = cx.active.current.hovered if not h or h.url ~= job.file.url then @@ -69,19 +70,13 @@ function M:seek(job) local search_occurrences = get_search_occurrences(job.file.url) if search_occurrences then - search_idx = (search_idx or 0) + 1 - -- local current_line = cx.active.preview.skip + 1 - -- ya.dbg("Current line:", current_line) - -- local next_occurrence = get_next_occurrence(search_occurrences, current_line, direction) - -- for _, occurrence in ipairs(search_occurrences) do - -- local search_line = occurrence[1] - -- -- local col = occurrence[2] - -- - -- if search_line >= current_line then - -- ya.emit("peek", { math.max(0, search_line - 1), only_if = job.file.url }) - -- return - -- end - -- end + local next_occurrence_idx = get_next_occurrence_idx(search_idx, direction, #search_occurrences) + local occurrence = search_occurrences[next_occurrence_idx] + local line = occurrence[1] + -- local col = occurrence[2] + + ya.emit("peek", { math.max(0, line - 1), only_if = job.file.url, search_idx = next_occurrence_idx }) + return end -- ya.emit("peek", { math.max(0, line - 1), only_if = job.file.url }) @@ -92,7 +87,7 @@ function M:seek(job) ya.emit("peek", { math.max(0, cx.active.preview.skip + step), only_if = job.file.url, - search_idx, + search_idx = search_idx, }) end diff --git a/yazi-runner/src/previewer/job.rs b/yazi-runner/src/previewer/job.rs index 09a2a1ab..f2a492a2 100644 --- a/yazi-runner/src/previewer/job.rs +++ b/yazi-runner/src/previewer/job.rs @@ -9,9 +9,10 @@ use yazi_shared::pool::Symbol; #[derive(Clone, Debug)] pub struct PeekJob { pub previewer: Arc, - pub file: yazi_fs::File, - pub mime: Symbol, - pub skip: usize, + pub file: yazi_fs::File, + pub mime: Symbol, + pub skip: usize, + pub search_idx: Option, } impl IntoLua for PeekJob { @@ -23,6 +24,7 @@ impl IntoLua for PeekJob { ("file", File::new(self.file).into_lua(lua)?), ("mime", self.mime.into_lua(lua)?), ("skip", self.skip.into_lua(lua)?), + ("search_idx", self.search_idx.into_lua(lua)?), ])? .into_lua(lua) } @@ -31,7 +33,7 @@ impl IntoLua for PeekJob { // --- Seek #[derive(Clone, Debug)] pub struct SeekJob { - pub file: yazi_fs::File, + pub file: yazi_fs::File, pub units: i16, } From 21ed8e0167d8ccf5e1f800d130edc9c130fdee05 Mon Sep 17 00:00:00 2001 From: Jed Date: Sun, 14 Jun 2026 22:55:02 +0200 Subject: [PATCH 06/15] feat: pass position to preview --- yazi-binding/src/elements/highlight.rs | 17 +++++++++++------ yazi-core/src/highlighter.rs | 3 +++ yazi-plugin/src/external/rg.rs | 3 +-- yazi-plugin/src/utils/preview.rs | 16 ++++++++++------ 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/yazi-binding/src/elements/highlight.rs b/yazi-binding/src/elements/highlight.rs index e94dc8da..57d454d1 100644 --- a/yazi-binding/src/elements/highlight.rs +++ b/yazi-binding/src/elements/highlight.rs @@ -1,19 +1,19 @@ -use mlua::{ - ExternalError, FromLua, IntoLua, Lua, Result as LuaResult, Table, UserData, UserDataMethods, - Value, -}; +use mlua::{ExternalError, FromLua, IntoLua, Lua, Result as LuaResult, Value}; const EXPECTED: &str = "expected a table containing a line and a length"; #[derive(Clone, Debug)] pub struct HighlightPosition { pub line: usize, + pub col: usize, pub length: usize, } impl HighlightPosition { pub fn compose(lua: &Lua) -> LuaResult { - let new = lua.create_function(|_, (line, length): (usize, usize)| Ok(Self { line, length }))?; + let new = lua.create_function(|_, (line, col, length): (usize, usize, usize)| { + Ok(Self { line, col, length }) + })?; let tbl = lua.create_table_from([("new", new)])?; tbl.into_lua(lua) @@ -23,7 +23,11 @@ impl HighlightPosition { impl FromLua for HighlightPosition { fn from_lua(value: Value, _: &Lua) -> mlua::Result { match value { - Value::Table(tbl) => Ok(Self { line: tbl.raw_get("line")?, length: tbl.raw_get("length")? }), + Value::Table(tbl) => Ok(Self { + line: tbl.raw_get("line")?, + col: tbl.raw_get("col")?, + length: tbl.raw_get("length")?, + }), _ => Err(EXPECTED.into_lua_err()), } } @@ -33,6 +37,7 @@ impl IntoLua for HighlightPosition { fn into_lua(self, lua: &Lua) -> LuaResult { let tbl = lua.create_table()?; tbl.set("line", self.line)?; + tbl.set("col", self.col)?; tbl.set("length", self.length)?; Ok(Value::Table(tbl)) } diff --git a/yazi-core/src/highlighter.rs b/yazi-core/src/highlighter.rs index 046d3667..99f2bd83 100644 --- a/yazi-core/src/highlighter.rs +++ b/yazi-core/src/highlighter.rs @@ -54,6 +54,8 @@ impl Highlighter { let path = path.into(); let (theme, syntaxes) = CACHE.get_or_init(Self::load); + // tracing::debug!("{:?}", theme); + Ok(Self { reader: BufReader::new(std::fs::File::open(&path)?), path, @@ -82,6 +84,7 @@ impl Highlighter { let mut lines = Vec::with_capacity(self.size.height as usize); let mut inspected = 0u16; while self.reader.read_until(b'\n', &mut buf).is_ok_and(|n| n > 0) { + // tracing::debug!("{:?}", buf); if Self::is_binary(&buf, &mut inspected) { Err(anyhow!("Binary file"))?; } diff --git a/yazi-plugin/src/external/rg.rs b/yazi-plugin/src/external/rg.rs index 82f9cd22..cf9ef811 100644 --- a/yazi-plugin/src/external/rg.rs +++ b/yazi-plugin/src/external/rg.rs @@ -19,8 +19,7 @@ pub struct RgOpt { pub fn rg(opt: RgOpt) -> Result> { let mut child = Command::new("rg") - .args(["--color=never", "--no-heading", "--column", "--smart-case"]) - // .args(["--color=never", "--files-with-matches", "--smart-case"]) + .args(["--color=never", "--no-heading", "--column", "--smart-case"]) .arg(if opt.hidden { "--hidden" } else { "--no-hidden" }) .args(opt.args) .arg(opt.subject) diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 2a3fc8fe..4cf50b2c 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -18,18 +18,22 @@ impl Utils { pub(super) fn preview_code(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, t: Table| async move { let area: Area = t.raw_get("area")?; - // let position: Option = t.raw_get("position").ok(); + let position: Option = t.raw_get("position").ok(); + + tracing::debug!("{:?}", position); let mut lock = PreviewLock::try_from(t)?; let path = lock.url.as_url().unified_path(); - // let skip = position.map(|p| p.line).unwrap_or(lock.skip); - let size = area.size(); + let search_subject = lock.url.as_url().scheme().domain(); - // tracing::debug!("skip size: {size}"); + tracing::debug!("search subject{:?}", search_subject); - let inner = match Highlighter::oneshot(path, lock.skip, size).await { - Ok(text) => text, + let inner = match Highlighter::oneshot(path, lock.skip, area.size()).await { + Ok(text) => { + // tracing::debug!("{:?}", text); + text + } Err(e @ PeekError::Exceeded(max)) => return (e, max).into_lua_multi(&lua), Err(e) => { return e.into_lua_multi(&lua); From d95d323a892312664d6101a42bfdae93f8e926eb Mon Sep 17 00:00:00 2001 From: Jed Date: Mon, 15 Jun 2026 22:05:44 +0200 Subject: [PATCH 07/15] feat: pass search length to the search url domain --- yazi-plugin/src/external/rg.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/yazi-plugin/src/external/rg.rs b/yazi-plugin/src/external/rg.rs index cf9ef811..be0c2e72 100644 --- a/yazi-plugin/src/external/rg.rs +++ b/yazi-plugin/src/external/rg.rs @@ -1,4 +1,4 @@ -use std::process::Stdio; +use std::{fmt::format, process::Stdio}; use anyhow::Result; use tokio::{ @@ -22,7 +22,7 @@ pub fn rg(opt: RgOpt) -> Result> { .args(["--color=never", "--no-heading", "--column", "--smart-case"]) .arg(if opt.hidden { "--hidden" } else { "--no-hidden" }) .args(opt.args) - .arg(opt.subject) + .arg(opt.subject.clone()) .current_dir(&*opt.cwd.as_url().unified_path()) .kill_on_drop(true) .stdout(Stdio::piped()) @@ -42,7 +42,9 @@ pub fn rg(opt: RgOpt) -> Result> { let Some((file_path, line, col)) = parse_rg_line(&search_line) else { continue }; if current_file != file_path { - let Some(url) = build_file_url(opt.cwd.clone(), ¤t_file, ¤t_occurrences) else { + let Some(url) = + build_file_url(opt.cwd.clone(), opt.subject.clone(), ¤t_file, ¤t_occurrences) + else { continue; }; @@ -57,7 +59,7 @@ pub fn rg(opt: RgOpt) -> Result> { } if current_occurrences.len() > 0 { - match build_file_url(opt.cwd, ¤t_file, ¤t_occurrences) { + match build_file_url(opt.cwd, opt.subject, ¤t_file, ¤t_occurrences) { Some(url) => { if let Ok(file) = File::new(url).await { tx.send(file).ok(); @@ -73,10 +75,16 @@ pub fn rg(opt: RgOpt) -> Result> { Ok(rx) } -fn build_file_url(cwd: UrlBuf, file_path: &str, occurences: &Vec<(u32, u32)>) -> Option { +fn build_file_url( + cwd: UrlBuf, + subject: String, + file_path: &str, + occurences: &Vec<(u32, u32)>, +) -> Option { let occurrences_str = occurences.iter().map(|(line, col)| format!("{line}-{col}")).collect::>().join(","); - let url = cwd.try_join(file_path).ok().and_then(|u| u.into_search(occurrences_str).ok()); + let search_str = format!("{}~{}", subject.len(), occurrences_str); + let url = cwd.try_join(file_path).ok().and_then(|u| u.into_search(search_str).ok()); url } From 04501c6aa973893b4edb619744a72e53cdfc54d2 Mon Sep 17 00:00:00 2001 From: Jed Date: Mon, 15 Jun 2026 22:06:28 +0200 Subject: [PATCH 08/15] feat: pass highlight position from code.lua to highlight.rs module --- yazi-core/src/highlighter.rs | 45 ++++++++++++++++++++--------- yazi-core/src/tab/preview.rs | 2 -- yazi-plugin/preset/plugins/code.lua | 19 ++++++------ yazi-plugin/src/utils/preview.rs | 10 ++----- 4 files changed, 43 insertions(+), 33 deletions(-) diff --git a/yazi-core/src/highlighter.rs b/yazi-core/src/highlighter.rs index 99f2bd83..0082e4d9 100644 --- a/yazi-core/src/highlighter.rs +++ b/yazi-core/src/highlighter.rs @@ -1,20 +1,20 @@ -use std::{ - io::{BufRead, BufReader, Cursor, Seek}, - path::PathBuf, - sync::OnceLock, -}; - use anyhow::{Result, anyhow, bail}; use ratatui::{ layout::Size, text::{Line, Span, Text}, }; +use std::{ + io::{BufRead, BufReader, Cursor, Seek}, + path::PathBuf, + sync::OnceLock, +}; use syntect::{ LoadingError, dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}, }; +use yazi_binding::elements::HighlightPosition; use yazi_config::{THEME, YAZI}; use yazi_runner::previewer::PeekError; use yazi_shared::{Id, Ids, replace_to_printable}; @@ -37,12 +37,17 @@ pub struct Highlighter { } impl Highlighter { - pub async fn oneshot

(path: P, skip: usize, size: Size) -> Result, PeekError> + pub async fn oneshot

( + path: P, + skip: usize, + size: Size, + position: Option, + ) -> Result, PeekError> where P: Into, { let path = path.into(); - tokio::task::spawn_blocking(move || Self::make(path, skip, size)?.highlight()).await? + tokio::task::spawn_blocking(move || Self::make(path, skip, size)?.highlight(position)).await? } fn make

(path: P, skip: usize, size: Size) -> Result @@ -75,7 +80,7 @@ impl Highlighter { INCR.next(); } - fn highlight(mut self) -> Result, PeekError> { + fn highlight(mut self, position: Option) -> Result, PeekError> { self.load_syntax()?; let mut plain = self.syntax.is_none(); @@ -84,7 +89,6 @@ impl Highlighter { let mut lines = Vec::with_capacity(self.size.height as usize); let mut inspected = 0u16; while self.reader.read_until(b'\n', &mut buf).is_ok_and(|n| n > 0) { - // tracing::debug!("{:?}", buf); if Self::is_binary(&buf, &mut inspected) { Err(anyhow!("Binary file"))?; } @@ -95,9 +99,9 @@ impl Highlighter { } self.ensure_not_cancelled()?; - if plain && !self.process_plain(&buf, &mut i, &mut lines)? { + if plain && !self.process_plain(&buf, &mut i, &position, &mut lines)? { break; - } else if !plain && !self.process_hyper(&buf, &mut i, &mut lines)? { + } else if !plain && !self.process_hyper(&buf, &mut i, &position, &mut lines)? { break; } buf.clear(); @@ -110,7 +114,13 @@ impl Highlighter { Ok(Text::from(lines)) } - fn process_plain(&mut self, buf: &[u8], i: &mut usize, lines: &mut Vec) -> Result { + fn process_plain( + &mut self, + buf: &[u8], + i: &mut usize, + position: &Option, + lines: &mut Vec, + ) -> Result { let b = replace_to_printable(buf, true, YAZI.preview.tab_size, false); let s = String::from_utf8_lossy(&b); @@ -131,11 +141,18 @@ impl Highlighter { Ok(true) } - fn process_hyper(&mut self, buf: &[u8], i: &mut usize, lines: &mut Vec) -> Result { + fn process_hyper( + &mut self, + buf: &[u8], + i: &mut usize, + position: &Option, + lines: &mut Vec, + ) -> Result { let Some(syntax) = self.syntax else { bail!("No syntax") }; let h = self.inner.get_or_insert_with(|| HighlightLines::new(syntax, self.theme)); let s = String::from_utf8_lossy(buf); + tracing::debug!("s {:?}", s); let line = [Self::to_line_widget(h.highlight_line(&s, self.syntaxes)?)]; let mut it = LineIter::parsed(&line, YAZI.preview.tab_size); diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index d1f9f4fe..ce182153 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -45,8 +45,6 @@ impl Preview { let job = PeekJob { previewer, file, mime, skip: self.skip, search_idx: self.search_idx }; - tracing::debug!("preview before to peek {:?}", self.search_idx); - self.handle = Some(tokio::spawn(async move { let mut rx = RUNNER.peek(&job).await; match rx.recv().await.unwrap_or(Err(PeekError::Cancelled)) { diff --git a/yazi-plugin/preset/plugins/code.lua b/yazi-plugin/preset/plugins/code.lua index 6ad206ee..d366718e 100644 --- a/yazi-plugin/preset/plugins/code.lua +++ b/yazi-plugin/preset/plugins/code.lua @@ -4,7 +4,8 @@ local function get_search_occurrences(url) if not url.is_search then return end - local occurrences = tostring(url):match("^search://(.-)//") + -- Shoud we encode ~ too? + local subject_len, occurrences = tostring(url):match("^search://([^~]+)~(.-)//") local lines = {} for occurrence in occurrences:gmatch("[^,]+") do local line, col = occurrence:match("(%d+)-(%d+)") @@ -15,7 +16,7 @@ local function get_search_occurrences(url) return end - return lines + return tonumber(subject_len), lines end local function get_next_occurrence_idx(search_idx, direction, occurrences_len) @@ -37,18 +38,19 @@ local function get_next_occurrence_idx(search_idx, direction, occurrences_len) end function M:peek(job) - local search_occurrences = get_search_occurrences(job.file.url) + local subject_len, search_occurrences = get_search_occurrences(job.file.url) local search_idx = job.search_idx - ya.dbg("code.lua: search_occurrences:", search_occurrences) - local occurrence if search_occurrences and search_idx then occurrence = search_occurrences[search_idx] + job.position = { + line = occurrence[1], + col = occurrence[2], + length = subject_len, + } end - ya.dbg("code.lua: Occurrence:", occurrence) - -- Todo: Pass the occurrence for highlighting local err, bound = ya.preview_code(job) if bound then ya.emit("peek", { bound, only_if = job.file.url, upper_bound = true }) @@ -61,14 +63,13 @@ function M:seek(job) local direction = job.units > 0 and "down" or "up" local search_idx = cx.active.preview.search_idx - ya.dbg("search index before to set it in seek", search_idx) local h = cx.active.current.hovered if not h or h.url ~= job.file.url then return end - local search_occurrences = get_search_occurrences(job.file.url) + local _, search_occurrences = get_search_occurrences(job.file.url) if search_occurrences then local next_occurrence_idx = get_next_occurrence_idx(search_idx, direction, #search_occurrences) local occurrence = search_occurrences[next_occurrence_idx] diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 4cf50b2c..70a9ae19 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -7,7 +7,7 @@ use yazi_binding::{ use yazi_core::{Highlighter, MgrProxy, tab::PreviewLock}; use yazi_fs::FsUrl; use yazi_runner::previewer::PeekError; -use yazi_shared::url::AsUrl; +use yazi_shared::url::{AsUrl, UrlLike}; use super::Utils; @@ -20,16 +20,10 @@ impl Utils { let area: Area = t.raw_get("area")?; let position: Option = t.raw_get("position").ok(); - tracing::debug!("{:?}", position); - let mut lock = PreviewLock::try_from(t)?; let path = lock.url.as_url().unified_path(); - let search_subject = lock.url.as_url().scheme().domain(); - - tracing::debug!("search subject{:?}", search_subject); - - let inner = match Highlighter::oneshot(path, lock.skip, area.size()).await { + let inner = match Highlighter::oneshot(path, lock.skip, area.size(), position).await { Ok(text) => { // tracing::debug!("{:?}", text); text From a53d404aa49a95912774b08e3f634b4dae5b05d6 Mon Sep 17 00:00:00 2001 From: Jed Date: Tue, 16 Jun 2026 08:03:46 +0200 Subject: [PATCH 09/15] feat: highlight line --- yazi-core/src/highlighter.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/yazi-core/src/highlighter.rs b/yazi-core/src/highlighter.rs index 0082e4d9..00d7cd6f 100644 --- a/yazi-core/src/highlighter.rs +++ b/yazi-core/src/highlighter.rs @@ -1,4 +1,6 @@ use anyhow::{Result, anyhow, bail}; +use ratatui::style; +use ratatui::style::Style; use ratatui::{ layout::Size, text::{Line, Span, Text}, @@ -150,11 +152,10 @@ impl Highlighter { ) -> Result { let Some(syntax) = self.syntax else { bail!("No syntax") }; let h = self.inner.get_or_insert_with(|| HighlightLines::new(syntax, self.theme)); - let s = String::from_utf8_lossy(buf); - tracing::debug!("s {:?}", s); - let line = [Self::to_line_widget(h.highlight_line(&s, self.syntaxes)?)]; + // tracing::debug!("s {:?}", s); + let line = [Self::to_line_widget(h.highlight_line(&s, self.syntaxes)?)]; let mut it = LineIter::parsed(&line, YAZI.preview.tab_size); if let Some(wrap) = YAZI.preview.wrap.into() { it = it.wrapped(wrap, self.size.width); @@ -165,7 +166,14 @@ impl Highlighter { if *i > self.skip + self.size.height as usize { return Ok(false); } else if *i > self.skip { - lines.push(spans.into_static_line()); + let mut static_line = spans.into_static_line(); + if let Some(pos) = position { + if pos.line == *i { + tracing::debug!("Position: {:?}, line: {}", pos, i); + static_line.style = Style::new().bg(style::Color::Red); + } + } + lines.push(static_line); } self.ensure_not_cancelled()?; } From 2bb1c8fb8f35c46eb3ee62fc2e9eb5c3cdcbe4e6 Mon Sep 17 00:00:00 2001 From: Jed Date: Thu, 18 Jun 2026 23:21:46 +0200 Subject: [PATCH 10/15] feat: Improve line highlighting for search --- yazi-core/src/highlighter.rs | 22 ++++++++------- yazi-core/src/proxy.rs | 5 ++-- yazi-core/src/tab/preview.rs | 3 +- yazi-core/src/tab/preview_lock.rs | 15 ++++++---- yazi-plugin/preset/plugins/code.lua | 17 ++++++++---- yazi-plugin/src/utils/preview.rs | 43 ++--------------------------- 6 files changed, 41 insertions(+), 64 deletions(-) diff --git a/yazi-core/src/highlighter.rs b/yazi-core/src/highlighter.rs index 00d7cd6f..c468dc53 100644 --- a/yazi-core/src/highlighter.rs +++ b/yazi-core/src/highlighter.rs @@ -61,8 +61,6 @@ impl Highlighter { let path = path.into(); let (theme, syntaxes) = CACHE.get_or_init(Self::load); - // tracing::debug!("{:?}", theme); - Ok(Self { reader: BufReader::new(std::fs::File::open(&path)?), path, @@ -112,7 +110,6 @@ impl Highlighter { if self.skip > 0 && i < self.skip + self.size.height as usize { return Err(PeekError::Exceeded(i.saturating_sub(self.size.height as _))); } - Ok(Text::from(lines)) } @@ -136,7 +133,13 @@ impl Highlighter { if *i > self.skip + self.size.height as usize { return Ok(false); } else if *i > self.skip { - lines.push(spans.into_static_line()); + let mut static_line = spans.into_static_line(); + if let Some(pos) = position + && pos.line == *i + { + static_line.style = Style::new().bg(style::Color::Red); + } + lines.push(static_line); } self.ensure_not_cancelled()?; } @@ -154,7 +157,6 @@ impl Highlighter { let h = self.inner.get_or_insert_with(|| HighlightLines::new(syntax, self.theme)); let s = String::from_utf8_lossy(buf); - // tracing::debug!("s {:?}", s); let line = [Self::to_line_widget(h.highlight_line(&s, self.syntaxes)?)]; let mut it = LineIter::parsed(&line, YAZI.preview.tab_size); if let Some(wrap) = YAZI.preview.wrap.into() { @@ -167,16 +169,16 @@ impl Highlighter { return Ok(false); } else if *i > self.skip { let mut static_line = spans.into_static_line(); - if let Some(pos) = position { - if pos.line == *i { - tracing::debug!("Position: {:?}, line: {}", pos, i); - static_line.style = Style::new().bg(style::Color::Red); - } + if let Some(pos) = position + && pos.line == *i + { + static_line.style = Style::new().bg(style::Color::Red); } lines.push(static_line); } self.ensure_not_cancelled()?; } + Ok(true) } diff --git a/yazi-core/src/proxy.rs b/yazi-core/src/proxy.rs index a8e1457e..45c357a1 100644 --- a/yazi-core/src/proxy.rs +++ b/yazi-core/src/proxy.rs @@ -43,11 +43,12 @@ impl MgrProxy { pub fn update_peeked_error(job: PeekJob, error: String) { let area = LAYOUT.get().preview; Self::update_peeked(PreviewLock { - url: job.file.url, - cha: job.file.cha, + url: job.file.url, + cha: job.file.cha, mime: job.mime, skip: job.skip, + search_idx: job.search_idx, area: area.into(), data: vec![ Renderable::Clear(yazi_binding::elements::Clear { area: area.into() }), diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index ce182153..9973a8d3 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -115,7 +115,8 @@ impl Preview { } pub fn same_lock(&self, file: &File, mime: &str) -> bool { - self.same_file(file, mime) && matches!(&self.lock, Some(l) if l.skip == self.skip) + self.same_file(file, mime) + && matches!(&self.lock, Some(l) if l.skip == self.skip && l.search_idx == self.search_idx) } pub fn same_folder(&self, url: &UrlBuf) -> bool { diff --git a/yazi-core/src/tab/preview_lock.rs b/yazi-core/src/tab/preview_lock.rs index b643ceae..7eb1f7dd 100644 --- a/yazi-core/src/tab/preview_lock.rs +++ b/yazi-core/src/tab/preview_lock.rs @@ -1,14 +1,18 @@ use mlua::Table; -use yazi_binding::{FileRef, elements::{Rect, Renderable}}; +use yazi_binding::{ + FileRef, + elements::{Rect, Renderable}, +}; use yazi_shared::pool::{InternStr, Symbol}; #[derive(Clone, Debug, Default)] pub struct PreviewLock { - pub url: yazi_shared::url::UrlBuf, - pub cha: yazi_fs::cha::Cha, + pub url: yazi_shared::url::UrlBuf, + pub cha: yazi_fs::cha::Cha, pub mime: Symbol, pub skip: usize, + pub search_idx: Option, pub area: Rect, pub data: Vec, } @@ -19,11 +23,12 @@ impl TryFrom for PreviewLock { fn try_from(t: Table) -> Result { let file: FileRef = t.raw_get("file")?; Ok(Self { - url: file.url_owned(), - cha: file.cha, + url: file.url_owned(), + cha: file.cha, mime: t.raw_get::("mime")?.to_str()?.intern(), skip: t.raw_get("skip")?, + search_idx: t.raw_get("search_idx")?, area: t.raw_get("area")?, data: Default::default(), }) diff --git a/yazi-plugin/preset/plugins/code.lua b/yazi-plugin/preset/plugins/code.lua index d366718e..c32a496a 100644 --- a/yazi-plugin/preset/plugins/code.lua +++ b/yazi-plugin/preset/plugins/code.lua @@ -41,6 +41,17 @@ function M:peek(job) local subject_len, search_occurrences = get_search_occurrences(job.file.url) local search_idx = job.search_idx + if search_idx == nil and search_occurrences then + local occurrence = search_occurrences[1] + local line = occurrence[1] + ya.emit("peek", { + math.max(0, line - 1), + only_if = job.file.url, + search_idx = 1, + }) + return + end + local occurrence if search_occurrences and search_idx then occurrence = search_occurrences[search_idx] @@ -53,7 +64,7 @@ function M:peek(job) local err, bound = ya.preview_code(job) if bound then - ya.emit("peek", { bound, only_if = job.file.url, upper_bound = true }) + ya.emit("peek", { bound, only_if = job.file.url, upper_bound = true, search_idx = search_idx }) elseif err and not err:find("cancelled", 1, true) then require("empty").msg(job, err) end @@ -61,7 +72,6 @@ end function M:seek(job) local direction = job.units > 0 and "down" or "up" - local search_idx = cx.active.preview.search_idx local h = cx.active.current.hovered @@ -74,14 +84,11 @@ function M:seek(job) local next_occurrence_idx = get_next_occurrence_idx(search_idx, direction, #search_occurrences) local occurrence = search_occurrences[next_occurrence_idx] local line = occurrence[1] - -- local col = occurrence[2] ya.emit("peek", { math.max(0, line - 1), only_if = job.file.url, search_idx = next_occurrence_idx }) return end - -- ya.emit("peek", { math.max(0, line - 1), only_if = job.file.url }) - local step = math.floor(job.units * job.area.h / 10) step = step == 0 and ya.clamp(-1, job.units, 1) or step diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 70a9ae19..fb2d19c4 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -7,7 +7,7 @@ use yazi_binding::{ use yazi_core::{Highlighter, MgrProxy, tab::PreviewLock}; use yazi_fs::FsUrl; use yazi_runner::previewer::PeekError; -use yazi_shared::url::{AsUrl, UrlLike}; +use yazi_shared::url::AsUrl; use super::Utils; @@ -24,18 +24,13 @@ impl Utils { let path = lock.url.as_url().unified_path(); let inner = match Highlighter::oneshot(path, lock.skip, area.size(), position).await { - Ok(text) => { - // tracing::debug!("{:?}", text); - text - } + Ok(text) => text, Err(e @ PeekError::Exceeded(max)) => return (e, max).into_lua_multi(&lua), Err(e) => { return e.into_lua_multi(&lua); } }; - // tracing::debug!("Inner: {inner}"); - lock.data = vec![Renderable::Text(Text { area, inner, ..Default::default() })]; MgrProxy::update_peeked(lock); @@ -72,37 +67,3 @@ impl Utils { }) } } - -// fn apply_highlight(text: &mut Text, column: usize, length: usize) { -// use ratatui::text::Span; -// if length == 0 { -// return; -// } -// let Some(line) = text.inner.lines.first_mut() else { return }; -// let mut new_spans = Vec::new(); -// let mut char_pos = 0usize; -// for span in std::mem::take(&mut line.spans) { -// let n = span.content.chars().count(); -// let range = char_pos..char_pos + n; -// if range.end <= column || range.start >= column + length || n == 0 { -// new_spans.push(span); -// } else { -// let chars: Vec = span.content.chars().collect(); -// let hl_start = column.saturating_sub(char_pos).min(n); -// let hl_end = (column + length).saturating_sub(char_pos).min(n); -// if hl_start > 0 { -// new_spans.push(Span { content: chars[..hl_start].iter().collect(), style: span.style }); -// } -// { -// let mut hl_style = span.style; -// hl_style.bg = hl_style.bg.or(Some(Color::Yellow)); -// new_spans.push(Span { content: chars[hl_start..hl_end].iter().collect(), style: hl_style }); -// } -// if hl_end < n { -// new_spans.push(Span { content: chars[hl_end..].iter().collect(), style: span.style }); -// } -// } -// char_pos = range.end; -// } -// line.spans = new_spans; -// } From d1a3f7bc54e178ad72bcb886486a6869ff02f4f2 Mon Sep 17 00:00:00 2001 From: Jed Date: Sun, 21 Jun 2026 18:56:52 +0200 Subject: [PATCH 11/15] feat: do not block skip to be set of making preview go beyond view, there is a guard already --- yazi-actor/src/mgr/peek.rs | 6 +----- yazi-plugin/preset/plugins/code.lua | 9 ++++++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/yazi-actor/src/mgr/peek.rs b/yazi-actor/src/mgr/peek.rs index 4399ed55..e320ed37 100644 --- a/yazi-actor/src/mgr/peek.rs +++ b/yazi-actor/src/mgr/peek.rs @@ -39,11 +39,7 @@ impl Actor for Peek { let preview = &mut cx.tab_mut().preview; if let Some(skip) = form.skip { - if form.upper_bound { - preview.skip = preview.skip.min(skip); - } else { - preview.skip = skip; - } + preview.skip = skip; } match form.search_idx { diff --git a/yazi-plugin/preset/plugins/code.lua b/yazi-plugin/preset/plugins/code.lua index c32a496a..168afc33 100644 --- a/yazi-plugin/preset/plugins/code.lua +++ b/yazi-plugin/preset/plugins/code.lua @@ -44,8 +44,9 @@ function M:peek(job) if search_idx == nil and search_occurrences then local occurrence = search_occurrences[1] local line = occurrence[1] + local skip = math.max(0, line - 1) ya.emit("peek", { - math.max(0, line - 1), + skip, only_if = job.file.url, search_idx = 1, }) @@ -84,16 +85,18 @@ function M:seek(job) local next_occurrence_idx = get_next_occurrence_idx(search_idx, direction, #search_occurrences) local occurrence = search_occurrences[next_occurrence_idx] local line = occurrence[1] + local skip = math.max(0, line - 1) - ya.emit("peek", { math.max(0, line - 1), only_if = job.file.url, search_idx = next_occurrence_idx }) + ya.emit("peek", { skip, only_if = job.file.url, search_idx = next_occurrence_idx }) return end local step = math.floor(job.units * job.area.h / 10) step = step == 0 and ya.clamp(-1, job.units, 1) or step + local skip = math.max(0, cx.active.preview.skip + step) ya.emit("peek", { - math.max(0, cx.active.preview.skip + step), + skip, only_if = job.file.url, search_idx = search_idx, }) From c4fc9c605ef086fa9df1ceab84f1eaffa8ac0911 Mon Sep 17 00:00:00 2001 From: Jed Date: Sun, 21 Jun 2026 19:18:46 +0200 Subject: [PATCH 12/15] feat: light refacto --- yazi-actor/src/mgr/peek.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/yazi-actor/src/mgr/peek.rs b/yazi-actor/src/mgr/peek.rs index e320ed37..7ef9861e 100644 --- a/yazi-actor/src/mgr/peek.rs +++ b/yazi-actor/src/mgr/peek.rs @@ -42,11 +42,8 @@ impl Actor for Peek { preview.skip = skip; } - match form.search_idx { - Some(index) => { - preview.search_idx = Some(index); - } - None => {} + if let Some(index) = form.search_idx { + preview.search_idx = Some(index); } if hovered.is_dir() { From de881ce526c9a16719c8126e30fc7a300d54d3da Mon Sep 17 00:00:00 2001 From: Jed Date: Sun, 21 Jun 2026 19:25:29 +0200 Subject: [PATCH 13/15] feat: removes comments --- yazi-plugin/src/external/rg.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/yazi-plugin/src/external/rg.rs b/yazi-plugin/src/external/rg.rs index 99651ff2..5bc3465f 100644 --- a/yazi-plugin/src/external/rg.rs +++ b/yazi-plugin/src/external/rg.rs @@ -1,7 +1,11 @@ use std::{fmt::format, process::Stdio}; use anyhow::Result; -use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}}; +use tokio::{ + io::{AsyncBufReadExt, BufReader}, + process::Command, + sync::mpsc::{self, UnboundedReceiver}, +}; use yazi_fs::{FsUrl, file::File}; use yazi_shared::url::{AsUrl, UrlBuf, UrlLike}; use yazi_vfs::VfsFile; @@ -29,8 +33,6 @@ pub fn rg(opt: RgOpt) -> Result> { let (tx, rx) = mpsc::unbounded_channel(); tokio::spawn(async move { - // let mut occurrences_per_file: HashMap> = HashMap::new(); - // let mut current_file_path: String = String::new(); let mut current_file: String = String::new(); let mut current_occurrences: Vec<(u32, u32)> = vec![]; From fb6de19e81d5ce0b31933556276a2797db204c07 Mon Sep 17 00:00:00 2001 From: Jed Date: Sun, 21 Jun 2026 19:31:34 +0200 Subject: [PATCH 14/15] feat: Fix imports --- yazi-core/src/highlighter.rs | 15 ++++++++------- yazi-plugin/src/utils/preview.rs | 5 ++++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/yazi-core/src/highlighter.rs b/yazi-core/src/highlighter.rs index 90d19a0a..f7883d38 100644 --- a/yazi-core/src/highlighter.rs +++ b/yazi-core/src/highlighter.rs @@ -1,7 +1,5 @@ use anyhow::{Result, anyhow, bail}; -use ratatui::style; -use ratatui::style::Style; -use ratatui::{ +use ratatui_core::{ layout::Size, text::{Line, Span, Text}, }; @@ -19,7 +17,10 @@ use syntect::{ use yazi_binding::elements::HighlightPosition; use yazi_config::{THEME, YAZI}; use yazi_runner::previewer::PeekError; -use yazi_shared::{id::{Id, Ids}, replace_to_printable}; +use yazi_shared::{ + id::{Id, Ids}, + replace_to_printable, +}; use yazi_shim::ratatui::LineIter; static INCR: Ids = Ids::new(); @@ -137,7 +138,7 @@ impl Highlighter { if let Some(pos) = position && pos.line == *i { - static_line.style = Style::new().bg(style::Color::Red); + static_line.style = ratatui_core::style::Style::new().bg(ratatui_core::style::Color::Red); } lines.push(static_line); } @@ -172,7 +173,7 @@ impl Highlighter { if let Some(pos) = position && pos.line == *i { - static_line.style = Style::new().bg(style::Color::Red); + static_line.style = ratatui_core::style::Style::new().bg(ratatui_core::style::Color::Red); } lines.push(static_line); } @@ -264,7 +265,7 @@ impl Highlighter { Span { content: s.into(), - style: ratatui_core::style::Style { + style: ratatui_core::style::Style { fg: Self::to_ansi_color(style.foreground), // bg: Self::to_ansi_color(style.background), add_modifier: modifier, diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 6709ccc1..619ce5d2 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -1,5 +1,8 @@ use mlua::{ExternalError, Function, IntoLuaMulti, Lua, Table, Value}; -use yazi_binding::{Error, elements::Area}; +use yazi_binding::{ + Error, + elements::{Area, HighlightPosition}, +}; use yazi_core::{Highlighter, MgrProxy, tab::PreviewLock}; use yazi_fs::FsUrl; use yazi_runner::previewer::PeekError; From 529ecbe6a66882382c7eda9b1ca0ac44e24c8729 Mon Sep 17 00:00:00 2001 From: Jed Date: Mon, 6 Jul 2026 22:09:33 +0200 Subject: [PATCH 15/15] feat: Fix linter --- yazi-actor/src/lives/preview.rs | 4 +-- yazi-binding/src/elements/highlight.rs | 8 ++--- yazi-core/src/highlighter.rs | 42 ++++++++------------------ yazi-core/src/proxy.rs | 10 +++--- yazi-core/src/tab/preview.rs | 26 +++++----------- yazi-core/src/tab/preview_lock.rs | 20 ++++++------ yazi-parser/src/mgr/peek.rs | 28 +++++++---------- yazi-plugin/src/external/rg.rs | 14 +++------ yazi-plugin/src/utils/preview.rs | 8 ++--- yazi-runner/src/previewer/job.rs | 10 +++--- 10 files changed, 65 insertions(+), 105 deletions(-) diff --git a/yazi-actor/src/lives/preview.rs b/yazi-actor/src/lives/preview.rs index 61db852a..bee57a44 100644 --- a/yazi-actor/src/lives/preview.rs +++ b/yazi-actor/src/lives/preview.rs @@ -13,9 +13,7 @@ pub(super) struct Preview { impl Deref for Preview { type Target = yazi_core::tab::Preview; - fn deref(&self) -> &Self::Target { - &self.tab.preview - } + fn deref(&self) -> &Self::Target { &self.tab.preview } } impl Preview { diff --git a/yazi-binding/src/elements/highlight.rs b/yazi-binding/src/elements/highlight.rs index 57d454d1..a4bb205b 100644 --- a/yazi-binding/src/elements/highlight.rs +++ b/yazi-binding/src/elements/highlight.rs @@ -4,8 +4,8 @@ const EXPECTED: &str = "expected a table containing a line and a length"; #[derive(Clone, Debug)] pub struct HighlightPosition { - pub line: usize, - pub col: usize, + pub line: usize, + pub col: usize, pub length: usize, } @@ -24,8 +24,8 @@ impl FromLua for HighlightPosition { fn from_lua(value: Value, _: &Lua) -> mlua::Result { match value { Value::Table(tbl) => Ok(Self { - line: tbl.raw_get("line")?, - col: tbl.raw_get("col")?, + line: tbl.raw_get("line")?, + col: tbl.raw_get("col")?, length: tbl.raw_get("length")?, }), _ => Err(EXPECTED.into_lua_err()), diff --git a/yazi-core/src/highlighter.rs b/yazi-core/src/highlighter.rs index f7883d38..ad507970 100644 --- a/yazi-core/src/highlighter.rs +++ b/yazi-core/src/highlighter.rs @@ -1,42 +1,28 @@ +use std::{io::{BufRead, BufReader, Cursor, Seek}, path::PathBuf, sync::OnceLock}; + use anyhow::{Result, anyhow, bail}; -use ratatui_core::{ - layout::Size, - text::{Line, Span, Text}, -}; -use std::{ - io::{BufRead, BufReader, Cursor, Seek}, - path::PathBuf, - sync::OnceLock, -}; -use syntect::{ - LoadingError, dumps, - easy::HighlightLines, - highlighting::{self, Theme, ThemeSet}, - parsing::{SyntaxReference, SyntaxSet}, -}; +use ratatui_core::{layout::Size, text::{Line, Span, Text}}; +use syntect::{LoadingError, dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}}; use yazi_binding::elements::HighlightPosition; use yazi_config::{THEME, YAZI}; use yazi_runner::previewer::PeekError; -use yazi_shared::{ - id::{Id, Ids}, - replace_to_printable, -}; +use yazi_shared::{id::{Id, Ids}, replace_to_printable}; use yazi_shim::ratatui::LineIter; static INCR: Ids = Ids::new(); pub struct Highlighter { - path: PathBuf, + path: PathBuf, reader: BufReader, - skip: usize, - size: Size, + skip: usize, + size: Size, ticket: Id, - theme: &'static Theme, + theme: &'static Theme, syntaxes: &'static SyntaxSet, - inner: Option>, - syntax: Option<&'static SyntaxReference>, + inner: Option>, + syntax: Option<&'static SyntaxReference>, } impl Highlighter { @@ -77,9 +63,7 @@ impl Highlighter { }) } - pub fn abort() { - INCR.next(); - } + pub fn abort() { INCR.next(); } fn highlight(mut self, position: Option) -> Result, PeekError> { self.load_syntax()?; @@ -265,7 +249,7 @@ impl Highlighter { Span { content: s.into(), - style: ratatui_core::style::Style { + style: ratatui_core::style::Style { fg: Self::to_ansi_color(style.foreground), // bg: Self::to_ansi_color(style.background), add_modifier: modifier, diff --git a/yazi-core/src/proxy.rs b/yazi-core/src/proxy.rs index 387c9e5c..88b069ce 100644 --- a/yazi-core/src/proxy.rs +++ b/yazi-core/src/proxy.rs @@ -44,14 +44,14 @@ impl MgrProxy { pub fn update_peeked_error(job: PeekJob, error: String) { let area = LAYOUT.get().preview; Self::update_peeked(PreviewLock { - url: job.file.url, - cha: job.file.cha, + url: job.file.url, + cha: job.file.cha, mime: job.mime, - skip: job.skip, + skip: job.skip, search_idx: job.search_idx, - area: area.into(), - data: vec![ + area: area.into(), + data: vec![ Renderable::Clear(Default::default()).with_area(area), Renderable::from(Error::custom(error)).with_area(area), ], diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index b2b1816e..79a16860 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -6,27 +6,21 @@ use yazi_adapter::ADAPTOR; use yazi_config::{LAYOUT, YAZI}; use yazi_fs::{Entries, FilesOp, cha::Cha, file::File}; use yazi_macro::render; -use yazi_runner::{ - RUNNER, - previewer::{PeekError, PeekJob}, -}; -use yazi_shared::{ - pool::Symbol, - url::{UrlBuf, UrlLike}, -}; +use yazi_runner::{RUNNER, previewer::{PeekError, PeekJob}}; +use yazi_shared::{pool::Symbol, url::{UrlBuf, UrlLike}}; use yazi_vfs::{VfsEntries, VfsFilesOp}; use crate::{AppProxy, Highlighter, MgrProxy, tab::PreviewLock}; #[derive(Default, Debug)] pub struct Preview { - pub lock: Option, - pub skip: usize, + pub lock: Option, + pub skip: usize, pub search_idx: Option, - handle: Option>, + handle: Option>, pub folder_lock: Option, - folder_loader: Option>, + folder_loader: Option>, } impl Preview { @@ -104,9 +98,7 @@ impl Preview { ADAPTOR.image_hide().ok(); } - pub fn same_url(&self, url: &UrlBuf) -> bool { - matches!(&self.lock, Some(l) if l.url == *url) - } + pub fn same_url(&self, url: &UrlBuf) -> bool { matches!(&self.lock, Some(l) if l.url == *url) } pub fn same_file(&self, file: &File, mime: &str) -> bool { self.same_url(&file.url) @@ -118,7 +110,5 @@ impl Preview { && matches!(&self.lock, Some(l) if l.skip == self.skip && l.search_idx == self.search_idx) } - pub fn same_folder(&self, url: &UrlBuf) -> bool { - self.folder_lock.as_ref() == Some(url) - } + pub fn same_folder(&self, url: &UrlBuf) -> bool { self.folder_lock.as_ref() == Some(url) } } diff --git a/yazi-core/src/tab/preview_lock.rs b/yazi-core/src/tab/preview_lock.rs index f78904b4..0b9c82c2 100644 --- a/yazi-core/src/tab/preview_lock.rs +++ b/yazi-core/src/tab/preview_lock.rs @@ -7,14 +7,14 @@ use yazi_widgets::Renderable; #[derive(Clone, Debug, Default)] pub struct PreviewLock { - pub url: yazi_shared::url::UrlBuf, - pub cha: yazi_fs::cha::Cha, + pub url: yazi_shared::url::UrlBuf, + pub cha: yazi_fs::cha::Cha, pub mime: Symbol, - pub skip: usize, + pub skip: usize, pub search_idx: Option, - pub area: Rect, - pub data: Vec, + pub area: Rect, + pub data: Vec, } impl_data_any!(PreviewLock); @@ -26,14 +26,14 @@ impl TryFrom
for PreviewLock { let file: FileRef = t.raw_get("file")?; file.borrow(|f| { Ok(Self { - url: f.url_owned(), - cha: f.cha, + url: f.url_owned(), + cha: f.cha, mime: t.raw_get::("mime")?.to_str()?.intern(), - skip: t.raw_get("skip")?, + skip: t.raw_get("skip")?, search_idx: t.raw_get("search_idx")?, - area: t.raw_get("area")?, - data: Default::default(), + area: t.raw_get("area")?, + data: Default::default(), }) }) } diff --git a/yazi-parser/src/mgr/peek.rs b/yazi-parser/src/mgr/peek.rs index bccdaf20..432952a8 100644 --- a/yazi-parser/src/mgr/peek.rs +++ b/yazi-parser/src/mgr/peek.rs @@ -3,39 +3,33 @@ use yazi_shared::{event::ActionCow, url::UrlBuf}; #[derive(Debug, Default)] pub struct PeekForm { - pub skip: Option, - pub force: bool, - pub only_if: Option, + pub skip: Option, + pub force: bool, + pub only_if: Option, pub upper_bound: bool, - pub search_idx: Option, + pub search_idx: Option, } impl From for PeekForm { fn from(mut a: ActionCow) -> Self { Self { - skip: a.first().ok(), - force: a.bool("force"), - only_if: a.take("only-if").ok(), + skip: a.first().ok(), + force: a.bool("force"), + only_if: a.take("only-if").ok(), upper_bound: a.bool("upper-bound"), - search_idx: a.take("search-idx").ok(), + search_idx: a.take("search-idx").ok(), } } } impl From for PeekForm { - fn from(force: bool) -> Self { - Self { force, ..Default::default() } - } + fn from(force: bool) -> Self { Self { force, ..Default::default() } } } impl FromLua for PeekForm { - fn from_lua(_: Value, _: &Lua) -> mlua::Result { - Err("unsupported".into_lua_err()) - } + fn from_lua(_: Value, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } impl IntoLua for PeekForm { - fn into_lua(self, _: &Lua) -> mlua::Result { - Err("unsupported".into_lua_err()) - } + fn into_lua(self, _: &Lua) -> mlua::Result { Err("unsupported".into_lua_err()) } } diff --git a/yazi-plugin/src/external/rg.rs b/yazi-plugin/src/external/rg.rs index 5bc3465f..7a83c490 100644 --- a/yazi-plugin/src/external/rg.rs +++ b/yazi-plugin/src/external/rg.rs @@ -1,20 +1,16 @@ -use std::{fmt::format, process::Stdio}; +use std::process::Stdio; use anyhow::Result; -use tokio::{ - io::{AsyncBufReadExt, BufReader}, - process::Command, - sync::mpsc::{self, UnboundedReceiver}, -}; +use tokio::{io::{AsyncBufReadExt, BufReader}, process::Command, sync::mpsc::{self, UnboundedReceiver}}; use yazi_fs::{FsUrl, file::File}; use yazi_shared::url::{AsUrl, UrlBuf, UrlLike}; use yazi_vfs::VfsFile; pub struct RgOpt { - pub cwd: UrlBuf, - pub hidden: bool, + pub cwd: UrlBuf, + pub hidden: bool, pub subject: String, - pub args: Vec, + pub args: Vec, } pub fn rg(opt: RgOpt) -> Result> { diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 619ce5d2..d869d906 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -1,8 +1,5 @@ use mlua::{ExternalError, Function, IntoLuaMulti, Lua, Table, Value}; -use yazi_binding::{ - Error, - elements::{Area, HighlightPosition}, -}; +use yazi_binding::{Error, elements::{Area, HighlightPosition}}; use yazi_core::{Highlighter, MgrProxy, tab::PreviewLock}; use yazi_fs::FsUrl; use yazi_runner::previewer::PeekError; @@ -38,7 +35,8 @@ impl Utils { }) } - // Note: You need to implement or update Highlighter::oneshot_with_highlight to accept line/column and highlight accordingly. + // Note: You need to implement or update Highlighter::oneshot_with_highlight to + // accept line/column and highlight accordingly. pub(super) fn preview_widget(lua: &Lua) -> mlua::Result { lua.create_async_function(|_, (t, value): (Table, Value)| async move { diff --git a/yazi-runner/src/previewer/job.rs b/yazi-runner/src/previewer/job.rs index e8430654..5db06144 100644 --- a/yazi-runner/src/previewer/job.rs +++ b/yazi-runner/src/previewer/job.rs @@ -6,10 +6,10 @@ use yazi_shared::{data::Sendable, pool::Symbol}; #[derive(Clone, Debug)] pub struct PeekJob { - pub previewer: PreviewerArc, - pub file: File, - pub mime: Symbol, - pub skip: usize, + pub previewer: PreviewerArc, + pub file: File, + pub mime: Symbol, + pub skip: usize, pub search_idx: Option, } @@ -31,7 +31,7 @@ impl IntoLua for PeekJob { // --- Seek #[derive(Clone, Debug)] pub struct SeekJob { - pub file: File, + pub file: File, pub units: i16, }