From 583345296f6480931855c622a3af016c18390daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Wed, 28 Jan 2026 22:26:47 +0800 Subject: [PATCH] refactor: move `Term` to its own `yazi-term` crate (#3629) --- Cargo.lock | 10 +++ scripts/validate-form/main.js | 32 +++++--- yazi-binding/Cargo.toml | 4 +- yazi-boot/src/actions/debug.rs | 1 + yazi-cli/Cargo.toml | 1 + yazi-cli/src/package/dependency.rs | 2 +- yazi-fm/Cargo.toml | 25 +++--- yazi-fm/src/app/app.rs | 3 +- yazi-fm/src/app/commands/quit.rs | 3 +- yazi-fm/src/app/commands/resume.rs | 3 +- yazi-fm/src/main.rs | 2 +- yazi-fm/src/panic.rs | 2 +- yazi-parser/Cargo.toml | 3 + yazi-shim/Cargo.toml | 4 + .../src => yazi-shim/src/crossterm}/if.rs | 0 yazi-shim/src/crossterm/mod.rs | 1 + yazi-shim/src/crossterm/restore_background.rs | 10 +++ .../src/crossterm/restore_cursor.rs | 26 ++---- yazi-shim/src/crossterm/set_background.rs | 10 +++ yazi-shim/src/lib.rs | 2 + yazi-term/Cargo.toml | 11 ++- yazi-term/src/background.rs | 16 ---- yazi-term/src/lib.rs | 2 +- yazi-term/src/option.rs | 17 ++++ yazi-term/src/state.rs | 45 ++++++++++ {yazi-fm => yazi-term}/src/term.rs | 82 ++++++++----------- yazi-watcher/Cargo.toml | 4 +- 27 files changed, 197 insertions(+), 124 deletions(-) rename {yazi-term/src => yazi-shim/src/crossterm}/if.rs (100%) create mode 100644 yazi-shim/src/crossterm/mod.rs create mode 100644 yazi-shim/src/crossterm/restore_background.rs rename yazi-term/src/cursor.rs => yazi-shim/src/crossterm/restore_cursor.rs (54%) create mode 100644 yazi-shim/src/crossterm/set_background.rs delete mode 100644 yazi-term/src/background.rs create mode 100644 yazi-term/src/option.rs create mode 100644 yazi-term/src/state.rs rename {yazi-fm => yazi-term}/src/term.rs (61%) diff --git a/Cargo.lock b/Cargo.lock index 670937bc..e160058e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5658,6 +5658,7 @@ dependencies = [ "yazi-fs", "yazi-macro", "yazi-shared", + "yazi-shim", "yazi-term", ] @@ -5809,6 +5810,7 @@ dependencies = [ "yazi-plugin", "yazi-proxy", "yazi-shared", + "yazi-shim", "yazi-term", "yazi-tty", "yazi-vfs", @@ -6004,6 +6006,7 @@ dependencies = [ name = "yazi-shim" version = "26.1.22" dependencies = [ + "crossterm 0.29.0", "twox-hash", "yazi-macro", ] @@ -6012,9 +6015,16 @@ dependencies = [ name = "yazi-term" version = "26.1.22" dependencies = [ + "anyhow", "crossterm 0.29.0", "libc", + "ratatui", + "yazi-config", + "yazi-emulator", "yazi-macro", + "yazi-shared", + "yazi-shim", + "yazi-tty", ] [[package]] diff --git a/scripts/validate-form/main.js b/scripts/validate-form/main.js index 9c37cc3a..6413b4a5 100644 --- a/scripts/validate-form/main.js +++ b/scripts/validate-form/main.js @@ -8,13 +8,17 @@ function bugReportBody(creator, content, hash) { return null } - return `Hey @${creator}, I noticed that you did not correctly follow the issue template. Please ensure that: + return `Hey @${creator}, thank you for opening this issue to help us improve Yazi, appreciate it! + +I noticed that you did not correctly follow the issue template. Please ensure that: - The bug can still be reproduced on the [newest nightly build](https://yazi-rs.github.io/docs/installation/#binaries). - The debug information (\`yazi --debug\`) is updated for the newest nightly. - The non-optional items in the checklist are checked. Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity. + +Our maintainers work on Yazi in a personal capacity, and debug info helps them work efficiently, understand your setup quickly, and find a more appropriate solution. Thank you for the understanding! ` } @@ -23,13 +27,17 @@ function featureRequestBody(creator, content) { return null } - return `Hey @${creator}, I noticed that you did not correctly follow the issue template. Please ensure that: + return `Hey @${creator}, thank you for opening this issue to help us improve Yazi, appreciate it! + +I noticed that you did not correctly follow the issue template. Please ensure that: - The requested feature does not exist in the [newest nightly build](https://yazi-rs.github.io/docs/installation/#binaries). - The debug information (\`yazi --debug\`) is updated for the newest nightly. - The non-optional items in the checklist are checked. Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity. + +Our maintainers work on Yazi in a personal capacity, and debug info helps them work efficiently, understand your setup quickly, and find a more appropriate solution. Thank you for the understanding! ` } @@ -66,7 +74,7 @@ module.exports = async ({ github, context, core }) => { const { data: events } = await github.rest.issues.listEvents({ ...context.repo, issue_number: id, - per_page : 100, + per_page: 100, }) const all = events.filter(v => v.event === "labeled" && v.label?.name === LABEL_NAME) @@ -82,7 +90,7 @@ module.exports = async ({ github, context, core }) => { const { data: events } = await github.rest.issues.listEvents({ ...context.repo, issue_number: id, - per_page : 100, + per_page: 100, }) const all = events.filter(v => v.event === "unlabeled" && v.label?.name === LABEL_NAME) @@ -101,14 +109,14 @@ module.exports = async ({ github, context, core }) => { await github.rest.issues.removeLabel({ ...context.repo, issue_number: id, - name : LABEL_NAME, + name: LABEL_NAME, }) await hideOldComments(id) } else if (mark && !marked && !await removedLabelManually(id)) { await github.rest.issues.addLabels({ ...context.repo, issue_number: id, - labels : [LABEL_NAME], + labels: [LABEL_NAME], }) await hideOldComments(id) await github.rest.issues.createComment({ @@ -127,7 +135,7 @@ module.exports = async ({ github, context, core }) => { const comments = await github.paginate(github.rest.issues.listComments, { ...context.repo, issue_number: id, - per_page : 100, + per_page: 100, }) for (const c of comments) { @@ -157,7 +165,7 @@ module.exports = async ({ github, context, core }) => { try { const { data: issues } = await github.rest.issues.listForRepo({ ...context.repo, - state : "open", + state: "open", labels: LABEL_NAME, }) @@ -170,13 +178,13 @@ module.exports = async ({ github, context, core }) => { await github.rest.issues.update({ ...context.repo, issue_number: issue.number, - state : "closed", + state: "closed", state_reason: "not_planned", }) await github.rest.issues.createComment({ ...context.repo, issue_number: issue.number, - body : `This issue has been automatically closed because it was marked as \`${LABEL_NAME}\` for more than 2 days without updates. + body: `This issue has been automatically closed because it was marked as \`${LABEL_NAME}\` for more than 2 days without updates. If the problem persists, please file a new issue and complete the issue template so we can capture all the details necessary to investigate further.`, }) } @@ -191,13 +199,13 @@ If the problem persists, please file a new issue and complete the issue template await github.rest.issues.update({ ...context.repo, issue_number: id, - state : "closed", + state: "closed", state_reason: "not_planned", }) await github.rest.issues.createComment({ ...context.repo, issue_number: id, - body : `Unsupported issue template. + body: `Unsupported issue template. Either the [Bug Report](https://github.com/sxyazi/yazi/issues/new?template=bug.yml) or [Feature Request](https://github.com/sxyazi/yazi/issues/new?template=feature.yml) template should be used.`, }) } catch (e) { diff --git a/yazi-binding/Cargo.toml b/yazi-binding/Cargo.toml index 01421358..1094be21 100644 --- a/yazi-binding/Cargo.toml +++ b/yazi-binding/Cargo.toml @@ -32,7 +32,5 @@ paste = { workspace = true } ratatui = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } +tracing = { workspace = true } unicode-width = { workspace = true } - -# Logging -tracing = { workspace = true } diff --git a/yazi-boot/src/actions/debug.rs b/yazi-boot/src/actions/debug.rs index 0b338e2e..681e51ab 100644 --- a/yazi-boot/src/actions/debug.rs +++ b/yazi-boot/src/actions/debug.rs @@ -62,6 +62,7 @@ impl Actions { writeln!(s, " YAZI_FILE_ONE : {:?}", env::var_os("YAZI_FILE_ONE"))?; writeln!(s, " YAZI_CONFIG_HOME : {:?}", env::var_os("YAZI_CONFIG_HOME"))?; writeln!(s, " YAZI_ZOXIDE_OPTS : {:?}", env::var_os("YAZI_ZOXIDE_OPTS"))?; + writeln!(s, " SSH_AUTH_SOCK : {:?}", env::var_os("SSH_AUTH_SOCK"))?; writeln!(s, " FZF_DEFAULT_OPTS : {:?}", env::var_os("FZF_DEFAULT_OPTS"))?; writeln!(s, " FZF_DEFAULT_COMMAND: {:?}", env::var_os("FZF_DEFAULT_COMMAND"))?; diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index f8c74f4c..38f01445 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -27,6 +27,7 @@ yazi-dds = { path = "../yazi-dds", version = "26.1.22" } yazi-fs = { path = "../yazi-fs", version = "26.1.22" } yazi-macro = { path = "../yazi-macro", version = "26.1.22" } yazi-shared = { path = "../yazi-shared", version = "26.1.22" } +yazi-shim = { path = "../yazi-shim", version = "26.1.22" } yazi-term = { path = "../yazi-term", version = "26.1.22" } # External dependencies diff --git a/yazi-cli/src/package/dependency.rs b/yazi-cli/src/package/dependency.rs index 37698ba0..1529b3e1 100644 --- a/yazi-cli/src/package/dependency.rs +++ b/yazi-cli/src/package/dependency.rs @@ -49,7 +49,7 @@ impl Dependency { use std::io::IsTerminal; use crossterm::style::{Attribute, Print, SetAttributes}; - use yazi_term::If; + use yazi_shim::crossterm::If; let ansi = env::var_os("YA_FORCE_ANSI").is_some_and(|v| v == "1") || io::stdout().is_terminal(); crossterm::execute!( diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 87f7ff9d..623f9a22 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -40,6 +40,7 @@ yazi-parser = { path = "../yazi-parser", version = "26.1.22" } yazi-plugin = { path = "../yazi-plugin", version = "26.1.22" } yazi-proxy = { path = "../yazi-proxy", version = "26.1.22" } yazi-shared = { path = "../yazi-shared", version = "26.1.22" } +yazi-shim = { path = "../yazi-shim", version = "26.1.22" } yazi-term = { path = "../yazi-term", version = "26.1.22" } yazi-tty = { path = "../yazi-tty", version = "26.1.22" } yazi-vfs = { path = "../yazi-vfs", version = "26.1.22" } @@ -47,19 +48,17 @@ yazi-watcher = { path = "../yazi-watcher", version = "26.1.22" } yazi-widgets = { path = "../yazi-widgets", version = "26.1.22" } # External dependencies -anyhow = { workspace = true } -better-panic = "0.3.0" -crossterm = { workspace = true } -fdlimit = "0.3.0" -futures = { workspace = true } -mlua = { workspace = true } -paste = { workspace = true } -ratatui = { workspace = true } -scopeguard = { workspace = true } -tokio = { workspace = true } -tokio-stream = { workspace = true } - -# Logging +anyhow = { workspace = true } +better-panic = "0.3.0" +crossterm = { workspace = true } +fdlimit = "0.3.0" +futures = { workspace = true } +mlua = { workspace = true } +paste = { workspace = true } +ratatui = { workspace = true } +scopeguard = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } tracing = { workspace = true } tracing-appender = "0.2.4" tracing-subscriber = { version = "0.3.22", features = [ "env-filter" ] } diff --git a/yazi-fm/src/app/app.rs b/yazi-fm/src/app/app.rs index 63fb9064..6bbade6d 100644 --- a/yazi-fm/src/app/app.rs +++ b/yazi-fm/src/app/app.rs @@ -5,8 +5,9 @@ use tokio::{select, time::sleep}; use yazi_core::Core; use yazi_macro::act; use yazi_shared::event::{Event, NEED_RENDER}; +use yazi_term::Term; -use crate::{Dispatcher, Signals, Term}; +use crate::{Dispatcher, Signals}; pub(crate) struct App { pub(crate) core: Core, diff --git a/yazi-fm/src/app/commands/quit.rs b/yazi-fm/src/app/commands/quit.rs index 807f0b99..302c504e 100644 --- a/yazi-fm/src/app/commands/quit.rs +++ b/yazi-fm/src/app/commands/quit.rs @@ -1,8 +1,9 @@ use yazi_boot::ARGS; use yazi_fs::provider::{Provider, local::Local}; use yazi_shared::{event::EventQuit, strand::{StrandBuf, StrandLike, ToStrand}}; +use yazi_term::Term; -use crate::{Term, app::App}; +use crate::app::App; impl App { pub(crate) fn quit(&mut self, opt: EventQuit) -> ! { diff --git a/yazi-fm/src/app/commands/resume.rs b/yazi-fm/src/app/commands/resume.rs index fef0404f..f54db7bd 100644 --- a/yazi-fm/src/app/commands/resume.rs +++ b/yazi-fm/src/app/commands/resume.rs @@ -2,8 +2,9 @@ use anyhow::Result; use yazi_macro::{act, render, succ}; use yazi_parser::app::ResumeOpt; use yazi_shared::data::Data; +use yazi_term::Term; -use crate::{Term, app::App}; +use crate::app::App; impl App { pub(crate) fn resume(&mut self, opt: ResumeOpt) -> Result { diff --git a/yazi-fm/src/main.rs b/yazi-fm/src/main.rs index feeb5266..2a6960be 100644 --- a/yazi-fm/src/main.rs +++ b/yazi-fm/src/main.rs @@ -4,7 +4,7 @@ static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; yazi_macro::mod_pub!(app cmp confirm help input mgr notify pick spot tasks which); -yazi_macro::mod_flat!(dispatcher executor logs panic root router signals term); +yazi_macro::mod_flat!(dispatcher executor logs panic root router signals); #[tokio::main] async fn main() -> anyhow::Result<()> { diff --git a/yazi-fm/src/panic.rs b/yazi-fm/src/panic.rs index 3c832d78..efb194e3 100644 --- a/yazi-fm/src/panic.rs +++ b/yazi-fm/src/panic.rs @@ -1,4 +1,4 @@ -use crate::Term; +use yazi_term::Term; pub(super) struct Panic; diff --git a/yazi-parser/Cargo.toml b/yazi-parser/Cargo.toml index 1a23037d..e9965e49 100644 --- a/yazi-parser/Cargo.toml +++ b/yazi-parser/Cargo.toml @@ -35,3 +35,6 @@ ordered-float = { workspace = true } serde = { workspace = true } serde_with = { workspace = true } tokio = { workspace = true } + +[target.'cfg(target_os = "macos")'.dependencies] +crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] } diff --git a/yazi-shim/Cargo.toml b/yazi-shim/Cargo.toml index dd514548..b701a873 100644 --- a/yazi-shim/Cargo.toml +++ b/yazi-shim/Cargo.toml @@ -15,4 +15,8 @@ workspace = true yazi-macro = { path = "../yazi-macro", version = "26.1.22" } # External dependencies +crossterm = { workspace = true } twox-hash = { workspace = true } + +[target.'cfg(target_os = "macos")'.dependencies] +crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] } diff --git a/yazi-term/src/if.rs b/yazi-shim/src/crossterm/if.rs similarity index 100% rename from yazi-term/src/if.rs rename to yazi-shim/src/crossterm/if.rs diff --git a/yazi-shim/src/crossterm/mod.rs b/yazi-shim/src/crossterm/mod.rs new file mode 100644 index 00000000..e6650ebf --- /dev/null +++ b/yazi-shim/src/crossterm/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(r#if restore_background restore_cursor set_background); diff --git a/yazi-shim/src/crossterm/restore_background.rs b/yazi-shim/src/crossterm/restore_background.rs new file mode 100644 index 00000000..ea61734a --- /dev/null +++ b/yazi-shim/src/crossterm/restore_background.rs @@ -0,0 +1,10 @@ +pub struct RestoreBackground; + +impl crossterm::Command for RestoreBackground { + fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result { + write!(f, "\x1b]111\x1b\\") + } + + #[cfg(windows)] + fn execute_winapi(&self) -> std::io::Result<()> { Ok(()) } +} diff --git a/yazi-term/src/cursor.rs b/yazi-shim/src/crossterm/restore_cursor.rs similarity index 54% rename from yazi-term/src/cursor.rs rename to yazi-shim/src/crossterm/restore_cursor.rs index 5808e541..22d1ef01 100644 --- a/yazi-term/src/cursor.rs +++ b/yazi-shim/src/crossterm/restore_cursor.rs @@ -1,34 +1,18 @@ -use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; - use crossterm::cursor::SetCursorStyle; -static SHAPE: AtomicU8 = AtomicU8::new(0); -static BLINK: AtomicBool = AtomicBool::new(false); - -pub struct RestoreCursor; - -impl RestoreCursor { - pub fn store(resp: &str) { - SHAPE.store( - resp - .split_once("\x1bP1$r") - .and_then(|(_, s)| s.bytes().next()) - .filter(|&b| matches!(b, b'0'..=b'6')) - .map_or(u8::MAX, |b| b - b'0'), - Ordering::Relaxed, - ); - BLINK.store(resp.contains("\x1b[?12;1$y"), Ordering::Relaxed); - } +pub struct RestoreCursor { + pub shape: u8, + pub blink: bool, } impl crossterm::Command for RestoreCursor { fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result { - let (shape, shape_blink) = match SHAPE.load(Ordering::Relaxed) { + let (shape, shape_blink) = match self.shape { u8::MAX => (0, None), n => (n.max(1).div_ceil(2), Some(n.max(1) & 1 == 1)), }; - let blink = shape_blink.unwrap_or(BLINK.load(Ordering::Relaxed)); + let blink = shape_blink.unwrap_or(self.blink); Ok(match shape { 2 if blink => SetCursorStyle::BlinkingUnderScore.write_ansi(f)?, 2 if !blink => SetCursorStyle::SteadyUnderScore.write_ansi(f)?, diff --git a/yazi-shim/src/crossterm/set_background.rs b/yazi-shim/src/crossterm/set_background.rs new file mode 100644 index 00000000..92ae2990 --- /dev/null +++ b/yazi-shim/src/crossterm/set_background.rs @@ -0,0 +1,10 @@ +pub struct SetBackground<'a>(pub &'a str); + +impl crossterm::Command for SetBackground<'_> { + fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result { + if self.0.is_empty() { Ok(()) } else { write!(f, "\x1b]11;{}\x1b\\", self.0) } + } + + #[cfg(windows)] + fn execute_winapi(&self) -> std::io::Result<()> { Ok(()) } +} diff --git a/yazi-shim/src/lib.rs b/yazi-shim/src/lib.rs index 856012c3..7610f38f 100644 --- a/yazi-shim/src/lib.rs +++ b/yazi-shim/src/lib.rs @@ -1 +1,3 @@ +yazi_macro::mod_pub!(crossterm); + yazi_macro::mod_flat!(twox); diff --git a/yazi-term/Cargo.toml b/yazi-term/Cargo.toml index e6147e41..92a45ad1 100644 --- a/yazi-term/Cargo.toml +++ b/yazi-term/Cargo.toml @@ -12,10 +12,17 @@ repository = "https://github.com/sxyazi/yazi" workspace = true [dependencies] -yazi-macro = { path = "../yazi-macro", version = "26.1.22" } +yazi-config = { path = "../yazi-config", version = "26.1.22" } +yazi-emulator = { path = "../yazi-emulator", version = "26.1.22" } +yazi-macro = { path = "../yazi-macro", version = "26.1.22" } +yazi-shared = { path = "../yazi-shared", version = "26.1.22" } +yazi-shim = { path = "../yazi-shim", version = "26.1.22" } +yazi-tty = { path = "../yazi-tty", version = "26.1.22" } # External dependencies -crossterm = { workspace = true } +anyhow = { workspace = true } +crossterm = { workspace = true } +ratatui = { workspace = true } [target."cfg(unix)".dependencies] libc = { workspace = true } diff --git a/yazi-term/src/background.rs b/yazi-term/src/background.rs deleted file mode 100644 index 0ec18df7..00000000 --- a/yazi-term/src/background.rs +++ /dev/null @@ -1,16 +0,0 @@ -pub struct SetBackground(pub bool, pub String); - -impl crossterm::Command for SetBackground { - fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result { - if self.1.is_empty() { - Ok(()) - } else if self.0 { - write!(f, "\x1b]11;{}\x1b\\", self.1) - } else { - write!(f, "\x1b]111\x1b\\") - } - } - - #[cfg(windows)] - fn execute_winapi(&self) -> std::io::Result<()> { Ok(()) } -} diff --git a/yazi-term/src/lib.rs b/yazi-term/src/lib.rs index e3bf6e8c..909223ea 100644 --- a/yazi-term/src/lib.rs +++ b/yazi-term/src/lib.rs @@ -1 +1 @@ -yazi_macro::mod_flat!(background cursor r#if); +yazi_macro::mod_flat!(option state term); diff --git a/yazi-term/src/option.rs b/yazi-term/src/option.rs new file mode 100644 index 00000000..05d6131d --- /dev/null +++ b/yazi-term/src/option.rs @@ -0,0 +1,17 @@ +use yazi_config::{THEME, YAZI}; + +pub(super) struct TermOption { + pub bg: String, + pub mouse: bool, + pub title: Option, +} + +impl Default for TermOption { + fn default() -> Self { + Self { + bg: THEME.app.bg_color(), + mouse: !YAZI.mgr.mouse_events.get().is_empty(), + title: YAZI.mgr.title(), + } + } +} diff --git a/yazi-term/src/state.rs b/yazi-term/src/state.rs new file mode 100644 index 00000000..02706255 --- /dev/null +++ b/yazi-term/src/state.rs @@ -0,0 +1,45 @@ +use crate::TermOption; + +#[derive(Clone, Copy)] +pub(super) struct TermState { + pub(super) bg: bool, + pub(super) csi_u: bool, + pub(super) mouse: bool, + pub(super) title: bool, + pub(super) cursor_shape: u8, + pub(super) cursor_blink: bool, +} + +impl TermState { + pub(super) const fn default() -> Self { + Self { + bg: false, + csi_u: false, + mouse: false, + title: false, + cursor_shape: 0, + cursor_blink: false, + } + } + + pub(super) fn new(resp: &str, opt: &TermOption) -> Self { + let csi_u = resp.contains("\x1b[?0u"); + + let cursor_shape = resp + .split_once("\x1bP1$r") + .and_then(|(_, s)| s.bytes().next()) + .filter(|&b| matches!(b, b'0'..=b'6')) + .map_or(u8::MAX, |b| b - b'0'); + + let cursor_blink = resp.contains("\x1b[?12;1$y"); + + Self { + bg: !opt.bg.is_empty(), + csi_u, + mouse: opt.mouse, + title: opt.title.is_some(), + cursor_shape, + cursor_blink, + } + } +} diff --git a/yazi-fm/src/term.rs b/yazi-term/src/term.rs similarity index 61% rename from yazi-fm/src/term.rs rename to yazi-term/src/term.rs index cf104619..296f0952 100644 --- a/yazi-fm/src/term.rs +++ b/yazi-term/src/term.rs @@ -1,23 +1,26 @@ -use std::{io, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, Ordering}}; +use std::{io, ops::Deref}; use anyhow::Result; use crossterm::{event::{DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, EnableFocusChange, EnableMouseCapture, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{EnterAlternateScreen, LeaveAlternateScreen, SetTitle, disable_raw_mode, enable_raw_mode}}; use ratatui::{CompletedFrame, Frame, Terminal, backend::CrosstermBackend, buffer::Buffer, layout::Rect}; -use yazi_config::{THEME, YAZI}; use yazi_emulator::{Emulator, Mux, TMUX}; use yazi_shared::SyncCell; +use yazi_shim::crossterm::{If, RestoreBackground, RestoreCursor, SetBackground}; use yazi_tty::{TTY, TtyWriter}; -static CSI_U: AtomicBool = AtomicBool::new(false); +use crate::{TermOption, TermState}; -pub(super) struct Term { +static STATE: SyncCell = SyncCell::new(TermState::default()); + +pub struct Term { inner: Terminal>>, last_area: Rect, last_buffer: Buffer, } impl Term { - pub(super) fn start() -> Result { + pub fn start() -> Result { + let opt = TermOption::default(); let mut term = Self { inner: Terminal::new(CrosstermBackend::new(TTY.writer()))?, last_area: Default::default(), @@ -32,24 +35,23 @@ impl Term { execute!( TTY.writer(), - yazi_term::If(!TMUX.get(), EnterAlternateScreen), + If(!TMUX.get(), EnterAlternateScreen), Print("\x1bP$q q\x1b\\"), // Request cursor shape (DECRQSS query for DECSCUSR) Print("\x1b[?12$p"), // Request cursor blink status (DECRQM query for DECSET 12) Print("\x1b[?u"), // Request keyboard enhancement flags (CSI u) Print("\x1b[0c"), // Request device attributes - yazi_term::If(TMUX.get(), EnterAlternateScreen), - yazi_term::SetBackground(true, THEME.app.bg_color()), // Set app background + If(TMUX.get(), EnterAlternateScreen), + SetBackground(&opt.bg), // Set app background EnableBracketedPaste, EnableFocusChange, - yazi_term::If(!YAZI.mgr.mouse_events.get().is_empty(), EnableMouseCapture), + If(opt.mouse, EnableMouseCapture), )?; let resp = Emulator::read_until_da1(); Mux::tmux_drain()?; - yazi_term::RestoreCursor::store(&resp); - CSI_U.store(resp.contains("\x1b[?0u"), Ordering::Relaxed); - if CSI_U.load(Ordering::Relaxed) { + STATE.set(TermState::new(&resp, &opt)); + if STATE.get().csi_u { _ = queue!( TTY.writer(), PushKeyboardEnhancementFlags( @@ -59,52 +61,45 @@ impl Term { ); } - if let Some(s) = YAZI.mgr.title() { + if let Some(s) = opt.title { queue!(TTY.writer(), SetTitle(s)).ok(); } - term.hide_cursor()?; - term.clear()?; - term.flush()?; + term.inner.hide_cursor()?; + term.inner.clear()?; + term.inner.flush()?; Ok(term) } fn stop(&mut self) -> Result<()> { - if CSI_U.swap(false, Ordering::Relaxed) { - queue!(TTY.writer(), PopKeyboardEnhancementFlags).ok(); - } - - if !YAZI.mgr.title_format.is_empty() { - queue!(TTY.writer(), SetTitle("")).ok(); - } + let state = STATE.get(); execute!( TTY.writer(), - yazi_term::If(!YAZI.mgr.mouse_events.get().is_empty(), DisableMouseCapture), - yazi_term::RestoreCursor, + If(state.mouse, DisableMouseCapture), + If(state.bg, RestoreBackground), + If(state.csi_u, PopKeyboardEnhancementFlags), + RestoreCursor { shape: state.cursor_shape, blink: state.cursor_blink }, + If(state.title, SetTitle("")), DisableFocusChange, DisableBracketedPaste, LeaveAlternateScreen, )?; - self.show_cursor()?; + self.inner.show_cursor()?; Ok(disable_raw_mode()?) } - pub(super) fn goodbye(f: impl FnOnce() -> i32) -> ! { - if CSI_U.swap(false, Ordering::Relaxed) { - queue!(TTY.writer(), PopKeyboardEnhancementFlags).ok(); - } - - if !YAZI.mgr.title_format.is_empty() { - queue!(TTY.writer(), SetTitle("")).ok(); - } + pub fn goodbye(f: impl FnOnce() -> i32) -> ! { + let state = STATE.get(); execute!( TTY.writer(), - yazi_term::If(!YAZI.mgr.mouse_events.get().is_empty(), DisableMouseCapture), - yazi_term::SetBackground(false, THEME.app.bg_color()), - yazi_term::RestoreCursor, + If(state.mouse, DisableMouseCapture), + If(state.bg, RestoreBackground), + If(state.csi_u, PopKeyboardEnhancementFlags), + RestoreCursor { shape: state.cursor_shape, blink: state.cursor_blink }, + If(state.title, SetTitle("")), DisableFocusChange, DisableBracketedPaste, LeaveAlternateScreen, @@ -117,7 +112,7 @@ impl Term { std::process::exit(f()); } - pub(super) fn draw(&mut self, f: impl FnOnce(&mut Frame)) -> io::Result> { + pub fn draw(&mut self, f: impl FnOnce(&mut Frame)) -> io::Result> { let last = self.inner.draw(f)?; self.last_area = last.area; @@ -125,10 +120,7 @@ impl Term { Ok(last) } - pub(super) fn draw_partial( - &mut self, - f: impl FnOnce(&mut Frame), - ) -> io::Result> { + pub fn draw_partial(&mut self, f: impl FnOnce(&mut Frame)) -> io::Result> { self.inner.draw(|frame| { let buffer = frame.buffer_mut(); for y in self.last_area.top()..self.last_area.bottom() { @@ -143,7 +135,7 @@ impl Term { }) } - pub(super) fn can_partial(&mut self) -> bool { + pub fn can_partial(&mut self) -> bool { self.inner.autoresize().is_ok() && self.last_area == self.inner.get_frame().area() } } @@ -157,7 +149,3 @@ impl Deref for Term { fn deref(&self) -> &Self::Target { &self.inner } } - -impl DerefMut for Term { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } -} diff --git a/yazi-watcher/Cargo.toml b/yazi-watcher/Cargo.toml index 47274985..951cba73 100644 --- a/yazi-watcher/Cargo.toml +++ b/yazi-watcher/Cargo.toml @@ -20,9 +20,6 @@ yazi-proxy = { path = "../yazi-proxy", version = "26.1.22" } yazi-shared = { path = "../yazi-shared", version = "26.1.22" } yazi-vfs = { path = "../yazi-vfs", version = "26.1.22" } -# Logging -tracing = { workspace = true } - # External dependencies anyhow = { workspace = true } hashbrown = { workspace = true } @@ -31,3 +28,4 @@ parking_lot = { workspace = true } percent-encoding = { workspace = true } tokio = { workspace = true } tokio-stream = { workspace = true } +tracing = { workspace = true }