feat: make the builtin code previewer handle invalid carriage return chars and binary streams better (#1550)

This commit is contained in:
三咲雅 · Misaki Masa 2024-08-25 02:56:38 +08:00 committed by GitHub
parent 69c20d4dd3
commit 9cf85c09d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 94 additions and 115 deletions

View file

@ -70,7 +70,7 @@ function Entity:symlink()
end end
local to = self._file.link_to local to = self._file.link_to
return ui.Line(to and { ui.Span(" -> " .. tostring(to)):italic() } or {}) return to and ui.Line(" -> " .. tostring(to)):italic() or ui.Line {}
end end
function Entity:render() function Entity:render()

View file

@ -1,9 +1,13 @@
local M = {} local M = {}
function M:peek() function M:peek()
local _, bound = ya.preview_code(self) local err, bound = ya.preview_code(self)
if bound then if bound then
ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true }) ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true })
elseif err then
ya.preview_widgets(self, {
ui.Paragraph(self.area, { ui.Line(err):reverse() }),
})
end end
end end

View file

@ -2,7 +2,7 @@ local M = {}
function M:msg(s) function M:msg(s)
local p = ui.Paragraph(self.area, { local p = ui.Paragraph(self.area, {
ui.Line { ui.Span(s):reverse() }, ui.Line(s):reverse(),
}) })
ya.preview_widgets(self, { p:wrap(ui.Paragraph.WRAP) }) ya.preview_widgets(self, { p:wrap(ui.Paragraph.WRAP) })
end end

View file

@ -53,9 +53,13 @@ function M:seek(units)
end end
function M:fallback_to_builtin() function M:fallback_to_builtin()
local _, bound = ya.preview_code(self) local err, bound = ya.preview_code(self)
if bound then if bound then
ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true }) ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true })
elseif err then
ya.preview_widgets(self, {
ui.Paragraph(self.area, { ui.Line(err):reverse() }),
})
end end
end end

View file

@ -82,6 +82,8 @@ impl Line {
impl UserData for Line { impl UserData for Line {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
crate::impl_style_shorthands!(methods, 0.style);
methods.add_function("width", |_, ud: AnyUserData| Ok(ud.borrow_mut::<Self>()?.0.width())); methods.add_function("width", |_, ud: AnyUserData| Ok(ud.borrow_mut::<Self>()?.0.width()));
methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| { methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
{ {

View file

@ -19,7 +19,7 @@ pub struct Paragraph {
pub area: ratatui::layout::Rect, pub area: ratatui::layout::Rect,
pub text: ratatui::text::Text<'static>, pub text: ratatui::text::Text<'static>,
pub style: Option<ratatui::style::Style>, pub style: ratatui::style::Style,
pub alignment: ratatui::layout::Alignment, pub alignment: ratatui::layout::Alignment,
pub wrap: u8, pub wrap: u8,
} }
@ -29,7 +29,7 @@ impl Paragraph {
let new = lua.create_function(|_, (_, area, lines): (Table, RectRef, Vec<Line>)| { let new = lua.create_function(|_, (_, area, lines): (Table, RectRef, Vec<Line>)| {
Ok(Paragraph { Ok(Paragraph {
area: *area, area: *area,
text: lines.into_iter().map(|s| s.0).collect::<Vec<_>>().into(), text: lines.into_iter().map(|s| s.0).collect(),
..Default::default() ..Default::default()
}) })
})?; })?;
@ -58,13 +58,15 @@ impl Paragraph {
impl UserData for Paragraph { impl UserData for Paragraph {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
crate::impl_style_shorthands!(methods, style);
methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| { methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
{ {
let mut me = ud.borrow_mut::<Self>()?; let mut me = ud.borrow_mut::<Self>()?;
match value { match value {
Value::Nil => me.style = None, Value::Nil => me.style = ratatui::style::Style::default(),
Value::Table(tb) => me.style = Some(Style::try_from(tb)?.0), Value::Table(tb) => me.style = Style::try_from(tb)?.0,
Value::UserData(ud) => me.style = Some(ud.borrow::<Style>()?.0), Value::UserData(ud) => me.style = ud.borrow::<Style>()?.0,
_ => return Err("expected a Style or Table or nil".into_lua_err()), _ => return Err("expected a Style or Table or nil".into_lua_err()),
} }
} }
@ -92,10 +94,7 @@ impl Renderable for Paragraph {
fn area(&self) -> ratatui::layout::Rect { self.area } fn area(&self) -> ratatui::layout::Rect { self.area }
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) { fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
let mut p = ratatui::widgets::Paragraph::new(self.text); let mut p = ratatui::widgets::Paragraph::new(self.text).style(self.style);
if let Some(style) = self.style {
p = p.style(style);
}
if self.wrap != WRAP_NO { if self.wrap != WRAP_NO {
p = p.wrap(ratatui::widgets::Wrap { trim: self.wrap == WRAP_TRIM }); p = p.wrap(ratatui::widgets::Wrap { trim: self.wrap == WRAP_TRIM });

View file

@ -1,5 +1,4 @@
use mlua::{AnyUserData, ExternalError, FromLua, Lua, Table, UserData, UserDataMethods, Value}; use mlua::{AnyUserData, ExternalError, FromLua, Lua, Table, UserData, UserDataMethods, Value};
use yazi_shared::theme::Color;
use super::Style; use super::Style;
@ -19,54 +18,7 @@ impl Span {
impl UserData for Span { impl UserData for Span {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("fg", |_, (ud, color): (AnyUserData, String)| { crate::impl_style_shorthands!(methods, 0.style);
ud.borrow_mut::<Self>()?.0.style.fg = Color::try_from(color).ok().map(Into::into);
Ok(ud)
});
methods.add_function("bg", |_, (ud, color): (AnyUserData, String)| {
ud.borrow_mut::<Self>()?.0.style.bg = Color::try_from(color).ok().map(Into::into);
Ok(ud)
});
methods.add_function("bold", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.style.add_modifier |= ratatui::style::Modifier::BOLD;
Ok(ud)
});
methods.add_function("dim", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.style.add_modifier |= ratatui::style::Modifier::DIM;
Ok(ud)
});
methods.add_function("italic", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.style.add_modifier |= ratatui::style::Modifier::ITALIC;
Ok(ud)
});
methods.add_function("underline", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.style.add_modifier |= ratatui::style::Modifier::UNDERLINED;
Ok(ud)
});
methods.add_function("blink", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.style.add_modifier |= ratatui::style::Modifier::SLOW_BLINK;
Ok(ud)
});
methods.add_function("blink_rapid", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.style.add_modifier |= ratatui::style::Modifier::RAPID_BLINK;
Ok(ud)
});
methods.add_function("reverse", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.style.add_modifier |= ratatui::style::Modifier::REVERSED;
Ok(ud)
});
methods.add_function("hidden", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.style.add_modifier |= ratatui::style::Modifier::HIDDEN;
Ok(ud)
});
methods.add_function("crossed", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.style.add_modifier |= ratatui::style::Modifier::CROSSED_OUT;
Ok(ud)
});
methods.add_function("reset", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.style.add_modifier = ratatui::style::Modifier::empty();
Ok(ud)
});
methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| { methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.0.style = match value { ud.borrow_mut::<Self>()?.0.style = match value {

View file

@ -40,54 +40,8 @@ impl<'a> TryFrom<Table<'a>> for Style {
impl UserData for Style { impl UserData for Style {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("fg", |_, (ud, color): (AnyUserData, String)| { crate::impl_style_shorthands!(methods, 0);
ud.borrow_mut::<Self>()?.0.fg = Color::try_from(color).ok().map(Into::into);
Ok(ud)
});
methods.add_function("bg", |_, (ud, color): (AnyUserData, String)| {
ud.borrow_mut::<Self>()?.0.bg = Color::try_from(color).ok().map(Into::into);
Ok(ud)
});
methods.add_function("bold", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.add_modifier |= ratatui::style::Modifier::BOLD;
Ok(ud)
});
methods.add_function("dim", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.add_modifier |= ratatui::style::Modifier::DIM;
Ok(ud)
});
methods.add_function("italic", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.add_modifier |= ratatui::style::Modifier::ITALIC;
Ok(ud)
});
methods.add_function("underline", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.add_modifier |= ratatui::style::Modifier::UNDERLINED;
Ok(ud)
});
methods.add_function("blink", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.add_modifier |= ratatui::style::Modifier::SLOW_BLINK;
Ok(ud)
});
methods.add_function("blink_rapid", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.add_modifier |= ratatui::style::Modifier::RAPID_BLINK;
Ok(ud)
});
methods.add_function("reverse", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.add_modifier |= ratatui::style::Modifier::REVERSED;
Ok(ud)
});
methods.add_function("hidden", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.add_modifier |= ratatui::style::Modifier::HIDDEN;
Ok(ud)
});
methods.add_function("crossed", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.add_modifier |= ratatui::style::Modifier::CROSSED_OUT;
Ok(ud)
});
methods.add_function("reset", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.0.add_modifier = ratatui::style::Modifier::empty();
Ok(ud)
});
methods.add_function("patch", |_, (ud, value): (AnyUserData, Value)| { methods.add_function("patch", |_, (ud, value): (AnyUserData, Value)| {
{ {
let mut me = ud.borrow_mut::<Self>()?; let mut me = ud.borrow_mut::<Self>()?;

View file

@ -84,6 +84,14 @@ impl Highlighter {
buf.push(b'\n'); buf.push(b'\n');
} }
for b in &mut buf {
match *b {
b'\0' => return Err("Binary file".into()),
b'\r' => *b = b'\n', // '\r' occurs in the middle of a line
_ => {}
}
}
if i > skip { if i > skip {
after.push(String::from_utf8_lossy(&buf).into_owned()); after.push(String::from_utf8_lossy(&buf).into_owned());
} else if !plain { } else if !plain {

View file

@ -12,6 +12,7 @@ pub mod fs;
pub mod isolate; pub mod isolate;
pub mod loader; pub mod loader;
mod lua; mod lua;
mod macros;
mod opt; mod opt;
pub mod process; pub mod process;
pub mod pubsub; pub mod pubsub;

53
yazi-plugin/src/macros.rs Normal file
View file

@ -0,0 +1,53 @@
#[macro_export]
macro_rules! impl_style_shorthands {
($methods:ident, $($field:tt).+) => {
$methods.add_function("fg", |_, (ud, color): (AnyUserData, String)| {
ud.borrow_mut::<Self>()?.$($field).+.fg = yazi_shared::theme::Color::try_from(color).ok().map(Into::into);
Ok(ud)
});
$methods.add_function("bg", |_, (ud, color): (AnyUserData, String)| {
ud.borrow_mut::<Self>()?.$($field).+.bg = yazi_shared::theme::Color::try_from(color).ok().map(Into::into);
Ok(ud)
});
$methods.add_function("bold", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::BOLD;
Ok(ud)
});
$methods.add_function("dim", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::DIM;
Ok(ud)
});
$methods.add_function("italic", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::ITALIC;
Ok(ud)
});
$methods.add_function("underline", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::UNDERLINED;
Ok(ud)
});
$methods.add_function("blink", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::SLOW_BLINK;
Ok(ud)
});
$methods.add_function("blink_rapid", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::RAPID_BLINK;
Ok(ud)
});
$methods.add_function("reverse", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::REVERSED;
Ok(ud)
});
$methods.add_function("hidden", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::HIDDEN;
Ok(ud)
});
$methods.add_function("crossed", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::CROSSED_OUT;
Ok(ud)
});
$methods.add_function("reset", |_, ud: AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier = ratatui::style::Modifier::empty();
Ok(ud)
});
};
}

