mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
fix: reverse the order of CSI-based and environment-based terminal detection (#2310)
This commit is contained in:
parent
17ff1e8812
commit
793c90f021
22 changed files with 133 additions and 166 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
cargo publish -p yazi-macro && sleep 30
|
cargo publish -p yazi-macro && sleep 30
|
||||||
cargo publish -p yazi-codegen && sleep 30
|
cargo publish -p yazi-codegen && sleep 30
|
||||||
cargo publish -p yazi-shared && sleep 30
|
cargo publish -p yazi-shared && sleep 30
|
||||||
|
cargo publish -p yazi-ffi && sleep 30
|
||||||
cargo publish -p yazi-fs && sleep 30
|
cargo publish -p yazi-fs && sleep 30
|
||||||
cargo publish -p yazi-config && sleep 30
|
cargo publish -p yazi-config && sleep 30
|
||||||
cargo publish -p yazi-proxy && sleep 30
|
cargo publish -p yazi-proxy && sleep 30
|
||||||
|
|
|
||||||
|
|
@ -81,9 +81,9 @@ impl Adapter {
|
||||||
|
|
||||||
impl Adapter {
|
impl Adapter {
|
||||||
pub fn matches(emulator: Emulator) -> Self {
|
pub fn matches(emulator: Emulator) -> Self {
|
||||||
if matches!(emulator.kind.left(), Some(Brand::Microsoft)) {
|
if emulator.kind.is_left_and(|&b| b == Brand::Microsoft) {
|
||||||
return Self::Sixel;
|
return Self::Sixel;
|
||||||
} else if *WSL && matches!(emulator.kind.left(), Some(Brand::WezTerm)) {
|
} else if WSL.get() && emulator.kind.is_left_and(|&b| b == Brand::WezTerm) {
|
||||||
return Self::KgpOld;
|
return Self::KgpOld;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,7 +92,7 @@ impl Adapter {
|
||||||
protocols.retain(|p| *p == Self::Iip);
|
protocols.retain(|p| *p == Self::Iip);
|
||||||
if env_exists("ZELLIJ_SESSION_NAME") {
|
if env_exists("ZELLIJ_SESSION_NAME") {
|
||||||
protocols.retain(|p| *p == Self::Sixel);
|
protocols.retain(|p| *p == Self::Sixel);
|
||||||
} else if *TMUX {
|
} else if TMUX.get() {
|
||||||
protocols.retain(|p| *p != Self::KgpOld);
|
protocols.retain(|p| *p != Self::KgpOld);
|
||||||
}
|
}
|
||||||
if let Some(p) = protocols.first() {
|
if let Some(p) = protocols.first() {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use yazi_shared::env_exists;
|
||||||
|
|
||||||
use crate::{Mux, NVIM};
|
use crate::{Mux, NVIM};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum Brand {
|
pub enum Brand {
|
||||||
Kitty,
|
Kitty,
|
||||||
Konsole,
|
Konsole,
|
||||||
|
|
@ -19,6 +19,7 @@ pub enum Brand {
|
||||||
Tabby,
|
Tabby,
|
||||||
Hyper,
|
Hyper,
|
||||||
Mintty,
|
Mintty,
|
||||||
|
Tmux,
|
||||||
Neovim,
|
Neovim,
|
||||||
Apple,
|
Apple,
|
||||||
Urxvt,
|
Urxvt,
|
||||||
|
|
@ -26,10 +27,24 @@ pub enum Brand {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Brand {
|
impl Brand {
|
||||||
|
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),
|
||||||
|
("tmux ", Self::Tmux),
|
||||||
|
("Bobcat", Self::Bobcat),
|
||||||
|
];
|
||||||
|
names.into_iter().find(|&(n, _)| resp.contains(n)).map(|(_, b)| b)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_env() -> Option<Self> {
|
pub fn from_env() -> Option<Self> {
|
||||||
use Brand as B;
|
use Brand as B;
|
||||||
|
|
||||||
if *NVIM {
|
if NVIM.get() {
|
||||||
return Some(Self::Neovim);
|
return Some(Self::Neovim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,19 +91,6 @@ impl Brand {
|
||||||
None
|
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),
|
|
||||||
("Bobcat", Self::Bobcat),
|
|
||||||
];
|
|
||||||
names.into_iter().find(|&(n, _)| resp.contains(n)).map(|(_, b)| b)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn adapters(self) -> &'static [crate::Adapter] {
|
pub(super) fn adapters(self) -> &'static [crate::Adapter] {
|
||||||
use Brand as B;
|
use Brand as B;
|
||||||
|
|
||||||
|
|
@ -109,6 +111,7 @@ impl Brand {
|
||||||
B::Tabby => &[A::Iip, A::Sixel],
|
B::Tabby => &[A::Iip, A::Sixel],
|
||||||
B::Hyper => &[A::Iip, A::Sixel],
|
B::Hyper => &[A::Iip, A::Sixel],
|
||||||
B::Mintty => &[A::Iip],
|
B::Mintty => &[A::Iip],
|
||||||
|
B::Tmux => &[],
|
||||||
B::Neovim => &[],
|
B::Neovim => &[],
|
||||||
B::Apple => &[],
|
B::Apple => &[],
|
||||||
B::Urxvt => &[],
|
B::Urxvt => &[],
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,8 @@ impl Dimension {
|
||||||
pub fn ratio() -> Option<(f64, f64)> {
|
pub fn ratio() -> Option<(f64, f64)> {
|
||||||
let s = Self::available();
|
let s = Self::available();
|
||||||
Some(if s.width == 0 || s.height == 0 {
|
Some(if s.width == 0 || s.height == 0 {
|
||||||
(EMULATOR.cell_size?.0 as f64, EMULATOR.cell_size?.1 as f64)
|
let s = EMULATOR.get().cell_size?;
|
||||||
|
(s.0 as f64, s.1 as f64)
|
||||||
} else {
|
} else {
|
||||||
(f64::from(s.width) / f64::from(s.columns), f64::from(s.height) / f64::from(s.rows))
|
(f64::from(s.width) / f64::from(s.columns), f64::from(s.height) / f64::from(s.rows))
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -53,14 +53,11 @@ impl Iip {
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
buf,
|
buf,
|
||||||
"{}]1337;File=inline=1;size={};width={}px;height={}px;doNotMoveCursor=1:",
|
"{START}]1337;File=inline=1;size={};width={w}px;height={h}px;doNotMoveCursor=1:",
|
||||||
START,
|
|
||||||
b.len(),
|
b.len(),
|
||||||
w,
|
|
||||||
h,
|
|
||||||
)?;
|
)?;
|
||||||
STANDARD.encode_string(b, &mut buf);
|
STANDARD.encode_string(b, &mut buf);
|
||||||
write!(buf, "\x07{}", CLOSE)?;
|
write!(buf, "\x07{CLOSE}")?;
|
||||||
|
|
||||||
Ok(buf.into_bytes())
|
Ok(buf.into_bytes())
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -336,7 +336,7 @@ impl Kgp {
|
||||||
write!(stderr, "{s}")?;
|
write!(stderr, "{s}")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(stderr, "{}_Gq=2,a=d,d=A{}\\{}", START, ESCAPE, CLOSE)?;
|
write!(stderr, "{START}_Gq=2,a=d,d=A{ESCAPE}\\{CLOSE}")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -350,31 +350,21 @@ impl Kgp {
|
||||||
if let Some(first) = it.next() {
|
if let Some(first) = it.next() {
|
||||||
write!(
|
write!(
|
||||||
buf,
|
buf,
|
||||||
"{}_Gq=2,a=T,i=1,C=1,U=1,f={},s={},v={},m={};{}{}\\{}",
|
"{START}_Gq=2,a=T,i=1,C=1,U=1,f={format},s={},v={},m={};{}{ESCAPE}\\{CLOSE}",
|
||||||
START,
|
|
||||||
format,
|
|
||||||
size.0,
|
size.0,
|
||||||
size.1,
|
size.1,
|
||||||
it.peek().is_some() as u8,
|
it.peek().is_some() as u8,
|
||||||
unsafe { str::from_utf8_unchecked(first) },
|
unsafe { str::from_utf8_unchecked(first) },
|
||||||
ESCAPE,
|
|
||||||
CLOSE
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(chunk) = it.next() {
|
while let Some(chunk) = it.next() {
|
||||||
write!(
|
write!(buf, "{START}_Gm={};{}{ESCAPE}\\{CLOSE}", it.peek().is_some() as u8, unsafe {
|
||||||
buf,
|
str::from_utf8_unchecked(chunk)
|
||||||
"{}_Gm={};{}{}\\{}",
|
})?;
|
||||||
START,
|
|
||||||
it.peek().is_some() as u8,
|
|
||||||
unsafe { str::from_utf8_unchecked(chunk) },
|
|
||||||
ESCAPE,
|
|
||||||
CLOSE
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(buf, "{}", CLOSE)?;
|
write!(buf, "{CLOSE}")?;
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ impl KgpOld {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn image_erase(_: Rect) -> Result<()> {
|
pub(crate) fn image_erase(_: Rect) -> Result<()> {
|
||||||
let mut stderr = LineWriter::new(stderr());
|
let mut stderr = LineWriter::new(stderr());
|
||||||
write!(stderr, "{}_Gq=2,a=d,d=A{}\\{}", START, ESCAPE, CLOSE)?;
|
write!(stderr, "{START}_Gq=2,a=d,d=A{ESCAPE}\\{CLOSE}")?;
|
||||||
stderr.flush()?;
|
stderr.flush()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -41,31 +41,21 @@ impl KgpOld {
|
||||||
if let Some(first) = it.next() {
|
if let Some(first) = it.next() {
|
||||||
write!(
|
write!(
|
||||||
buf,
|
buf,
|
||||||
"{}_Gq=2,a=T,z=-1,C=1,f={},s={},v={},m={};{}{}\\{}",
|
"{START}_Gq=2,a=T,z=-1,C=1,f={format},s={},v={},m={};{}{ESCAPE}\\{CLOSE}",
|
||||||
START,
|
|
||||||
format,
|
|
||||||
size.0,
|
size.0,
|
||||||
size.1,
|
size.1,
|
||||||
it.peek().is_some() as u8,
|
it.peek().is_some() as u8,
|
||||||
unsafe { str::from_utf8_unchecked(first) },
|
unsafe { str::from_utf8_unchecked(first) },
|
||||||
ESCAPE,
|
|
||||||
CLOSE
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(chunk) = it.next() {
|
while let Some(chunk) = it.next() {
|
||||||
write!(
|
write!(buf, "{START}_Gm={};{}{ESCAPE}\\{CLOSE}", it.peek().is_some() as u8, unsafe {
|
||||||
buf,
|
str::from_utf8_unchecked(chunk)
|
||||||
"{}_Gm={};{}{}\\{}",
|
})?;
|
||||||
START,
|
|
||||||
it.peek().is_some() as u8,
|
|
||||||
unsafe { str::from_utf8_unchecked(chunk) },
|
|
||||||
ESCAPE,
|
|
||||||
CLOSE
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(buf, "{}", CLOSE)?;
|
write!(buf, "{CLOSE}")?;
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ impl Sixel {
|
||||||
let nq = NeuQuant::new(PREVIEW.sixel_fraction as i32, 256 - alpha as usize, &img);
|
let nq = NeuQuant::new(PREVIEW.sixel_fraction as i32, 256 - alpha as usize, &img);
|
||||||
|
|
||||||
let mut buf: Vec<u8> = Vec::with_capacity(1 << 16);
|
let mut buf: Vec<u8> = Vec::with_capacity(1 << 16);
|
||||||
write!(buf, "{}P0;1;8q\"1;1;{};{}", START, img.width(), img.height())?;
|
write!(buf, "{START}P0;1;8q\"1;1;{};{}", img.width(), img.height())?;
|
||||||
|
|
||||||
// Palette
|
// Palette
|
||||||
for (i, c) in nq.color_map_rgba().chunks(4).enumerate() {
|
for (i, c) in nq.color_map_rgba().chunks(4).enumerate() {
|
||||||
|
|
@ -96,7 +96,7 @@ impl Sixel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(buf, "{}\\{}", ESCAPE, CLOSE)?;
|
write!(buf, "{ESCAPE}\\{CLOSE}")?;
|
||||||
Ok(buf)
|
Ok(buf)
|
||||||
})
|
})
|
||||||
.await?
|
.await?
|
||||||
|
|
|
||||||
|
|
@ -17,40 +17,37 @@ pub struct Emulator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Emulator {
|
impl Default for Emulator {
|
||||||
fn default() -> Self {
|
fn default() -> Self { Self::unknown() }
|
||||||
Self { kind: Either::Right(Unknown::default()), light: false, cell_size: None }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Emulator {
|
impl Emulator {
|
||||||
pub fn detect() -> Self {
|
pub fn detect() -> Result<Self> {
|
||||||
if let Some(brand) = Brand::from_env() {
|
|
||||||
Self { kind: Either::Left(brand), ..Self::detect_base().unwrap_or_default() }
|
|
||||||
} else {
|
|
||||||
Self::detect_full().unwrap_or_default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn detect_full() -> Result<Self> {
|
|
||||||
defer! { disable_raw_mode().ok(); }
|
defer! { disable_raw_mode().ok(); }
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
|
|
||||||
|
let resort = Brand::from_env();
|
||||||
|
let kgp_seq = if resort.is_none() {
|
||||||
|
Mux::csi("\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\")
|
||||||
|
} else {
|
||||||
|
"".into()
|
||||||
|
};
|
||||||
|
|
||||||
execute!(
|
execute!(
|
||||||
LineWriter::new(stderr()),
|
LineWriter::new(stderr()),
|
||||||
SavePosition,
|
SavePosition,
|
||||||
Print(Mux::csi("\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\")), // Detect KGP
|
Print(kgp_seq), // Detect KGP
|
||||||
Print(Mux::csi("\x1b[>q")), // Request terminal version
|
Print(Mux::csi("\x1b[>q")), // Request terminal version
|
||||||
Print("\x1b[16t"), // Request cell size
|
Print("\x1b[16t"), // Request cell size
|
||||||
Print("\x1b]11;?\x07"), // Request background color
|
Print("\x1b]11;?\x07"), // Request background color
|
||||||
Print(Mux::csi("\x1b[0c")), // Request device attributes
|
Print(Mux::csi("\x1b[0c")), // Request device attributes
|
||||||
RestorePosition
|
RestorePosition
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let resp = futures::executor::block_on(Self::read_until_da1());
|
let resp = futures::executor::block_on(Self::read_until_da1());
|
||||||
Mux::tmux_drain()?;
|
Mux::tmux_drain()?;
|
||||||
|
|
||||||
let kind = if let Some(brand) = Brand::from_csi(&resp) {
|
let kind = if let Some(b) = Brand::from_csi(&resp).or(resort) {
|
||||||
Either::Left(brand)
|
Either::Left(b)
|
||||||
} else {
|
} else {
|
||||||
Either::Right(Unknown {
|
Either::Right(Unknown {
|
||||||
kgp: resp.contains("\x1b_Gi=31;OK"),
|
kgp: resp.contains("\x1b_Gi=31;OK"),
|
||||||
|
|
@ -65,6 +62,10 @@ impl Emulator {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn unknown() -> Self {
|
||||||
|
Self { kind: Either::Right(Unknown::default()), light: false, cell_size: None }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn adapters(self) -> &'static [Adapter] {
|
pub fn adapters(self) -> &'static [Adapter] {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
Either::Left(brand) => brand.adapters(),
|
Either::Left(brand) => brand.adapters(),
|
||||||
|
|
@ -84,7 +85,7 @@ impl Emulator {
|
||||||
|
|
||||||
// I really don't want to add this,
|
// I really don't want to add this,
|
||||||
// But tmux and ConPTY sometimes cause the cursor position to get out of sync.
|
// But tmux and ConPTY sometimes cause the cursor position to get out of sync.
|
||||||
if *TMUX || cfg!(windows) {
|
if TMUX.get() || cfg!(windows) {
|
||||||
execute!(buf, SavePosition, MoveTo(x, y), Show)?;
|
execute!(buf, SavePosition, MoveTo(x, y), Show)?;
|
||||||
execute!(buf, MoveTo(x, y), Show)?;
|
execute!(buf, MoveTo(x, y), Show)?;
|
||||||
execute!(buf, MoveTo(x, y), Show)?;
|
execute!(buf, MoveTo(x, y), Show)?;
|
||||||
|
|
@ -94,7 +95,7 @@ impl Emulator {
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = cb(&mut buf);
|
let result = cb(&mut buf);
|
||||||
if *TMUX || cfg!(windows) {
|
if TMUX.get() || cfg!(windows) {
|
||||||
queue!(buf, Hide, RestorePosition)?;
|
queue!(buf, Hide, RestorePosition)?;
|
||||||
} else {
|
} else {
|
||||||
queue!(buf, RestorePosition)?;
|
queue!(buf, RestorePosition)?;
|
||||||
|
|
@ -194,27 +195,6 @@ impl Emulator {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detect_base() -> Result<Self> {
|
|
||||||
defer! { disable_raw_mode().ok(); }
|
|
||||||
enable_raw_mode()?;
|
|
||||||
|
|
||||||
execute!(
|
|
||||||
LineWriter::new(stderr()),
|
|
||||||
Print("\x1b[16t"), // Request cell size
|
|
||||||
Print("\x1b]11;?\x07"), // Request background color
|
|
||||||
Print("\x1b[0c"), // Request device attributes
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let resp = futures::executor::block_on(Self::read_until_da1());
|
|
||||||
Mux::tmux_drain()?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
light: Self::light_bg(&resp).unwrap_or_default(),
|
|
||||||
cell_size: Self::cell_size(&resp),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cell_size(resp: &str) -> Option<(u16, u16)> {
|
fn cell_size(resp: &str) -> Option<(u16, u16)> {
|
||||||
let b = resp.split_once("\x1b[6;")?.1.as_bytes();
|
let b = resp.split_once("\x1b[6;")?.1.as_bytes();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,48 +4,49 @@ yazi_macro::mod_pub!(drivers);
|
||||||
|
|
||||||
yazi_macro::mod_flat!(adapter brand dimension emulator image info mux unknown);
|
yazi_macro::mod_flat!(adapter brand dimension emulator image info mux unknown);
|
||||||
|
|
||||||
use yazi_shared::{RoCell, SyncCell, env_exists, in_wsl};
|
use yazi_shared::{SyncCell, env_exists, in_wsl};
|
||||||
|
|
||||||
pub static EMULATOR: RoCell<Emulator> = RoCell::new();
|
pub static EMULATOR: SyncCell<Emulator> = SyncCell::new(Emulator::unknown());
|
||||||
pub static ADAPTOR: RoCell<Adapter> = RoCell::new();
|
pub static ADAPTOR: SyncCell<Adapter> = SyncCell::new(Adapter::Chafa);
|
||||||
|
|
||||||
// Image state
|
// Image state
|
||||||
static SHOWN: SyncCell<Option<ratatui::layout::Rect>> = SyncCell::new(None);
|
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();
|
|
||||||
static START: RoCell<&'static str> = RoCell::new();
|
|
||||||
static CLOSE: RoCell<&'static str> = RoCell::new();
|
|
||||||
|
|
||||||
// WSL support
|
// WSL support
|
||||||
pub static WSL: RoCell<bool> = RoCell::new();
|
pub static WSL: SyncCell<bool> = SyncCell::new(false);
|
||||||
|
|
||||||
// Neovim support
|
// Neovim support
|
||||||
pub static NVIM: RoCell<bool> = RoCell::new();
|
pub static NVIM: SyncCell<bool> = SyncCell::new(false);
|
||||||
|
|
||||||
|
// Tmux support
|
||||||
|
pub static TMUX: SyncCell<bool> = SyncCell::new(false);
|
||||||
|
static ESCAPE: SyncCell<&'static str> = SyncCell::new("\x1b");
|
||||||
|
static START: SyncCell<&'static str> = SyncCell::new("\x1b");
|
||||||
|
static CLOSE: SyncCell<&'static str> = SyncCell::new("");
|
||||||
|
|
||||||
pub fn init() -> anyhow::Result<()> {
|
pub fn init() -> anyhow::Result<()> {
|
||||||
init_default();
|
|
||||||
|
|
||||||
EMULATOR.init(Emulator::detect());
|
|
||||||
yazi_config::init_flavor(EMULATOR.light)?;
|
|
||||||
|
|
||||||
ADAPTOR.init(Adapter::matches(*EMULATOR));
|
|
||||||
ADAPTOR.start();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_default() {
|
|
||||||
// Tmux support
|
|
||||||
TMUX.init(Mux::tmux_passthrough());
|
|
||||||
ESCAPE.init(if *TMUX { "\x1b\x1b" } else { "\x1b" });
|
|
||||||
START.init(if *TMUX { "\x1bPtmux;\x1b\x1b" } else { "\x1b" });
|
|
||||||
CLOSE.init(if *TMUX { "\x1b\\" } else { "" });
|
|
||||||
|
|
||||||
// WSL support
|
// WSL support
|
||||||
WSL.init(in_wsl());
|
WSL.set(in_wsl());
|
||||||
|
|
||||||
// Neovim support
|
// Neovim support
|
||||||
NVIM.init(env_exists("NVIM_LOG_FILE") && env_exists("NVIM"));
|
NVIM.set(env_exists("NVIM_LOG_FILE") && env_exists("NVIM"));
|
||||||
|
|
||||||
|
// Emulator detection
|
||||||
|
EMULATOR.set(Emulator::detect().unwrap_or_default());
|
||||||
|
TMUX.set(EMULATOR.get().kind.is_left_and(|&b| b == Brand::Tmux));
|
||||||
|
|
||||||
|
// Tmux support
|
||||||
|
if TMUX.get() {
|
||||||
|
ESCAPE.set("\x1b\x1b");
|
||||||
|
START.set("\x1bPtmux;\x1b\x1b");
|
||||||
|
CLOSE.set("\x1b\\");
|
||||||
|
Mux::tmux_passthrough();
|
||||||
|
EMULATOR.set(Emulator::detect().unwrap_or_default());
|
||||||
|
}
|
||||||
|
|
||||||
|
yazi_config::init_flavor(EMULATOR.get().light)?;
|
||||||
|
|
||||||
|
ADAPTOR.set(Adapter::matches(EMULATOR.get()));
|
||||||
|
ADAPTOR.get().start();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,25 +7,17 @@ pub struct Mux;
|
||||||
|
|
||||||
impl Mux {
|
impl Mux {
|
||||||
pub fn csi(s: &str) -> std::borrow::Cow<str> {
|
pub fn csi(s: &str) -> std::borrow::Cow<str> {
|
||||||
if *TMUX && !*NVIM {
|
if TMUX.get() && !NVIM.get() {
|
||||||
std::borrow::Cow::Owned(format!(
|
std::borrow::Cow::Owned(format!(
|
||||||
"{}{}{}",
|
"{START}{}{CLOSE}",
|
||||||
*START,
|
s.trim_start_matches('\x1b').replace('\x1b', ESCAPE.get()),
|
||||||
s.trim_start_matches('\x1b').replace('\x1b', *ESCAPE),
|
|
||||||
*CLOSE
|
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
std::borrow::Cow::Borrowed(s)
|
std::borrow::Cow::Borrowed(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tmux_passthrough() -> bool {
|
pub fn tmux_passthrough() {
|
||||||
if !std::env::var("TERM_PROGRAM").is_ok_and(|s| s == "tmux")
|
|
||||||
&& !std::env::var("TERM").is_ok_and(|s| s.starts_with("tmux"))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let child = std::process::Command::new("tmux")
|
let child = std::process::Command::new("tmux")
|
||||||
.args(["set", "-p", "allow-passthrough", "on"])
|
.args(["set", "-p", "allow-passthrough", "on"])
|
||||||
.stdin(std::process::Stdio::null())
|
.stdin(std::process::Stdio::null())
|
||||||
|
|
@ -46,11 +38,10 @@ impl Mux {
|
||||||
error!("Failed to spawn `tmux set -p allow-passthrough on`: {e}");
|
error!("Failed to spawn `tmux set -p allow-passthrough on`: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tmux_drain() -> Result<()> {
|
pub fn tmux_drain() -> Result<()> {
|
||||||
if *TMUX && !*NVIM {
|
if TMUX.get() && !NVIM.get() {
|
||||||
crossterm::execute!(std::io::stderr(), crossterm::style::Print(Mux::csi("\x1b[5n")))?;
|
crossterm::execute!(std::io::stderr(), crossterm::style::Print(Mux::csi("\x1b[5n")))?;
|
||||||
_ = futures::executor::block_on(Emulator::read_until_dsr());
|
_ = futures::executor::block_on(Emulator::read_until_dsr());
|
||||||
}
|
}
|
||||||
|
|
@ -74,7 +65,7 @@ impl Mux {
|
||||||
|
|
||||||
pub(super) fn term_program() -> (Option<String>, Option<String>) {
|
pub(super) fn term_program() -> (Option<String>, Option<String>) {
|
||||||
let (mut term, mut program) = (None, None);
|
let (mut term, mut program) = (None, None);
|
||||||
if !*TMUX {
|
if !TMUX.get() {
|
||||||
return (term, program);
|
return (term, program);
|
||||||
}
|
}
|
||||||
let Ok(output) = std::process::Command::new("tmux").arg("show-environment").output() else {
|
let Ok(output) = std::process::Command::new("tmux").arg("show-environment").output() else {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
use crate::Adapter;
|
use crate::Adapter;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Unknown {
|
pub struct Unknown {
|
||||||
pub kgp: bool,
|
pub kgp: bool,
|
||||||
pub sixel: bool,
|
pub sixel: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Unknown {
|
impl Unknown {
|
||||||
|
pub(super) const fn default() -> Self { Self { kgp: false, sixel: false } }
|
||||||
|
|
||||||
pub(super) fn adapters(self) -> &'static [Adapter] {
|
pub(super) fn adapters(self) -> &'static [Adapter] {
|
||||||
use Adapter as A;
|
use Adapter as A;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,13 @@ impl Actions {
|
||||||
writeln!(s, "\nYa")?;
|
writeln!(s, "\nYa")?;
|
||||||
writeln!(s, " Version: {}", Self::process_output("ya", "--version"))?;
|
writeln!(s, " Version: {}", Self::process_output("ya", "--version"))?;
|
||||||
|
|
||||||
let emulator = yazi_adapter::Emulator::detect();
|
let emulator = yazi_adapter::Emulator::detect().unwrap_or_default();
|
||||||
writeln!(s, "\nEmulator")?;
|
writeln!(s, "\nEmulator")?;
|
||||||
writeln!(s, " TERM : {:?}", env::var_os("TERM"))?;
|
writeln!(s, " TERM : {:?}", env::var_os("TERM"))?;
|
||||||
writeln!(s, " TERM_PROGRAM : {:?}", env::var_os("TERM_PROGRAM"))?;
|
writeln!(s, " TERM_PROGRAM : {:?}", env::var_os("TERM_PROGRAM"))?;
|
||||||
writeln!(s, " TERM_PROGRAM_VERSION: {:?}", env::var_os("TERM_PROGRAM_VERSION"))?;
|
writeln!(s, " TERM_PROGRAM_VERSION: {:?}", env::var_os("TERM_PROGRAM_VERSION"))?;
|
||||||
writeln!(s, " Brand.from_env : {:?}", yazi_adapter::Brand::from_env())?;
|
writeln!(s, " Brand.from_env : {:?}", yazi_adapter::Brand::from_env())?;
|
||||||
writeln!(s, " Emulator.detect : {:?}", emulator)?;
|
writeln!(s, " Emulator.detect : {:?}", emulator)?;
|
||||||
writeln!(s, " Emulator.detect_full: {:?}", yazi_adapter::Emulator::detect_full())?;
|
|
||||||
|
|
||||||
writeln!(s, "\nAdapter")?;
|
writeln!(s, "\nAdapter")?;
|
||||||
writeln!(s, " Adapter.matches: {:?}", yazi_adapter::Adapter::matches(emulator))?;
|
writeln!(s, " Adapter.matches: {:?}", yazi_adapter::Adapter::matches(emulator))?;
|
||||||
|
|
@ -42,10 +41,10 @@ impl Actions {
|
||||||
writeln!(s, " shared.in_ssh_connection: {:?}", yazi_shared::in_ssh_connection())?;
|
writeln!(s, " shared.in_ssh_connection: {:?}", yazi_shared::in_ssh_connection())?;
|
||||||
|
|
||||||
writeln!(s, "\nWSL")?;
|
writeln!(s, "\nWSL")?;
|
||||||
writeln!(s, " WSL: {:?}", *yazi_adapter::WSL)?;
|
writeln!(s, " WSL: {:?}", yazi_adapter::WSL.get())?;
|
||||||
|
|
||||||
writeln!(s, "\nNeovim")?;
|
writeln!(s, "\nNeovim")?;
|
||||||
writeln!(s, " NVIM : {}", *yazi_adapter::NVIM)?;
|
writeln!(s, " NVIM : {}", yazi_adapter::NVIM.get())?;
|
||||||
writeln!(s, " Neovim version: {}", Self::process_output("nvim", "--version"))?;
|
writeln!(s, " Neovim version: {}", Self::process_output("nvim", "--version"))?;
|
||||||
|
|
||||||
writeln!(s, "\nVariables")?;
|
writeln!(s, "\nVariables")?;
|
||||||
|
|
@ -75,7 +74,7 @@ impl Actions {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
writeln!(s, "\nMultiplexers")?;
|
writeln!(s, "\nMultiplexers")?;
|
||||||
writeln!(s, " TMUX : {}", *yazi_adapter::TMUX)?;
|
writeln!(s, " TMUX : {}", yazi_adapter::TMUX.get())?;
|
||||||
writeln!(s, " tmux version : {}", Self::process_output("tmux", "-V"))?;
|
writeln!(s, " tmux version : {}", Self::process_output("tmux", "-V"))?;
|
||||||
writeln!(s, " tmux build flags : enable-sixel={}", Mux::tmux_sixel_flag())?;
|
writeln!(s, " tmux build flags : enable-sixel={}", Mux::tmux_sixel_flag())?;
|
||||||
writeln!(s, " ZELLIJ_SESSION_NAME: {:?}", env::var_os("ZELLIJ_SESSION_NAME"))?;
|
writeln!(s, " ZELLIJ_SESSION_NAME: {:?}", env::var_os("ZELLIJ_SESSION_NAME"))?;
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ However, if you want to customize certain configurations:
|
||||||
|
|
||||||
- Create a `yazi.toml` in your config directory to override certain settings in [`yazi-default.toml`][yazi-default], so either:
|
- Create a `yazi.toml` in your config directory to override certain settings in [`yazi-default.toml`][yazi-default], so either:
|
||||||
- `~/.config/yazi/yazi.toml` on Unix-like systems
|
- `~/.config/yazi/yazi.toml` on Unix-like systems
|
||||||
- `C:\Users\USERNAME\AppData\Roaming\yazi\config\yazi.toml` on Windows
|
- `%AppData%\yazi\config\yazi.toml` on Windows
|
||||||
- Create a `keymap.toml` in your config directory to override certain settings in [`keymap-default.toml`][keymap-default], so either:
|
- Create a `keymap.toml` in your config directory to override certain settings in [`keymap-default.toml`][keymap-default], so either:
|
||||||
- `~/.config/yazi/keymap.toml` on Unix-like systems
|
- `~/.config/yazi/keymap.toml` on Unix-like systems
|
||||||
- `C:\Users\USERNAME\AppData\Roaming\yazi\config\keymap.toml` on Windows
|
- `%AppData%\yazi\config\keymap.toml` on Windows
|
||||||
- Create a `theme.toml` in your config directory to override certain settings in [`theme-light.toml`][theme-light] and [`theme-dark.toml`][theme-dark], so either:
|
- Create a `theme.toml` in your config directory to override certain settings in [`theme-light.toml`][theme-light] and [`theme-dark.toml`][theme-dark], so either:
|
||||||
- `~/.config/yazi/theme.toml` on Unix-like systems
|
- `~/.config/yazi/theme.toml` on Unix-like systems
|
||||||
- `C:\Users\USERNAME\AppData\Roaming\yazi\config\theme.toml` on Windows
|
- `%AppData%\yazi\config\theme.toml` on Windows
|
||||||
|
|
||||||
For the user's `theme.toml` file, you can only apply the same color scheme to both the light and dark themes.
|
For the user's `theme.toml` file, you can only apply the same color scheme to both the light and dark themes.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ impl Watcher {
|
||||||
};
|
};
|
||||||
|
|
||||||
let config = notify::Config::default().with_poll_interval(Duration::from_millis(500));
|
let config = notify::Config::default().with_poll_interval(Duration::from_millis(500));
|
||||||
if *yazi_adapter::WSL {
|
if yazi_adapter::WSL.get() {
|
||||||
tokio::spawn(Self::fan_in(in_rx, PollWatcher::new(handler, config).unwrap()));
|
tokio::spawn(Self::fan_in(in_rx, PollWatcher::new(handler, config).unwrap()));
|
||||||
} else {
|
} else {
|
||||||
tokio::spawn(Self::fan_in(in_rx, RecommendedWatcher::new(handler, config).unwrap()));
|
tokio::spawn(Self::fan_in(in_rx, RecommendedWatcher::new(handler, config).unwrap()));
|
||||||
|
|
|
||||||
|
|
@ -70,14 +70,14 @@ impl Preview {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
self.abort();
|
self.abort();
|
||||||
ADAPTOR.image_hide().ok();
|
ADAPTOR.get().image_hide().ok();
|
||||||
render!(self.lock.take().is_some())
|
render!(self.lock.take().is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn reset_image(&mut self) {
|
pub fn reset_image(&mut self) {
|
||||||
self.abort();
|
self.abort();
|
||||||
ADAPTOR.image_hide().ok();
|
ADAPTOR.get().image_hide().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ impl Term {
|
||||||
};
|
};
|
||||||
|
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
if *yazi_adapter::TMUX {
|
if yazi_adapter::TMUX.get() {
|
||||||
yazi_adapter::Mux::tmux_passthrough();
|
yazi_adapter::Mux::tmux_passthrough();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,12 +268,12 @@ mod screen {
|
||||||
|
|
||||||
impl crossterm::Command for SetScreen {
|
impl crossterm::Command for SetScreen {
|
||||||
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
|
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
|
||||||
if self.0 == *TMUX { Ok(()) } else { EnterAlternateScreen.write_ansi(f) }
|
if self.0 == TMUX.get() { Ok(()) } else { EnterAlternateScreen.write_ansi(f) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn execute_winapi(&self) -> std::io::Result<()> {
|
fn execute_winapi(&self) -> std::io::Result<()> {
|
||||||
if self.0 == *TMUX { Ok(()) } else { EnterAlternateScreen.execute_winapi() }
|
if self.0 == TMUX.get() { Ok(()) } else { EnterAlternateScreen.execute_winapi() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ function M:setup()
|
||||||
ps.sub_remote("extract", function(args)
|
ps.sub_remote("extract", function(args)
|
||||||
local noisy = #args == 1 and ' "" --noisy' or ' ""'
|
local noisy = #args == 1 and ' "" --noisy' or ' ""'
|
||||||
for _, arg in ipairs(args) do
|
for _, arg in ipairs(args) do
|
||||||
ya.manager_emit("plugin", { self._id, args = ya.quote(arg, true) .. noisy })
|
ya.manager_emit("plugin", { self._id, ya.quote(arg, true) .. noisy })
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,11 @@ impl ratatui::widgets::Widget for Clear {
|
||||||
{
|
{
|
||||||
ratatui::widgets::Clear.render(area, buf);
|
ratatui::widgets::Clear.render(area, buf);
|
||||||
|
|
||||||
let Some(r) = ADAPTOR.shown_load().and_then(|r| overlap(area, r)) else {
|
let Some(r) = ADAPTOR.get().shown_load().and_then(|r| overlap(area, r)) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
ADAPTOR.image_erase(r).ok();
|
ADAPTOR.get().image_erase(r).ok();
|
||||||
COLLISION.store(true, Ordering::Relaxed);
|
COLLISION.store(true, Ordering::Relaxed);
|
||||||
for y in r.top()..r.bottom() {
|
for y in r.top()..r.bottom() {
|
||||||
for x in r.left()..r.right() {
|
for x in r.left()..r.right() {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ impl Utils {
|
||||||
|
|
||||||
pub(super) fn image_show(lua: &Lua) -> mlua::Result<Function> {
|
pub(super) fn image_show(lua: &Lua) -> mlua::Result<Function> {
|
||||||
lua.create_async_function(|lua, (url, rect): (UrlRef, Rect)| async move {
|
lua.create_async_function(|lua, (url, rect): (UrlRef, Rect)| async move {
|
||||||
if let Ok(area) = ADAPTOR.image_show(&url, *rect).await {
|
if let Ok(area) = ADAPTOR.get().image_show(&url, *rect).await {
|
||||||
Rect::from(area).into_lua(&lua)
|
Rect::from(area).into_lua(&lua)
|
||||||
} else {
|
} else {
|
||||||
Value::Nil.into_lua(&lua)
|
Value::Nil.into_lua(&lua)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,14 @@ impl<L, R> Either<L, R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_left_and<F: FnOnce(&L) -> bool>(&self, f: F) -> bool {
|
||||||
|
self.left().map(f).unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_right_and<F: FnOnce(&R) -> bool>(&self, f: F) -> bool {
|
||||||
|
self.right().map(f).unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_left(self) -> Option<L> {
|
pub fn into_left(self) -> Option<L> {
|
||||||
match self {
|
match self {
|
||||||
Either::Left(l) => Some(l),
|
Either::Left(l) => Some(l),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{cell::Cell, fmt::{Debug, Formatter}, ops::Deref};
|
use std::{cell::Cell, fmt::{Debug, Display, Formatter}, ops::Deref};
|
||||||
|
|
||||||
/// [`SyncCell`], but [`Sync`].
|
/// [`SyncCell`], but [`Sync`].
|
||||||
///
|
///
|
||||||
|
|
@ -34,3 +34,7 @@ impl<T: Copy + Debug> Debug for SyncCell<T> {
|
||||||
f.debug_struct("SyncCell").field("value", &self.get()).finish()
|
f.debug_struct("SyncCell").field("value", &self.get()).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Copy + Display> Display for SyncCell<T> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Display::fmt(&self.get(), f) }
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue