mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Merge branch 'sxyazi:main' into copy-content
This commit is contained in:
commit
a330b6dec6
82 changed files with 1030 additions and 561 deletions
92
Cargo.lock
generated
92
Cargo.lock
generated
|
|
@ -299,12 +299,6 @@ version = "1.22.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder-lite"
|
||||
version = "0.1.0"
|
||||
|
|
@ -373,9 +367,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.31"
|
||||
version = "4.5.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"
|
||||
checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
|
@ -383,9 +377,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.31"
|
||||
version = "4.5.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"
|
||||
checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
|
@ -424,9 +418,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.28"
|
||||
version = "4.5.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
|
||||
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
|
@ -1237,9 +1231,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.1"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
|
|
@ -1773,9 +1767,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.3"
|
||||
version = "1.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||
checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad"
|
||||
|
||||
[[package]]
|
||||
name = "onig"
|
||||
|
|
@ -1913,9 +1907,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
|||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
|
@ -2252,9 +2246,9 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.218"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
|
@ -2271,9 +2265,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.218"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2452,9 +2446,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.99"
|
||||
version = "2.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2"
|
||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2595,9 +2589,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.38"
|
||||
version = "0.3.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb041120f25f8fbe8fd2dbe4671c7c2ed74d83be2e7a77529bf7e0790ae3f472"
|
||||
checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
|
|
@ -2638,9 +2632,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.43.0"
|
||||
version = "1.44.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
|
||||
checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
|
|
@ -3321,7 +3315,7 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
|||
|
||||
[[package]]
|
||||
name = "yazi-adapter"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"ansi-to-tui",
|
||||
"anyhow",
|
||||
|
|
@ -3329,12 +3323,10 @@ dependencies = [
|
|||
"color_quant",
|
||||
"crossterm",
|
||||
"image",
|
||||
"libc",
|
||||
"ratatui",
|
||||
"scopeguard",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
"yazi-config",
|
||||
"yazi-macro",
|
||||
"yazi-shared",
|
||||
|
|
@ -3342,7 +3334,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yazi-boot"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
|
|
@ -3360,7 +3352,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yazi-cli"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
|
|
@ -3383,7 +3375,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yazi-codegen"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
|
|
@ -3391,7 +3383,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yazi-config"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.9.0",
|
||||
|
|
@ -3411,7 +3403,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yazi-core"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.9.0",
|
||||
|
|
@ -3445,7 +3437,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yazi-dds"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"mlua",
|
||||
|
|
@ -3466,7 +3458,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yazi-ffi"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"core-foundation-sys",
|
||||
|
|
@ -3477,7 +3469,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yazi-fm"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"better-panic",
|
||||
|
|
@ -3513,7 +3505,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yazi-fs"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
|
|
@ -3539,11 +3531,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yazi-macro"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
|
||||
[[package]]
|
||||
name = "yazi-plugin"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"ansi-to-tui",
|
||||
"anyhow",
|
||||
|
|
@ -3584,7 +3576,7 @@ checksum = "f4b6c8e12e39ac0f79fa96f36e5b88e0da8d230691abd729eec709b43c74f632"
|
|||
|
||||
[[package]]
|
||||
name = "yazi-proxy"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"mlua",
|
||||
|
|
@ -3597,7 +3589,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yazi-scheduler"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-priority-channel",
|
||||
|
|
@ -3620,7 +3612,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yazi-shared"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"crossterm",
|
||||
|
|
@ -3632,6 +3624,7 @@ dependencies = [
|
|||
"ratatui",
|
||||
"serde",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uzers",
|
||||
"windows-sys 0.59.0",
|
||||
"yazi-macro",
|
||||
|
|
@ -3663,19 +3656,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ ansi-to-tui = "7.0.0"
|
|||
anyhow = "1.0.97"
|
||||
base64 = "0.22.1"
|
||||
bitflags = "2.9.0"
|
||||
clap = { version = "4.5.31", features = [ "derive" ] }
|
||||
clap = { version = "4.5.32", features = [ "derive" ] }
|
||||
core-foundation-sys = "0.8.7"
|
||||
crossterm = { version = "0.28.1", features = [ "event-stream" ] }
|
||||
dirs = "6.0.0"
|
||||
foldhash = "0.1.4"
|
||||
futures = "0.3.31"
|
||||
globset = "0.4.16"
|
||||
indexmap = { version = "2.7.1", features = [ "serde" ] }
|
||||
indexmap = { version = "2.8.0", features = [ "serde" ] }
|
||||
libc = "0.2.170"
|
||||
lru = "0.13.0"
|
||||
md-5 = "0.10.6"
|
||||
|
|
@ -35,9 +35,9 @@ parking_lot = "0.12.3"
|
|||
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info" ] }
|
||||
regex = "1.11.1"
|
||||
scopeguard = "1.2.0"
|
||||
serde = { version = "1.0.218", features = [ "derive" ] }
|
||||
serde = { version = "1.0.219", features = [ "derive" ] }
|
||||
serde_json = "1.0.140"
|
||||
tokio = { version = "1.43.0", features = [ "full" ] }
|
||||
tokio = { version = "1.44.0", features = [ "full" ] }
|
||||
tokio-stream = "0.1.17"
|
||||
tokio-util = "0.7.13"
|
||||
toml = { version = "0.8.20" }
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Yazi (means "duck") is a terminal file manager written in Rust, based on non-blo
|
|||
- 🌟 **Built-in Code Highlighting and Image Decoding**: Combined with the pre-loading mechanism, greatly accelerates image and normal file loading.
|
||||
- 🔌 **Concurrent Plugin System**: UI plugins (rewriting most of the UI), functional plugins, custom previewer/preloader/spotter/fetcher; Just some pieces of Lua.
|
||||
- 📡 **Data Distribution Service**: Built on a client-server architecture (no additional server process required), integrated with a Lua-based publish-subscribe model, achieving cross-instance communication and state persistence.
|
||||
- 📦 **Package Manager**: Install plugins and themes with one command, keeping them up to date, or pin them to a specific version.
|
||||
- 📦 **Package Manager**: Install plugins and themes with one command, keeping them up-to-date, or pin them to a specific version.
|
||||
- 🧰 Integration with ripgrep, fd, fzf, zoxide
|
||||
- 💫 Vim-like input/pick/confirm/which/notify component, auto-completion for cd paths
|
||||
- 🏷️ Multi-Tab Support, Cross-directory selection, Scrollable Preview (for videos, PDFs, archives, code, directories, etc.)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit"],"flagWords":[],"version":"0.2","language":"en"}
|
||||
{"version":"0.2","language":"en","words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition"],"flagWords":[]}
|
||||
|
|
@ -61,10 +61,49 @@ module.exports = async ({ github, context, core }) => {
|
|||
}
|
||||
}
|
||||
|
||||
async function lastLabeledAt(id) {
|
||||
try {
|
||||
const { data: events } = await github.rest.issues.listEvents({
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
per_page : 100,
|
||||
})
|
||||
|
||||
const all = events.filter(v => v.event === "labeled" && v.label?.name === LABEL_NAME)
|
||||
return all.at(-1)?.created_at
|
||||
} catch (e) {
|
||||
core.error(`Error getting label timestamp: ${e.message}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async function removedLabelManually(id) {
|
||||
try {
|
||||
const { data: events } = await github.rest.issues.listEvents({
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
per_page : 100,
|
||||
})
|
||||
|
||||
const all = events.filter(v => v.event === "unlabeled" && v.label?.name === LABEL_NAME)
|
||||
return all.length === 0 ? false : !all.at(-1).actor.login.endsWith("[bot]")
|
||||
} catch (e) {
|
||||
core.error(`Error checking label removal history: ${e.message}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function updateLabels(id, mark, body) {
|
||||
try {
|
||||
const marked = await hasLabel(id, LABEL_NAME)
|
||||
if (mark && !marked) {
|
||||
|
||||
if (!mark && marked) {
|
||||
await github.rest.issues.removeLabel({
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
name : LABEL_NAME,
|
||||
})
|
||||
} else if (mark && !marked && !await removedLabelManually(id)) {
|
||||
await github.rest.issues.addLabels({
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
|
|
@ -75,12 +114,6 @@ module.exports = async ({ github, context, core }) => {
|
|||
issue_number: id,
|
||||
body,
|
||||
})
|
||||
} else if (!mark && marked) {
|
||||
await github.rest.issues.removeLabel({
|
||||
...context.repo,
|
||||
issue_number: id,
|
||||
name : LABEL_NAME,
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
core.error(`Error updating labels: ${e.message}`)
|
||||
|
|
@ -99,7 +132,7 @@ module.exports = async ({ github, context, core }) => {
|
|||
const twoDaysAgo = new Date(now - 2 * 24 * 60 * 60 * 1000)
|
||||
|
||||
for (const issue of issues) {
|
||||
const markedAt = new Date(issue.labels_at || issue.created_at)
|
||||
const markedAt = new Date(await lastLabeledAt(issue.number) || issue.created_at)
|
||||
if (markedAt < twoDaysAgo) {
|
||||
await github.rest.issues.update({
|
||||
...context.repo,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-adapter"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -9,9 +9,9 @@ homepage = "https://yazi-rs.github.io"
|
|||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
[dependencies]
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.2" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
ansi-to-tui = { workspace = true }
|
||||
|
|
@ -25,11 +25,5 @@ scopeguard = { workspace = true }
|
|||
tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[target."cfg(unix)".dependencies]
|
||||
libc = { workspace = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-sys = { version = "0.59.0", features = [ "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_Threading" ] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] }
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ pub enum Brand {
|
|||
Foot,
|
||||
Ghostty,
|
||||
Microsoft,
|
||||
Warp,
|
||||
Rio,
|
||||
BlackBox,
|
||||
VSCode,
|
||||
|
|
@ -69,6 +70,7 @@ impl Brand {
|
|||
"iTerm.app" => return Some(B::Iterm2),
|
||||
"WezTerm" => return Some(B::WezTerm),
|
||||
"ghostty" => return Some(B::Ghostty),
|
||||
"WarpTerminal" => return Some(B::Warp),
|
||||
"rio" => return Some(B::Rio),
|
||||
"BlackBox" => return Some(B::BlackBox),
|
||||
"vscode" => return Some(B::VSCode),
|
||||
|
|
@ -99,6 +101,7 @@ impl Brand {
|
|||
B::Foot => &[A::Sixel],
|
||||
B::Ghostty => &[A::Kgp],
|
||||
B::Microsoft => &[A::Sixel],
|
||||
B::Warp => &[],
|
||||
B::Rio => &[A::Iip, A::Sixel],
|
||||
B::BlackBox => &[A::Sixel],
|
||||
B::VSCode => &[A::Iip, A::Sixel],
|
||||
|
|
|
|||
|
|
@ -55,10 +55,10 @@ impl Chafa {
|
|||
|
||||
Adapter::Chafa.image_hide()?;
|
||||
Adapter::shown_store(area);
|
||||
Emulator::move_lock((max.x, max.y), |stderr| {
|
||||
Emulator::move_lock((max.x, max.y), |w| {
|
||||
for (i, line) in lines.into_iter().enumerate() {
|
||||
stderr.write_all(line)?;
|
||||
queue!(stderr, MoveTo(max.x, max.y + i as u16 + 1))?;
|
||||
w.write_all(line)?;
|
||||
queue!(w, MoveTo(max.x, max.y + i as u16 + 1))?;
|
||||
}
|
||||
Ok(area)
|
||||
})
|
||||
|
|
@ -66,10 +66,10 @@ impl Chafa {
|
|||
|
||||
pub(crate) fn image_erase(area: Rect) -> Result<()> {
|
||||
let s = " ".repeat(area.width as usize);
|
||||
Emulator::move_lock((0, 0), |stderr| {
|
||||
Emulator::move_lock((0, 0), |w| {
|
||||
for y in area.top()..area.bottom() {
|
||||
queue!(stderr, MoveTo(area.x, y))?;
|
||||
write!(stderr, "{s}")?;
|
||||
queue!(w, MoveTo(area.x, y))?;
|
||||
write!(w, "{s}")?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
|
|
|||
|
|
@ -19,18 +19,18 @@ impl Iip {
|
|||
|
||||
Adapter::Iip.image_hide()?;
|
||||
Adapter::shown_store(area);
|
||||
Emulator::move_lock((max.x, max.y), |stderr| {
|
||||
stderr.write_all(&b)?;
|
||||
Emulator::move_lock((max.x, max.y), |w| {
|
||||
w.write_all(&b)?;
|
||||
Ok(area)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn image_erase(area: Rect) -> Result<()> {
|
||||
let s = " ".repeat(area.width as usize);
|
||||
Emulator::move_lock((0, 0), |stderr| {
|
||||
Emulator::move_lock((0, 0), |w| {
|
||||
for y in area.top()..area.bottom() {
|
||||
queue!(stderr, MoveTo(area.x, y))?;
|
||||
write!(stderr, "{s}")?;
|
||||
queue!(w, MoveTo(area.x, y))?;
|
||||
write!(w, "{s}")?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
|
|
|||
|
|
@ -321,22 +321,22 @@ impl Kgp {
|
|||
|
||||
Adapter::Kgp.image_hide()?;
|
||||
Adapter::shown_store(area);
|
||||
Emulator::move_lock((area.x, area.y), |stderr| {
|
||||
stderr.write_all(&b1)?;
|
||||
stderr.write_all(&b2)?;
|
||||
Emulator::move_lock((area.x, area.y), |w| {
|
||||
w.write_all(&b1)?;
|
||||
w.write_all(&b2)?;
|
||||
Ok(area)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn image_erase(area: Rect) -> Result<()> {
|
||||
let s = " ".repeat(area.width as usize);
|
||||
Emulator::move_lock((0, 0), |stderr| {
|
||||
Emulator::move_lock((0, 0), |w| {
|
||||
for y in area.top()..area.bottom() {
|
||||
queue!(stderr, MoveTo(area.x, y))?;
|
||||
write!(stderr, "{s}")?;
|
||||
queue!(w, MoveTo(area.x, y))?;
|
||||
write!(w, "{s}")?;
|
||||
}
|
||||
|
||||
write!(stderr, "{START}_Gq=2,a=d,d=A{ESCAPE}\\{CLOSE}")?;
|
||||
write!(w, "{START}_Gq=2,a=d,d=A{ESCAPE}\\{CLOSE}")?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use core::str;
|
||||
use std::{io::{LineWriter, Write, stderr}, path::Path};
|
||||
use std::{io::Write, path::Path};
|
||||
|
||||
use anyhow::Result;
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use image::DynamicImage;
|
||||
use ratatui::layout::Rect;
|
||||
use yazi_shared::tty::TTY;
|
||||
|
||||
use crate::{CLOSE, ESCAPE, Emulator, Image, START, adapter::Adapter};
|
||||
|
||||
|
|
@ -18,17 +19,17 @@ impl KgpOld {
|
|||
|
||||
Adapter::KgpOld.image_hide()?;
|
||||
Adapter::shown_store(area);
|
||||
Emulator::move_lock((area.x, area.y), |stderr| {
|
||||
stderr.write_all(&b)?;
|
||||
Emulator::move_lock((area.x, area.y), |w| {
|
||||
w.write_all(&b)?;
|
||||
Ok(area)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn image_erase(_: Rect) -> Result<()> {
|
||||
let mut stderr = LineWriter::new(stderr());
|
||||
write!(stderr, "{START}_Gq=2,a=d,d=A{ESCAPE}\\{CLOSE}")?;
|
||||
stderr.flush()?;
|
||||
let mut w = TTY.lockout();
|
||||
write!(w, "{START}_Gq=2,a=d,d=A{ESCAPE}\\{CLOSE}")?;
|
||||
w.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,18 +19,18 @@ impl Sixel {
|
|||
|
||||
Adapter::Sixel.image_hide()?;
|
||||
Adapter::shown_store(area);
|
||||
Emulator::move_lock((area.x, area.y), |stderr| {
|
||||
stderr.write_all(&b)?;
|
||||
Emulator::move_lock((area.x, area.y), |w| {
|
||||
w.write_all(&b)?;
|
||||
Ok(area)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn image_erase(area: Rect) -> Result<()> {
|
||||
let s = " ".repeat(area.width as usize);
|
||||
Emulator::move_lock((0, 0), |stderr| {
|
||||
Emulator::move_lock((0, 0), |w| {
|
||||
for y in area.top()..area.bottom() {
|
||||
queue!(stderr, MoveTo(area.x, y))?;
|
||||
write!(stderr, "{s}")?;
|
||||
queue!(w, MoveTo(area.x, y))?;
|
||||
write!(w, "{s}")?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use std::{io::{LineWriter, stderr}, time::Duration};
|
||||
use std::{io::BufWriter, time::Duration};
|
||||
|
||||
use anyhow::Result;
|
||||
use crossterm::{cursor::{RestorePosition, SavePosition}, execute, style::Print, terminal::{disable_raw_mode, enable_raw_mode}};
|
||||
use scopeguard::defer;
|
||||
use tokio::time::sleep;
|
||||
use tracing::{debug, error, warn};
|
||||
use yazi_shared::Either;
|
||||
use yazi_shared::{Either, tty::{Handle, TTY}};
|
||||
|
||||
use crate::{Adapter, AsyncStdin, Brand, Mux, TMUX, Unknown};
|
||||
use crate::{Adapter, Brand, Mux, TMUX, Unknown};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Emulator {
|
||||
|
|
@ -33,7 +33,7 @@ impl Emulator {
|
|||
};
|
||||
|
||||
execute!(
|
||||
LineWriter::new(stderr()),
|
||||
TTY.writer(),
|
||||
SavePosition,
|
||||
Print(kgp_seq), // Detect KGP
|
||||
Print(Mux::csi("\x1b[>q")), // Request terminal version
|
||||
|
|
@ -75,33 +75,33 @@ impl Emulator {
|
|||
|
||||
pub fn move_lock<F, T>((x, y): (u16, u16), cb: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(&mut std::io::BufWriter<std::io::StderrLock>) -> Result<T>,
|
||||
F: FnOnce(&mut BufWriter<Handle>) -> Result<T>,
|
||||
{
|
||||
use std::{io::Write, thread, time::Duration};
|
||||
|
||||
use crossterm::{cursor::{Hide, MoveTo, RestorePosition, SavePosition, Show}, queue};
|
||||
|
||||
let mut buf = std::io::BufWriter::new(stderr().lock());
|
||||
let mut w = TTY.lockout();
|
||||
|
||||
// I really don't want to add this,
|
||||
// But tmux and ConPTY sometimes cause the cursor position to get out of sync.
|
||||
if TMUX.get() || cfg!(windows) {
|
||||
execute!(buf, SavePosition, MoveTo(x, y), Show)?;
|
||||
execute!(buf, MoveTo(x, y), Show)?;
|
||||
execute!(buf, MoveTo(x, y), Show)?;
|
||||
execute!(w, SavePosition, MoveTo(x, y), Show)?;
|
||||
execute!(w, MoveTo(x, y), Show)?;
|
||||
execute!(w, MoveTo(x, y), Show)?;
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
} else {
|
||||
queue!(buf, SavePosition, MoveTo(x, y))?;
|
||||
queue!(w, SavePosition, MoveTo(x, y))?;
|
||||
}
|
||||
|
||||
let result = cb(&mut buf);
|
||||
let result = cb(&mut w);
|
||||
if TMUX.get() || cfg!(windows) {
|
||||
queue!(buf, Hide, RestorePosition)?;
|
||||
queue!(w, Hide, RestorePosition)?;
|
||||
} else {
|
||||
queue!(buf, RestorePosition)?;
|
||||
queue!(w, RestorePosition)?;
|
||||
}
|
||||
|
||||
buf.flush()?;
|
||||
w.flush()?;
|
||||
result
|
||||
}
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ impl Emulator {
|
|||
let now = std::time::Instant::now();
|
||||
let h = tokio::spawn(Self::error_to_user());
|
||||
|
||||
let (buf, result) = AsyncStdin::default().read_until(Duration::from_millis(500), |b, buf| {
|
||||
let (buf, result) = TTY.read_until(Duration::from_millis(500), |b, buf| {
|
||||
b == b'c'
|
||||
&& buf.contains(&0x1b)
|
||||
&& buf.rsplitn(2, |&b| b == 0x1b).next().is_some_and(|s| s.starts_with(b"[?"))
|
||||
|
|
@ -128,7 +128,7 @@ impl Emulator {
|
|||
|
||||
pub fn read_until_dsr() -> String {
|
||||
let now = std::time::Instant::now();
|
||||
let (buf, result) = AsyncStdin::default().read_until(Duration::from_millis(100), |b, buf| {
|
||||
let (buf, result) = TTY.read_until(Duration::from_millis(100), |b, buf| {
|
||||
b == b'n' && (buf.ends_with(b"\x1b[0n") || buf.ends_with(b"\x1b[3n"))
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
yazi_macro::mod_pub!(drivers);
|
||||
|
||||
yazi_macro::mod_flat!(adapter brand dimension emulator image info mux stdin unknown);
|
||||
yazi_macro::mod_flat!(adapter brand dimension emulator image info mux unknown);
|
||||
|
||||
use yazi_shared::{SyncCell, in_wsl};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use anyhow::Result;
|
||||
use tracing::error;
|
||||
use yazi_macro::time;
|
||||
use yazi_shared::tty::TTY;
|
||||
|
||||
use crate::{CLOSE, ESCAPE, Emulator, START, TMUX};
|
||||
|
||||
|
|
@ -47,7 +48,7 @@ impl Mux {
|
|||
|
||||
pub fn tmux_drain() -> Result<()> {
|
||||
if TMUX.get() {
|
||||
crossterm::execute!(std::io::stderr(), crossterm::style::Print(Mux::csi("\x1b[5n")))?;
|
||||
crossterm::execute!(TTY.writer(), crossterm::style::Print(Mux::csi("\x1b[5n")))?;
|
||||
_ = Emulator::read_until_dsr();
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,174 +0,0 @@
|
|||
use std::{io::{Error, ErrorKind}, ops::Deref, time::{Duration, Instant}};
|
||||
|
||||
pub struct AsyncStdin {
|
||||
fd: Fd,
|
||||
}
|
||||
|
||||
impl Default for AsyncStdin {
|
||||
fn default() -> Self { Self { fd: Fd::new().expect("failed to open stdin") } }
|
||||
}
|
||||
|
||||
impl AsyncStdin {
|
||||
pub fn read_until<P>(&mut self, timeout: Duration, predicate: P) -> (Vec<u8>, std::io::Result<()>)
|
||||
where
|
||||
P: Fn(u8, &[u8]) -> bool,
|
||||
{
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(200);
|
||||
let now = Instant::now();
|
||||
|
||||
let mut read = || {
|
||||
loop {
|
||||
if now.elapsed() > timeout {
|
||||
return Err(Error::new(ErrorKind::TimedOut, "timed out"));
|
||||
} else if !self.poll(Duration::from_millis(30))? {
|
||||
continue;
|
||||
}
|
||||
|
||||
let b = self.read_u8()?;
|
||||
buf.push(b);
|
||||
|
||||
if predicate(b, &buf) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let result = read();
|
||||
(buf, result)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Unix
|
||||
#[cfg(unix)]
|
||||
impl AsyncStdin {
|
||||
pub fn poll(&mut self, timeout: Duration) -> std::io::Result<bool> {
|
||||
let mut tv = libc::timeval {
|
||||
tv_sec: timeout.as_secs() as libc::time_t,
|
||||
tv_usec: timeout.subsec_micros() as libc::suseconds_t,
|
||||
};
|
||||
|
||||
let result = unsafe {
|
||||
let mut set: libc::fd_set = std::mem::zeroed();
|
||||
libc::FD_ZERO(&mut set);
|
||||
libc::FD_SET(*self.fd, &mut set);
|
||||
libc::select(*self.fd + 1, &mut set, std::ptr::null_mut(), std::ptr::null_mut(), &mut tv)
|
||||
};
|
||||
|
||||
match result {
|
||||
-1 => Err(Error::last_os_error()),
|
||||
0 => Ok(false),
|
||||
_ => Ok(true),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_u8(&mut self) -> std::io::Result<u8> {
|
||||
let mut b = 0;
|
||||
match unsafe { libc::read(*self.fd, &mut b as *mut _ as *mut _, 1) } {
|
||||
-1 => Err(Error::last_os_error()),
|
||||
0 => Err(Error::new(ErrorKind::UnexpectedEof, "unexpected EOF")),
|
||||
_ => Ok(b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Windows
|
||||
#[cfg(windows)]
|
||||
impl AsyncStdin {
|
||||
pub fn poll(&mut self, timeout: Duration) -> std::io::Result<bool> {
|
||||
use windows_sys::Win32::{Foundation::{WAIT_FAILED, WAIT_OBJECT_0}, System::Threading::WaitForSingleObject};
|
||||
|
||||
let millis = timeout.as_millis();
|
||||
match unsafe { WaitForSingleObject(*self.fd, millis as u32) } {
|
||||
WAIT_FAILED => Err(Error::last_os_error()),
|
||||
WAIT_OBJECT_0 => Ok(true),
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_u8(&mut self) -> std::io::Result<u8> {
|
||||
use windows_sys::Win32::Storage::FileSystem::ReadFile;
|
||||
|
||||
let mut buf = 0;
|
||||
let mut bytes = 0;
|
||||
let success = unsafe { ReadFile(*self.fd, &mut buf, 1, &mut bytes, std::ptr::null_mut()) };
|
||||
|
||||
if success == 0 {
|
||||
return Err(Error::last_os_error());
|
||||
} else if bytes == 0 {
|
||||
return Err(Error::new(ErrorKind::UnexpectedEof, "unexpected EOF"));
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Fd
|
||||
struct Fd {
|
||||
#[cfg(unix)]
|
||||
inner: std::os::fd::RawFd,
|
||||
#[cfg(windows)]
|
||||
inner: std::os::windows::io::RawHandle,
|
||||
close: bool,
|
||||
}
|
||||
|
||||
impl Deref for Fd {
|
||||
#[cfg(unix)]
|
||||
type Target = std::os::fd::RawFd;
|
||||
#[cfg(windows)]
|
||||
type Target = std::os::windows::io::RawHandle;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
||||
impl Drop for Fd {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(unix)]
|
||||
if self.close {
|
||||
unsafe { libc::close(self.inner) };
|
||||
}
|
||||
#[cfg(windows)]
|
||||
if self.close {
|
||||
unsafe { windows_sys::Win32::Foundation::CloseHandle(self.inner) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Fd {
|
||||
#[cfg(unix)]
|
||||
fn new() -> std::io::Result<Self> {
|
||||
use std::{fs::OpenOptions, os::fd::IntoRawFd};
|
||||
|
||||
Ok(if unsafe { libc::isatty(libc::STDIN_FILENO) } == 1 {
|
||||
Self { inner: libc::STDIN_FILENO, close: false }
|
||||
} else {
|
||||
Self {
|
||||
inner: OpenOptions::new().read(true).write(true).open("/dev/tty")?.into_raw_fd(),
|
||||
close: true,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn new() -> std::io::Result<Self> {
|
||||
use windows_sys::Win32::{Foundation::{GENERIC_READ, GENERIC_WRITE, INVALID_HANDLE_VALUE}, Storage::FileSystem::{CreateFileW, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING}};
|
||||
|
||||
let name: Vec<u16> = "CONIN$\0".encode_utf16().collect();
|
||||
let result = unsafe {
|
||||
CreateFileW(
|
||||
name.as_ptr(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
std::ptr::null_mut(),
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
if result == INVALID_HANDLE_VALUE {
|
||||
Err(std::io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(Self { inner: result, close: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-boot"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -9,11 +9,11 @@ homepage = "https://yazi-rs.github.io"
|
|||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
[dependencies]
|
||||
yazi-adapter = { path = "../yazi-adapter", version = "25.3.2" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.2" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.2" }
|
||||
yazi-adapter = { path = "../yazi-adapter", version = "25.3.7" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.7" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
clap = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ impl Actions {
|
|||
writeln!(s, " Emulator.detect : {:?}", yazi_adapter::EMULATOR)?;
|
||||
|
||||
writeln!(s, "\nAdapter")?;
|
||||
writeln!(s, " Adapter.matches: {:?}", yazi_adapter::ADAPTOR)?;
|
||||
writeln!(s, " Adapter.matches : {:?}", yazi_adapter::ADAPTOR)?;
|
||||
writeln!(s, " Dimension.available: {:?}", yazi_adapter::Dimension::available())?;
|
||||
|
||||
writeln!(s, "\nDesktop")?;
|
||||
writeln!(s, " XDG_SESSION_TYPE : {:?}", env::var_os("XDG_SESSION_TYPE"))?;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-cli"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -9,11 +9,11 @@ homepage = "https://yazi-rs.github.io"
|
|||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
[dependencies]
|
||||
yazi-boot = { path = "../yazi-boot", version = "25.3.2" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "25.3.2" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.2" }
|
||||
yazi-boot = { path = "../yazi-boot", version = "25.3.7" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "25.3.7" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
@ -26,7 +26,7 @@ toml = { workspace = true }
|
|||
twox-hash = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External build dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-codegen"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -13,5 +13,5 @@ proc-macro = true
|
|||
|
||||
[dependencies]
|
||||
# External dependencies
|
||||
syn = { version = "2.0.99", features = [ "full" ] }
|
||||
syn = { version = "2.0.100", features = [ "full" ] }
|
||||
quote = "1.0.39"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-config"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -9,9 +9,9 @@ homepage = "https://yazi-rs.github.io"
|
|||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
[dependencies]
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.2" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ yazi_macro::mod_pub!(keymap mgr open plugin popup preview tasks theme which);
|
|||
|
||||
yazi_macro::mod_flat!(layout pattern preset priority);
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::{io::{Read, Write}, str::FromStr};
|
||||
|
||||
use yazi_shared::{RoCell, SyncCell};
|
||||
use yazi_shared::{RoCell, SyncCell, tty::TTY};
|
||||
|
||||
pub static KEYMAP: RoCell<keymap::Keymap> = RoCell::new();
|
||||
pub static MGR: RoCell<mgr::Mgr> = RoCell::new();
|
||||
|
|
@ -93,14 +93,16 @@ fn try_init(merge: bool) -> anyhow::Result<()> {
|
|||
}
|
||||
|
||||
fn wait_for_key(e: anyhow::Error) -> anyhow::Result<()> {
|
||||
eprintln!("{e}");
|
||||
let stdout = &mut *TTY.lockout();
|
||||
|
||||
writeln!(stdout, "{e}")?;
|
||||
if let Some(src) = e.source() {
|
||||
eprintln!("\nCaused by:\n{src}");
|
||||
writeln!(stdout, "\nCaused by:\n{src}")?;
|
||||
}
|
||||
|
||||
use crossterm::style::{Attribute, Print, SetAttributes};
|
||||
crossterm::execute!(
|
||||
std::io::stderr(),
|
||||
stdout,
|
||||
SetAttributes(Attribute::Reverse.into()),
|
||||
SetAttributes(Attribute::Bold.into()),
|
||||
Print("Press <Enter> to continue with preset settings..."),
|
||||
|
|
@ -108,6 +110,6 @@ fn wait_for_key(e: anyhow::Error) -> anyhow::Result<()> {
|
|||
Print("\n"),
|
||||
)?;
|
||||
|
||||
std::io::stdin().read_line(&mut String::new())?;
|
||||
TTY.reader().read_exact(&mut [0])?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use serde::Deserialize;
|
||||
use yazi_fs::Cha;
|
||||
use yazi_fs::cha::Cha;
|
||||
|
||||
#[derive(Default, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-core"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -9,17 +9,17 @@ homepage = "https://yazi-rs.github.io"
|
|||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
[dependencies]
|
||||
yazi-adapter = { path = "../yazi-adapter", version = "25.3.2" }
|
||||
yazi-boot = { path = "../yazi-boot", version = "25.3.2" }
|
||||
yazi-codegen = { path = "../yazi-codegen", version = "25.3.2" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.2" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "25.3.2" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-plugin = { path = "../yazi-plugin", version = "25.3.2" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "25.3.2" }
|
||||
yazi-scheduler = { path = "../yazi-scheduler", version = "25.3.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.2" }
|
||||
yazi-adapter = { path = "../yazi-adapter", version = "25.3.7" }
|
||||
yazi-boot = { path = "../yazi-boot", version = "25.3.7" }
|
||||
yazi-codegen = { path = "../yazi-codegen", version = "25.3.7" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.7" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "25.3.7" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" }
|
||||
yazi-scheduler = { path = "../yazi-scheduler", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use std::{borrow::Cow, collections::HashMap, ffi::{OsStr, OsString}, io::{BufWriter, Write, stderr}, path::PathBuf};
|
||||
use std::{borrow::Cow, collections::HashMap, ffi::{OsStr, OsString}, io::{Read, Write}, path::PathBuf};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use scopeguard::defer;
|
||||
use tokio::{fs::{self, OpenOptions}, io::{AsyncReadExt, AsyncWriteExt, stdin}};
|
||||
use tokio::{fs::{self, OpenOptions}, io::AsyncWriteExt};
|
||||
use yazi_config::{OPEN, PREVIEW};
|
||||
use yazi_dds::Pubsub;
|
||||
use yazi_fs::{File, FilesOp, max_common_root, maybe_exists, paths_to_same_file};
|
||||
use yazi_proxy::{AppProxy, HIDER, TasksProxy, WATCHER};
|
||||
use yazi_shared::{terminal_clear, url::Url};
|
||||
use yazi_shared::{terminal_clear, tty::TTY, url::Url};
|
||||
|
||||
use crate::mgr::Mgr;
|
||||
|
||||
|
|
@ -51,10 +51,10 @@ impl Mgr {
|
|||
}
|
||||
|
||||
async fn bulk_rename_do(root: PathBuf, old: Vec<PathBuf>, new: Vec<PathBuf>) -> Result<()> {
|
||||
terminal_clear(&mut stderr())?;
|
||||
terminal_clear(TTY.writer())?;
|
||||
if old.len() != new.len() {
|
||||
eprintln!("Number of old and new differ, press ENTER to exit");
|
||||
stdin().read_exact(&mut [0]).await?;
|
||||
TTY.writer().write_all(b"Number of old and new differ, press ENTER to exit")?;
|
||||
TTY.reader().read_exact(&mut [0])?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
|
@ -65,16 +65,16 @@ impl Mgr {
|
|||
}
|
||||
|
||||
{
|
||||
let mut stderr = BufWriter::new(stderr().lock());
|
||||
for (o, n) in &todo {
|
||||
writeln!(stderr, "{} -> {}", o.display(), n.display())?;
|
||||
let mut w = TTY.lockout();
|
||||
for (old, new) in &todo {
|
||||
writeln!(w, "{} -> {}", old.display(), new.display())?;
|
||||
}
|
||||
write!(stderr, "Continue to rename? (y/N): ")?;
|
||||
stderr.flush()?;
|
||||
write!(w, "Continue to rename? (y/N): ")?;
|
||||
w.flush()?;
|
||||
}
|
||||
|
||||
let mut buf = [0; 10];
|
||||
_ = stdin().read(&mut buf).await?;
|
||||
_ = TTY.reader().read(&mut buf)?;
|
||||
if buf[0] != b'y' && buf[0] != b'Y' {
|
||||
return Ok(());
|
||||
}
|
||||
|
|
@ -108,19 +108,17 @@ impl Mgr {
|
|||
}
|
||||
|
||||
async fn output_failed(failed: Vec<(PathBuf, PathBuf, anyhow::Error)>) -> Result<()> {
|
||||
terminal_clear(&mut stderr())?;
|
||||
let mut stdout = TTY.lockout();
|
||||
terminal_clear(&mut *stdout)?;
|
||||
|
||||
{
|
||||
let mut stderr = BufWriter::new(stderr().lock());
|
||||
writeln!(stderr, "Failed to rename:")?;
|
||||
for (o, n, e) in failed {
|
||||
writeln!(stderr, "{} -> {}: {e}", o.display(), n.display())?;
|
||||
}
|
||||
writeln!(stderr, "\nPress ENTER to exit")?;
|
||||
stderr.flush()?;
|
||||
writeln!(stdout, "Failed to rename:")?;
|
||||
for (old, new, err) in failed {
|
||||
writeln!(stdout, "{} -> {}: {err}", old.display(), new.display())?;
|
||||
}
|
||||
writeln!(stdout, "\nPress ENTER to exit")?;
|
||||
|
||||
stdin().read_exact(&mut [0]).await?;
|
||||
stdout.flush()?;
|
||||
TTY.reader().read_exact(&mut [0])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ use std::path::MAIN_SEPARATOR;
|
|||
use crossterm::{execute, terminal::SetTitle};
|
||||
use yazi_config::MGR;
|
||||
use yazi_fs::CWD;
|
||||
use yazi_shared::event::CmdCow;
|
||||
use yazi_shared::{event::CmdCow, tty::TTY};
|
||||
|
||||
use crate::{mgr::Mgr, tasks::Tasks};
|
||||
|
||||
impl Mgr {
|
||||
pub fn refresh(&mut self, _: CmdCow, tasks: &Tasks) {
|
||||
if CWD.set(self.cwd()) && !MGR.title_format.is_empty() {
|
||||
execute!(std::io::stderr(), SetTitle(self.title())).ok();
|
||||
execute!(TTY.writer(), SetTitle(self.title())).ok();
|
||||
}
|
||||
|
||||
self.active_mut().apply_files_attrs();
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ impl Tabs {
|
|||
if opt.idx > self.cursor {
|
||||
self.set_idx(self.cursor);
|
||||
} else {
|
||||
self.set_idx(self.absolute(1));
|
||||
self.set_idx(usize::min(self.cursor + 1, self.items.len() - 1));
|
||||
}
|
||||
|
||||
render!();
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ impl From<CmdCow> for Opt {
|
|||
impl Tabs {
|
||||
#[yazi_codegen::command]
|
||||
pub fn swap(&mut self, opt: Opt) {
|
||||
let idx = self.absolute(opt.step);
|
||||
let idx = opt.step.saturating_add_unsigned(self.cursor).rem_euclid(self.items.len() as _) as _;
|
||||
if idx == self.cursor {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ impl Tabs {
|
|||
#[yazi_codegen::command]
|
||||
pub fn switch(&mut self, opt: Opt) {
|
||||
let idx = if opt.relative {
|
||||
(self.cursor as isize + opt.step).rem_euclid(self.items.len() as isize) as usize
|
||||
opt.step.saturating_add_unsigned(self.cursor).rem_euclid(self.items.len() as _) as _
|
||||
} else {
|
||||
opt.step as usize
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,14 +29,6 @@ impl Tabs {
|
|||
tabs
|
||||
}
|
||||
|
||||
pub(super) fn absolute(&self, rel: isize) -> usize {
|
||||
if rel > 0 {
|
||||
(self.cursor + rel as usize).min(self.items.len() - 1)
|
||||
} else {
|
||||
self.cursor.saturating_sub(rel.unsigned_abs())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set_idx(&mut self, idx: usize) {
|
||||
// Reset the preview of the last active tab
|
||||
if let Some(active) = self.items.get_mut(self.cursor) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use parking_lot::RwLock;
|
|||
use tokio::{fs, pin, sync::{mpsc::{self, UnboundedReceiver}, watch}};
|
||||
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
||||
use tracing::error;
|
||||
use yazi_fs::{Cha, File, Files, FilesOp, realname_unchecked};
|
||||
use yazi_fs::{File, Files, FilesOp, cha::Cha, realname_unchecked};
|
||||
use yazi_proxy::WATCHER;
|
||||
use yazi_shared::{RoCell, url::Url};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use tokio::pin;
|
|||
use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
||||
use tracing::error;
|
||||
use yazi_config::popup::InputCfg;
|
||||
use yazi_fs::{Cha, FilesOp};
|
||||
use yazi_fs::{FilesOp, cha::Cha};
|
||||
use yazi_plugin::external;
|
||||
use yazi_proxy::{AppProxy, InputProxy, MgrProxy, TabProxy, options::{SearchOpt, SearchOptVia}};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::mem;
|
|||
|
||||
use yazi_config::{LAYOUT, MGR};
|
||||
use yazi_dds::Pubsub;
|
||||
use yazi_fs::{Cha, File, Files, FilesOp, FolderStage, Step};
|
||||
use yazi_fs::{File, Files, FilesOp, FolderStage, Step, cha::Cha};
|
||||
use yazi_proxy::MgrProxy;
|
||||
use yazi_shared::{Id, url::{Url, Urn, UrnBuf}};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use tokio_stream::{StreamExt, wrappers::UnboundedReceiverStream};
|
|||
use tokio_util::sync::CancellationToken;
|
||||
use yazi_adapter::ADAPTOR;
|
||||
use yazi_config::PLUGIN;
|
||||
use yazi_fs::{Cha, File, Files, FilesOp};
|
||||
use yazi_fs::{File, Files, FilesOp, cha::Cha};
|
||||
use yazi_macro::render;
|
||||
use yazi_plugin::{external::Highlighter, isolate, utils::PreviewLock};
|
||||
use yazi_shared::{MIME_DIR, url::Url};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use std::{io::{BufWriter, LineWriter, Write, stderr}, mem};
|
||||
use std::io::Write;
|
||||
|
||||
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
|
||||
use crossterm::{execute, terminal::{disable_raw_mode, enable_raw_mode}};
|
||||
use scopeguard::defer;
|
||||
use tokio::{io::{AsyncReadExt, stdin}, select, sync::mpsc, time};
|
||||
use yazi_proxy::{AppProxy, HIDER};
|
||||
use yazi_shared::{event::CmdCow, terminal_clear};
|
||||
use yazi_shared::{event::CmdCow, terminal_clear, tty::TTY};
|
||||
|
||||
use crate::tasks::Tasks;
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ impl Tasks {
|
|||
let _permit = HIDER.acquire().await.unwrap();
|
||||
let (tx, mut rx) = mpsc::unbounded_channel();
|
||||
|
||||
let mut buffered = {
|
||||
let buffered = {
|
||||
let mut ongoing = ongoing.lock();
|
||||
let Some(task) = ongoing.get_mut(id) else { return };
|
||||
|
||||
|
|
@ -30,24 +30,22 @@ impl Tasks {
|
|||
defer!(AppProxy::resume());
|
||||
AppProxy::stop().await;
|
||||
|
||||
terminal_clear(&mut stderr()).ok();
|
||||
BufWriter::new(stderr().lock()).write_all(mem::take(&mut buffered).as_bytes()).ok();
|
||||
terminal_clear(TTY.writer()).ok();
|
||||
TTY.writer().write_all(buffered.as_bytes()).ok();
|
||||
|
||||
defer! { disable_raw_mode().ok(); }
|
||||
enable_raw_mode().ok();
|
||||
|
||||
let mut stdin = stdin();
|
||||
let mut stdin = stdin(); // TODO: stdin
|
||||
let mut answer = 0;
|
||||
loop {
|
||||
select! {
|
||||
Some(line) = rx.recv() => {
|
||||
let mut stderr = LineWriter::new(stderr().lock());
|
||||
stderr.write_all(line.as_bytes()).ok();
|
||||
stderr.write_all(b"\r\n").ok();
|
||||
execute!(TTY.writer(), crossterm::style::Print(line), crossterm::style::Print("\r\n")).ok();
|
||||
}
|
||||
_ = time::sleep(time::Duration::from_millis(500)) => {
|
||||
if ongoing.lock().get(id).is_none() {
|
||||
stderr().write_all(b"Task finished, press `q` to quit\r\n").ok();
|
||||
execute!(TTY.writer(), crossterm::style::Print("Task finished, press `q` to quit\r\n")).ok();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-dds"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -13,10 +13,10 @@ default = [ "vendored-lua" ]
|
|||
vendored-lua = [ "mlua/vendored" ]
|
||||
|
||||
[dependencies]
|
||||
yazi-boot = { path = "../yazi-boot", version = "25.3.2" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.2" }
|
||||
yazi-boot = { path = "../yazi-boot", version = "25.3.7" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ impl Sendable {
|
|||
Value::UserData(ud) => {
|
||||
if let Ok(t) = ud.take::<yazi_shared::url::Url>() {
|
||||
Data::Url(t)
|
||||
} else if let Ok(t) = ud.take::<yazi_fs::FilesOp>() {
|
||||
Data::Any(Box::new(t))
|
||||
} else if let Ok(t) = ud.take::<super::body::BodyYankIter>() {
|
||||
Data::Any(Box::new(t))
|
||||
} else {
|
||||
|
|
@ -72,13 +74,13 @@ impl Sendable {
|
|||
Value::Table(tbl)
|
||||
}
|
||||
Data::Url(u) => Value::UserData(lua.create_any_userdata(u)?),
|
||||
Data::Any(a) => {
|
||||
if let Ok(t) = a.downcast::<super::body::BodyYankIter>() {
|
||||
Value::UserData(lua.create_userdata(*t)?)
|
||||
} else {
|
||||
Err("unsupported userdata included".into_lua_err())?
|
||||
}
|
||||
}
|
||||
Data::Any(a) => Value::UserData(if a.is::<yazi_fs::FilesOp>() {
|
||||
lua.create_any_userdata(*a.downcast::<yazi_fs::FilesOp>().unwrap())?
|
||||
} else if a.is::<super::body::BodyYankIter>() {
|
||||
lua.create_userdata(*a.downcast::<super::body::BodyYankIter>().unwrap())?
|
||||
} else {
|
||||
Err("unsupported userdata included".into_lua_err())?
|
||||
}),
|
||||
data => Self::data_to_value_ref(lua, &data)?,
|
||||
})
|
||||
}
|
||||
|
|
@ -107,13 +109,13 @@ impl Sendable {
|
|||
}
|
||||
Data::Url(u) => Value::UserData(lua.create_any_userdata(u.clone())?),
|
||||
Data::Bytes(b) => Value::String(lua.create_string(b)?),
|
||||
Data::Any(a) => {
|
||||
if let Some(t) = a.downcast_ref::<super::body::BodyYankIter>() {
|
||||
Value::UserData(lua.create_userdata(t.clone())?)
|
||||
} else {
|
||||
Err("unsupported userdata included".into_lua_err())?
|
||||
}
|
||||
}
|
||||
Data::Any(a) => Value::UserData(if let Some(t) = a.downcast_ref::<yazi_fs::FilesOp>() {
|
||||
lua.create_any_userdata(t.clone())?
|
||||
} else if let Some(t) = a.downcast_ref::<super::body::BodyYankIter>() {
|
||||
lua.create_userdata(t.clone())?
|
||||
} else {
|
||||
Err("unsupported userdata included".into_lua_err())?
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-ffi"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -9,7 +9,7 @@ homepage = "https://yazi-rs.github.io"
|
|||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
[dependencies]
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-fm"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -13,17 +13,17 @@ default = [ "vendored-lua" ]
|
|||
vendored-lua = [ "mlua/vendored" ]
|
||||
|
||||
[dependencies]
|
||||
yazi-adapter = { path = "../yazi-adapter", version = "25.3.2" }
|
||||
yazi-boot = { path = "../yazi-boot", version = "25.3.2" }
|
||||
yazi-codegen = { path = "../yazi-codegen", version = "25.3.2" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.2" }
|
||||
yazi-core = { path = "../yazi-core", version = "25.3.2" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "25.3.2" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-plugin = { path = "../yazi-plugin", version = "25.3.2" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "25.3.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.2" }
|
||||
yazi-adapter = { path = "../yazi-adapter", version = "25.3.7" }
|
||||
yazi-boot = { path = "../yazi-boot", version = "25.3.7" }
|
||||
yazi-codegen = { path = "../yazi-codegen", version = "25.3.7" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.7" }
|
||||
yazi-core = { path = "../yazi-core", version = "25.3.7" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "25.3.7" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
use std::{io::{BufWriter, stderr}, sync::atomic::Ordering};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use crossterm::{execute, queue, terminal::{BeginSynchronizedUpdate, EndSynchronizedUpdate}};
|
||||
use ratatui::{CompletedFrame, backend::{Backend, CrosstermBackend}, buffer::Buffer};
|
||||
use scopeguard::defer;
|
||||
use yazi_plugin::elements::COLLISION;
|
||||
use yazi_shared::event::NEED_RENDER;
|
||||
use yazi_shared::{event::NEED_RENDER, tty::TTY};
|
||||
|
||||
use crate::{app::App, lives::Lives, root::Root};
|
||||
|
||||
|
|
@ -13,8 +13,8 @@ impl App {
|
|||
NEED_RENDER.store(false, Ordering::Relaxed);
|
||||
let Some(term) = &mut self.term else { return };
|
||||
|
||||
queue!(stderr(), BeginSynchronizedUpdate).ok();
|
||||
defer! { execute!(stderr(), EndSynchronizedUpdate).ok(); }
|
||||
queue!(TTY.writer(), BeginSynchronizedUpdate).ok();
|
||||
defer! { execute!(TTY.writer(), EndSynchronizedUpdate).ok(); }
|
||||
|
||||
let collision = COLLISION.swap(false, Ordering::Relaxed);
|
||||
let frame = term
|
||||
|
|
@ -79,7 +79,9 @@ impl App {
|
|||
}
|
||||
|
||||
let patches = frame.buffer.diff(&new);
|
||||
let mut backend = CrosstermBackend::new(BufWriter::new(stderr().lock()));
|
||||
let stdout = &mut *TTY.lockout();
|
||||
|
||||
let mut backend = CrosstermBackend::new(stdout);
|
||||
backend.draw(patches.into_iter()).ok();
|
||||
if let Some(pos) = cursor {
|
||||
backend.show_cursor().ok();
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
use std::{io::{self, BufWriter, Stderr, stderr}, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, AtomicU8, Ordering}};
|
||||
use std::{io, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, AtomicU8, Ordering}};
|
||||
|
||||
use anyhow::Result;
|
||||
use crossterm::{event::{DisableBracketedPaste, EnableBracketedPaste, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{LeaveAlternateScreen, SetTitle, disable_raw_mode, enable_raw_mode}};
|
||||
use crossterm::{Command, event::{DisableBracketedPaste, EnableBracketedPaste, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{LeaveAlternateScreen, SetTitle, disable_raw_mode, enable_raw_mode}};
|
||||
use cursor::RestoreCursor;
|
||||
use ratatui::{CompletedFrame, Frame, Terminal, backend::CrosstermBackend, buffer::Buffer, layout::Rect};
|
||||
use yazi_adapter::{Emulator, Mux};
|
||||
use yazi_config::{INPUT, MGR};
|
||||
use yazi_shared::SyncCell;
|
||||
use yazi_shared::{SyncCell, tty::{TTY, TtyWriter}};
|
||||
|
||||
static CSI_U: AtomicBool = AtomicBool::new(false);
|
||||
static BLINK: AtomicBool = AtomicBool::new(false);
|
||||
static SHAPE: AtomicU8 = AtomicU8::new(0);
|
||||
|
||||
pub(super) struct Term {
|
||||
inner: Terminal<CrosstermBackend<BufWriter<Stderr>>>,
|
||||
inner: Terminal<CrosstermBackend<TtyWriter<'static>>>,
|
||||
last_area: Rect,
|
||||
last_buffer: Buffer,
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ impl Term {
|
|||
pub(super) fn start() -> Result<Self> {
|
||||
static SKIP: SyncCell<bool> = SyncCell::new(false);
|
||||
let mut term = Self {
|
||||
inner: Terminal::new(CrosstermBackend::new(BufWriter::new(stderr())))?,
|
||||
inner: Terminal::new(CrosstermBackend::new(TTY.writer()))?,
|
||||
last_area: Default::default(),
|
||||
last_buffer: Default::default(),
|
||||
};
|
||||
|
|
@ -33,7 +33,7 @@ impl Term {
|
|||
}
|
||||
|
||||
execute!(
|
||||
BufWriter::new(stderr()),
|
||||
TTY.writer(),
|
||||
screen::SetScreen(true),
|
||||
Print(Mux::csi("\x1bP$q q\x1b\\")), // Request cursor shape (DECRQSS query for DECSCUSR)
|
||||
Print(Mux::csi("\x1b[?12$p")), // Request cursor blink status (DECSET)
|
||||
|
|
@ -59,13 +59,11 @@ impl Term {
|
|||
);
|
||||
|
||||
if CSI_U.load(Ordering::Relaxed) {
|
||||
queue!(
|
||||
stderr(),
|
||||
PushKeyboardEnhancementFlags(
|
||||
KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
| KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
|
||||
)
|
||||
)?;
|
||||
PushKeyboardEnhancementFlags(
|
||||
KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
| KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS,
|
||||
)
|
||||
.write_ansi(&mut TTY.writer())?;
|
||||
}
|
||||
|
||||
term.hide_cursor()?;
|
||||
|
|
@ -76,11 +74,11 @@ impl Term {
|
|||
|
||||
fn stop(&mut self) -> Result<()> {
|
||||
if CSI_U.swap(false, Ordering::Relaxed) {
|
||||
execute!(stderr(), PopKeyboardEnhancementFlags)?;
|
||||
PopKeyboardEnhancementFlags.write_ansi(&mut TTY.writer())?;
|
||||
}
|
||||
|
||||
execute!(
|
||||
stderr(),
|
||||
TTY.writer(),
|
||||
mouse::SetMouse(false),
|
||||
RestoreCursor,
|
||||
DisableBracketedPaste,
|
||||
|
|
@ -93,15 +91,15 @@ impl Term {
|
|||
|
||||
pub(super) fn goodbye(f: impl FnOnce() -> bool) -> ! {
|
||||
if CSI_U.swap(false, Ordering::Relaxed) {
|
||||
execute!(stderr(), PopKeyboardEnhancementFlags).ok();
|
||||
PopKeyboardEnhancementFlags.write_ansi(&mut TTY.writer()).ok();
|
||||
}
|
||||
|
||||
if !MGR.title_format.is_empty() {
|
||||
execute!(stderr(), SetTitle("")).ok();
|
||||
execute!(TTY.writer(), SetTitle("")).ok();
|
||||
}
|
||||
|
||||
execute!(
|
||||
stderr(),
|
||||
TTY.writer(),
|
||||
mouse::SetMouse(false),
|
||||
RestoreCursor,
|
||||
SetTitle(""),
|
||||
|
|
@ -148,9 +146,9 @@ impl Term {
|
|||
pub(super) fn set_cursor_block() -> Result<()> {
|
||||
use crossterm::cursor::SetCursorStyle;
|
||||
Ok(if INPUT.cursor_blink {
|
||||
queue!(stderr(), SetCursorStyle::BlinkingBlock)?
|
||||
queue!(TTY.writer(), SetCursorStyle::BlinkingBlock)?
|
||||
} else {
|
||||
queue!(stderr(), SetCursorStyle::SteadyBlock)?
|
||||
queue!(TTY.writer(), SetCursorStyle::SteadyBlock)?
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -158,9 +156,9 @@ impl Term {
|
|||
pub(super) fn set_cursor_bar() -> Result<()> {
|
||||
use crossterm::cursor::SetCursorStyle;
|
||||
Ok(if INPUT.cursor_blink {
|
||||
queue!(stderr(), SetCursorStyle::BlinkingBar)?
|
||||
queue!(TTY.writer(), SetCursorStyle::BlinkingBar)?
|
||||
} else {
|
||||
queue!(stderr(), SetCursorStyle::SteadyBar)?
|
||||
queue!(TTY.writer(), SetCursorStyle::SteadyBar)?
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -168,9 +166,9 @@ impl Term {
|
|||
pub(super) fn set_cursor_underscore() -> Result<()> {
|
||||
use crossterm::cursor::SetCursorStyle;
|
||||
Ok(if INPUT.cursor_blink {
|
||||
queue!(stderr(), SetCursorStyle::BlinkingUnderScore)?
|
||||
queue!(TTY.writer(), SetCursorStyle::BlinkingUnderScore)?
|
||||
} else {
|
||||
queue!(stderr(), SetCursorStyle::SteadyUnderScore)?
|
||||
queue!(TTY.writer(), SetCursorStyle::SteadyUnderScore)?
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -180,7 +178,7 @@ impl Drop for Term {
|
|||
}
|
||||
|
||||
impl Deref for Term {
|
||||
type Target = Terminal<CrosstermBackend<BufWriter<Stderr>>>;
|
||||
type Target = Terminal<CrosstermBackend<TtyWriter<'static>>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.inner }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-fs"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -9,9 +9,9 @@ homepage = "https://yazi-rs.github.io"
|
|||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
[dependencies]
|
||||
yazi-ffi = { path = "../yazi-ffi", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.2" }
|
||||
yazi-ffi = { path = "../yazi-ffi", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,22 +1,8 @@
|
|||
use std::{fs::{FileType, Metadata}, path::Path, time::SystemTime};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use yazi_macro::{unix_either, win_either};
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ChaKind: u8 {
|
||||
const DIR = 0b00000001;
|
||||
|
||||
const HIDDEN = 0b00000010;
|
||||
const LINK = 0b00000100;
|
||||
const ORPHAN = 0b00001000;
|
||||
|
||||
const DUMMY = 0b00010000;
|
||||
#[cfg(windows)]
|
||||
const SYSTEM = 0b00100000;
|
||||
}
|
||||
}
|
||||
use super::ChaKind;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct Cha {
|
||||
|
|
@ -39,8 +25,8 @@ pub struct Cha {
|
|||
pub nlink: libc::nlink_t,
|
||||
}
|
||||
|
||||
impl From<Metadata> for Cha {
|
||||
fn from(m: Metadata) -> Self {
|
||||
impl From<&Metadata> for Cha {
|
||||
fn from(m: &Metadata) -> Self {
|
||||
let mut kind = ChaKind::empty();
|
||||
if m.is_dir() {
|
||||
kind |= ChaKind::DIR;
|
||||
|
|
@ -89,6 +75,10 @@ impl From<Metadata> for Cha {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Metadata> for Cha {
|
||||
fn from(m: Metadata) -> Self { Self::from(&m) }
|
||||
}
|
||||
|
||||
impl From<FileType> for Cha {
|
||||
fn from(t: FileType) -> Self {
|
||||
let mut kind = ChaKind::DUMMY;
|
||||
|
|
@ -134,9 +124,8 @@ impl From<FileType> for Cha {
|
|||
}
|
||||
|
||||
impl Cha {
|
||||
#[inline]
|
||||
pub async fn new(path: &Path, mut meta: Metadata) -> Self {
|
||||
let mut attached = ChaKind::empty();
|
||||
let mut attached = ChaKind::hidden(path, &meta);
|
||||
|
||||
if meta.is_symlink() {
|
||||
attached |= ChaKind::LINK;
|
||||
|
|
@ -146,35 +135,16 @@ impl Cha {
|
|||
attached |= ChaKind::ORPHAN;
|
||||
}
|
||||
|
||||
let mut cha = Self::new_nofollow(path, meta);
|
||||
cha.kind |= attached;
|
||||
cha
|
||||
let mut me = Self::from(meta);
|
||||
me.kind |= attached;
|
||||
me
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn new_nofollow(_path: &Path, meta: Metadata) -> Self {
|
||||
let mut attached = ChaKind::empty();
|
||||
|
||||
#[cfg(unix)]
|
||||
if yazi_shared::url::Urn::new(_path).is_hidden() {
|
||||
attached |= ChaKind::HIDDEN;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::fs::MetadataExt;
|
||||
|
||||
use windows_sys::Win32::Storage::FileSystem::{FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_SYSTEM};
|
||||
if meta.file_attributes() & FILE_ATTRIBUTE_HIDDEN != 0 {
|
||||
attached |= ChaKind::HIDDEN;
|
||||
}
|
||||
if meta.file_attributes() & FILE_ATTRIBUTE_SYSTEM != 0 {
|
||||
attached |= ChaKind::SYSTEM;
|
||||
}
|
||||
}
|
||||
|
||||
let mut cha = Self::from(meta);
|
||||
cha.kind |= attached;
|
||||
cha
|
||||
let mut me = Self::from(&meta);
|
||||
me.kind |= ChaKind::hidden(_path, &meta);
|
||||
me
|
||||
}
|
||||
|
||||
#[inline]
|
||||
44
yazi-fs/src/cha/kind.rs
Normal file
44
yazi-fs/src/cha/kind.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use std::{fs::Metadata, path::Path};
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ChaKind: u8 {
|
||||
const DIR = 0b00000001;
|
||||
|
||||
const HIDDEN = 0b00000010;
|
||||
const LINK = 0b00000100;
|
||||
const ORPHAN = 0b00001000;
|
||||
|
||||
const DUMMY = 0b00010000;
|
||||
#[cfg(windows)]
|
||||
const SYSTEM = 0b00100000;
|
||||
}
|
||||
}
|
||||
|
||||
impl ChaKind {
|
||||
#[inline]
|
||||
pub(super) fn hidden(_path: &Path, _meta: &Metadata) -> Self {
|
||||
let mut me = Self::empty();
|
||||
|
||||
#[cfg(unix)]
|
||||
if yazi_shared::url::Urn::new(_path).is_hidden() {
|
||||
me |= Self::HIDDEN;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::fs::MetadataExt;
|
||||
|
||||
use windows_sys::Win32::Storage::FileSystem::{FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_SYSTEM};
|
||||
if _meta.file_attributes() & FILE_ATTRIBUTE_HIDDEN != 0 {
|
||||
me |= Self::HIDDEN;
|
||||
}
|
||||
if _meta.file_attributes() & FILE_ATTRIBUTE_SYSTEM != 0 {
|
||||
me |= Self::SYSTEM;
|
||||
}
|
||||
}
|
||||
|
||||
me
|
||||
}
|
||||
}
|
||||
1
yazi-fs/src/cha/mod.rs
Normal file
1
yazi-fs/src/cha/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_flat!(cha kind);
|
||||
|
|
@ -4,7 +4,7 @@ use anyhow::Result;
|
|||
use tokio::fs;
|
||||
use yazi_shared::{SyncCell, theme::IconCache, url::{Url, Urn, UrnBuf}};
|
||||
|
||||
use crate::Cha;
|
||||
use crate::cha::Cha;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct File {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use tokio::{fs::{self, DirEntry}, select, sync::mpsc::{self, UnboundedReceiver}}
|
|||
use yazi_shared::{Id, url::{Url, Urn, UrnBuf}};
|
||||
|
||||
use super::{FilesSorter, Filter};
|
||||
use crate::{Cha, FILES_TICKET, File, FilesOp, SortBy, maybe_exists, mounts::PARTITIONS};
|
||||
use crate::{FILES_TICKET, File, FilesOp, SortBy, cha::Cha, maybe_exists, mounts::PARTITIONS};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Files {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{borrow::Cow, collections::{HashMap, HashSet, VecDeque}, ffi::{OsStr, O
|
|||
use anyhow::{Result, bail};
|
||||
use tokio::{fs, io::{self, AsyncWriteExt}, select, sync::{mpsc, oneshot}, time};
|
||||
|
||||
use super::Cha;
|
||||
use crate::cha::Cha;
|
||||
|
||||
#[inline]
|
||||
pub async fn must_exists(p: impl AsRef<Path>) -> bool { fs::symlink_metadata(p).await.is_ok() }
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
#![allow(clippy::if_same_then_else, clippy::option_map_unit_fn)]
|
||||
|
||||
yazi_macro::mod_pub!(mounts);
|
||||
yazi_macro::mod_pub!(cha mounts);
|
||||
|
||||
yazi_macro::mod_flat!(cha cwd file files filter fns op path sorter sorting stage step xdg);
|
||||
yazi_macro::mod_flat!(cwd file files filter fns op path sorter sorting stage step xdg);
|
||||
|
||||
pub fn init() {
|
||||
CWD.init(<_>::default());
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use parking_lot::RwLock;
|
|||
use yazi_shared::RoCell;
|
||||
|
||||
use super::Partition;
|
||||
use crate::Cha;
|
||||
use crate::cha::Cha;
|
||||
|
||||
pub(super) type Locked = Arc<RwLock<Partitions>>;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ use std::collections::{HashMap, HashSet};
|
|||
|
||||
use yazi_shared::{Id, Ids, event::Cmd, url::{Url, UrnBuf}};
|
||||
|
||||
use super::{Cha, File};
|
||||
use super::File;
|
||||
use crate::cha::Cha;
|
||||
|
||||
pub static FILES_TICKET: Ids = Ids::new();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-macro"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-plugin"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -13,14 +13,14 @@ default = [ "vendored-lua" ]
|
|||
vendored-lua = [ "mlua/vendored" ]
|
||||
|
||||
[dependencies]
|
||||
yazi-adapter = { path = "../yazi-adapter", version = "25.3.2" }
|
||||
yazi-boot = { path = "../yazi-boot", version = "25.3.2" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.2" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "25.3.2" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "25.3.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.2" }
|
||||
yazi-adapter = { path = "../yazi-adapter", version = "25.3.7" }
|
||||
yazi-boot = { path = "../yazi-boot", version = "25.3.7" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.7" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "25.3.7" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
ansi-to-tui = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -48,13 +48,11 @@ end
|
|||
|
||||
function Status:size()
|
||||
local h = self._current.hovered
|
||||
if not h then
|
||||
return ""
|
||||
end
|
||||
local size = h and (h:size() or h.cha.len) or 0
|
||||
|
||||
local style = self:style()
|
||||
return ui.Line {
|
||||
ui.Span(" " .. ya.readable_size(h:size() or h.cha.len) .. " "):style(style.alt),
|
||||
ui.Span(" " .. ya.readable_size(size) .. " "):style(style.alt),
|
||||
ui.Span(th.status.sep_left.close):fg(style.alt.bg),
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ function M:try_with(from, pwd, to)
|
|||
|
||||
local output, err = child:wait_with_output()
|
||||
if output and output.status.code == 2 and archive.is_encrypted(output.stderr) then
|
||||
fs.remove("dir_clean", tmp)
|
||||
fs.remove("dir_all", tmp)
|
||||
return true -- Need to retry
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
use std::{ops::Deref, time::{Duration, SystemTime, UNIX_EPOCH}};
|
||||
|
||||
use mlua::{ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataFields, UserDataMethods};
|
||||
use yazi_fs::ChaKind;
|
||||
use yazi_fs::cha::ChaKind;
|
||||
|
||||
#[derive(Clone, Copy, FromLua)]
|
||||
pub struct Cha(yazi_fs::Cha);
|
||||
pub struct Cha(yazi_fs::cha::Cha);
|
||||
|
||||
impl Deref for Cha {
|
||||
type Target = yazi_fs::Cha;
|
||||
type Target = yazi_fs::cha::Cha;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl<T: Into<yazi_fs::Cha>> From<T> for Cha {
|
||||
impl<T: Into<yazi_fs::cha::Cha>> From<T> for Cha {
|
||||
fn from(cha: T) -> Self { Self(cha.into()) }
|
||||
}
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ impl Cha {
|
|||
let kind =
|
||||
ChaKind::from_bits(t.raw_get("kind")?).ok_or_else(|| "Invalid kind".into_lua_err())?;
|
||||
|
||||
Self::from(yazi_fs::Cha {
|
||||
Self::from(yazi_fs::cha::Cha {
|
||||
kind,
|
||||
len: t.raw_get("len").unwrap_or_default(),
|
||||
atime: parse_time(t.raw_get("atime").ok())?,
|
||||
|
|
|
|||
|
|
@ -55,13 +55,14 @@ impl Clipboard {
|
|||
|
||||
#[cfg(unix)]
|
||||
pub async fn set(&self, s: impl AsRef<std::ffi::OsStr>) {
|
||||
use std::{io::{BufWriter, stderr}, process::Stdio};
|
||||
use std::process::Stdio;
|
||||
|
||||
use crossterm::execute;
|
||||
use tokio::{io::AsyncWriteExt, process::Command};
|
||||
use yazi_shared::tty::TTY;
|
||||
|
||||
s.as_ref().clone_into(&mut self.content.lock());
|
||||
execute!(BufWriter::new(stderr()), osc52::SetClipboard::new(s.as_ref())).ok();
|
||||
execute!(TTY.writer(), osc52::SetClipboard::new(s.as_ref())).ok();
|
||||
|
||||
let all = [
|
||||
("pbcopy", &[][..]),
|
||||
|
|
|
|||
|
|
@ -31,8 +31,11 @@ impl<'a> Runtime<'a> {
|
|||
fn args(lua: &Lua) -> mlua::Result<Value> {
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use mlua::{AnyUserData, IntoLua, Lua, Table, UserDataRef};
|
||||
use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, Table, UserDataRef, Value};
|
||||
|
||||
use crate::{bindings::Cha, impl_file_fields, impl_file_methods};
|
||||
|
||||
|
|
@ -29,8 +29,17 @@ impl File {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromLua for File {
|
||||
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
|
||||
match value {
|
||||
Value::UserData(ud) => ud.take().map(Self),
|
||||
_ => Err("Expected a File".into_lua_err()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for File {
|
||||
fn into_lua(self, lua: &Lua) -> mlua::Result<mlua::Value> {
|
||||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||
lua.create_any_userdata(self.0)?.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@ use crate::{Composer, Error, bindings::Cha, file::File, url::{Url, UrlRef}};
|
|||
pub fn compose(lua: &Lua) -> mlua::Result<Value> {
|
||||
Composer::make(lua, 10, |lua, key| {
|
||||
match key {
|
||||
b"op" => op(lua)?,
|
||||
b"cwd" => cwd(lua)?,
|
||||
b"cha" => cha(lua)?,
|
||||
b"write" => write(lua)?,
|
||||
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),
|
||||
|
|
@ -22,6 +24,14 @@ pub fn compose(lua: &Lua) -> mlua::Result<Value> {
|
|||
})
|
||||
}
|
||||
|
||||
fn op(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|lua, (name, t): (mlua::String, Table)| match name.as_bytes().as_ref() {
|
||||
b"part" => super::FilesOp::part(lua, t),
|
||||
b"done" => super::FilesOp::done(lua, t),
|
||||
_ => Err("Unknown operation".into_lua_err())?,
|
||||
})
|
||||
}
|
||||
|
||||
fn cwd(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|lua, ()| match std::env::current_dir() {
|
||||
Ok(p) => (Url::from(p), Value::Nil).into_lua_multi(lua),
|
||||
|
|
@ -141,6 +151,17 @@ fn read_dir(lua: &Lua) -> mlua::Result<Function> {
|
|||
})
|
||||
}
|
||||
|
||||
fn expand_url(lua: &Lua) -> mlua::Result<Function> {
|
||||
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::<yazi_shared::url::Url>()?),
|
||||
_ => Err("must be a string or a Url".into_lua_err())?,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
fn unique_name(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_async_function(|lua, url: UrlRef| async move {
|
||||
match yazi_fs::unique_name(url.clone(), async { false }).await {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
#![allow(clippy::module_inception)]
|
||||
|
||||
yazi_macro::mod_flat!(fs);
|
||||
yazi_macro::mod_flat!(fs op);
|
||||
|
|
|
|||
33
yazi-plugin/src/fs/op.rs
Normal file
33
yazi-plugin/src/fs/op.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
use mlua::{IntoLua, Lua, Table};
|
||||
|
||||
use crate::{Id, bindings::Cha, file::File, url::Url};
|
||||
|
||||
pub(super) struct FilesOp(yazi_fs::FilesOp);
|
||||
|
||||
impl FilesOp {
|
||||
pub(super) fn part(_: &Lua, t: Table) -> mlua::Result<Self> {
|
||||
let id: Id = t.raw_get("id")?;
|
||||
let url: Url = t.raw_get("url")?;
|
||||
let files: Table = t.raw_get("files")?;
|
||||
|
||||
Ok(Self(yazi_fs::FilesOp::Part(
|
||||
url.0,
|
||||
files.sequence_values::<File>().map(|f| f.map(|f| f.0)).collect::<mlua::Result<Vec<_>>>()?,
|
||||
*id,
|
||||
)))
|
||||
}
|
||||
|
||||
pub(super) fn done(_: &Lua, t: Table) -> mlua::Result<Self> {
|
||||
let id: Id = t.raw_get("id")?;
|
||||
let cha: Cha = t.raw_get("cha")?;
|
||||
let url: Url = t.raw_get("url")?;
|
||||
|
||||
Ok(Self(yazi_fs::FilesOp::Done(url.0, *cha, *id)))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for FilesOp {
|
||||
fn into_lua(self, lua: &Lua) -> mlua::Result<mlua::Value> {
|
||||
lua.create_any_userdata(self.0)?.into_lua(lua)
|
||||
}
|
||||
}
|
||||
15
yazi-plugin/src/id.rs
Normal file
15
yazi-plugin/src/id.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use mlua::{FromLua, UserData};
|
||||
|
||||
#[derive(Clone, Copy, FromLua)]
|
||||
pub struct Id(pub yazi_shared::Id);
|
||||
|
||||
impl Deref for Id {
|
||||
type Target = yazi_shared::Id;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
|
||||
impl UserData for Id {}
|
||||
|
|
@ -4,7 +4,7 @@ mod macros;
|
|||
|
||||
yazi_macro::mod_pub!(bindings config elements external file fs isolate loader process pubsub url utils);
|
||||
|
||||
yazi_macro::mod_flat!(clipboard composer error lua runtime);
|
||||
yazi_macro::mod_flat!(clipboard composer error id lua runtime);
|
||||
|
||||
pub fn init() -> anyhow::Result<()> {
|
||||
CLIPBOARD.with(<_>::default);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use mlua::{ExternalError, IntoLua, Lua, MetaMethod, UserDataFields, UserDataMethods, UserDataRef, Value};
|
||||
use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, MetaMethod, UserDataFields, UserDataMethods, UserDataRef, Value};
|
||||
|
||||
pub type UrlRef = UserDataRef<yazi_shared::url::Url>;
|
||||
|
||||
|
|
@ -54,6 +54,10 @@ impl Url {
|
|||
Ok(path.ok().map(Self::from))
|
||||
});
|
||||
|
||||
reg.add_function_mut("into_search", |_, (ud, frag): (AnyUserData, mlua::String)| {
|
||||
Ok(Self(ud.take::<yazi_shared::url::Url>()?.into_search(&frag.to_str()?)))
|
||||
});
|
||||
|
||||
reg.add_meta_method(MetaMethod::Eq, |_, me, other: UrlRef| Ok(me == &*other));
|
||||
reg.add_meta_method(MetaMethod::ToString, |lua, me, ()| {
|
||||
lua.create_string(me.as_os_str().as_encoded_bytes())
|
||||
|
|
@ -67,7 +71,13 @@ impl Url {
|
|||
pub fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
lua.globals().raw_set(
|
||||
"Url",
|
||||
lua.create_function(|_, url: mlua::String| Ok(Self::from(url.to_str()?.as_ref())))?,
|
||||
lua.create_function(|_, value: Value| {
|
||||
Ok(match value {
|
||||
Value::String(s) => Self::from(s.to_str()?.as_ref()),
|
||||
Value::UserData(ud) => Self(ud.borrow::<yazi_shared::url::Url>()?.clone()),
|
||||
_ => Err("Expected a string or a Url".into_lua_err())?,
|
||||
})
|
||||
})?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -76,6 +86,15 @@ impl<T: Into<yazi_shared::url::Url>> From<T> for Url {
|
|||
fn from(value: T) -> Self { Self(value.into()) }
|
||||
}
|
||||
|
||||
impl FromLua for Url {
|
||||
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
|
||||
match value {
|
||||
Value::UserData(ud) => ud.take().map(Self),
|
||||
_ => Err("Expected a Url".into_lua_err()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLua for Url {
|
||||
fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
|
||||
lua.create_any_userdata(self.0)?.into_lua(lua)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,18 @@ use mlua::{AnyUserData, ExternalError, Function, Lua};
|
|||
use yazi_proxy::{AppProxy, HIDER};
|
||||
|
||||
use super::Utils;
|
||||
use crate::bindings::{Permit, PermitRef};
|
||||
use crate::{Id, bindings::{Permit, PermitRef}};
|
||||
|
||||
impl Utils {
|
||||
pub(super) fn id(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_function(|_, type_: mlua::String| {
|
||||
Ok(Id(match type_.as_bytes().as_ref() {
|
||||
b"ft" => yazi_fs::FILES_TICKET.next(),
|
||||
_ => Err("Invalid id type".into_lua_err())?,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn hide(lua: &Lua) -> mlua::Result<Function> {
|
||||
lua.create_async_function(|lua, ()| async move {
|
||||
if lua.named_registry_value::<PermitRef<fn()>>("HIDE_PERMIT").is_ok_and(|h| h.is_some()) {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::{elements::{Area, Rect, Renderable, Text, WRAP, WRAP_NO}, external::H
|
|||
#[derive(Debug, Default)]
|
||||
pub struct PreviewLock {
|
||||
pub url: yazi_shared::url::Url,
|
||||
pub cha: yazi_fs::Cha,
|
||||
pub cha: yazi_fs::cha::Cha,
|
||||
pub mime: String,
|
||||
|
||||
pub skip: usize,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::{elements::Renderable, file::FileRef};
|
|||
|
||||
pub struct SpotLock {
|
||||
pub url: yazi_shared::url::Url,
|
||||
pub cha: yazi_fs::Cha,
|
||||
pub cha: yazi_fs::cha::Cha,
|
||||
pub mime: String,
|
||||
|
||||
pub skip: usize,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ pub fn compose(lua: &Lua, isolate: bool) -> mlua::Result<Value> {
|
|||
Composer::make(lua, 45, move |lua, key| {
|
||||
match key {
|
||||
// App
|
||||
b"id" => Utils::id(lua)?,
|
||||
b"hide" => Utils::hide(lua)?,
|
||||
|
||||
// Cache
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-proxy"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -13,9 +13,9 @@ default = [ "vendored-lua" ]
|
|||
vendored-lua = [ "mlua/vendored" ]
|
||||
|
||||
[dependencies]
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.2" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-scheduler"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -9,13 +9,13 @@ homepage = "https://yazi-rs.github.io"
|
|||
repository = "https://github.com/sxyazi/yazi"
|
||||
|
||||
[dependencies]
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.2" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "25.3.2" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-plugin = { path = "../yazi-plugin", version = "25.3.2" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "25.3.2" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.2" }
|
||||
yazi-config = { path = "../yazi-config", version = "25.3.7" }
|
||||
yazi-dds = { path = "../yazi-dds", version = "25.3.7" }
|
||||
yazi-fs = { path = "../yazi-fs", version = "25.3.7" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
yazi-plugin = { path = "../yazi-plugin", version = "25.3.7" }
|
||||
yazi-proxy = { path = "../yazi-proxy", version = "25.3.7" }
|
||||
yazi-shared = { path = "../yazi-shared", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use anyhow::{Result, anyhow};
|
|||
use tokio::{fs::{self, DirEntry}, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc};
|
||||
use tracing::warn;
|
||||
use yazi_config::TASKS;
|
||||
use yazi_fs::{Cha, calculate_size, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, skip_path};
|
||||
use yazi_fs::{calculate_size, cha::Cha, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, skip_path};
|
||||
use yazi_shared::url::Url;
|
||||
|
||||
use super::{FileOp, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpTrash};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use yazi_fs::Cha;
|
||||
use yazi_fs::cha::Cha;
|
||||
use yazi_shared::url::Url;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yazi-shared"
|
||||
version = "25.3.2"
|
||||
version = "25.3.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
authors = [ "sxyazi <sxyazi@gmail.com>" ]
|
||||
|
|
@ -10,7 +10,7 @@ repository = "https://github.com/sxyazi/yazi"
|
|||
rust-version = "1.83.0"
|
||||
|
||||
[dependencies]
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.2" }
|
||||
yazi-macro = { path = "../yazi-macro", version = "25.3.7" }
|
||||
|
||||
# External dependencies
|
||||
anyhow = { workspace = true }
|
||||
|
|
@ -22,13 +22,14 @@ percent-encoding = "2.3.1"
|
|||
ratatui = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[target."cfg(unix)".dependencies]
|
||||
libc = { workspace = true }
|
||||
uzers = { workspace = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-sys = { version = "0.59.0", features = [ "Win32_UI_Shell" ] }
|
||||
windows-sys = { version = "0.59.0", features = [ "Win32_Globalization", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_IO", "Win32_System_Threading", "Win32_UI_Shell" ] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] }
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
#![allow(clippy::option_map_unit_fn)]
|
||||
|
||||
yazi_macro::mod_pub!(errors event shell theme translit url);
|
||||
yazi_macro::mod_pub!(errors event shell theme translit tty url);
|
||||
|
||||
yazi_macro::mod_flat!(chars condition debounce either env id layer natsort number os rand ro_cell sync_cell terminal throttle time);
|
||||
yazi_macro::mod_flat!(chars condition debounce either env id layer natsort number os rand ro_cell sync_cell terminal throttle time utf8);
|
||||
|
||||
pub fn init() {
|
||||
tty::init();
|
||||
|
||||
LOG_LEVEL.replace(<_>::from(std::env::var("YAZI_LOG").unwrap_or_default()));
|
||||
|
||||
#[cfg(unix)]
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ pub fn hostname() -> Option<&'static str> {
|
|||
|
||||
#[cfg(unix)]
|
||||
fn hostname_impl() -> Result<String, std::io::Error> {
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
use libc::{gethostname, strlen};
|
||||
|
||||
let mut s = [0; 256];
|
||||
|
|
@ -24,6 +22,6 @@ fn hostname_impl() -> Result<String, std::io::Error> {
|
|||
};
|
||||
|
||||
std::str::from_utf8(&s[..len])
|
||||
.map_err(|_| Error::new(ErrorKind::Other, "invalid hostname"))
|
||||
.map_err(|_| std::io::Error::other("invalid hostname"))
|
||||
.map(|s| s.to_owned())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
use std::io::Write;
|
||||
|
||||
use crossterm::queue;
|
||||
use crossterm::execute;
|
||||
|
||||
#[inline]
|
||||
pub fn terminal_clear(w: &mut impl Write) -> std::io::Result<()> {
|
||||
queue!(w, crossterm::terminal::Clear(crossterm::terminal::ClearType::All))?;
|
||||
writeln!(w)?;
|
||||
w.flush()
|
||||
pub fn terminal_clear(mut w: impl Write) -> std::io::Result<()> {
|
||||
execute!(
|
||||
w,
|
||||
crossterm::terminal::Clear(crossterm::terminal::ClearType::All),
|
||||
crossterm::style::Print("\n")
|
||||
)
|
||||
}
|
||||
|
|
|
|||
196
yazi-shared/src/tty/handle.rs
Normal file
196
yazi-shared/src/tty/handle.rs
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
use std::{io::{Error, ErrorKind, Read, Write}, time::Duration};
|
||||
|
||||
use tracing::error;
|
||||
|
||||
pub struct Handle {
|
||||
#[cfg(unix)]
|
||||
inner: std::os::fd::RawFd,
|
||||
#[cfg(windows)]
|
||||
inner: std::os::windows::io::RawHandle,
|
||||
close: bool,
|
||||
#[cfg(windows)]
|
||||
out_utf8: bool,
|
||||
#[cfg(windows)]
|
||||
incomplete_utf8: super::IncompleteUtf8,
|
||||
}
|
||||
|
||||
impl Drop for Handle {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(unix)]
|
||||
if self.close {
|
||||
unsafe { libc::close(self.inner) };
|
||||
}
|
||||
#[cfg(windows)]
|
||||
if self.close {
|
||||
unsafe { windows_sys::Win32::Foundation::CloseHandle(self.inner) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Handle {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
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);
|
||||
_ = f.into_raw_fd();
|
||||
result
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::io::{FromRawHandle, IntoRawHandle};
|
||||
let mut f = unsafe { std::fs::File::from_raw_handle(self.inner) };
|
||||
let result = f.read(buf);
|
||||
_ = f.into_raw_handle();
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Handle {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::{fd::IntoRawFd, unix::io::FromRawFd};
|
||||
let mut f = unsafe { std::fs::File::from_raw_fd(self.inner) };
|
||||
let result = f.write(buf);
|
||||
_ = f.into_raw_fd();
|
||||
result
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::io::{FromRawHandle, IntoRawHandle};
|
||||
if self.out_utf8 {
|
||||
let mut f = unsafe { std::fs::File::from_raw_handle(self.inner) };
|
||||
let result = f.write(buf);
|
||||
_ = f.into_raw_handle();
|
||||
result
|
||||
} else {
|
||||
super::write_console_utf16(buf, &mut self.incomplete_utf8, self.inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl Handle {
|
||||
pub(super) fn new(out: bool) -> Self {
|
||||
use std::{fs::OpenOptions, os::fd::IntoRawFd};
|
||||
|
||||
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<bool> {
|
||||
let mut tv = libc::timeval {
|
||||
tv_sec: timeout.as_secs() as libc::time_t,
|
||||
tv_usec: timeout.subsec_micros() as libc::suseconds_t,
|
||||
};
|
||||
|
||||
let result = unsafe {
|
||||
let mut set: libc::fd_set = std::mem::zeroed();
|
||||
libc::FD_ZERO(&mut set);
|
||||
libc::FD_SET(self.inner, &mut set);
|
||||
libc::select(self.inner + 1, &mut set, std::ptr::null_mut(), std::ptr::null_mut(), &mut tv)
|
||||
};
|
||||
|
||||
match result {
|
||||
-1 => Err(Error::last_os_error()),
|
||||
0 => Ok(false),
|
||||
_ => Ok(true),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn read_u8(&mut self) -> std::io::Result<u8> {
|
||||
let mut b = 0;
|
||||
match unsafe { libc::read(self.inner, &mut b as *mut _ as *mut _, 1) } {
|
||||
-1 => Err(Error::last_os_error()),
|
||||
0 => Err(Error::new(ErrorKind::UnexpectedEof, "unexpected EOF")),
|
||||
_ => Ok(b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl Handle {
|
||||
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<u16> = if out { "CONOUT$\0" } else { "CONIN$\0" }.encode_utf16().collect();
|
||||
let result = unsafe {
|
||||
CreateFileW(
|
||||
name.as_ptr(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
std::ptr::null_mut(),
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn poll(&mut self, timeout: Duration) -> std::io::Result<bool> {
|
||||
use windows_sys::Win32::{Foundation::{WAIT_FAILED, WAIT_OBJECT_0}, System::Threading::WaitForSingleObject};
|
||||
|
||||
let millis = timeout.as_millis();
|
||||
match unsafe { WaitForSingleObject(self.inner, millis as u32) } {
|
||||
WAIT_FAILED => Err(Error::last_os_error()),
|
||||
WAIT_OBJECT_0 => Ok(true),
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn read_u8(&mut self) -> std::io::Result<u8> {
|
||||
use windows_sys::Win32::Storage::FileSystem::ReadFile;
|
||||
|
||||
let mut buf = 0;
|
||||
let mut bytes = 0;
|
||||
let success = unsafe { ReadFile(self.inner, &mut buf, 1, &mut bytes, std::ptr::null_mut()) };
|
||||
|
||||
if success == 0 {
|
||||
return Err(Error::last_os_error());
|
||||
} else if bytes == 0 {
|
||||
return Err(Error::new(ErrorKind::UnexpectedEof, "unexpected EOF"));
|
||||
}
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
8
yazi-shared/src/tty/mod.rs
Normal file
8
yazi-shared/src/tty/mod.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
yazi_macro::mod_flat!(handle tty);
|
||||
|
||||
#[cfg(windows)]
|
||||
yazi_macro::mod_flat!(windows);
|
||||
|
||||
pub static TTY: crate::RoCell<Tty> = crate::RoCell::new();
|
||||
|
||||
pub(super) fn init() { TTY.with(<_>::default); }
|
||||
81
yazi-shared/src/tty/tty.rs
Normal file
81
yazi-shared/src/tty/tty.rs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
use std::{io::{BufWriter, Error, ErrorKind, Read, Write}, time::{Duration, Instant}};
|
||||
|
||||
use parking_lot::{Mutex, MutexGuard};
|
||||
|
||||
use super::Handle;
|
||||
|
||||
pub struct Tty {
|
||||
stdin: Mutex<Handle>,
|
||||
stdout: Mutex<BufWriter<Handle>>,
|
||||
}
|
||||
|
||||
impl Default for Tty {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
stdin: Mutex::new(Handle::new(false)),
|
||||
stdout: Mutex::new(BufWriter::new(Handle::new(true))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tty {
|
||||
pub const fn reader(&self) -> TtyReader { TtyReader(&self.stdin) }
|
||||
|
||||
pub const fn writer(&self) -> TtyWriter { TtyWriter(&self.stdout) }
|
||||
|
||||
pub fn lockin(&self) -> MutexGuard<Handle> { self.stdin.lock() }
|
||||
|
||||
pub fn lockout(&self) -> MutexGuard<BufWriter<Handle>> { self.stdout.lock() }
|
||||
|
||||
pub fn read_until<P>(&self, timeout: Duration, predicate: P) -> (Vec<u8>, std::io::Result<()>)
|
||||
where
|
||||
P: Fn(u8, &[u8]) -> bool,
|
||||
{
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(200);
|
||||
let now = Instant::now();
|
||||
|
||||
let mut read = || {
|
||||
let mut stdin = self.stdin.lock();
|
||||
loop {
|
||||
if now.elapsed() > timeout {
|
||||
return Err(Error::new(ErrorKind::TimedOut, "timed out"));
|
||||
} else if !stdin.poll(Duration::from_millis(30))? {
|
||||
continue;
|
||||
}
|
||||
|
||||
let b = stdin.read_u8()?;
|
||||
buf.push(b);
|
||||
|
||||
if predicate(b, &buf) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let result = read();
|
||||
(buf, result)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Reader
|
||||
pub struct TtyReader<'a>(&'a Mutex<Handle>);
|
||||
|
||||
impl Read for TtyReader<'_> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { self.0.lock().read(buf) }
|
||||
}
|
||||
|
||||
// --- Writer
|
||||
pub struct TtyWriter<'a>(&'a Mutex<BufWriter<Handle>>);
|
||||
|
||||
impl std::io::Write for TtyWriter<'_> {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { self.0.lock().write(buf) }
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> { self.0.lock().flush() }
|
||||
}
|
||||
|
||||
impl std::fmt::Write for TtyWriter<'_> {
|
||||
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
||||
self.0.lock().write_all(s.as_bytes()).map_err(|_| std::fmt::Error)
|
||||
}
|
||||
}
|
||||
160
yazi-shared/src/tty/windows.rs
Normal file
160
yazi-shared/src/tty/windows.rs
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/windows/stdio.rs
|
||||
|
||||
use std::{mem::MaybeUninit, os::windows::io::RawHandle, str};
|
||||
|
||||
use windows_sys::Win32::{Globalization::{CP_UTF8, MB_ERR_INVALID_CHARS, MultiByteToWideChar}, System::Console::WriteConsoleW};
|
||||
|
||||
use crate::{floor_char_boundary, utf8_char_width};
|
||||
|
||||
// Apparently Windows doesn't handle large reads on stdin or writes to
|
||||
// stdout/stderr well (see #13304 for details).
|
||||
//
|
||||
// From MSDN (2011): "The storage for this buffer is allocated from a shared
|
||||
// heap for the process that is 64 KB in size. The maximum size of the buffer
|
||||
// will depend on heap usage."
|
||||
//
|
||||
// We choose the cap at 8 KiB because libuv does the same, and it seems to be
|
||||
// acceptable so far.
|
||||
const MAX_BUFFER_SIZE: usize = 8192;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct IncompleteUtf8 {
|
||||
bytes: [u8; 4],
|
||||
len: u8,
|
||||
}
|
||||
|
||||
pub(super) fn write_console_utf16(
|
||||
data: &[u8],
|
||||
incomplete_utf8: &mut IncompleteUtf8,
|
||||
handle: RawHandle,
|
||||
) -> std::io::Result<usize> {
|
||||
if incomplete_utf8.len > 0 {
|
||||
assert!(incomplete_utf8.len < 4, "Unexpected number of bytes for incomplete UTF-8 codepoint.");
|
||||
if data[0] >> 6 != 0b10 {
|
||||
// not a continuation byte - reject
|
||||
incomplete_utf8.len = 0;
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
|
||||
));
|
||||
}
|
||||
incomplete_utf8.bytes[incomplete_utf8.len as usize] = data[0];
|
||||
incomplete_utf8.len += 1;
|
||||
let char_width = utf8_char_width(incomplete_utf8.bytes[0]);
|
||||
if (incomplete_utf8.len as usize) < char_width {
|
||||
// more bytes needed
|
||||
return Ok(1);
|
||||
}
|
||||
let s = str::from_utf8(&incomplete_utf8.bytes[0..incomplete_utf8.len as usize]);
|
||||
incomplete_utf8.len = 0;
|
||||
match s {
|
||||
Ok(s) => {
|
||||
assert_eq!(char_width, s.len());
|
||||
let written = write_valid_utf8_to_console(handle, s)?;
|
||||
assert_eq!(written, s.len()); // guaranteed by write_valid_utf8_to_console() for single codepoint writes
|
||||
return Ok(1);
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// As the console is meant for presenting text, we assume bytes of `data` are
|
||||
// encoded as UTF-8, which needs to be encoded as UTF-16.
|
||||
//
|
||||
// If the data is not valid UTF-8 we write out as many bytes as are valid.
|
||||
// If the first byte is invalid it is either first byte of a multi-byte sequence
|
||||
// but the provided byte slice is too short or it is the first byte of an
|
||||
// invalid multi-byte sequence.
|
||||
let len = std::cmp::min(data.len(), MAX_BUFFER_SIZE / 2);
|
||||
let utf8 = match str::from_utf8(&data[..len]) {
|
||||
Ok(s) => s,
|
||||
Err(ref e) if e.valid_up_to() == 0 => {
|
||||
let first_byte_char_width = utf8_char_width(data[0]);
|
||||
if first_byte_char_width > 1 && data.len() < first_byte_char_width {
|
||||
incomplete_utf8.bytes[0] = data[0];
|
||||
incomplete_utf8.len = 1;
|
||||
return Ok(1);
|
||||
} else {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(),
|
||||
};
|
||||
|
||||
write_valid_utf8_to_console(handle, utf8)
|
||||
}
|
||||
|
||||
fn write_valid_utf8_to_console(handle: RawHandle, utf8: &str) -> std::io::Result<usize> {
|
||||
debug_assert!(!utf8.is_empty());
|
||||
|
||||
let mut utf16 = [MaybeUninit::<u16>::uninit(); MAX_BUFFER_SIZE / 2];
|
||||
let utf8 = &utf8[..floor_char_boundary(utf8, utf16.len())];
|
||||
|
||||
let utf16: &[u16] = unsafe {
|
||||
// Note that this theoretically checks validity twice in the (most common) case
|
||||
// where the underlying byte sequence is valid utf-8 (given the check in
|
||||
// `write()`).
|
||||
let result = MultiByteToWideChar(
|
||||
CP_UTF8,
|
||||
MB_ERR_INVALID_CHARS,
|
||||
utf8.as_ptr(),
|
||||
utf8.len() as i32,
|
||||
utf16.as_mut_ptr() as *mut _,
|
||||
utf16.len() as i32,
|
||||
);
|
||||
assert!(result != 0, "Unexpected error in MultiByteToWideChar");
|
||||
|
||||
// Safety: MultiByteToWideChar initializes `result` values.
|
||||
&*(&utf16[..result as usize] as *const [MaybeUninit<u16>] as *const [u16])
|
||||
};
|
||||
|
||||
let mut written = write_u16s(handle, utf16)?;
|
||||
|
||||
// Figure out how many bytes of as UTF-8 were written away as UTF-16.
|
||||
if written == utf16.len() {
|
||||
Ok(utf8.len())
|
||||
} else {
|
||||
// Make sure we didn't end up writing only half of a surrogate pair (even though
|
||||
// the chance is tiny). Because it is not possible for user code to re-slice
|
||||
// `data` in such a way that a missing surrogate can be produced (and also
|
||||
// because of the UTF-8 validation above), write the missing surrogate out
|
||||
// now. Buffering it would mean we have to lie about the number of bytes
|
||||
// written.
|
||||
let first_code_unit_remaining = utf16[written];
|
||||
if matches!(first_code_unit_remaining, 0xdcee..=0xdfff) {
|
||||
// low surrogate
|
||||
// We just hope this works, and give up otherwise
|
||||
let _ = write_u16s(handle, &utf16[written..written + 1]);
|
||||
written += 1;
|
||||
}
|
||||
// Calculate the number of bytes of `utf8` that were actually written.
|
||||
let mut count = 0;
|
||||
for ch in utf16[..written].iter() {
|
||||
count += match ch {
|
||||
0x0000..=0x007f => 1,
|
||||
0x0080..=0x07ff => 2,
|
||||
0xdcee..=0xdfff => 1, // Low surrogate. We already counted 3 bytes for the other.
|
||||
_ => 3,
|
||||
};
|
||||
}
|
||||
debug_assert!(String::from_utf16(&utf16[..written]).unwrap() == utf8[..count]);
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_u16s(handle: RawHandle, data: &[u16]) -> std::io::Result<usize> {
|
||||
debug_assert!(data.len() < u32::MAX as usize);
|
||||
let mut written = 0;
|
||||
let result = unsafe {
|
||||
WriteConsoleW(handle, data.as_ptr(), data.len() as u32, &mut written, std::ptr::null_mut())
|
||||
};
|
||||
if result == 0 { Err(std::io::Error::last_os_error()) } else { Ok(written as usize) }
|
||||
}
|
||||
53
yazi-shared/src/utf8.rs
Normal file
53
yazi-shared/src/utf8.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// https://tools.ietf.org/html/rfc3629
|
||||
const UTF8_CHAR_WIDTH: &[u8; 256] = &[
|
||||
// 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
|
||||
0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E
|
||||
4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F
|
||||
];
|
||||
|
||||
/// Given a first byte, determines how many bytes are in this UTF-8 character.
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn utf8_char_width(b: u8) -> usize { UTF8_CHAR_WIDTH[b as usize] as usize }
|
||||
|
||||
/// Finds the closest `x` not exceeding `index` where [`is_char_boundary(x)`] is
|
||||
/// `true`.
|
||||
///
|
||||
/// This method can help you truncate a string so that it's still valid UTF-8,
|
||||
/// but doesn't exceed a given number of bytes. Note that this is done purely at
|
||||
/// the character level and can still visually split graphemes, even though the
|
||||
/// underlying characters aren't split. For example, the emoji 🧑🔬 (scientist)
|
||||
/// could be split so that the string only includes 🧑 (person) instead.
|
||||
#[inline]
|
||||
pub fn floor_char_boundary(s: &str, index: usize) -> usize {
|
||||
if index >= s.len() {
|
||||
s.len()
|
||||
} else {
|
||||
let lower_bound = index.saturating_sub(3);
|
||||
let new_index =
|
||||
s.as_bytes()[lower_bound..=index].iter().rposition(|&b| is_utf8_char_boundary(b));
|
||||
|
||||
// SAFETY: we know that the character boundary will be within four bytes
|
||||
unsafe { lower_bound + new_index.unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn is_utf8_char_boundary(b: u8) -> bool {
|
||||
// This is bit magic equivalent to: b < 128 || b >= 192
|
||||
(b as i8) >= -0x40
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue