This commit is contained in:
sxyazi 2024-11-25 14:22:01 +08:00
parent 6ff64cd33a
commit 435d910751
No known key found for this signature in database
20 changed files with 404 additions and 216 deletions

View file

@ -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"}

View file

@ -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
View 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()),
)
}
}

View file

@ -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[>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)
}
}
}
}

View file

@ -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(())
}

View 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) => &[],
}
}
}

View file

@ -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"))?;

View file

@ -214,16 +214,3 @@ rules = [
]
# : }}}
# : Icons {{{
[icon]
globs = []
dirs = []
files = []
exts = []
conds = []
# : }}}

View file

@ -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(())
}

View file

@ -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]

View file

@ -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,
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()
}
#[derive(Deserialize)]
struct Inner {
#[serde(rename = "use")]
pub use_: String,
})
}
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"))),
}
}
}

View file

@ -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)
}
}

View file

@ -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)
}

View file

@ -20,7 +20,7 @@ async fn main() -> anyhow::Result<()> {
yazi_config::init()?;
yazi_adapter::init();
yazi_adapter::init()?;
yazi_boot::init();

View file

@ -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"))
))
}
}};
}

View file

@ -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
View 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,
}
}
}

View file

@ -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)]