From 947e7348609707dd8f50462923221c074214a81b Mon Sep 17 00:00:00 2001 From: Jed Date: Mon, 8 Jun 2026 23:42:15 +0200 Subject: [PATCH] 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; +// }