From b10f2de16d46df3ed7f6efe99ac966fd49d6e919 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Sat, 9 Mar 2024 19:04:46 +0800 Subject: [PATCH] feat: add `--debug` flag to print debug information (#794) --- .github/ISSUE_TEMPLATE/bug.yml | 23 ++-- Cargo.lock | 1 + yazi-adaptor/src/adaptor.rs | 222 ++++++--------------------------- yazi-adaptor/src/emulator.rs | 155 +++++++++++++++++++++++ yazi-adaptor/src/lib.rs | 8 +- yazi-adaptor/src/ueberzug.rs | 21 ++-- yazi-boot/Cargo.toml | 5 +- yazi-boot/src/args.rs | 4 + yazi-boot/src/boot.rs | 71 +++++++++-- 9 files changed, 299 insertions(+), 211 deletions(-) create mode 100644 yazi-adaptor/src/emulator.rs diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index a4e315be2..1c0b17e9a 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -12,21 +12,14 @@ body: - Linux Wayland - macOS - Windows + - Windows WSL validations: required: true - type: input id: terminal attributes: label: What terminal are you running Yazi in? - placeholder: "ex: Kitty v0.30.1" - validations: - required: true - - type: input - id: version - attributes: - label: Yazi version - description: Please do a `yazi -V` and paste the output here. - placeholder: "ex: yazi 0.1.5 (3867c29 2023-11-25)" + placeholder: "ex: kitty v0.32.2" validations: required: true - type: dropdown @@ -38,6 +31,18 @@ body: - Not tried, and I'll explain why below validations: required: true + - type: textarea + id: debug + attributes: + label: "`yazi --debug` output" + description: Please do a `yazi --debug` and paste the output here. + value: | + + ```sh + + ``` + validations: + required: true - type: textarea id: description attributes: diff --git a/Cargo.lock b/Cargo.lock index d9d53dd82..5c03f312a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2716,6 +2716,7 @@ dependencies = [ "clap_complete_nushell", "serde", "vergen", + "yazi-adaptor", "yazi-config", "yazi-shared", ] diff --git a/yazi-adaptor/src/adaptor.rs b/yazi-adaptor/src/adaptor.rs index d24c9abb9..0cece50af 100644 --- a/yazi-adaptor/src/adaptor.rs +++ b/yazi-adaptor/src/adaptor.rs @@ -1,13 +1,12 @@ -use std::{env, fmt::Display, io::{Read, Write}, path::Path, sync::Arc}; +use std::{env, fmt::Display, path::Path, sync::Arc}; -use anyhow::{anyhow, Result}; -use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; +use anyhow::Result; use ratatui::layout::Rect; use tracing::warn; use yazi_shared::{env_exists, term::Term}; use super::{Iterm2, Kitty, KittyOld}; -use crate::{ueberzug::Ueberzug, Sixel, SHOWN, TMUX}; +use crate::{ueberzug::Ueberzug, Emulator, Sixel, SHOWN, TMUX}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Adaptor { @@ -22,181 +21,6 @@ pub enum Adaptor { Chafa, } -#[derive(Clone)] -enum Emulator { - Unknown(Vec), - Kitty, - Konsole, - Iterm2, - WezTerm, - Foot, - Ghostty, - BlackBox, - VSCode, - Tabby, - Hyper, - Mintty, - Neovim, -} - -impl Adaptor { - fn emulator() -> Emulator { - if env_exists("NVIM_LOG_FILE") && env_exists("NVIM") { - return Emulator::Neovim; - } - - let vars = [ - ("KITTY_WINDOW_ID", Emulator::Kitty), - ("KONSOLE_VERSION", Emulator::Konsole), - ("ITERM_SESSION_ID", Emulator::Iterm2), - ("WEZTERM_EXECUTABLE", Emulator::WezTerm), - ("GHOSTTY_RESOURCES_DIR", Emulator::Ghostty), - ("VSCODE_INJECTION", Emulator::VSCode), - ("TABBY_CONFIG_DIRECTORY", Emulator::Tabby), - ]; - match vars.into_iter().find(|v| env_exists(v.0)) { - Some(var) => return var.1, - None => warn!("[Adaptor] No special environment variables detected"), - } - - let (term, program) = Self::via_env(); - match program.as_str() { - "iTerm.app" => return Emulator::Iterm2, - "WezTerm" => return Emulator::WezTerm, - "ghostty" => return Emulator::Ghostty, - "BlackBox" => return Emulator::BlackBox, - "vscode" => return Emulator::VSCode, - "Tabby" => return Emulator::Tabby, - "Hyper" => return Emulator::Hyper, - "mintty" => return Emulator::Mintty, - _ => warn!("[Adaptor] Unknown TERM_PROGRAM: {program}"), - } - match term.as_str() { - "xterm-kitty" => return Emulator::Kitty, - "foot" => return Emulator::Foot, - "foot-extra" => return Emulator::Foot, - "xterm-ghostty" => return Emulator::Ghostty, - _ => warn!("[Adaptor] Unknown TERM: {term}"), - } - - Self::via_csi().unwrap_or(Emulator::Unknown(vec![])) - } - - pub(super) fn detect() -> Self { - let mut protocols = match Self::emulator() { - Emulator::Unknown(adapters) => adapters, - Emulator::Kitty => vec![Self::Kitty], - Emulator::Konsole => vec![Self::KittyOld, Self::Iterm2, Self::Sixel], - Emulator::Iterm2 => vec![Self::Iterm2, Self::Sixel], - Emulator::WezTerm => vec![Self::Iterm2, Self::Sixel], - Emulator::Foot => vec![Self::Sixel], - Emulator::Ghostty => vec![Self::KittyOld], - Emulator::BlackBox => vec![Self::Sixel], - Emulator::VSCode => vec![Self::Iterm2, Self::Sixel], - Emulator::Tabby => vec![Self::Iterm2, Self::Sixel], - Emulator::Hyper => vec![Self::Iterm2, Self::Sixel], - Emulator::Mintty => vec![Self::Iterm2], - Emulator::Neovim => vec![], - }; - - #[cfg(windows)] - protocols.retain(|p| *p == Self::Iterm2); - if env_exists("ZELLIJ_SESSION_NAME") { - protocols.retain(|p| *p == Self::Sixel); - } - if *TMUX && protocols.len() > 1 { - protocols.retain(|p| *p != Self::KittyOld); - } - if let Some(p) = protocols.first() { - return *p; - } - - match env::var("XDG_SESSION_TYPE").unwrap_or_default().as_str() { - "x11" => return Self::X11, - "wayland" => return Self::Wayland, - _ => warn!("[Adaptor] Could not identify XDG_SESSION_TYPE"), - } - if env_exists("WAYLAND_DISPLAY") { - return Self::Wayland; - } - if env_exists("DISPLAY") { - return Self::X11; - } - if std::fs::symlink_metadata("/proc/sys/fs/binfmt_misc/WSLInterop").is_ok() { - return Self::KittyOld; - } - - warn!("[Adaptor] Falling back to chafa"); - Self::Chafa - } - - fn via_env() -> (String, String) { - fn tmux_env(name: &str) -> Result { - let output = std::process::Command::new("tmux").args(["show-environment", name]).output()?; - - String::from_utf8(output.stdout)? - .trim() - .strip_prefix(&format!("{name}=")) - .map_or_else(|| Err(anyhow!("")), |s| Ok(s.to_string())) - } - - let mut term = env::var("TERM").unwrap_or_default(); - let mut program = env::var("TERM_PROGRAM").unwrap_or_default(); - - if *TMUX { - term = tmux_env("TERM").unwrap_or(term); - program = tmux_env("TERM_PROGRAM").unwrap_or(program); - } - - (term, program) - } - - fn via_csi() -> Result { - enable_raw_mode()?; - std::io::stdout().write_all(b"\x1b[>q\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\\x1b[c")?; - std::io::stdout().flush()?; - - let mut stdin = std::io::stdin().lock(); - let mut buf = String::with_capacity(200); - loop { - let mut c = [0; 1]; - if stdin.read(&mut c)? == 0 { - break; - } - if c[0] == b'c' && buf.contains("\x1b[?") { - break; - } - buf.push(c[0] as char); - } - - disable_raw_mode().ok(); - let names = [ - ("kitty", Emulator::Kitty), - ("Konsole", Emulator::Konsole), - ("iTerm2", Emulator::Iterm2), - ("WezTerm", Emulator::WezTerm), - ("foot", Emulator::Foot), - ("ghostty", Emulator::Ghostty), - ]; - - for (name, emulator) in names.iter() { - if buf.contains(name) { - return Ok(emulator.clone()); - } - } - - let mut adapters = Vec::with_capacity(2); - if buf.contains("\x1b_Gi=31;OK") { - adapters.push(Adaptor::KittyOld); - } - if ["?4;", "?4c", ";4;", ";4c"].iter().any(|s| buf.contains(s)) { - adapters.push(Adaptor::Sixel); - } - - Ok(Emulator::Unknown(adapters)) - } -} - impl Display for Adaptor { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -212,8 +36,6 @@ impl Display for Adaptor { } impl Adaptor { - pub(super) fn start(self) { Ueberzug::start(self); } - pub async fn image_show(self, path: &Path, rect: Rect) -> Result<(u32, u32)> { match self { Self::Kitty => Kitty::image_show(path, rect).await, @@ -241,6 +63,8 @@ impl Adaptor { #[inline] pub fn shown_load(self) -> Option { SHOWN.load_full().map(|r| *r) } + pub(super) fn start(self) { Ueberzug::start(self); } + #[inline] pub(super) fn shown_store(rect: Rect, size: (u32, u32)) { SHOWN.store(Some(Arc::new( @@ -260,3 +84,39 @@ impl Adaptor { !matches!(self, Self::Kitty | Self::KittyOld | Self::Iterm2 | Self::Sixel) } } + +impl Adaptor { + pub fn matches() -> Self { + let mut protocols = Emulator::detect().adapters(); + + #[cfg(windows)] + protocols.retain(|p| *p == Self::Iterm2); + if env_exists("ZELLIJ_SESSION_NAME") { + protocols.retain(|p| *p == Self::Sixel); + } + if *TMUX && protocols.len() > 1 { + protocols.retain(|p| *p != Self::KittyOld); + } + if let Some(p) = protocols.first() { + return *p; + } + + match env::var("XDG_SESSION_TYPE").unwrap_or_default().as_str() { + "x11" => return Self::X11, + "wayland" => return Self::Wayland, + _ => warn!("[Adaptor] Could not identify XDG_SESSION_TYPE"), + } + if env_exists("WAYLAND_DISPLAY") { + return Self::Wayland; + } + if env_exists("DISPLAY") { + return Self::X11; + } + if std::fs::symlink_metadata("/proc/sys/fs/binfmt_misc/WSLInterop").is_ok() { + return Self::KittyOld; + } + + warn!("[Adaptor] Falling back to chafa"); + Self::Chafa + } +} diff --git a/yazi-adaptor/src/emulator.rs b/yazi-adaptor/src/emulator.rs new file mode 100644 index 000000000..30702fff8 --- /dev/null +++ b/yazi-adaptor/src/emulator.rs @@ -0,0 +1,155 @@ +use std::{env, io::{Read, Write}}; + +use anyhow::{anyhow, Result}; +use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; +use tracing::warn; +use yazi_shared::env_exists; + +use crate::{Adaptor, TMUX}; + +#[derive(Clone, Debug)] +pub enum Emulator { + Unknown(Vec), + Kitty, + Konsole, + Iterm2, + WezTerm, + Foot, + Ghostty, + BlackBox, + VSCode, + Tabby, + Hyper, + Mintty, + Neovim, +} + +impl Emulator { + pub fn adapters(self) -> Vec { + match self { + Self::Unknown(adapters) => adapters, + Self::Kitty => vec![Adaptor::Kitty], + Self::Konsole => vec![Adaptor::KittyOld, Adaptor::Iterm2, Adaptor::Sixel], + Self::Iterm2 => vec![Adaptor::Iterm2, Adaptor::Sixel], + Self::WezTerm => vec![Adaptor::Iterm2, Adaptor::Sixel], + Self::Foot => vec![Adaptor::Sixel], + Self::Ghostty => vec![Adaptor::KittyOld], + Self::BlackBox => vec![Adaptor::Sixel], + Self::VSCode => vec![Adaptor::Iterm2, Adaptor::Sixel], + Self::Tabby => vec![Adaptor::Iterm2, Adaptor::Sixel], + Self::Hyper => vec![Adaptor::Iterm2, Adaptor::Sixel], + Self::Mintty => vec![Adaptor::Iterm2], + Self::Neovim => vec![], + } + } +} + +impl Emulator { + pub fn detect() -> Self { + if env_exists("NVIM_LOG_FILE") && env_exists("NVIM") { + return Self::Neovim; + } + + 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), + ("VSCODE_INJECTION", Self::VSCode), + ("TABBY_CONFIG_DIRECTORY", Self::Tabby), + ]; + match vars.into_iter().find(|v| env_exists(v.0)) { + Some(var) => return var.1, + None => warn!("[Adaptor] 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, + "BlackBox" => return Self::BlackBox, + "vscode" => return Self::VSCode, + "Tabby" => return Self::Tabby, + "Hyper" => return Self::Hyper, + "mintty" => return Self::Mintty, + _ => warn!("[Adaptor] 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, + _ => warn!("[Adaptor] Unknown TERM: {term}"), + } + + Self::via_csi().unwrap_or(Self::Unknown(vec![])) + } + + pub fn via_env() -> (String, String) { + fn tmux_env(name: &str) -> Result { + let output = std::process::Command::new("tmux").args(["show-environment", name]).output()?; + + String::from_utf8(output.stdout)? + .trim() + .strip_prefix(&format!("{name}=")) + .map_or_else(|| Err(anyhow!("")), |s| Ok(s.to_string())) + } + + let mut term = env::var("TERM").unwrap_or_default(); + let mut program = env::var("TERM_PROGRAM").unwrap_or_default(); + + if *TMUX { + term = tmux_env("TERM").unwrap_or(term); + program = tmux_env("TERM_PROGRAM").unwrap_or(program); + } + + (term, program) + } + + pub fn via_csi() -> Result { + enable_raw_mode()?; + std::io::stdout().write_all(b"\x1b[>q\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\\x1b[c")?; + std::io::stdout().flush()?; + + let mut stdin = std::io::stdin().lock(); + let mut buf = String::with_capacity(200); + loop { + let mut c = [0; 1]; + if stdin.read(&mut c)? == 0 { + break; + } + if c[0] == b'c' && buf.contains("\x1b[?") { + break; + } + buf.push(c[0] as char); + } + + disable_raw_mode().ok(); + let names = [ + ("kitty", Self::Kitty), + ("Konsole", Self::Konsole), + ("iTerm2", Self::Iterm2), + ("WezTerm", Self::WezTerm), + ("foot", Self::Foot), + ("ghostty", Self::Ghostty), + ]; + + for (name, emulator) in names.iter() { + if buf.contains(name) { + return Ok(emulator.clone()); + } + } + + let mut adapters = Vec::with_capacity(2); + if buf.contains("\x1b_Gi=31;OK") { + adapters.push(Adaptor::KittyOld); + } + if ["?4;", "?4c", ";4;", ";4c"].iter().any(|s| buf.contains(s)) { + adapters.push(Adaptor::Sixel); + } + + Ok(Self::Unknown(adapters)) + } +} diff --git a/yazi-adaptor/src/lib.rs b/yazi-adaptor/src/lib.rs index b4d52f4a8..700e944e4 100644 --- a/yazi-adaptor/src/lib.rs +++ b/yazi-adaptor/src/lib.rs @@ -1,6 +1,7 @@ #![allow(clippy::unit_arg)] mod adaptor; +mod emulator; mod image; mod iterm2; mod kitty; @@ -8,7 +9,8 @@ mod kitty_old; mod sixel; mod ueberzug; -use adaptor::*; +pub use adaptor::*; +pub use emulator::*; use iterm2::*; use kitty::*; use kitty_old::*; @@ -20,7 +22,7 @@ pub use crate::image::*; pub static ADAPTOR: RoCell = RoCell::new(); // Tmux support -static TMUX: RoCell = RoCell::new(); +pub static TMUX: RoCell = RoCell::new(); static ESCAPE: RoCell<&'static str> = RoCell::new(); static START: RoCell<&'static str> = RoCell::new(); static CLOSE: RoCell<&'static str> = RoCell::new(); @@ -36,7 +38,7 @@ pub fn init() { SHOWN.with(Default::default); - ADAPTOR.init(Adaptor::detect()); + ADAPTOR.init(Adaptor::matches()); ADAPTOR.start(); if *TMUX { diff --git a/yazi-adaptor/src/ueberzug.rs b/yazi-adaptor/src/ueberzug.rs index 35d8c2328..c2f9a6c1b 100644 --- a/yazi-adaptor/src/ueberzug.rs +++ b/yazi-adaptor/src/ueberzug.rs @@ -4,7 +4,7 @@ use anyhow::{bail, Result}; use imagesize::ImageSize; use ratatui::layout::Rect; use tokio::{io::AsyncWriteExt, process::{Child, Command}, sync::mpsc::{self, UnboundedSender}}; -use tracing::debug; +use tracing::{debug, warn}; use yazi_config::PREVIEW; use yazi_shared::RoCell; @@ -71,14 +71,17 @@ impl Ueberzug { } fn create_demon(adaptor: Adaptor) -> Result { - Ok( - Command::new("ueberzug") - .args(["layer", "-so", &adaptor.to_string()]) - .kill_on_drop(true) - .stdin(Stdio::piped()) - .stderr(Stdio::null()) - .spawn()?, - ) + let result = Command::new("ueberzug") + .args(["layer", "-so", &adaptor.to_string()]) + .kill_on_drop(true) + .stdin(Stdio::piped()) + .stderr(Stdio::null()) + .spawn(); + + if let Err(ref e) = result { + warn!("ueberzug spawning failed: {}", e); + } + Ok(result?) } fn adjust_rect(mut rect: Rect) -> Rect { diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 42c5b4c4e..dfe8cbc0e 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -9,8 +9,9 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -yazi-config = { path = "../yazi-config", version = "0.2.4" } -yazi-shared = { path = "../yazi-shared", version = "0.2.4" } +yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.4" } +yazi-config = { path = "../yazi-config", version = "0.2.4" } +yazi-shared = { path = "../yazi-shared", version = "0.2.4" } # External dependencies clap = { version = "^4", features = [ "derive" ] } diff --git a/yazi-boot/src/args.rs b/yazi-boot/src/args.rs index 08c4a11aa..8c9181da8 100644 --- a/yazi-boot/src/args.rs +++ b/yazi-boot/src/args.rs @@ -20,6 +20,10 @@ pub struct Args { #[arg(long, action)] pub clear_cache: bool, + /// Print debug information + #[arg(long, action)] + pub debug: bool, + /// Print version #[arg(short = 'V', long)] pub version: bool, diff --git a/yazi-boot/src/boot.rs b/yazi-boot/src/boot.rs index 65fee0f04..6380445cf 100644 --- a/yazi-boot/src/boot.rs +++ b/yazi-boot/src/boot.rs @@ -1,4 +1,4 @@ -use std::{ffi::OsString, path::{Path, PathBuf}, process}; +use std::{env, ffi::OsString, path::{Path, PathBuf}, process}; use clap::Parser; use serde::Serialize; @@ -32,6 +32,63 @@ impl Boot { (parent.unwrap().to_owned(), Some(entry.file_name().unwrap().to_owned())) } + + fn action_version() { + println!( + "yazi {} ({} {})", + env!("CARGO_PKG_VERSION"), + env!("VERGEN_GIT_SHA"), + env!("VERGEN_BUILD_DATE") + ); + } + + fn action_debug() { + print!("Yazi\n "); + Self::action_version(); + + println!("\nEnvironment"); + println!( + " OS: {}-{} ({})", + std::env::consts::OS, + std::env::consts::ARCH, + std::env::consts::FAMILY + ); + println!(" Debug: {}", cfg!(debug_assertions)); + + println!("\nEmulator"); + println!(" Emulator.via_env: {:?}", yazi_adaptor::Emulator::via_env()); + println!(" Emulator.via_csi: {:?}", yazi_adaptor::Emulator::via_csi()); + println!(" Emulator.detect: {:?}", yazi_adaptor::Emulator::detect()); + + println!("\nAdaptor"); + println!(" Adaptor.matches: {:?}", yazi_adaptor::Adaptor::matches()); + + println!("\ntmux"); + println!(" TMUX: {:?}", *yazi_adaptor::TMUX); + + println!("\nZellij"); + println!(" ZELLIJ_SESSION_NAME: {:?}", env::var_os("ZELLIJ_SESSION_NAME")); + + println!("\nDesktop"); + println!(" XDG_SESSION_TYPE: {:?}", env::var_os("XDG_SESSION_TYPE")); + println!(" WAYLAND_DISPLAY: {:?}", env::var_os("WAYLAND_DISPLAY")); + println!(" DISPLAY: {:?}", env::var_os("DISPLAY")); + + println!("\nUeberzug"); + println!(" Version: {:?}", std::process::Command::new("ueberzug").arg("--version").output()); + + println!("\nWSL"); + println!( + " /proc/sys/fs/binfmt_misc/WSLInterop: {:?}", + std::fs::symlink_metadata("/proc/sys/fs/binfmt_misc/WSLInterop").is_ok() + ); + + println!("\n\n--------------------------------------------------"); + println!( + "When reporting a bug, please also upload the `yazi.log` log file - only upload the most recent content by time." + ); + println!("You can find it in the {:?} directory.", Xdg::state_dir()); + } } impl Default for Boot { @@ -58,13 +115,13 @@ impl Default for Args { fn default() -> Self { let args = Self::parse(); + if args.debug { + Boot::action_debug(); + process::exit(0); + } + if args.version { - println!( - "yazi {} ({} {})", - env!("CARGO_PKG_VERSION"), - env!("VERGEN_GIT_SHA"), - env!("VERGEN_BUILD_DATE") - ); + Boot::action_version(); process::exit(0); }