mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
6ff64cd33a
commit
435d910751
20 changed files with 404 additions and 216 deletions
|
|
@ -1 +1 @@
|
|||
{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","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","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","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","Sysinfo","ffprobe","vframes"],"version":"0.2"}
|
||||
{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","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","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","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","Sysinfo","ffprobe","vframes","luma"],"language":"en"}
|
||||
|
|
@ -5,7 +5,7 @@ use ratatui::layout::Rect;
|
|||
use tracing::warn;
|
||||
use yazi_shared::env_exists;
|
||||
|
||||
use crate::{Emulator, SHOWN, TMUX, WSL, drivers};
|
||||
use crate::{Brand, Emulator, SHOWN, TMUX, WSL, drivers};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Adapter {
|
||||
|
|
@ -80,15 +80,14 @@ impl Adapter {
|
|||
}
|
||||
|
||||
impl Adapter {
|
||||
pub fn matches() -> Self {
|
||||
let emulator = Emulator::detect();
|
||||
if matches!(emulator, Emulator::Microsoft) {
|
||||
pub fn matches(emulator: Emulator) -> Self {
|
||||
if matches!(emulator.kind.left(), Some(Brand::Microsoft)) {
|
||||
return Self::Sixel;
|
||||
} else if *WSL && matches!(emulator, Emulator::WezTerm) {
|
||||
} else if *WSL && matches!(emulator.kind.left(), Some(Brand::WezTerm)) {
|
||||
return Self::KgpOld;
|
||||
}
|
||||
|
||||
let mut protocols = emulator.adapters();
|
||||
let mut protocols = emulator.adapters().to_owned();
|
||||
#[cfg(windows)]
|
||||
protocols.retain(|p| *p == Self::Iip);
|
||||
if env_exists("ZELLIJ_SESSION_NAME") {
|
||||
|
|
|
|||
119
yazi-adapter/src/brand.rs
Normal file
119
yazi-adapter/src/brand.rs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
use tracing::warn;
|
||||
use yazi_shared::env_exists;
|
||||
|
||||
use crate::Mux;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Brand {
|
||||
Kitty,
|
||||
Konsole,
|
||||
Iterm2,
|
||||
WezTerm,
|
||||
Foot,
|
||||
Ghostty,
|
||||
Microsoft,
|
||||
Rio,
|
||||
BlackBox,
|
||||
VSCode,
|
||||
Tabby,
|
||||
Hyper,
|
||||
Mintty,
|
||||
Neovim,
|
||||
Apple,
|
||||
Urxvt,
|
||||
}
|
||||
|
||||
impl Brand {
|
||||
pub fn from_env() -> Option<Self> {
|
||||
use Brand as B;
|
||||
|
||||
if env_exists("NVIM_LOG_FILE") && env_exists("NVIM") {
|
||||
return Some(Self::Neovim);
|
||||
}
|
||||
|
||||
let vars = [
|
||||
("KITTY_WINDOW_ID", B::Kitty),
|
||||
("KONSOLE_VERSION", B::Konsole),
|
||||
("ITERM_SESSION_ID", B::Iterm2),
|
||||
("WEZTERM_EXECUTABLE", B::WezTerm),
|
||||
("GHOSTTY_RESOURCES_DIR", B::Ghostty),
|
||||
("WT_Session", B::Microsoft),
|
||||
("VSCODE_INJECTION", B::VSCode),
|
||||
("TABBY_CONFIG_DIRECTORY", B::Tabby),
|
||||
];
|
||||
match vars.into_iter().find(|&(s, _)| env_exists(s)) {
|
||||
Some((_, brand)) => return Some(brand),
|
||||
None => warn!("[Adapter] No special environment variables detected"),
|
||||
}
|
||||
|
||||
let (term, program) = B::env();
|
||||
match program.as_str() {
|
||||
"iTerm.app" => return Some(B::Iterm2),
|
||||
"WezTerm" => return Some(B::WezTerm),
|
||||
"ghostty" => return Some(B::Ghostty),
|
||||
"rio" => return Some(B::Rio),
|
||||
"BlackBox" => return Some(B::BlackBox),
|
||||
"vscode" => return Some(B::VSCode),
|
||||
"Tabby" => return Some(B::Tabby),
|
||||
"Hyper" => return Some(B::Hyper),
|
||||
"mintty" => return Some(B::Mintty),
|
||||
"Apple_Terminal" => return Some(B::Apple),
|
||||
_ => warn!("[Adapter] Unknown TERM_PROGRAM: {program}"),
|
||||
}
|
||||
match term.as_str() {
|
||||
"xterm-kitty" => return Some(B::Kitty),
|
||||
"foot" => return Some(B::Foot),
|
||||
"foot-extra" => return Some(B::Foot),
|
||||
"xterm-ghostty" => return Some(B::Ghostty),
|
||||
"rio" => return Some(B::Rio),
|
||||
"rxvt-unicode-256color" => return Some(B::Urxvt),
|
||||
_ => warn!("[Adapter] Unknown TERM: {term}"),
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) fn from_csi(resp: &str) -> Option<Self> {
|
||||
let names = [
|
||||
("kitty", Self::Kitty),
|
||||
("Konsole", Self::Konsole),
|
||||
("iTerm2", Self::Iterm2),
|
||||
("WezTerm", Self::WezTerm),
|
||||
("foot", Self::Foot),
|
||||
("ghostty", Self::Ghostty),
|
||||
];
|
||||
names.into_iter().find(|&(n, _)| resp.contains(n)).map(|(_, b)| b)
|
||||
}
|
||||
|
||||
pub(super) fn adapters(self) -> &'static [crate::Adapter] {
|
||||
use Brand as B;
|
||||
|
||||
use crate::Adapter as A;
|
||||
|
||||
match self {
|
||||
B::Kitty => &[A::Kgp],
|
||||
B::Konsole => &[A::KgpOld],
|
||||
B::Iterm2 => &[A::Iip, A::Sixel],
|
||||
B::WezTerm => &[A::Iip, A::Sixel],
|
||||
B::Foot => &[A::Sixel],
|
||||
B::Ghostty => &[A::Kgp],
|
||||
B::Microsoft => &[A::Sixel],
|
||||
B::Rio => &[A::Iip, A::Sixel],
|
||||
B::BlackBox => &[A::Sixel],
|
||||
B::VSCode => &[A::Iip, A::Sixel],
|
||||
B::Tabby => &[A::Iip, A::Sixel],
|
||||
B::Hyper => &[A::Iip, A::Sixel],
|
||||
B::Mintty => &[A::Iip],
|
||||
B::Neovim => &[],
|
||||
B::Apple => &[],
|
||||
B::Urxvt => &[],
|
||||
}
|
||||
}
|
||||
|
||||
fn env() -> (String, String) {
|
||||
let (term, program) = Mux::term_program();
|
||||
(
|
||||
term.unwrap_or(std::env::var("TERM").unwrap_or_default()),
|
||||
program.unwrap_or(std::env::var("TERM_PROGRAM").unwrap_or_default()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,150 +4,61 @@ use anyhow::{Result, bail};
|
|||
use crossterm::{cursor::{RestorePosition, SavePosition}, execute, style::Print, terminal::{disable_raw_mode, enable_raw_mode}};
|
||||
use scopeguard::defer;
|
||||
use tokio::{io::{AsyncReadExt, BufReader}, time::timeout};
|
||||
use tracing::{error, warn};
|
||||
use yazi_shared::env_exists;
|
||||
use tracing::{debug, error, warn};
|
||||
use yazi_shared::Either;
|
||||
|
||||
use crate::{Adapter, Mux, TMUX};
|
||||
use crate::{Adapter, Brand, Mux, TMUX, Unknown};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Emulator {
|
||||
Unknown(Vec<Adapter>),
|
||||
Kitty,
|
||||
Konsole,
|
||||
Iterm2,
|
||||
WezTerm,
|
||||
Foot,
|
||||
Ghostty,
|
||||
Microsoft,
|
||||
Rio,
|
||||
BlackBox,
|
||||
VSCode,
|
||||
Tabby,
|
||||
Hyper,
|
||||
Mintty,
|
||||
Neovim,
|
||||
Apple,
|
||||
Urxvt,
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Emulator {
|
||||
pub kind: Either<Brand, Unknown>,
|
||||
pub light: bool,
|
||||
}
|
||||
|
||||
impl Emulator {
|
||||
pub fn adapters(self) -> Vec<Adapter> {
|
||||
match self {
|
||||
Self::Unknown(adapters) => adapters,
|
||||
Self::Kitty => vec![Adapter::Kgp],
|
||||
Self::Konsole => vec![Adapter::KgpOld],
|
||||
Self::Iterm2 => vec![Adapter::Iip, Adapter::Sixel],
|
||||
Self::WezTerm => vec![Adapter::Iip, Adapter::Sixel],
|
||||
Self::Foot => vec![Adapter::Sixel],
|
||||
Self::Ghostty => vec![Adapter::Kgp],
|
||||
Self::Microsoft => vec![Adapter::Sixel],
|
||||
Self::Rio => vec![Adapter::Iip, Adapter::Sixel],
|
||||
Self::BlackBox => vec![Adapter::Sixel],
|
||||
Self::VSCode => vec![Adapter::Iip, Adapter::Sixel],
|
||||
Self::Tabby => vec![Adapter::Iip, Adapter::Sixel],
|
||||
Self::Hyper => vec![Adapter::Iip, Adapter::Sixel],
|
||||
Self::Mintty => vec![Adapter::Iip],
|
||||
Self::Neovim => vec![],
|
||||
Self::Apple => vec![],
|
||||
Self::Urxvt => vec![],
|
||||
}
|
||||
}
|
||||
impl Default for Emulator {
|
||||
fn default() -> Self { Self { kind: Either::Right(Unknown::default()), light: false } }
|
||||
}
|
||||
|
||||
impl Emulator {
|
||||
pub fn detect() -> Self {
|
||||
if env_exists("NVIM_LOG_FILE") && env_exists("NVIM") {
|
||||
return Self::Neovim;
|
||||
if let Some(brand) = Brand::from_env() {
|
||||
Self { kind: Either::Left(brand), light: Self::detect_base().unwrap_or_default().light }
|
||||
} else {
|
||||
Self::detect_full().unwrap_or_default()
|
||||
}
|
||||
|
||||
let vars = [
|
||||
("KITTY_WINDOW_ID", Self::Kitty),
|
||||
("KONSOLE_VERSION", Self::Konsole),
|
||||
("ITERM_SESSION_ID", Self::Iterm2),
|
||||
("WEZTERM_EXECUTABLE", Self::WezTerm),
|
||||
("GHOSTTY_RESOURCES_DIR", Self::Ghostty),
|
||||
("WT_Session", Self::Microsoft),
|
||||
("VSCODE_INJECTION", Self::VSCode),
|
||||
("TABBY_CONFIG_DIRECTORY", Self::Tabby),
|
||||
];
|
||||
match vars.into_iter().find(|(env, _)| env_exists(env)) {
|
||||
Some((_, emulator)) => return emulator,
|
||||
None => warn!("[Adapter] No special environment variables detected"),
|
||||
}
|
||||
|
||||
let (term, program) = Self::via_env();
|
||||
match program.as_str() {
|
||||
"iTerm.app" => return Self::Iterm2,
|
||||
"WezTerm" => return Self::WezTerm,
|
||||
"ghostty" => return Self::Ghostty,
|
||||
"rio" => return Self::Rio,
|
||||
"BlackBox" => return Self::BlackBox,
|
||||
"vscode" => return Self::VSCode,
|
||||
"Tabby" => return Self::Tabby,
|
||||
"Hyper" => return Self::Hyper,
|
||||
"mintty" => return Self::Mintty,
|
||||
"Apple_Terminal" => return Self::Apple,
|
||||
_ => warn!("[Adapter] Unknown TERM_PROGRAM: {program}"),
|
||||
}
|
||||
match term.as_str() {
|
||||
"xterm-kitty" => return Self::Kitty,
|
||||
"foot" => return Self::Foot,
|
||||
"foot-extra" => return Self::Foot,
|
||||
"xterm-ghostty" => return Self::Ghostty,
|
||||
"rio" => return Self::Rio,
|
||||
"rxvt-unicode-256color" => return Self::Urxvt,
|
||||
_ => warn!("[Adapter] Unknown TERM: {term}"),
|
||||
}
|
||||
|
||||
Self::via_csi().unwrap_or(Self::Unknown(vec![]))
|
||||
}
|
||||
|
||||
pub fn via_env() -> (String, String) {
|
||||
let (term, program) = Mux::term_program();
|
||||
(
|
||||
term.unwrap_or(std::env::var("TERM").unwrap_or_default()),
|
||||
program.unwrap_or(std::env::var("TERM_PROGRAM").unwrap_or_default()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn via_csi() -> Result<Self> {
|
||||
pub fn detect_full() -> Result<Self> {
|
||||
defer! { disable_raw_mode().ok(); }
|
||||
enable_raw_mode()?;
|
||||
|
||||
execute!(
|
||||
LineWriter::new(stderr()),
|
||||
SavePosition,
|
||||
Print(Mux::csi("\x1b[>q")), // Request terminal version
|
||||
Print(Mux::csi("\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\")), // Detect KGP
|
||||
Print(Mux::csi("\x1b[c")), // Request device attributes
|
||||
Print(Mux::csi("\x1b[>q")), // Request terminal version
|
||||
Print(Mux::csi("\x1b[c")), // Request device attributes
|
||||
RestorePosition
|
||||
)?;
|
||||
|
||||
let resp = futures::executor::block_on(Self::read_until_da1());
|
||||
let names = [
|
||||
("kitty", Self::Kitty),
|
||||
("Konsole", Self::Konsole),
|
||||
("iTerm2", Self::Iterm2),
|
||||
("WezTerm", Self::WezTerm),
|
||||
("foot", Self::Foot),
|
||||
("ghostty", Self::Ghostty),
|
||||
];
|
||||
let kind = if let Some(brand) = Brand::from_csi(&resp) {
|
||||
Either::Left(brand)
|
||||
} else {
|
||||
Either::Right(Unknown {
|
||||
kgp: resp.contains("\x1b_Gi=31;OK"),
|
||||
sixel: ["?4;", "?4c", ";4;", ";4c"].iter().any(|s| resp.contains(s)),
|
||||
})
|
||||
};
|
||||
|
||||
for (name, emulator) in names.iter() {
|
||||
if resp.contains(name) {
|
||||
return Ok(emulator.clone());
|
||||
}
|
||||
}
|
||||
Ok(Self { kind, light: Self::light_bg(&resp).unwrap_or_default() })
|
||||
}
|
||||
|
||||
let mut adapters = Vec::with_capacity(2);
|
||||
if resp.contains("\x1b_Gi=31;OK") {
|
||||
adapters.push(Adapter::KgpOld);
|
||||
pub fn adapters(self) -> &'static [Adapter] {
|
||||
match self.kind {
|
||||
Either::Left(brand) => brand.adapters(),
|
||||
Either::Right(unknown) => unknown.adapters(),
|
||||
}
|
||||
if ["?4;", "?4c", ";4;", ";4c"].iter().any(|s| resp.contains(s)) {
|
||||
adapters.push(Adapter::Sixel);
|
||||
}
|
||||
|
||||
Ok(Self::Unknown(adapters))
|
||||
}
|
||||
|
||||
pub fn move_lock<F, T>((x, y): (u16, u16), cb: F) -> Result<T>
|
||||
|
|
@ -209,4 +120,35 @@ impl Emulator {
|
|||
}
|
||||
String::from_utf8_lossy(&buf).into_owned()
|
||||
}
|
||||
|
||||
fn detect_base() -> Result<Self> {
|
||||
defer! { disable_raw_mode().ok(); }
|
||||
enable_raw_mode()?;
|
||||
|
||||
execute!(
|
||||
LineWriter::new(stderr()),
|
||||
Print(Mux::csi("\x1b]11;?\x07")), // Request background color
|
||||
Print(Mux::csi("\x1b[c")), // Request device attributes
|
||||
)?;
|
||||
|
||||
let resp = futures::executor::block_on(Self::read_until_da1());
|
||||
Ok(Self { light: Self::light_bg(&resp).unwrap_or_default(), ..Default::default() })
|
||||
}
|
||||
|
||||
fn light_bg(resp: &str) -> Result<bool> {
|
||||
match resp.split_once("]11;rgb:") {
|
||||
Some((_, s)) if s.len() >= 14 => {
|
||||
let r = u8::from_str_radix(&s[0..2], 16)? as f32;
|
||||
let g = u8::from_str_radix(&s[5..7], 16)? as f32;
|
||||
let b = u8::from_str_radix(&s[10..12], 16)? as f32;
|
||||
let luma = r * 0.2627 / 256.0 + g * 0.6780 / 256.0 + b * 0.0593 / 256.0;
|
||||
debug!("Detected background color: {} (luma = {luma:.2})", &s[..14]);
|
||||
Ok(luma > 0.6)
|
||||
}
|
||||
_ => {
|
||||
warn!("Failed to detect background color: {resp:?}");
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,16 @@
|
|||
|
||||
yazi_macro::mod_pub!(drivers);
|
||||
|
||||
yazi_macro::mod_flat!(adapter dimension emulator image info mux);
|
||||
yazi_macro::mod_flat!(adapter brand dimension emulator image info mux unknown);
|
||||
|
||||
use yazi_shared::{RoCell, SyncCell, env_exists, in_wsl};
|
||||
|
||||
pub static EMULATOR: RoCell<Emulator> = RoCell::new();
|
||||
pub static ADAPTOR: RoCell<Adapter> = RoCell::new();
|
||||
|
||||
// Image state
|
||||
static SHOWN: SyncCell<Option<ratatui::layout::Rect>> = SyncCell::new(None);
|
||||
|
||||
// Tmux support
|
||||
pub static TMUX: RoCell<bool> = RoCell::new();
|
||||
static ESCAPE: RoCell<&'static str> = RoCell::new();
|
||||
|
|
@ -16,10 +21,7 @@ static CLOSE: RoCell<&'static str> = RoCell::new();
|
|||
// WSL support
|
||||
pub static WSL: RoCell<bool> = RoCell::new();
|
||||
|
||||
// Image state
|
||||
static SHOWN: SyncCell<Option<ratatui::layout::Rect>> = SyncCell::new(None);
|
||||
|
||||
pub fn init() {
|
||||
pub fn init() -> anyhow::Result<()> {
|
||||
// Tmux support
|
||||
TMUX.init(env_exists("TMUX_PANE") && env_exists("TMUX"));
|
||||
ESCAPE.init(if *TMUX { "\x1b\x1b" } else { "\x1b" });
|
||||
|
|
@ -38,6 +40,11 @@ pub fn init() {
|
|||
// WSL support
|
||||
WSL.init(in_wsl());
|
||||
|
||||
ADAPTOR.init(Adapter::matches());
|
||||
EMULATOR.init(Emulator::detect());
|
||||
yazi_config::init_flavor(EMULATOR.light)?;
|
||||
|
||||
ADAPTOR.init(Adapter::matches(*EMULATOR));
|
||||
ADAPTOR.start();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
20
yazi-adapter/src/unknown.rs
Normal file
20
yazi-adapter/src/unknown.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
use crate::Adapter;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct Unknown {
|
||||
pub kgp: bool,
|
||||
pub sixel: bool,
|
||||
}
|
||||
|
||||
impl Unknown {
|
||||
pub(super) fn adapters(self) -> &'static [Adapter] {
|
||||
use Adapter as A;
|
||||
|
||||
match (self.kgp, self.sixel) {
|
||||
(true, true) => &[A::Kgp, A::Sixel],
|
||||
(true, false) => &[A::Kgp],
|
||||
(false, true) => &[A::Sixel],
|
||||
(false, false) => &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,13 +17,14 @@ impl Actions {
|
|||
writeln!(s, "\nYa")?;
|
||||
writeln!(s, " Version: {}", Self::process_output("ya", "--version"))?;
|
||||
|
||||
let emulator = yazi_adapter::Emulator::detect();
|
||||
writeln!(s, "\nEmulator")?;
|
||||
writeln!(s, " Emulator.via_env: {:?}", yazi_adapter::Emulator::via_env())?;
|
||||
writeln!(s, " Emulator.via_csi: {:?}", yazi_adapter::Emulator::via_csi())?;
|
||||
writeln!(s, " Emulator.detect : {:?}", yazi_adapter::Emulator::detect())?;
|
||||
writeln!(s, " Brand.from_env : {:?}", yazi_adapter::Brand::from_env())?;
|
||||
writeln!(s, " Emulator.detect : {:?}", emulator)?;
|
||||
writeln!(s, " Emulator.detect_full: {:?}", yazi_adapter::Emulator::detect_full())?;
|
||||
|
||||
writeln!(s, "\nAdapter")?;
|
||||
writeln!(s, " Adapter.matches: {:?}", yazi_adapter::Adapter::matches())?;
|
||||
writeln!(s, " Adapter.matches: {:?}", yazi_adapter::Adapter::matches(emulator))?;
|
||||
|
||||
writeln!(s, "\nDesktop")?;
|
||||
writeln!(s, " XDG_SESSION_TYPE : {:?}", env::var_os("XDG_SESSION_TYPE"))?;
|
||||
|
|
|
|||
|
|
@ -214,16 +214,3 @@ rules = [
|
|||
]
|
||||
|
||||
# : }}}
|
||||
|
||||
|
||||
# : Icons {{{
|
||||
|
||||
[icon]
|
||||
|
||||
globs = []
|
||||
dirs = []
|
||||
files = []
|
||||
exts = []
|
||||
conds = []
|
||||
|
||||
# : }}}
|
||||
|
|
|
|||
|
|
@ -24,22 +24,7 @@ pub static LAYOUT: SyncCell<Layout> = SyncCell::new(Layout::default());
|
|||
|
||||
pub fn init() -> anyhow::Result<()> {
|
||||
if let Err(e) = try_init(true) {
|
||||
eprintln!("{e}");
|
||||
if let Some(src) = e.source() {
|
||||
eprintln!("\nCaused by:\n{src}");
|
||||
}
|
||||
|
||||
use crossterm::style::{Attribute, Print, SetAttributes};
|
||||
crossterm::execute!(
|
||||
std::io::stderr(),
|
||||
SetAttributes(Attribute::Reverse.into()),
|
||||
SetAttributes(Attribute::Bold.into()),
|
||||
Print("Press <Enter> to continue with preset settings..."),
|
||||
SetAttributes(Attribute::Reset.into()),
|
||||
Print("\n"),
|
||||
)?;
|
||||
|
||||
std::io::stdin().read_line(&mut String::new())?;
|
||||
wait_for_key(e)?;
|
||||
try_init(false)?;
|
||||
}
|
||||
|
||||
|
|
@ -78,13 +63,29 @@ Please change `create_title = "Create:"` to `create_title = ["Create:", "Create
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn init_flavor(light: bool) -> anyhow::Result<()> {
|
||||
let mut flavor_toml = Preset::flavor(light, true);
|
||||
if let Err(e) = flavor_toml {
|
||||
wait_for_key(e)?;
|
||||
flavor_toml = Preset::flavor(light, false);
|
||||
}
|
||||
|
||||
let mut theme: theme::Theme = <_>::from_str(&flavor_toml.unwrap())?;
|
||||
theme.manager.syntect_theme = theme
|
||||
.flavor
|
||||
.syntect_path(light)
|
||||
.unwrap_or_else(|| yazi_shared::fs::expand_path(&theme.manager.syntect_theme));
|
||||
|
||||
THEME.init(theme);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_init(merge: bool) -> anyhow::Result<()> {
|
||||
let (yazi_toml, keymap_toml, theme_toml) = if merge {
|
||||
let (yazi_toml, keymap_toml) = if merge {
|
||||
let p = Xdg::config_dir();
|
||||
(Preset::yazi(&p)?, Preset::keymap(&p)?, Preset::theme(&p)?)
|
||||
(Preset::yazi(&p)?, Preset::keymap(&p)?)
|
||||
} else {
|
||||
use yazi_macro::config_preset as preset;
|
||||
(preset!("yazi"), preset!("keymap"), preset!("theme"))
|
||||
(yazi_macro::config_preset!("yazi"), yazi_macro::config_preset!("keymap"))
|
||||
};
|
||||
|
||||
let keymap = <_>::from_str(&keymap_toml)?;
|
||||
|
|
@ -93,7 +94,6 @@ fn try_init(merge: bool) -> anyhow::Result<()> {
|
|||
let plugin = <_>::from_str(&yazi_toml)?;
|
||||
let preview = <_>::from_str(&yazi_toml)?;
|
||||
let tasks = <_>::from_str(&yazi_toml)?;
|
||||
let theme = <_>::from_str(&theme_toml)?;
|
||||
let input = <_>::from_str(&yazi_toml)?;
|
||||
let confirm = <_>::from_str(&yazi_toml)?;
|
||||
let pick = <_>::from_str(&yazi_toml)?;
|
||||
|
|
@ -105,7 +105,6 @@ fn try_init(merge: bool) -> anyhow::Result<()> {
|
|||
PLUGIN.init(plugin);
|
||||
PREVIEW.init(preview);
|
||||
TASKS.init(tasks);
|
||||
THEME.init(theme);
|
||||
INPUT.init(input);
|
||||
CONFIRM.init(confirm);
|
||||
PICK.init(pick);
|
||||
|
|
@ -113,3 +112,23 @@ fn try_init(merge: bool) -> anyhow::Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn wait_for_key(e: anyhow::Error) -> anyhow::Result<()> {
|
||||
eprintln!("{e}");
|
||||
if let Some(src) = e.source() {
|
||||
eprintln!("\nCaused by:\n{src}");
|
||||
}
|
||||
|
||||
use crossterm::style::{Attribute, Print, SetAttributes};
|
||||
crossterm::execute!(
|
||||
std::io::stderr(),
|
||||
SetAttributes(Attribute::Reverse.into()),
|
||||
SetAttributes(Attribute::Bold.into()),
|
||||
Print("Press <Enter> to continue with preset settings..."),
|
||||
SetAttributes(Attribute::Reset.into()),
|
||||
Print("\n"),
|
||||
)?;
|
||||
|
||||
std::io::stdin().read_line(&mut String::new())?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use std::{borrow::Cow, path::{Path, PathBuf}};
|
||||
use std::{borrow::Cow, path::{Path, PathBuf}, str::FromStr};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use toml::{Table, Value};
|
||||
use yazi_macro::config_preset as preset;
|
||||
use yazi_shared::Xdg;
|
||||
|
||||
use crate::theme::Flavor;
|
||||
|
||||
|
|
@ -10,26 +10,26 @@ pub(crate) struct Preset;
|
|||
|
||||
impl Preset {
|
||||
pub(crate) fn yazi(p: &Path) -> Result<Cow<'static, str>> {
|
||||
Self::merge_path(p.join("yazi.toml"), preset!("yazi"))
|
||||
Self::merge_path(p.join("yazi.toml"), yazi_macro::config_preset!("yazi"))
|
||||
}
|
||||
|
||||
pub(crate) fn keymap(p: &Path) -> Result<Cow<'static, str>> {
|
||||
Self::merge_path(p.join("keymap.toml"), preset!("keymap"))
|
||||
Self::merge_path(p.join("keymap.toml"), yazi_macro::config_preset!("keymap"))
|
||||
}
|
||||
|
||||
pub(crate) fn theme(p: &Path) -> Result<Cow<'static, str>> {
|
||||
let Ok(user) = std::fs::read_to_string(p.join("theme.toml")) else {
|
||||
return Ok(preset!("theme"));
|
||||
};
|
||||
let Some(use_) = Flavor::parse_use(&user) else {
|
||||
return Self::merge_str(&user, &preset!("theme"));
|
||||
pub(crate) fn flavor(light: bool, merge: bool) -> Result<Cow<'static, str>> {
|
||||
let theme = if merge {
|
||||
std::fs::read_to_string(Xdg::config_dir().join("theme.toml")).unwrap_or_default()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
let p = p.join(format!("flavors/{use_}.yazi/flavor.toml"));
|
||||
let flavor =
|
||||
std::fs::read_to_string(&p).with_context(|| format!("failed to load flavor {p:?}"))?;
|
||||
let flavor = Flavor::from_str(&theme)?;
|
||||
|
||||
Self::merge_str(&user, &Self::merge_str(&flavor, &preset!("theme"))?)
|
||||
let preset =
|
||||
if light { yazi_macro::theme_preset!("light") } else { yazi_macro::theme_preset!("dark") };
|
||||
|
||||
Self::merge_str(&theme, &Self::merge_str(&flavor.read(light)?, &preset)?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
|||
|
|
@ -1,23 +1,50 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::{borrow::Cow, path::PathBuf, str::FromStr};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
use anyhow::{Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yazi_shared::Xdg;
|
||||
|
||||
#[derive(Default, Deserialize, Serialize)]
|
||||
pub struct Flavor {
|
||||
#[serde(rename = "use")]
|
||||
pub use_: String,
|
||||
#[serde(default)]
|
||||
pub dark: String,
|
||||
#[serde(default)]
|
||||
pub light: String,
|
||||
}
|
||||
|
||||
impl FromStr for Flavor {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
#[serde(default)]
|
||||
flavor: Flavor,
|
||||
}
|
||||
|
||||
Ok(
|
||||
toml::from_str::<Outer>(s)
|
||||
.context("Failed to parse the [flavor] section in your theme.toml")?
|
||||
.flavor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Flavor {
|
||||
pub fn parse_use(s: &str) -> Option<String> {
|
||||
#[derive(Deserialize)]
|
||||
struct Outer {
|
||||
flavor: Inner,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
struct Inner {
|
||||
#[serde(rename = "use")]
|
||||
pub use_: String,
|
||||
}
|
||||
pub(crate) fn read(&self, light: bool) -> Result<Cow<'static, str>> {
|
||||
Ok(match if light { self.light.as_str() } else { self.dark.as_str() } {
|
||||
"" => Cow::Borrowed(""),
|
||||
name => {
|
||||
let p = Xdg::config_dir().join(format!("flavors/{name}.yazi/flavor.toml"));
|
||||
std::fs::read_to_string(&p).with_context(|| format!("Failed to load flavor {p:?}"))?.into()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
toml::from_str::<Outer>(s).ok().map(|o| o.flavor.use_).filter(|s| !s.is_empty())
|
||||
pub(crate) fn syntect_path(&self, light: bool) -> Option<PathBuf> {
|
||||
match if light { self.light.as_str() } else { self.dark.as_str() } {
|
||||
"" => None,
|
||||
name => Some(Xdg::config_dir().join(format!("flavors/{name}.yazi/tmtheme.xml"))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::{path::PathBuf, str::FromStr};
|
|||
use anyhow::Context;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use validator::Validate;
|
||||
use yazi_shared::{Xdg, fs::expand_path, theme::Style};
|
||||
use yazi_shared::theme::Style;
|
||||
|
||||
use super::{Filetype, Flavor, Icons};
|
||||
|
||||
|
|
@ -32,17 +32,10 @@ impl FromStr for Theme {
|
|||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut theme: Self = toml::from_str(s).context("Failed to parse your yazi.toml")?;
|
||||
let theme: Self = toml::from_str(s).context("Failed to parse your theme.toml")?;
|
||||
theme.manager.validate()?;
|
||||
theme.which.validate()?;
|
||||
|
||||
if theme.flavor.use_.is_empty() {
|
||||
theme.manager.syntect_theme = expand_path(&theme.manager.syntect_theme);
|
||||
} else {
|
||||
theme.manager.syntect_theme =
|
||||
Xdg::config_dir().join(format!("flavors/{}.yazi/tmtheme.xml", theme.flavor.use_));
|
||||
}
|
||||
|
||||
Ok(theme)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ impl FromStr for Which {
|
|||
which: Which,
|
||||
}
|
||||
|
||||
let outer = toml::from_str::<Outer>(s)
|
||||
.context("Failed to parse the [which] section in your yazi.toml")?;
|
||||
let outer: Outer =
|
||||
toml::from_str(s).context("Failed to parse the [which] section in your yazi.toml")?;
|
||||
|
||||
Ok(outer.which)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ async fn main() -> anyhow::Result<()> {
|
|||
|
||||
yazi_config::init()?;
|
||||
|
||||
yazi_adapter::init();
|
||||
yazi_adapter::init()?;
|
||||
|
||||
yazi_boot::init();
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ macro_rules! config_preset {
|
|||
($name:literal) => {{
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
std::borrow::Cow::Owned(
|
||||
std::borrow::Cow::from(
|
||||
std::fs::read_to_string(concat!(env!("CARGO_MANIFEST_DIR"), "/preset/", $name, ".toml"))
|
||||
.expect(concat!("Failed to read 'yazi-config/preset/", $name, ".toml'")),
|
||||
)
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
std::borrow::Cow::Borrowed(include_str!(concat!(
|
||||
std::borrow::Cow::from(include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/preset/",
|
||||
$name,
|
||||
|
|
@ -37,3 +37,28 @@ macro_rules! plugin_preset {
|
|||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! theme_preset {
|
||||
($name:literal) => {{
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let append = std::fs::read_to_string(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/preset/theme+",
|
||||
$name,
|
||||
".toml"
|
||||
))
|
||||
.expect(concat!("Failed to read 'yazi-config/preset/theme+", $name, ".toml'"));
|
||||
std::borrow::Cow::from(format!("{}\n{append}", &$crate::config_preset!("theme")))
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
std::borrow::Cow::from(concat!(
|
||||
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/preset/theme.toml")),
|
||||
"\n",
|
||||
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/preset/theme+", $name, ".toml"))
|
||||
))
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl Loader {
|
|||
|
||||
let p = BOOT.plugin_dir.join(format!("{name}.yazi/init.lua"));
|
||||
let chunk =
|
||||
fs::read(&p).await.with_context(|| format!("failed to load plugin from {p:?}"))?.into();
|
||||
fs::read(&p).await.with_context(|| format!("Failed to load plugin from {p:?}"))?.into();
|
||||
|
||||
self.cache.write().insert(name.to_owned(), chunk);
|
||||
Ok(())
|
||||
|
|
|
|||
49
yazi-shared/src/either.rs
Normal file
49
yazi-shared/src/either.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Either<L, R> {
|
||||
Left(L),
|
||||
Right(R),
|
||||
}
|
||||
|
||||
impl<L, R> Either<L, R> {
|
||||
pub fn left(&self) -> Option<&L> {
|
||||
match self {
|
||||
Either::Left(l) => Some(l),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn right(&self) -> Option<&R> {
|
||||
match self {
|
||||
Either::Right(r) => Some(r),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left_mut(&mut self) -> Option<&mut L> {
|
||||
match self {
|
||||
Either::Left(l) => Some(l),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn right_mut(&mut self) -> Option<&mut R> {
|
||||
match self {
|
||||
Either::Right(r) => Some(r),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_left(self) -> Option<L> {
|
||||
match self {
|
||||
Either::Left(l) => Some(l),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_right(self) -> Option<R> {
|
||||
match self {
|
||||
Either::Right(r) => Some(r),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
yazi_macro::mod_pub!(errors event fs shell theme translit);
|
||||
|
||||
yazi_macro::mod_flat!(chars condition debounce env id layer natsort number os rand ro_cell sync_cell terminal throttle time xdg);
|
||||
yazi_macro::mod_flat!(chars condition debounce either env id layer natsort number os rand ro_cell sync_cell terminal throttle time xdg);
|
||||
|
||||
pub fn init() {
|
||||
#[cfg(unix)]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue