fix: can't display file name with invalid utf-8 (#529)

This commit is contained in:
三咲雅 · Misaki Masa 2024-01-19 00:49:15 +08:00 committed by GitHub
parent d41cd5684e
commit e987855fca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 86 additions and 73 deletions

View file

@ -33,10 +33,10 @@ impl Manager {
let ext = url.extension(); let ext = url.extension();
match by { match by {
"name" => ext.map_or_else(String::new, |s| format!(".{}", s.to_string_lossy().to_string())), "name" => ext.map_or_else(String::new, |s| format!(".{}", s.to_string_lossy().into_owned())),
"ext" if ext.is_some() => format!("{}.", url.file_stem().unwrap().to_string_lossy()), "ext" if ext.is_some() => format!("{}.", url.file_stem().unwrap().to_string_lossy()),
"dot_ext" if ext.is_some() => url.file_stem().unwrap().to_string_lossy().to_string(), "dot_ext" if ext.is_some() => url.file_stem().unwrap().to_string_lossy().into_owned(),
_ => url.file_name().map_or_else(String::new, |s| s.to_string_lossy().to_string()), _ => url.file_name().map_or_else(String::new, |s| s.to_string_lossy().into_owned()),
} }
} }

View file

@ -1,5 +1,5 @@
use mlua::{Table, TableExt}; use mlua::{Table, TableExt};
use ratatui::{prelude::Buffer, widgets::Widget}; use ratatui::{buffer::Buffer, widgets::Widget};
use tracing::error; use tracing::error;
use yazi_plugin::{bindings::Cast, elements::{render_widgets, Rect}, LUA}; use yazi_plugin::{bindings::Cast, elements::{render_widgets, Rect}, LUA};

View file

@ -1,5 +1,5 @@
use mlua::{Table, TableExt}; use mlua::{Table, TableExt};
use ratatui::{prelude::Buffer, widgets::Widget}; use ratatui::{buffer::Buffer, widgets::Widget};
use tracing::error; use tracing::error;
use yazi_plugin::{bindings::Cast, elements::{render_widgets, Rect}, LUA}; use yazi_plugin::{bindings::Cast, elements::{render_widgets, Rect}, LUA};

View file

@ -1,4 +1,4 @@
use ratatui::{prelude::Buffer, widgets::Widget}; use ratatui::{buffer::Buffer, widgets::Widget};
use crate::Ctx; use crate::Ctx;

View file

@ -1,4 +1,4 @@
use ratatui::{layout::{self, Constraint}, prelude::{Buffer, Direction, Rect}, widgets::{List, ListItem, Widget}}; use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::{List, ListItem, Widget}};
use yazi_config::THEME; use yazi_config::THEME;
use crate::Ctx; use crate::Ctx;

View file

@ -1,4 +1,4 @@
use ratatui::{buffer::Buffer, layout::{self, Rect}, prelude::{Constraint, Direction}, widgets::{Paragraph, Widget}}; use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::{Paragraph, Widget}};
use yazi_config::THEME; use yazi_config::THEME;
use super::Bindings; use super::Bindings;

View file

@ -19,12 +19,14 @@ impl<'a, 'b> Tabs<'a, 'b> {
lua.register_userdata_type::<yazi_core::tab::Tab>(|reg| { lua.register_userdata_type::<yazi_core::tab::Tab>(|reg| {
reg.add_method("name", |lua, me, ()| { reg.add_method("name", |lua, me, ()| {
Some(lua.create_string( Some(
me.current.cwd.file_name().map_or_else( lua.create_string(
|| me.current.cwd.as_os_str().as_encoded_bytes(), me.current
|n| n.as_encoded_bytes(), .cwd
.file_name()
.map_or(me.current.cwd.as_os_str().as_encoded_bytes(), |n| n.as_encoded_bytes()),
), ),
)) )
.transpose() .transpose()
}); });

View file

@ -1,4 +1,4 @@
use ratatui::{layout, prelude::{Buffer, Constraint, Direction, Rect}, widgets::{Block, Widget}}; use ratatui::{buffer::Buffer, layout, layout::{Constraint, Direction, Rect}, widgets::{Block, Widget}};
use yazi_config::THEME; use yazi_config::THEME;
use super::Side; use super::Side;

View file

@ -1,4 +1,4 @@
use ratatui::{prelude::{Buffer, Rect}, text::{Line, Span}, widgets::{Block, List, ListItem, Padding, Widget}}; use ratatui::{buffer::Buffer, layout::Rect, text::{Line, Span}, widgets::{Block, List, ListItem, Padding, Widget}};
use yazi_config::{keymap::Control, THEME}; use yazi_config::{keymap::Control, THEME};
pub(super) struct Side<'a> { pub(super) struct Side<'a> {

View file

@ -1,4 +1,4 @@
use mlua::{prelude::LuaUserDataFields, FromLua, UserData}; use mlua::{FromLua, UserData};
use yazi_shared::term::Term; use yazi_shared::term::Term;
#[derive(Debug, Clone, Copy, FromLua)] #[derive(Debug, Clone, Copy, FromLua)]
@ -17,7 +17,7 @@ impl Default for Window {
} }
impl UserData for Window { impl UserData for Window {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("rows", |_, me| Ok(me.rows)); fields.add_field_method_get("rows", |_, me| Ok(me.rows));
fields.add_field_method_get("cols", |_, me| Ok(me.cols)); fields.add_field_method_get("cols", |_, me| Ok(me.cols));
fields.add_field_method_get("width", |_, me| Ok(me.width)); fields.add_field_method_get("width", |_, me| Ok(me.width));

View file

@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use anyhow::bail; use anyhow::bail;
use mlua::{prelude::{Lua, LuaResult}, AnyUserData, IntoLua, Value}; use mlua::{AnyUserData, IntoLua, Lua, Value};
use yazi_shared::OrderedFloat; use yazi_shared::OrderedFloat;
use crate::elements::Renderable; use crate::elements::Renderable;
@ -59,7 +59,7 @@ impl<'a> TryFrom<Value<'a>> for ValueSendable {
} }
impl<'lua> IntoLua<'lua> for ValueSendable { impl<'lua> IntoLua<'lua> for ValueSendable {
fn into_lua(self, lua: &Lua) -> LuaResult<Value> { fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
match self { match self {
ValueSendable::Nil => Ok(Value::Nil), ValueSendable::Nil => Ok(Value::Nil),
ValueSendable::Boolean(b) => Ok(Value::Boolean(b)), ValueSendable::Boolean(b) => Ok(Value::Boolean(b)),
@ -121,7 +121,7 @@ impl TryInto<ValueSendableKey> for ValueSendable {
} }
impl<'lua> IntoLua<'lua> for ValueSendableKey { impl<'lua> IntoLua<'lua> for ValueSendableKey {
fn into_lua(self, lua: &Lua) -> LuaResult<Value> { fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
match self { match self {
ValueSendableKey::Nil => Ok(Value::Nil), ValueSendableKey::Nil => Ok(Value::Nil),
ValueSendableKey::Boolean(b) => Ok(Value::Boolean(b)), ValueSendableKey::Boolean(b) => Ok(Value::Boolean(b)),

View file

@ -58,7 +58,7 @@ impl ListItem {
return Ok(Self { content, style: None }); return Ok(Self { content, style: None });
} }
Value::String(s) => { Value::String(s) => {
return Ok(Self { content: s.to_str()?.to_string().into(), style: None }); return Ok(Self { content: s.to_string_lossy().into_owned().into(), style: None });
} }
_ => {} _ => {}
} }

View file

@ -10,7 +10,9 @@ impl Span {
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
ui.set( ui.set(
"Span", "Span",
lua.create_function(|_, content: String| Ok(Self(ratatui::text::Span::raw(content))))?, lua.create_function(|_, content: mlua::String| {
Ok(Self(ratatui::text::Span::raw(content.to_string_lossy().into_owned())))
})?,
) )
} }
} }

View file

@ -54,7 +54,7 @@ pub fn shell(opt: ShellOpt) -> Result<Child> {
#[cfg(windows)] #[cfg(windows)]
{ {
let args: Vec<String> = opt.args.iter().map(|s| s.to_string_lossy().to_string()).collect(); let args: Vec<String> = opt.args.iter().map(|s| s.to_string_lossy().into_owned()).collect();
let args_: Vec<&str> = args.iter().map(|s| s.as_ref()).collect(); let args_: Vec<&str> = args.iter().map(|s| s.as_ref()).collect();
let expanded = parser::parse(opt.cmd.to_string_lossy().as_ref(), &args_); let expanded = parser::parse(opt.cmd.to_string_lossy().as_ref(), &args_);
Ok( Ok(

View file

@ -1,6 +1,6 @@
use std::time::Duration; use std::time::Duration;
use mlua::{prelude::LuaUserDataMethods, IntoLua, Table, UserData, Value}; use mlua::{IntoLua, Table, UserData, Value};
use tokio::{io::{AsyncBufReadExt, AsyncReadExt, BufReader}, process::{ChildStderr, ChildStdin, ChildStdout}, select}; use tokio::{io::{AsyncBufReadExt, AsyncReadExt, BufReader}, process::{ChildStderr, ChildStdin, ChildStdout}, select};
use super::Status; use super::Status;
@ -22,14 +22,12 @@ impl Child {
} }
impl UserData for Child { impl UserData for Child {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
#[inline] #[inline]
// TODO: return mlua::String instead of String
async fn read_line(me: &mut Child) -> (String, u8) { async fn read_line(me: &mut Child) -> (String, u8) {
async fn read(t: Option<impl AsyncBufReadExt + Unpin>) -> Option<String> { async fn read(t: Option<impl AsyncBufReadExt + Unpin>) -> Option<String> {
let Some(mut r) = t else { let mut r = t?;
return None;
};
let mut buf = String::new(); let mut buf = String::new();
match r.read_line(&mut buf).await { match r.read_line(&mut buf).await {
Ok(0) | Err(_) => None, Ok(0) | Err(_) => None,
@ -46,10 +44,7 @@ impl UserData for Child {
methods.add_async_method_mut("read", |_, me, len: usize| async move { methods.add_async_method_mut("read", |_, me, len: usize| async move {
async fn read(t: Option<impl AsyncBufReadExt + Unpin>, len: usize) -> Option<Vec<u8>> { async fn read(t: Option<impl AsyncBufReadExt + Unpin>, len: usize) -> Option<Vec<u8>> {
let Some(mut r) = t else { let mut r = t?;
return None;
};
let mut buf = vec![0; len]; let mut buf = vec![0; len];
match r.read(&mut buf).await { match r.read(&mut buf).await {
Ok(0) | Err(_) => return None, Ok(0) | Err(_) => return None,

View file

@ -1,6 +1,6 @@
use std::process::Stdio; use std::process::Stdio;
use mlua::{prelude::LuaUserDataMethods, AnyUserData, IntoLua, Lua, Table, UserData, Value}; use mlua::{AnyUserData, IntoLua, Lua, Table, UserData, Value};
use super::{output::Output, Child}; use super::{output::Output, Child};
@ -35,19 +35,29 @@ impl Command {
} }
impl UserData for Command { impl UserData for Command {
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("arg", |_, (ud, arg): (AnyUserData, String)| { methods.add_function("arg", |_, (ud, arg): (AnyUserData, mlua::String)| {
ud.borrow_mut::<Self>()?.inner.arg(arg); ud.borrow_mut::<Self>()?.inner.arg(arg.to_string_lossy().as_ref());
Ok(ud) Ok(ud)
}); });
methods.add_function("args", |_, (ud, args): (AnyUserData, Vec<String>)| { methods.add_function("args", |_, (ud, args): (AnyUserData, Vec<mlua::String>)| {
ud.borrow_mut::<Self>()?.inner.args(args); {
Ok(ud) let mut me = ud.borrow_mut::<Self>()?;
}); for arg in args {
methods.add_function("env", |_, (ud, key, value): (AnyUserData, String, String)| { me.inner.arg(arg.to_string_lossy().as_ref());
ud.borrow_mut::<Self>()?.inner.env(key, value); }
}
Ok(ud) Ok(ud)
}); });
methods.add_function(
"env",
|_, (ud, key, value): (AnyUserData, mlua::String, mlua::String)| {
ud.borrow_mut::<Self>()?
.inner
.env(key.to_string_lossy().as_ref(), value.to_string_lossy().as_ref());
Ok(ud)
},
);
methods.add_function("stdin", |_, (ud, stdio): (AnyUserData, u8)| { methods.add_function("stdin", |_, (ud, stdio): (AnyUserData, u8)| {
ud.borrow_mut::<Self>()?.inner.stdin(match stdio { ud.borrow_mut::<Self>()?.inner.stdin(match stdio {
PIPED => Stdio::piped(), PIPED => Stdio::piped(),

View file

@ -1,4 +1,4 @@
use mlua::{prelude::LuaUserDataFields, UserData}; use mlua::UserData;
use super::Status; use super::Status;
@ -11,7 +11,7 @@ impl Output {
} }
impl UserData for Output { impl UserData for Output {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("status", |_, me| Ok(Status::new(me.inner.status))); fields.add_field_method_get("status", |_, me| Ok(Status::new(me.inner.status)));
fields.add_field_method_get("stdout", |lua, me| lua.create_string(&me.inner.stdout)); fields.add_field_method_get("stdout", |lua, me| lua.create_string(&me.inner.stdout));
fields.add_field_method_get("stderr", |lua, me| lua.create_string(&me.inner.stderr)); fields.add_field_method_get("stderr", |lua, me| lua.create_string(&me.inner.stderr));

View file

@ -9,7 +9,7 @@ impl Status {
} }
impl UserData for Status { impl UserData for Status {
fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("success", |_, me, ()| Ok(me.inner.success())); methods.add_method("success", |_, me, ()| Ok(me.inner.success()));
methods.add_method("code", |_, me, ()| Ok(me.inner.code())); methods.add_method("code", |_, me, ()| Ok(me.inner.code()));
} }

View file

@ -17,10 +17,10 @@ impl Utils {
match k { match k {
Value::Integer(_) => { Value::Integer(_) => {
args.push(v.to_str()?.to_owned()); args.push(v.to_string_lossy().into_owned());
} }
Value::String(s) => { Value::String(s) => {
named.insert(s.to_str()?.replace('_', "-"), v.to_str()?.to_owned()); named.insert(s.to_str()?.replace('_', "-"), v.to_string_lossy().into_owned());
} }
_ => return Err("invalid key in exec".into_lua_err()), _ => return Err("invalid key in exec".into_lua_err()),
} }

View file

@ -10,17 +10,18 @@ impl Utils {
pub(super) fn text(lua: &Lua, ya: &Table) -> mlua::Result<()> { pub(super) fn text(lua: &Lua, ya: &Table) -> mlua::Result<()> {
ya.set( ya.set(
"truncate", "truncate",
lua.create_function(|_, (text, max): (String, usize)| { lua.create_function(|_, (text, max): (mlua::String, usize)| {
let mut width = 0; let mut width = 0;
let flow = text.chars().try_fold(String::with_capacity(max), |mut s, c| { let flow =
width += c.width().unwrap_or(0); text.to_string_lossy().chars().try_fold(String::with_capacity(max), |mut s, c| {
if s.width() < max { width += c.width().unwrap_or(0);
s.push(c); if s.width() < max {
ControlFlow::Continue(s) s.push(c);
} else { ControlFlow::Continue(s)
ControlFlow::Break(s) } else {
} ControlFlow::Break(s)
}); }
});
Ok(match flow { Ok(match flow {
ControlFlow::Break(s) => s, ControlFlow::Break(s) => s,
@ -29,7 +30,10 @@ impl Utils {
})?, })?,
)?; )?;
ya.set("mime_valid", lua.create_function(|_, mime: String| Ok(mime_valid(&mime)))?)?; ya.set(
"mime_valid",
lua.create_function(|_, mime: mlua::String| Ok(mime_valid(mime.as_bytes())))?,
)?;
Ok(()) Ok(())
} }

View file

@ -98,7 +98,7 @@ impl AsRef<OsStr> for Url {
impl ToString for Url { impl ToString for Url {
fn to_string(&self) -> String { fn to_string(&self) -> String {
if self.scheme == UrlScheme::Regular { if self.scheme == UrlScheme::Regular {
return self.path.to_string_lossy().to_string(); return self.path.to_string_lossy().into_owned();
} }
let scheme = match self.scheme { let scheme = match self.scheme {

View file

@ -1,23 +1,23 @@
pub const MIME_DIR: &str = "inode/directory"; pub const MIME_DIR: &str = "inode/directory";
pub fn mime_valid(s: &str) -> bool { pub fn mime_valid(b: &[u8]) -> bool {
let parts = s.split('/').collect::<Vec<_>>(); let parts = b.split(|&b| b == b'/').collect::<Vec<_>>();
if parts.len() != 2 || parts[1].is_empty() { if parts.len() != 2 || parts[1].is_empty() {
return false; return false;
} }
matches!( matches!(
parts[0], parts[0],
"application" b"application"
| "audio" | b"audio"
| "example" | b"example"
| "font" | b"font"
| "image" | b"image"
| "inode" | b"inode"
| "message" | b"message"
| "model" | b"model"
| "multipart" | b"multipart"
| "text" | b"text"
| "video" | b"video"
) )
} }