View file

@ -39,13 +39,15 @@ impl Utils {
let text = let text =
match Highlighter::new(&lock.url).highlight(lock.skip, area.height as usize).await { match Highlighter::new(&lock.url).highlight(lock.skip, area.height as usize).await {
Ok(text) => text, Ok(text) => text,
Err(PeekError::Exceed(max)) => return (false, max).into_lua_multi(lua), Err(e @ PeekError::Exceed(max)) => return (e.to_string(), max).into_lua_multi(lua),
Err(_) => return (false, Value::Nil).into_lua_multi(lua), Err(e @ PeekError::Unexpected(_)) => {
return (e.to_string(), Value::Nil).into_lua_multi(lua);
}
}; };
lock.data = vec![Box::new(Paragraph { area: *area, text, ..Default::default() })]; lock.data = vec![Box::new(Paragraph { area: *area, text, ..Default::default() })];
emit!(Call(Cmd::new("preview").with_any("lock", lock), Layer::Manager)); emit!(Call(Cmd::new("preview").with_any("lock", lock), Layer::Manager));
(true, Value::Nil).into_lua_multi(lua) (Value::Nil, Value::Nil).into_lua_multi(lua)
})?, })?,
)?; )?;

View file

@ -9,8 +9,8 @@ pub enum PeekError {
impl Display for PeekError { impl Display for PeekError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Exceed(lines) => write!(f, "Exceed: {lines}"), Self::Exceed(lines) => write!(f, "Exceed maximum lines {lines}"),
Self::Unexpected(msg) => write!(f, "Unexpected error: {msg}"), Self::Unexpected(msg) => write!(f, "{msg}"),
} }
} }
} }