mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: wip store search_idx in preview to be usable in code.lua
This commit is contained in:
parent
ba43e04fb8
commit
947e734860
6 changed files with 156 additions and 63 deletions
|
|
@ -15,7 +15,9 @@ pub(super) struct Preview {
|
||||||
impl Deref for Preview {
|
impl Deref for Preview {
|
||||||
type Target = yazi_core::tab::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 {
|
impl Preview {
|
||||||
|
|
@ -27,6 +29,7 @@ impl Preview {
|
||||||
impl UserData for Preview {
|
impl UserData for Preview {
|
||||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||||
fields.add_field_method_get("skip", |_, me| Ok(me.skip));
|
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| {
|
cached_field!(fields, folder, |_, me| {
|
||||||
me.tab
|
me.tab
|
||||||
.hovered_folder()
|
.hovered_folder()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use yazi_macro::succ;
|
use yazi_macro::succ;
|
||||||
use yazi_parser::mgr::PeekForm;
|
use yazi_parser::mgr::PeekForm;
|
||||||
use yazi_shared::data::Data;
|
use yazi_shared::{data::Data, url::UrlLike};
|
||||||
|
|
||||||
use crate::{Actor, Ctx};
|
use crate::{Actor, Ctx};
|
||||||
|
|
||||||
|
|
@ -37,8 +37,8 @@ impl Actor for Peek {
|
||||||
succ!();
|
succ!();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(skip) = form.skip {
|
|
||||||
let preview = &mut cx.tab_mut().preview;
|
let preview = &mut cx.tab_mut().preview;
|
||||||
|
if let Some(skip) = form.skip {
|
||||||
if form.upper_bound {
|
if form.upper_bound {
|
||||||
preview.skip = preview.skip.min(skip);
|
preview.skip = preview.skip.min(skip);
|
||||||
} else {
|
} 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() {
|
if hovered.is_dir() {
|
||||||
cx.tab_mut().preview.go_folder(hovered, folder.map(|(_, cha)| cha), mime, form.force);
|
cx.tab_mut().preview.go_folder(hovered, folder.map(|(_, cha)| cha), mime, form.force);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,23 @@ use yazi_adapter::ADAPTOR;
|
||||||
use yazi_config::{LAYOUT, YAZI};
|
use yazi_config::{LAYOUT, YAZI};
|
||||||
use yazi_fs::{File, Files, FilesOp, cha::Cha};
|
use yazi_fs::{File, Files, FilesOp, cha::Cha};
|
||||||
use yazi_macro::render;
|
use yazi_macro::render;
|
||||||
use yazi_runner::{RUNNER, previewer::{PeekError, PeekJob}};
|
use yazi_runner::{
|
||||||
use yazi_shared::{pool::Symbol, url::{UrlBuf, UrlLike}};
|
RUNNER,
|
||||||
|
previewer::{PeekError, PeekJob},
|
||||||
|
};
|
||||||
|
use yazi_shared::{
|
||||||
|
pool::Symbol,
|
||||||
|
url::{UrlBuf, UrlLike},
|
||||||
|
};
|
||||||
use yazi_vfs::{VfsFiles, VfsFilesOp};
|
use yazi_vfs::{VfsFiles, VfsFilesOp};
|
||||||
|
|
||||||
use crate::{AppProxy, Highlighter, MgrProxy, tab::PreviewLock};
|
use crate::{AppProxy, Highlighter, MgrProxy, tab::PreviewLock};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Preview {
|
pub struct Preview {
|
||||||
pub lock: Option<PreviewLock>,
|
pub lock: Option<PreviewLock>,
|
||||||
pub skip: usize,
|
pub skip: usize,
|
||||||
|
pub search_idx: Option<usize>,
|
||||||
|
|
||||||
handle: Option<JoinHandle<()>>,
|
handle: Option<JoinHandle<()>>,
|
||||||
pub folder_lock: Option<UrlBuf>,
|
pub folder_lock: Option<UrlBuf>,
|
||||||
|
|
@ -35,8 +42,12 @@ impl Preview {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.abort();
|
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 };
|
||||||
|
|
||||||
|
tracing::debug!("preview {:?}", self);
|
||||||
|
|
||||||
self.handle = Some(tokio::spawn(async move {
|
self.handle = Some(tokio::spawn(async move {
|
||||||
let mut rx = RUNNER.peek(&job).await;
|
let mut rx = RUNNER.peek(&job).await;
|
||||||
match rx.recv().await.unwrap_or(Err(PeekError::Cancelled)) {
|
match rx.recv().await.unwrap_or(Err(PeekError::Cancelled)) {
|
||||||
|
|
@ -85,7 +96,9 @@ impl Preview {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
|
self.search_idx = None;
|
||||||
self.abort();
|
self.abort();
|
||||||
|
|
||||||
ADAPTOR.get().image_hide().ok();
|
ADAPTOR.get().image_hide().ok();
|
||||||
render!(self.lock.take().is_some())
|
render!(self.lock.take().is_some())
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +108,9 @@ impl Preview {
|
||||||
ADAPTOR.get().image_hide().ok();
|
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 {
|
pub fn same_file(&self, file: &File, mime: &str) -> bool {
|
||||||
self.same_url(&file.url)
|
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)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ pub struct PeekForm {
|
||||||
pub force: bool,
|
pub force: bool,
|
||||||
pub only_if: Option<UrlBuf>,
|
pub only_if: Option<UrlBuf>,
|
||||||
pub upper_bound: bool,
|
pub upper_bound: bool,
|
||||||
|
pub search_idx: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ActionCow> for PeekForm {
|
impl From<ActionCow> for PeekForm {
|
||||||
|
|
@ -16,18 +17,25 @@ impl From<ActionCow> for PeekForm {
|
||||||
force: a.bool("force"),
|
force: a.bool("force"),
|
||||||
only_if: a.take("only-if").ok(),
|
only_if: a.take("only-if").ok(),
|
||||||
upper_bound: a.bool("upper-bound"),
|
upper_bound: a.bool("upper-bound"),
|
||||||
|
search_idx: a.take("search_idx").ok(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<bool> for PeekForm {
|
impl From<bool> for PeekForm {
|
||||||
fn from(force: bool) -> Self { Self { force, ..Default::default() } }
|
fn from(force: bool) -> Self {
|
||||||
|
Self { force, ..Default::default() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromLua for PeekForm {
|
impl FromLua for PeekForm {
|
||||||
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> { Err("unsupported".into_lua_err()) }
|
fn from_lua(_: Value, _: &Lua) -> mlua::Result<Self> {
|
||||||
|
Err("unsupported".into_lua_err())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoLua for PeekForm {
|
impl IntoLua for PeekForm {
|
||||||
fn into_lua(self, _: &Lua) -> mlua::Result<Value> { Err("unsupported".into_lua_err()) }
|
fn into_lua(self, _: &Lua) -> mlua::Result<Value> {
|
||||||
|
Err("unsupported".into_lua_err())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ local function get_search_occurrences(url)
|
||||||
local lines = {}
|
local lines = {}
|
||||||
for occurrence in occurrences:gmatch("[^,]+") do
|
for occurrence in occurrences:gmatch("[^,]+") do
|
||||||
local line, col = occurrence:match("(%d+)-(%d+)")
|
local line, col = occurrence:match("(%d+)-(%d+)")
|
||||||
ya.dbg("line col:", line, col)
|
|
||||||
table.insert(lines, { tonumber(line), tonumber(col) })
|
table.insert(lines, { tonumber(line), tonumber(col) })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -19,35 +18,38 @@ local function get_search_occurrences(url)
|
||||||
return lines
|
return lines
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_next_occurrence(occurrences, current_line, direction)
|
-- local function get_next_occurrence(occurrences, current_line, direction)
|
||||||
local step = direction == "up" and -1 or 1
|
-- local step = direction == "up" and -1 or 1
|
||||||
local start = direction == "up" and #occurrences or 1
|
-- local start = direction == "up" and #occurrences or 1
|
||||||
local stop = direction == "up" and 1 or #occurrences
|
-- local stop = direction == "up" and 1 or #occurrences
|
||||||
|
--
|
||||||
for i = start, stop, step do
|
-- for i = start, stop, step do
|
||||||
local line = occurrences[i]
|
-- local line = occurrences[i]
|
||||||
if go_up and line >= current_line then
|
-- if go_up and line >= current_line then
|
||||||
return line
|
-- return line
|
||||||
end
|
-- end
|
||||||
if direction == "down" and line <= current_line then
|
-- if direction == "down" and line <= current_line then
|
||||||
return line
|
-- return line
|
||||||
end
|
-- end
|
||||||
|
--
|
||||||
-- TODO: Handle return to first element or last one
|
-- -- TODO: Handle return to first element or last one
|
||||||
end
|
-- end
|
||||||
end
|
-- end
|
||||||
|
|
||||||
function M:peek(job)
|
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)
|
local search_occurrences = get_search_occurrences(job.file.url)
|
||||||
if search_occurrences then
|
local search_idx = cx.active.preview.search_idx
|
||||||
local line = search_occurrences[1][1]
|
ya.dbg(" search index", search_idx)
|
||||||
ya.dbg("liiiiine: ", line)
|
local occurrence
|
||||||
ya.emit("peek", { math.max(0, search_occurrences[1][1] - 1), only_if = job.file.url })
|
if search_occurrences and search_idx then
|
||||||
return
|
occurrence = search_occurrences[search_idx]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ya.dgb("Peek 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 })
|
ya.emit("peek", { bound, only_if = job.file.url, upper_bound = true })
|
||||||
elseif err and not err:find("cancelled", 1, true) then
|
elseif err and not err:find("cancelled", 1, true) then
|
||||||
require("empty").msg(job, err)
|
require("empty").msg(job, err)
|
||||||
|
|
@ -55,8 +57,11 @@ function M:peek(job)
|
||||||
end
|
end
|
||||||
|
|
||||||
function M:seek(job)
|
function M:seek(job)
|
||||||
ya.dbg("UNITS IN SEEK ", job.units)
|
local direction = job.units > 0 and "down" or "up"
|
||||||
local direction = job.units > 0 and "down" or "up" end
|
|
||||||
|
local search_idx = cx.active.preview.search_idx
|
||||||
|
ya.dbg(" search index", search_idx)
|
||||||
|
|
||||||
local h = cx.active.current.hovered
|
local h = cx.active.current.hovered
|
||||||
if not h or h.url ~= job.file.url then
|
if not h or h.url ~= job.file.url then
|
||||||
return
|
return
|
||||||
|
|
@ -64,20 +69,21 @@ function M:seek(job)
|
||||||
|
|
||||||
local search_occurrences = get_search_occurrences(job.file.url)
|
local search_occurrences = get_search_occurrences(job.file.url)
|
||||||
if search_occurrences then
|
if search_occurrences then
|
||||||
local current_line = cx.active.preview.skip + 1
|
search_idx = (search_idx or 0) + 1
|
||||||
|
-- local current_line = cx.active.preview.skip + 1
|
||||||
local next_occurrence = get_next_occurrence(search_occurrences, current_line, direction)
|
-- ya.dbg("Current line:", current_line)
|
||||||
|
-- local next_occurrence = get_next_occurrence(search_occurrences, current_line, direction)
|
||||||
for _, occurrence in ipairs(search_occurrences) do
|
-- for _, occurrence in ipairs(search_occurrences) do
|
||||||
local search_line = occurrence[1]
|
-- local search_line = occurrence[1]
|
||||||
-- local col = occurrence[2]
|
-- -- local col = occurrence[2]
|
||||||
|
--
|
||||||
if search_line >= current_line then
|
-- if search_line >= current_line then
|
||||||
ya.emit("peek", { math.max(0, search_line - 1), only_if = job.file.url })
|
-- ya.emit("peek", { math.max(0, search_line - 1), only_if = job.file.url })
|
||||||
return
|
-- return
|
||||||
end
|
-- end
|
||||||
end
|
-- end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ya.emit("peek", { math.max(0, line - 1), only_if = job.file.url })
|
-- ya.emit("peek", { math.max(0, line - 1), only_if = job.file.url })
|
||||||
|
|
||||||
local step = math.floor(job.units * job.area.h / 10)
|
local step = math.floor(job.units * job.area.h / 10)
|
||||||
|
|
@ -86,6 +92,7 @@ function M:seek(job)
|
||||||
ya.emit("peek", {
|
ya.emit("peek", {
|
||||||
math.max(0, cx.active.preview.skip + step),
|
math.max(0, cx.active.preview.skip + step),
|
||||||
only_if = job.file.url,
|
only_if = job.file.url,
|
||||||
|
search_idx,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
use mlua::{ExternalError, Function, IntoLuaMulti, Lua, Table, Value};
|
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_core::{Highlighter, MgrProxy, tab::PreviewLock};
|
||||||
use yazi_fs::FsUrl;
|
use yazi_fs::FsUrl;
|
||||||
use yazi_runner::previewer::PeekError;
|
use yazi_runner::previewer::PeekError;
|
||||||
|
|
@ -14,10 +18,17 @@ impl Utils {
|
||||||
pub(super) fn preview_code(lua: &Lua) -> mlua::Result<Function> {
|
pub(super) fn preview_code(lua: &Lua) -> mlua::Result<Function> {
|
||||||
lua.create_async_function(|lua, t: Table| async move {
|
lua.create_async_function(|lua, t: Table| async move {
|
||||||
let area: Area = t.raw_get("area")?;
|
let area: Area = t.raw_get("area")?;
|
||||||
let mut lock = PreviewLock::try_from(t)?;
|
// let position: Option<HighlightPosition> = t.raw_get("position").ok();
|
||||||
|
|
||||||
|
let mut lock = PreviewLock::try_from(t)?;
|
||||||
let path = lock.url.as_url().unified_path();
|
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,
|
Ok(text) => text,
|
||||||
Err(e @ PeekError::Exceeded(max)) => return (e, max).into_lua_multi(&lua),
|
Err(e @ PeekError::Exceeded(max)) => return (e, max).into_lua_multi(&lua),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -25,6 +36,8 @@ impl Utils {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// tracing::debug!("Inner: {inner}");
|
||||||
|
|
||||||
lock.data = vec![Renderable::Text(Text { area, inner, ..Default::default() })];
|
lock.data = vec![Renderable::Text(Text { area, inner, ..Default::default() })];
|
||||||
|
|
||||||
MgrProxy::update_peeked(lock);
|
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<Function> {
|
pub(super) fn preview_widget(lua: &Lua) -> mlua::Result<Function> {
|
||||||
lua.create_async_function(|_, (t, value): (Table, Value)| async move {
|
lua.create_async_function(|_, (t, value): (Table, Value)| async move {
|
||||||
let mut lock = PreviewLock::try_from(t)?;
|
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<char> = 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;
|
||||||
|
// }
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue