feat: prepare for Windows (#44)

This commit is contained in:
Pig Fang 2023-08-19 11:10:26 +08:00 committed by GitHub
parent b70f83ced7
commit 47846932d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 376 additions and 192 deletions

169
Cargo.lock generated
View file

@ -118,9 +118,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.73"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f768393e7fabd388fe8409b13faa4d93ab0fef35db1508438dfdb066918bcf38"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "app"
@ -277,9 +277,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.3.21"
version = "4.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd"
checksum = "b417ae4361bca3f5de378294fc7472d3c4ed86a5ef9f49e93ae722f432aae8d2"
dependencies = [
"clap_builder",
"clap_derive",
@ -288,9 +288,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.3.21"
version = "4.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa"
checksum = "9c90dc0f0e42c64bff177ca9d7be6fcc9ddb0f26a6e062174a61c84dd6c644d4"
dependencies = [
"anstream",
"anstyle",
@ -307,7 +307,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.29",
]
[[package]]
@ -344,6 +344,7 @@ dependencies = [
"anyhow",
"clap",
"crossterm 0.27.0",
"dirs",
"futures",
"glob",
"indexmap 2.0.0",
@ -355,7 +356,6 @@ dependencies = [
"shell-words",
"toml",
"validator",
"xdg",
]
[[package]]
@ -515,6 +515,27 @@ dependencies = [
"crypto-common",
]
[[package]]
name = "dirs"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
]
[[package]]
name = "either"
version = "1.9.0"
@ -587,7 +608,7 @@ checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"redox_syscall 0.3.5",
"windows-sys 0.48.0",
]
@ -694,7 +715,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.29",
]
[[package]]
@ -1212,6 +1233,12 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "overload"
version = "0.1.1"
@ -1236,9 +1263,9 @@ checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"redox_syscall 0.3.5",
"smallvec",
"windows-targets 0.48.2",
"windows-targets 0.48.3",
]
[[package]]
@ -1270,7 +1297,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.29",
]
[[package]]
@ -1371,9 +1398,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.32"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@ -1415,6 +1442,15 @@ dependencies = [
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
@ -1424,6 +1460,17 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_users"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom",
"redox_syscall 0.2.16",
"thiserror",
]
[[package]]
name = "regex"
version = "1.9.3"
@ -1516,14 +1563,14 @@ checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.29",
]
[[package]]
name = "serde_json"
version = "1.0.104"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
dependencies = [
"itoa",
"ryu",
@ -1667,9 +1714,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.28"
version = "2.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
dependencies = [
"proc-macro2",
"quote",
@ -1699,22 +1746,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.45"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dedd246497092a89beedfe2c9f176d44c1b672ea6090edc20544ade01fbb7ea0"
checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.45"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d7b1fadccbbc7e19ea64708629f9d8dccd007c260d66485f20a6d41bc1cf4b3"
checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.29",
]
[[package]]
@ -1783,9 +1830,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.31.0"
version = "1.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40de3a2ba249dcb097e01be5e67a5ff53cf250397715a071a81543e8a832a920"
checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
dependencies = [
"backtrace",
"bytes",
@ -1808,7 +1855,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.29",
]
[[package]]
@ -1877,7 +1924,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.29",
]
[[package]]
@ -2078,7 +2125,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.29",
"wasm-bindgen-shared",
]
@ -2100,7 +2147,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.28",
"syn 2.0.29",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -2163,7 +2210,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets 0.48.2",
"windows-targets 0.48.3",
]
[[package]]
@ -2181,7 +2228,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.2",
"windows-targets 0.48.3",
]
[[package]]
@ -2201,17 +2248,17 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.48.2"
version = "0.48.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1eeca1c172a285ee6c2c84c341ccea837e7c01b12fbb2d0fe3c9e550ce49ec8"
checksum = "27f51fb4c64f8b770a823c043c7fad036323e1c48f55287b7bbb7987b2fcdf3b"
dependencies = [
"windows_aarch64_gnullvm 0.48.2",
"windows_aarch64_msvc 0.48.2",
"windows_i686_gnu 0.48.2",
"windows_i686_msvc 0.48.2",
"windows_x86_64_gnu 0.48.2",
"windows_x86_64_gnullvm 0.48.2",
"windows_x86_64_msvc 0.48.2",
"windows_aarch64_gnullvm 0.48.3",
"windows_aarch64_msvc 0.48.3",
"windows_i686_gnu 0.48.3",
"windows_i686_msvc 0.48.3",
"windows_x86_64_gnu 0.48.3",
"windows_x86_64_gnullvm 0.48.3",
"windows_x86_64_msvc 0.48.3",
]
[[package]]
@ -2222,9 +2269,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.2"
version = "0.48.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f"
checksum = "fde1bb55ae4ce76a597a8566d82c57432bc69c039449d61572a7a353da28f68c"
[[package]]
name = "windows_aarch64_msvc"
@ -2234,9 +2281,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.2"
version = "0.48.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058"
checksum = "1513e8d48365a78adad7322fd6b5e4c4e99d92a69db8df2d435b25b1f1f286d4"
[[package]]
name = "windows_i686_gnu"
@ -2246,9 +2293,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.2"
version = "0.48.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd"
checksum = "60587c0265d2b842298f5858e1a5d79d146f9ee0c37be5782e92a6eb5e1d7a83"
[[package]]
name = "windows_i686_msvc"
@ -2258,9 +2305,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.2"
version = "0.48.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287"
checksum = "224fe0e0ffff5d2ea6a29f82026c8f43870038a0ffc247aa95a52b47df381ac4"
[[package]]
name = "windows_x86_64_gnu"
@ -2270,9 +2317,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.2"
version = "0.48.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a"
checksum = "62fc52a0f50a088de499712cbc012df7ebd94e2d6eb948435449d76a6287e7ad"
[[package]]
name = "windows_x86_64_gnullvm"
@ -2282,9 +2329,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.2"
version = "0.48.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d"
checksum = "2093925509d91ea3d69bcd20238f4c2ecdb1a29d3c281d026a09705d0dd35f3d"
[[package]]
name = "windows_x86_64_msvc"
@ -2294,25 +2341,19 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.2"
version = "0.48.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9"
checksum = "b6ade45bc8bf02ae2aa34a9d54ba660a1a58204da34ba793c00d83ca3730b5f1"
[[package]]
name = "winnow"
version = "0.5.10"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5504cc7644f4b593cbc05c4a55bf9bd4e94b867c3c0bd440934174d50482427d"
checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97"
dependencies = [
"memchr",
]
[[package]]
name = "xdg"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546"
[[package]]
name = "yaml-rust"
version = "0.4.5"

View file

@ -19,10 +19,6 @@ pub struct Adaptor;
impl Adaptor {
pub async fn image_show(mut path: &Path, rect: Rect) -> Result<()> {
if IMAGE_SHOWN.swap(true, Ordering::Relaxed) {
Self::image_hide(rect).ok();
}
let cache = BOOT.cache(path);
if fs::metadata(&cache).await.is_ok() {
path = cache.as_path();
@ -35,7 +31,9 @@ impl Adaptor {
_ => Ok(if let Some(tx) = &*UEBERZUG {
tx.send(Some((path.to_path_buf(), rect)))?;
}),
}
}?;
Ok(IMAGE_SHOWN.store(true, Ordering::Relaxed))
}
pub fn image_hide(rect: Rect) -> Result<()> {

View file

@ -3,18 +3,19 @@ use std::{path::Path, sync::Arc};
use anyhow::Result;
use config::PREVIEW;
use image::{imageops::FilterType, DynamicImage, ImageFormat};
use shared::tty_ratio;
use shared::Term;
use tokio::fs;
pub struct Image;
impl Image {
pub(super) async fn crop(path: &Path, size: (u16, u16)) -> Result<DynamicImage> {
let (w, h) = {
let r = tty_ratio();
let (w, h) = ((size.0 as f64 * r.0) as u32, (size.1 as f64 * r.1) as u32);
(w.min(PREVIEW.max_width), h.min(PREVIEW.max_height))
};
let (w, h) = Term::ratio()
.map(|(w, h)| {
let (w, h) = ((size.0 as f64 * w) as u32, (size.1 as f64 * h) as u32);
(w.min(PREVIEW.max_width), h.min(PREVIEW.max_height))
})
.unwrap_or((PREVIEW.max_width, PREVIEW.max_height));
let img = fs::read(path).await?;
let img = tokio::task::spawn_blocking(move || -> Result<DynamicImage> {

View file

@ -15,21 +15,19 @@ impl Iterm2 {
let img = Image::crop(path, (rect.width, rect.height)).await?;
let b = Self::encode(img).await?;
let mut stdout = stdout().lock();
Term::move_to(&mut stdout, rect.x, rect.y)?;
Ok(stdout.write_all(&b)?)
Self::image_hide(rect)?;
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| Ok(stdout.write_all(&b)?))
}
#[inline]
pub(super) fn image_hide(rect: Rect) -> Result<()> {
let s = " ".repeat(rect.width as usize);
let mut stdout = BufWriter::new(stdout().lock());
for y in rect.top()..=rect.bottom() {
Term::move_to(&mut stdout, rect.x, y)?;
stdout.write_all(s.as_bytes())?;
}
Ok(())
let stdout = BufWriter::new(stdout().lock());
Term::move_lock(stdout, (0, 0), |stdout| {
for y in rect.top()..rect.bottom() {
Term::move_to(stdout, rect.x, y)?;
Term::kill_to_end(stdout)?;
}
Ok(())
})
}
async fn encode(img: DynamicImage) -> Result<Vec<u8>> {

View file

@ -15,9 +15,8 @@ impl Kitty {
let img = Image::crop(path, (rect.width, rect.height)).await?;
let b = Self::encode(img).await?;
let mut stdout = stdout().lock();
Term::move_to(&mut stdout, rect.x, rect.y)?;
Ok(stdout.write_all(&b)?)
Self::image_hide()?;
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| Ok(stdout.write_all(&b)?))
}
#[inline]
@ -32,7 +31,7 @@ impl Kitty {
if let Some(first) = it.next() {
write!(
buf,
"\x1b\\\x1b_Ga=d\x1b\\\x1b_Ga=T,f={},s={},v={},m={};{}\x1b\\",
"\x1b_Ga=T,f={},s={},v={},m={};{}\x1b\\",
format,
size.0,
size.1,

View file

@ -15,21 +15,19 @@ impl Sixel {
let img = Image::crop(path, (rect.width, rect.height)).await?;
let b = Self::encode(img).await?;
let mut stdout = stdout().lock();
Term::move_to(&mut stdout, rect.x, rect.y)?;
Ok(stdout.write_all(&b)?)
Self::image_hide(rect)?;
Term::move_lock(stdout().lock(), (rect.x, rect.y), |stdout| Ok(stdout.write_all(&b)?))
}
#[inline]
pub(super) fn image_hide(rect: Rect) -> Result<()> {
let s = " ".repeat(rect.width as usize);
let mut stdout = BufWriter::new(stdout().lock());
for y in rect.top()..=rect.bottom() {
Term::move_to(&mut stdout, rect.x, y)?;
stdout.write_all(s.as_bytes())?;
}
Ok(())
let stdout = BufWriter::new(stdout().lock());
Term::move_lock(stdout, (0, 0), |stdout| {
for y in rect.top()..rect.bottom() {
Term::move_to(stdout, rect.x, y)?;
Term::kill_to_end(stdout)?;
}
Ok(())
})
}
async fn encode(img: DynamicImage) -> Result<Vec<u8>> {

View file

@ -10,21 +10,23 @@ core = { path = "../core" }
shared = { path = "../shared" }
# External dependencies
ansi-to-tui = "^3"
anyhow = "^1"
crossterm = { version = "^0", features = [ "event-stream" ] }
futures = "^0"
libc = "^0"
ratatui = "^0"
signal-hook-tokio = { version = "^0", features = [ "futures-v0_3" ] }
tokio = { version = "^1", features = [ "parking_lot" ] }
unicode-width = "^0"
ansi-to-tui = "^3"
anyhow = "^1"
crossterm = { version = "^0", features = [ "event-stream" ] }
futures = "^0"
ratatui = "^0"
tokio = { version = "^1", features = [ "parking_lot" ] }
unicode-width = "^0"
# Logging
tracing = "^0"
tracing-appender = "^0"
tracing-subscriber = "^0"
[target.'cfg(not(target_os = "windows"))'.dependencies]
libc = "^0"
signal-hook-tokio = { version = "^0", features = [ "futures-v0_3" ] }
[[bin]]
name = "yazi"
path = "src/main.rs"

View file

@ -1,5 +1,5 @@
use core::{emit, files::FilesOp, input::InputMode, Event};
use std::{ffi::OsString, os::unix::prelude::OsStrExt};
use std::ffi::OsString;
use anyhow::{Ok, Result};
use config::{keymap::{Control, Key, KeymapLayer}, BOOT};
@ -44,7 +44,16 @@ impl App {
fn dispatch_quit(&mut self) {
if let Some(p) = &BOOT.cwd_file {
let cwd = self.cx.manager.cwd().as_os_str();
std::fs::write(p, cwd.as_bytes()).ok();
#[cfg(target_os = "windows")]
{
std::fs::write(p, cwd.to_string_lossy().as_bytes()).ok();
}
#[cfg(not(target_os = "windows"))]
{
use std::os::unix::ffi::OsStrExt;
std::fs::write(p, cwd.as_bytes()).ok();
}
}
}
@ -174,7 +183,15 @@ impl App {
s
});
std::fs::write(p, paths.as_bytes()).ok();
#[cfg(target_os = "windows")]
{
std::fs::write(p, paths.to_string_lossy().as_bytes()).ok();
}
#[cfg(not(target_os = "windows"))]
{
use std::os::unix::ffi::OsStrExt;
std::fs::write(p, paths.as_bytes()).ok();
}
return emit!(Quit);
}

View file

@ -1,9 +1,9 @@
use core::{input::Input, manager::Manager, select::Select, tasks::Tasks, which::Which, Position};
use config::keymap::KeymapLayer;
use libc::winsize;
use crossterm::terminal::WindowSize;
use ratatui::prelude::Rect;
use shared::tty_size;
use shared::Term;
pub struct Ctx {
pub manager: Manager,
@ -25,14 +25,14 @@ impl Ctx {
}
pub(super) fn area(&self, pos: &Position) -> Rect {
let winsize { ws_row, ws_col, .. } = tty_size();
let WindowSize { columns, rows, .. } = Term::size();
let (x, y) = match pos {
Position::None => return Rect::default(),
Position::Top(Rect { mut x, mut y, width, height }) => {
x = x.min(ws_col.saturating_sub(*width));
y = y.min(ws_row.saturating_sub(*height));
((tty_size().ws_col / 2).saturating_sub(width / 2) + x, y)
x = x.min(columns.saturating_sub(*width));
y = y.min(rows.saturating_sub(*height));
((columns / 2).saturating_sub(width / 2) + x, y)
}
Position::Hovered(rect @ Rect { mut x, y, width, height }) => {
let Some(r) =
@ -41,8 +41,8 @@ impl Ctx {
return self.area(&Position::Top(*rect));
};
x = x.min(ws_col.saturating_sub(*width));
if y + height + r.y + r.height > ws_row {
x = x.min(columns.saturating_sub(*width));
if y + height + r.y + r.height > rows {
(x + r.x, r.y.saturating_sub(height.saturating_sub(1)))
} else {
(x + r.x, y + r.y + r.height)
@ -51,7 +51,7 @@ impl Ctx {
};
let (w, h) = pos.dimension().unwrap();
Rect { x, y, width: w.min(ws_col.saturating_sub(x)), height: h.min(ws_row.saturating_sub(y)) }
Rect { x, y, width: w.min(columns.saturating_sub(x)), height: h.min(rows.saturating_sub(y)) }
}
#[inline]

View file

@ -1,9 +1,8 @@
use core::Event;
use anyhow::Result;
use crossterm::event::{Event as CrosstermEvent, EventStream};
use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, KeyEventKind};
use futures::StreamExt;
use libc::{SIGHUP, SIGINT, SIGQUIT, SIGTERM};
use tokio::{select, sync::{mpsc::{self, UnboundedReceiver, UnboundedSender}, oneshot}, task::JoinHandle};
pub(super) struct Signals {
@ -46,7 +45,13 @@ impl Signals {
}
}
#[cfg(target_os = "windows")]
fn spawn_system_task(&self) -> Result<()> { Ok(()) }
#[cfg(not(target_os = "windows"))]
fn spawn_system_task(&self) -> Result<JoinHandle<()>> {
use libc::{SIGHUP, SIGINT, SIGQUIT, SIGTERM};
let tx = self.tx.clone();
let mut signals = signal_hook_tokio::Signals::new([SIGHUP, SIGTERM, SIGINT, SIGQUIT])?;
@ -76,7 +81,9 @@ impl Signals {
_ = &mut stop_rx => break,
Some(Ok(event)) = reader.next() => {
let event = match event {
CrosstermEvent::Key(key) => Event::Key(key),
// We need to check key event kind;
// otherwise event will be dispatched twice.
CrosstermEvent::Key(key @ KeyEvent { kind: KeyEventKind::Press, .. }) => Event::Key(key),
CrosstermEvent::Paste(str) => Event::Paste(str),
CrosstermEvent::Resize(cols, rows) => Event::Resize(cols, rows),
_ => continue,

View file

@ -1,8 +1,5 @@
use std::os::unix::prelude::PermissionsExt;
use config::THEME;
use ratatui::{buffer::Buffer, layout::{Alignment, Rect}, text::{Line, Span}, widgets::{Paragraph, Widget}};
use shared::file_mode;
use super::Progress;
use crate::Ctx;
@ -14,6 +11,7 @@ pub(super) struct Right<'a> {
impl<'a> Right<'a> {
pub(super) fn new(cx: &'a Ctx) -> Self { Self { cx } }
#[cfg(not(target_os = "windows"))]
fn permissions(&self, s: &str) -> Vec<Span> {
// Colors
let mode = self.cx.manager.active().mode();
@ -70,8 +68,10 @@ impl Widget for Right<'_> {
let mut spans = vec![];
// Permissions
#[cfg(not(target_os = "windows"))]
if let Some(h) = &manager.hovered {
spans.extend(self.permissions(&file_mode(h.meta.permissions().mode())))
use std::os::unix::prelude::PermissionsExt;
spans.extend(self.permissions(&shared::file_mode(h.meta.permissions().mode())))
}
// Position

View file

@ -1,11 +1,14 @@
cargo +stable build --release --target aarch64-apple-darwin
cargo +stable build --release --target x86_64-apple-darwin
cargo +stable build --release --target x86_64-unknown-linux-gnu
cargo +stable build --release --target x86_64-pc-windows-gnu
mv target/aarch64-apple-darwin/release/yazi yazi-aarch64-apple-darwin
mv target/x86_64-apple-darwin/release/yazi yazi-x86_64-apple-darwin
mv target/x86_64-unknown-linux-gnu/release/yazi yazi-x86_64-unknown-linux-gnu
mv target/x86_64-pc-windows-gnu/release/yazi yazi-x86_64-pc-windows-gnu
zip -j yazi-aarch64-apple-darwin.zip yazi-aarch64-apple-darwin
zip -j yazi-x86_64-apple-darwin.zip yazi-x86_64-apple-darwin
zip -j yazi-x86_64-unknown-linux-gnu.zip yazi-x86_64-unknown-linux-gnu
zip -j yazi-x86_64-pc-windows-gnu.zip yazi-x86_64-pc-windows-gnu

View file

@ -10,6 +10,7 @@ shared = { path = "../shared" }
anyhow = "^1"
clap = { version = "^4", features = [ "derive" ] }
crossterm = "^0"
dirs = "^5"
futures = "^0"
glob = "^0"
indexmap = "^2"
@ -20,4 +21,3 @@ serde = { version = "^1", features = [ "derive" ] }
shell-words = "^1"
toml = { version = "^0", features = [ "preserve_order" ] }
validator = { version = "^0", features = [ "derive" ] }
xdg = "^2"

View file

@ -1,9 +1,11 @@
use std::{env, fs, os::unix::prelude::OsStrExt, path::{Path, PathBuf}, time::{self, SystemTime}};
use std::{env, fs, path::{Path, PathBuf}, time::{self, SystemTime}};
use clap::{command, Parser};
use md5::{Digest, Md5};
use shared::absolute_path;
use crate::Xdg;
#[derive(Debug)]
pub struct Boot {
pub cwd: PathBuf,
@ -43,7 +45,7 @@ impl Default for Boot {
let boot = Self {
cwd: cwd.unwrap_or("/".into()),
cache_dir: env::temp_dir().join("yazi"),
state_dir: xdg::BaseDirectories::with_prefix("yazi").unwrap().get_state_home(),
state_dir: Xdg::state_dir().unwrap(),
cwd_file: args.cwd_file,
chooser_file: args.chooser_file,
@ -63,9 +65,16 @@ impl Default for Boot {
impl Boot {
#[inline]
pub fn cache(&self, path: &Path) -> PathBuf {
self
.cache_dir
.join(format!("{:x}", Md5::new_with_prefix(path.as_os_str().as_bytes()).finalize()))
#[cfg(target_os = "windows")]
let h = Md5::new_with_prefix(path.to_string_lossy().as_bytes());
#[cfg(not(target_os = "windows"))]
let h = {
use std::os::unix::ffi::OsStrExt;
Md5::new_with_prefix(path.as_os_str().as_bytes())
};
self.cache_dir.join(format!("{:x}", h.finalize()))
}
#[inline]

View file

@ -13,9 +13,11 @@ pub mod preview;
pub mod tasks;
pub mod theme;
mod validation;
mod xdg;
pub(crate) use pattern::*;
pub(crate) use preset::*;
pub(crate) use xdg::*;
static MERGED_KEYMAP: RoCell<String> = RoCell::new();
static MERGED_THEME: RoCell<String> = RoCell::new();

View file

@ -1,7 +1,8 @@
use std::fs;
use toml::Table;
use xdg::BaseDirectories;
use crate::xdg::Xdg;
pub(crate) struct Preset;
@ -28,7 +29,7 @@ impl Preset {
}
fn merge_str(user: &str, base: &str) -> String {
let path = BaseDirectories::new().unwrap().get_config_file(user);
let path = Xdg::config_dir().unwrap().join(user);
let mut user = fs::read_to_string(path).unwrap_or_default().parse::<Table>().unwrap();
let base = base.parse::<Table>().unwrap();
@ -38,16 +39,16 @@ impl Preset {
#[inline]
pub(crate) fn keymap() -> String {
Self::merge_str("yazi/keymap.toml", include_str!("../preset/keymap.toml"))
Self::merge_str("keymap.toml", include_str!("../preset/keymap.toml"))
}
#[inline]
pub(crate) fn theme() -> String {
Self::merge_str("yazi/theme.toml", include_str!("../preset/theme.toml"))
Self::merge_str("theme.toml", include_str!("../preset/theme.toml"))
}
#[inline]
pub(crate) fn yazi() -> String {
Self::merge_str("yazi/yazi.toml", include_str!("../preset/yazi.toml"))
Self::merge_str("yazi.toml", include_str!("../preset/yazi.toml"))
}
}

View file

@ -27,7 +27,7 @@ impl Default for PreviewAdaptor {
}
match env::var("TERM_PROGRAM").unwrap_or_default().as_str() {
"iTerm.app" => return Self::Iterm2,
"WezTerm" => return Self::Kitty,
"WezTerm" => return if cfg!(windows) { Self::Iterm2 } else { Self::Kitty },
"vscode" => return Self::Sixel,
"Hyper" => return Self::Sixel,
_ => {}

35
config/src/xdg.rs Normal file
View file

@ -0,0 +1,35 @@
use std::path::PathBuf;
pub(super) struct Xdg;
impl Xdg {
pub(super) fn config_dir() -> Option<PathBuf> {
#[cfg(target_os = "windows")]
{
dirs::config_dir().map(|p| p.join("yazi").join("config"))
}
#[cfg(not(target_os = "windows"))]
{
std::env::var_os("XDG_CONFIG_HOME")
.map(PathBuf::from)
.and_then(|p| p.is_absolute().then_some(p))
.or_else(|| dirs::home_dir().map(|h| h.join(".config")))
.map(|p| p.join("yazi"))
}
}
pub(super) fn state_dir() -> Option<PathBuf> {
#[cfg(target_os = "windows")]
{
dirs::data_dir().map(|p| p.join("yazi").join("state"))
}
#[cfg(not(target_os = "windows"))]
{
std::env::var_os("XDG_STATE_HOME")
.map(PathBuf::from)
.and_then(|p| p.is_absolute().then_some(p))
.or_else(|| dirs::home_dir().map(|h| h.join(".local/state")))
.map(|p| p.join("yazi"))
}
}
}

View file

@ -10,7 +10,7 @@ pub async fn file(files: &[impl AsRef<OsStr>]) -> Result<BTreeMap<PathBuf, Strin
}
let output = Command::new("file")
.args(["-bL", "--mime-type"])
.args(["-b", "--mime-type"])
.args(files)
.kill_on_drop(true)
.output()

View file

@ -1,8 +1,9 @@
use std::path::{Path, PathBuf};
use crossterm::terminal::WindowSize;
use indexmap::map::Slice;
use ratatui::layout::Rect;
use shared::tty_size;
use shared::Term;
use super::{ALL_RATIO, CURRENT_RATIO, DIR_PADDING, PARENT_RATIO};
use crate::{emit, files::{File, Files, FilesOp}};
@ -27,7 +28,7 @@ impl Folder {
}
#[inline]
pub fn limit() -> usize { tty_size().ws_row.saturating_sub(DIR_PADDING) as usize }
pub fn limit() -> usize { Term::size().rows.saturating_sub(DIR_PADDING) as usize }
pub fn update(&mut self, op: FilesOp) -> bool {
let b = match op {
@ -183,12 +184,12 @@ impl Folder {
pub fn rect_current(&self, path: &Path) -> Option<Rect> {
let pos = self.position(path)? - self.offset;
let s = tty_size();
let WindowSize { columns, .. } = Term::size();
Some(Rect {
x: (s.ws_col as u32 * PARENT_RATIO / ALL_RATIO) as u16,
x: (columns as u32 * PARENT_RATIO / ALL_RATIO) as u16,
y: pos as u16,
width: (s.ws_col as u32 * CURRENT_RATIO / ALL_RATIO) as u16,
width: (columns as u32 * CURRENT_RATIO / ALL_RATIO) as u16,
height: 1,
})
}

View file

@ -1,4 +1,4 @@
use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, env, ffi::OsStr, io::{stdout, BufWriter, Write}, mem, os::unix::prelude::OsStrExt, path::{Path, PathBuf}};
use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, env, ffi::OsStr, io::{stdout, BufWriter, Write}, mem, path::{Path, PathBuf}};
use anyhow::{anyhow, bail, Error, Result};
use config::{open::Opener, BOOT, OPEN};
@ -231,9 +231,17 @@ impl Manager {
};
{
let b = old.iter().map(|o| o.as_os_str()).collect::<Vec<_>>().join(OsStr::new("\n"));
let s = old.iter().map(|o| o.as_os_str()).collect::<Vec<_>>().join(OsStr::new("\n"));
let mut f = OpenOptions::new().write(true).create_new(true).open(&tmp).await?;
f.write_all(b.as_bytes()).await?;
#[cfg(target_os = "windows")]
{
f.write_all(s.to_string_lossy().as_bytes()).await?;
}
#[cfg(not(target_os = "windows"))]
{
use std::os::unix::ffi::OsStrExt;
f.write_all(s.as_bytes()).await?;
}
}
let _guard = BLOCKER.acquire().await.unwrap();

View file

@ -3,8 +3,9 @@ use std::{io::BufRead, mem, path::{Path, PathBuf}, sync::{atomic::{AtomicUsize,
use adaptor::Adaptor;
use anyhow::{anyhow, bail, Result};
use config::{BOOT, PREVIEW};
use crossterm::terminal::WindowSize;
use ratatui::prelude::Rect;
use shared::{tty_size, MimeKind};
use shared::{MimeKind, Term};
use syntect::{easy::HighlightFile, util::as_24_bit_terminal_escaped};
use tokio::{fs, task::JoinHandle};
@ -31,16 +32,16 @@ pub enum PreviewData {
impl Preview {
fn rect() -> Rect {
let s = tty_size();
let WindowSize { columns, rows, .. } = Term::size();
let x = (s.ws_col as u32 * (PARENT_RATIO + CURRENT_RATIO) / ALL_RATIO) as u16;
let width = (s.ws_col as u32 * PREVIEW_RATIO / ALL_RATIO) as u16;
let x = (columns as u32 * (PARENT_RATIO + CURRENT_RATIO) / ALL_RATIO) as u16;
let width = (columns as u32 * PREVIEW_RATIO / ALL_RATIO) as u16;
Rect {
x: x.saturating_add(PREVIEW_BORDER / 2),
y: PREVIEW_MARGIN / 2,
width: width.saturating_sub(PREVIEW_BORDER),
height: s.ws_row.saturating_sub(PREVIEW_MARGIN),
height: rows.saturating_sub(PREVIEW_MARGIN),
}
}

View file

@ -2,7 +2,7 @@ use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, io::{stdout, Wr
use config::{manager::SortBy, open::Opener, OPEN};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use shared::{tty_size, Defer, MimeKind, Term};
use shared::{Defer, MimeKind, Term};
use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time};
use tracing::trace;
@ -29,7 +29,7 @@ impl Tasks {
#[inline]
pub fn limit() -> usize {
(tty_size().ws_row * TASKS_PERCENT / 100).saturating_sub(TASKS_PADDING) as usize
(Term::size().rows * TASKS_PERCENT / 100).saturating_sub(TASKS_PADDING) as usize
}
pub fn toggle(&mut self) -> bool {

View file

@ -125,7 +125,16 @@ impl File {
match fs::remove_file(&task.to).await {
Err(e) if e.kind() != NotFound => Err(e)?,
_ => fs::symlink(src, &task.to).await?,
_ => {
#[cfg(target_os = "windows")]
{
fs::symlink_file(src, &task.to).await?
}
#[cfg(not(target_os = "windows"))]
{
fs::symlink(src, &task.to).await?
}
}
}
if task.cut {

View file

@ -92,6 +92,7 @@ pub fn copy_with_progress(from: &Path, to: &Path) -> mpsc::Receiver<Result<u64,
}
// Convert a file mode to a string representation
#[cfg(not(target_os = "windows"))]
#[allow(clippy::collapsible_else_if)]
pub fn file_mode(mode: u32) -> String {
use libc::{S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFSOCK, S_IRGRP, S_IROTH, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR};

View file

@ -7,7 +7,6 @@ mod mime;
mod ro_cell;
mod term;
mod throttle;
mod tty;
pub use buffer::*;
pub use chars::*;
@ -18,4 +17,3 @@ pub use mime::*;
pub use ro_cell::*;
pub use term::*;
pub use throttle::*;
pub use tty::*;

49
shared/src/term/cursor.rs Normal file
View file

@ -0,0 +1,49 @@
use std::io::{stdout, Write};
use anyhow::Result;
use crossterm::{cursor::{MoveTo, RestorePosition, SavePosition, SetCursorStyle}, execute, queue, terminal::{Clear, ClearType}};
use crate::Term;
impl Term {
#[inline]
pub fn kill_to_end(stdout: &mut impl Write) -> Result<()> {
Ok(queue!(stdout, Clear(ClearType::UntilNewLine))?)
}
#[inline]
pub fn move_to(stdout: &mut impl Write, x: u16, y: u16) -> Result<()> {
Ok(queue!(stdout, MoveTo(x, y))?)
}
#[inline]
pub fn move_lock<W, F>(mut stdout: W, (x, y): (u16, u16), cb: F) -> Result<()>
where
W: Write,
F: FnOnce(&mut W) -> Result<()>,
{
#[cfg(target_os = "windows")]
{
use crossterm::cursor::{Hide, Show};
queue!(&mut stdout, SavePosition, MoveTo(x, y), Show)?;
let result = cb(&mut stdout);
queue!(&mut stdout, Hide, RestorePosition)?;
stdout.flush()?;
result
}
#[cfg(not(target_os = "windows"))]
{
queue!(&mut stdout, SavePosition, MoveTo(x, y))?;
let result = cb(&mut stdout);
queue!(&mut stdout, RestorePosition)?;
stdout.flush()?;
result
}
}
#[inline]
pub fn set_cursor_block() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBlock)?) }
#[inline]
pub fn set_cursor_bar() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBar)?) }
}

5
shared/src/term/mod.rs Normal file
View file

@ -0,0 +1,5 @@
mod cursor;
mod term;
pub use cursor::*;
pub use term::*;

View file

@ -1,7 +1,7 @@
use std::{io::{stdout, Stdout, Write}, ops::{Deref, DerefMut}};
use std::{io::{stdout, Stdout, Write}, mem, ops::{Deref, DerefMut}};
use anyhow::Result;
use crossterm::{cursor::{MoveTo, SetCursorStyle}, event::{DisableBracketedPaste, DisableFocusChange, EnableBracketedPaste, EnableFocusChange, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, terminal::{disable_raw_mode, enable_raw_mode, supports_keyboard_enhancement, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen}};
use crossterm::{event::{DisableBracketedPaste, DisableFocusChange, EnableBracketedPaste, EnableFocusChange, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, terminal::{disable_raw_mode, enable_raw_mode, supports_keyboard_enhancement, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen, WindowSize}};
use ratatui::{backend::CrosstermBackend, Terminal};
pub struct Term {
@ -32,23 +32,40 @@ impl Term {
Ok(term)
}
pub fn size() -> WindowSize {
let mut size = WindowSize { rows: 0, columns: 0, width: 0, height: 0 };
if let Ok(s) = crossterm::terminal::window_size() {
let _ = mem::replace(&mut size, s);
}
if size.rows == 0 || size.columns == 0 {
if let Ok(s) = crossterm::terminal::size() {
size.columns = s.0;
size.rows = s.1;
}
}
// TODO: Use `CSI 14 t` to get the actual size of the terminal
// if size.width == 0 || size.height == 0 {}
size
}
#[inline]
pub fn ratio() -> Option<(f64, f64)> {
let s = Self::size();
if s.width == 0 || s.height == 0 {
return None;
}
Some((f64::from(s.width) / f64::from(s.columns), f64::from(s.height) / f64::from(s.rows)))
}
#[inline]
pub fn clear(stdout: &mut impl Write) -> Result<()> {
execute!(stdout, Clear(ClearType::All))?;
queue!(stdout, Clear(ClearType::All))?;
writeln!(stdout)?;
Ok(stdout.flush()?)
}
#[inline]
pub fn move_to(stdout: &mut impl Write, x: u16, y: u16) -> Result<()> {
Ok(execute!(stdout, MoveTo(x, y))?)
}
#[inline]
pub fn set_cursor_block() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBlock)?) }
#[inline]
pub fn set_cursor_bar() -> Result<()> { Ok(execute!(stdout(), SetCursorStyle::BlinkingBar)?) }
}
impl Drop for Term {

View file

@ -1,16 +0,0 @@
use libc::{ioctl, winsize, STDOUT_FILENO, TIOCGWINSZ};
#[inline]
pub fn tty_size() -> winsize {
unsafe {
let s: winsize = std::mem::zeroed();
ioctl(STDOUT_FILENO, TIOCGWINSZ, &s);
s
}
}
#[inline]
pub fn tty_ratio() -> (f64, f64) {
let s = tty_size();
(f64::from(s.ws_xpixel) / f64::from(s.ws_col), f64::from(s.ws_ypixel) / f64::from(s.ws_row))
}