From abfbd1cd9f0dd428d134f257ec89c25dac842b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Wed, 12 Mar 2025 14:17:07 +0800 Subject: [PATCH] feat: new `fs.expand_url` API (#2476) --- Cargo.lock | 1 + yazi-plugin/src/config/runtime.rs | 5 ++- yazi-plugin/src/fs/fs.rs | 12 +++++++ yazi-shared/Cargo.toml | 1 + yazi-shared/src/tty/handle.rs | 53 +++++++++++++++++++++---------- yazi-shared/src/tty/tty.rs | 7 ++-- 6 files changed, 58 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f32328e..37aa8560 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3624,6 +3624,7 @@ dependencies = [ "ratatui", "serde", "tokio", + "tracing", "uzers", "windows-sys 0.59.0", "yazi-macro", diff --git a/yazi-plugin/src/config/runtime.rs b/yazi-plugin/src/config/runtime.rs index 3a770865..164293a3 100644 --- a/yazi-plugin/src/config/runtime.rs +++ b/yazi-plugin/src/config/runtime.rs @@ -31,8 +31,11 @@ impl<'a> Runtime<'a> { fn args(lua: &Lua) -> mlua::Result { Composer::make(lua, 5, |lua, key| { match key { - b"chooser_file" => ARGS.chooser_file.as_ref().map(Url::from).into_lua(lua)?, + b"entries" => { + lua.create_sequence_from(ARGS.entries.iter().map(Url::from))?.into_lua(lua)? + } b"cwd_file" => ARGS.cwd_file.as_ref().map(Url::from).into_lua(lua)?, + b"chooser_file" => ARGS.chooser_file.as_ref().map(Url::from).into_lua(lua)?, _ => return Ok(Value::Nil), } .into_lua(lua) diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index 8419514a..347a1547 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -15,6 +15,7 @@ pub fn compose(lua: &Lua) -> mlua::Result { b"create" => create(lua)?, b"remove" => remove(lua)?, b"read_dir" => read_dir(lua)?, + b"expand_url" => expand_url(lua)?, b"unique_name" => unique_name(lua)?, b"partitions" => partitions(lua)?, _ => return Ok(Value::Nil), @@ -150,6 +151,17 @@ fn read_dir(lua: &Lua) -> mlua::Result { }) } +fn expand_url(lua: &Lua) -> mlua::Result { + lua.create_function(|_, value: Value| { + use yazi_fs::expand_path; + Ok(Url::from(match value { + Value::String(s) => expand_path(s.to_str()?.as_ref()), + Value::UserData(ud) => expand_path(&*ud.borrow::()?), + _ => Err("must be a string or a Url".into_lua_err())?, + })) + }) +} + fn unique_name(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, url: UrlRef| async move { match yazi_fs::unique_name(url.clone(), async { false }).await { diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 8c80fa90..0afc9b26 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -22,6 +22,7 @@ percent-encoding = "2.3.1" ratatui = { workspace = true } serde = { workspace = true } tokio = { workspace = true } +tracing = { workspace = true } [target."cfg(unix)".dependencies] libc = { workspace = true } diff --git a/yazi-shared/src/tty/handle.rs b/yazi-shared/src/tty/handle.rs index 7a54936f..4a4effce 100644 --- a/yazi-shared/src/tty/handle.rs +++ b/yazi-shared/src/tty/handle.rs @@ -1,5 +1,7 @@ use std::{io::{Error, ErrorKind, Read, Write}, time::Duration}; +use tracing::error; + pub struct Handle { #[cfg(unix)] inner: std::os::fd::RawFd, @@ -32,7 +34,7 @@ impl Read for Handle { use std::os::{fd::IntoRawFd, unix::io::FromRawFd}; let mut f = unsafe { std::fs::File::from_raw_fd(self.inner) }; let result = f.read(buf); - self.inner = f.into_raw_fd(); + _ = f.into_raw_fd(); result } #[cfg(windows)] @@ -40,7 +42,7 @@ impl Read for Handle { use std::os::windows::io::{FromRawHandle, IntoRawHandle}; let mut f = unsafe { std::fs::File::from_raw_handle(self.inner) }; let result = f.read(buf); - self.inner = f.into_raw_handle(); + _ = f.into_raw_handle(); result } } @@ -75,18 +77,23 @@ impl Write for Handle { #[cfg(unix)] impl Handle { - pub(super) fn new(out: bool) -> std::io::Result { + pub(super) fn new(out: bool) -> Self { use std::{fs::OpenOptions, os::fd::IntoRawFd}; - let fileno = if out { libc::STDOUT_FILENO } else { libc::STDIN_FILENO }; - Ok(if unsafe { libc::isatty(fileno) } == 1 { - Self { inner: fileno, close: false } - } else { - Self { - inner: OpenOptions::new().read(!out).write(out).open("/dev/tty")?.into_raw_fd(), - close: true, + use libc::{STDIN_FILENO, STDOUT_FILENO}; + + let resort = Self { inner: if out { STDOUT_FILENO } else { STDIN_FILENO }, close: false }; + if unsafe { libc::isatty(resort.inner) } == 1 { + return resort; + } + + match OpenOptions::new().read(!out).write(out).open("/dev/tty") { + Ok(f) => Self { inner: f.into_raw_fd(), close: true }, + Err(err) => { + error!("Failed to open /dev/tty, falling back to stdin/stdout: {err}"); + resort } - }) + } } pub(super) fn poll(&mut self, timeout: Duration) -> std::io::Result { @@ -121,7 +128,9 @@ impl Handle { #[cfg(windows)] impl Handle { - pub(super) fn new(out: bool) -> std::io::Result { + pub(super) fn new(out: bool) -> Self { + use std::{io::{Error, stdin, stdout}, os::windows::io::AsRawHandle}; + use windows_sys::Win32::{Foundation::{GENERIC_READ, GENERIC_WRITE, INVALID_HANDLE_VALUE}, Globalization::CP_UTF8, Storage::FileSystem::{CreateFileW, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING}, System::Console::GetConsoleOutputCP}; let name: Vec = if out { "CONOUT$\0" } else { "CONIN$\0" }.encode_utf16().collect(); @@ -137,15 +146,25 @@ impl Handle { ) }; - if result == INVALID_HANDLE_VALUE { - Err(std::io::Error::last_os_error()) - } else { - Ok(Self { + if result != INVALID_HANDLE_VALUE { + return Self { inner: result, close: true, out_utf8: unsafe { GetConsoleOutputCP() } == CP_UTF8, incomplete_utf8: Default::default(), - }) + }; + } + + error!( + "Failed to open {}, falling back to stdin/stdout: {}", + if out { "CONOUT$" } else { "CONIN$" }, + Error::last_os_error() + ); + Self { + inner: if out { stdout().as_raw_handle() } else { stdin().as_raw_handle() }, + close: false, + out_utf8: unsafe { GetConsoleOutputCP() } == CP_UTF8, + incomplete_utf8: Default::default(), } } diff --git a/yazi-shared/src/tty/tty.rs b/yazi-shared/src/tty/tty.rs index 3da833ec..cbf4d8f3 100644 --- a/yazi-shared/src/tty/tty.rs +++ b/yazi-shared/src/tty/tty.rs @@ -11,9 +11,10 @@ pub struct Tty { impl Default for Tty { fn default() -> Self { - let stdin = Handle::new(false).expect("failed to open stdin"); - let stdout = Handle::new(true).expect("failed to open stdout"); - Self { stdin: Mutex::new(stdin), stdout: Mutex::new(BufWriter::new(stdout)) } + Self { + stdin: Mutex::new(Handle::new(false)), + stdout: Mutex::new(BufWriter::new(Handle::new(true))), + } } }