fix: no title is set when starts the first time (#2700)

This commit is contained in:
三咲雅 · Misaki Masa 2025-04-30 22:26:27 +08:00 committed by GitHub
parent 9d92cdbf79
commit 13a515c0ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 147 additions and 154 deletions

46
Cargo.lock generated
View file

@ -295,9 +295,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]]
name = "bytemuck"
version = "1.22.0"
version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
[[package]]
name = "byteorder-lite"
@ -328,9 +328,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.19"
version = "1.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a"
dependencies = [
"jobserver",
"libc",
@ -355,9 +355,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.40"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"android-tzdata",
"iana-time-zone",
@ -787,7 +787,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -996,9 +996,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.15"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
@ -1948,7 +1948,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.15",
"getrandom 0.2.16",
]
[[package]]
@ -2046,7 +2046,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
dependencies = [
"getrandom 0.2.15",
"getrandom 0.2.16",
"libredox",
"thiserror 2.0.12",
]
@ -2132,7 +2132,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -2145,7 +2145,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.9.4",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -3000,7 +3000,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -3314,6 +3314,7 @@ dependencies = [
"anyhow",
"bitflags 2.9.0",
"crossterm 0.29.0",
"dirs",
"globset",
"indexmap",
"ratatui",
@ -3431,6 +3432,7 @@ dependencies = [
"yazi-plugin",
"yazi-proxy",
"yazi-shared",
"yazi-term",
"yazi-widgets",
]
@ -3565,6 +3567,14 @@ dependencies = [
"yazi-macro",
]
[[package]]
name = "yazi-term"
version = "25.4.8"
dependencies = [
"crossterm 0.29.0",
"yazi-macro",
]
[[package]]
name = "yazi-widgets"
version = "25.4.8"
@ -3582,18 +3592,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.24"
version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.24"
version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
dependencies = [
"proc-macro2",
"quote",

View file

@ -18,6 +18,7 @@ yazi-shared = { path = "../yazi-shared", version = "25.4.8" }
anyhow = { workspace = true }
bitflags = { workspace = true }
crossterm = { workspace = true }
dirs = { workspace = true }
globset = { workspace = true }
indexmap = { workspace = true }
ratatui = { workspace = true }

View file

@ -1,7 +1,7 @@
use anyhow::{Result, bail};
use serde::{Deserialize, Serialize};
use yazi_codegen::DeserializeOver2;
use yazi_fs::SortBy;
use yazi_fs::{CWD, SortBy};
use super::{MgrRatio, MouseEvents};
@ -26,6 +26,21 @@ pub struct Mgr {
}
impl Mgr {
pub fn title(&self) -> Option<String> {
if self.title_format.is_empty() {
return None;
}
let home = dirs::home_dir().unwrap_or_default();
let cwd = if let Ok(p) = CWD.load().strip_prefix(home) {
format!("~{}{}", std::path::MAIN_SEPARATOR, p.display())
} else {
format!("{}", CWD.load().display())
};
Some(self.title_format.replace("{cwd}", &cwd))
}
pub(crate) fn reshape(self) -> Result<Self> {
if self.linemode.is_empty() || self.linemode.len() > 20 {
bail!("[mgr].linemode must be between 1 and 20 characters.");

View file

@ -1,5 +1,3 @@
use std::path::MAIN_SEPARATOR;
use crossterm::{execute, terminal::SetTitle};
use yazi_config::YAZI;
use yazi_fs::CWD;
@ -9,8 +7,8 @@ use crate::{mgr::Mgr, tasks::Tasks};
impl Mgr {
pub fn refresh(&mut self, _: CmdCow, tasks: &Tasks) {
if CWD.set(self.cwd()) && !YAZI.mgr.title_format.is_empty() {
execute!(TTY.writer(), SetTitle(self.title())).ok();
if let (_, Some(s)) = (CWD.set(self.cwd()), YAZI.mgr.title()) {
execute!(TTY.writer(), SetTitle(s)).ok();
}
self.active_mut().apply_files_attrs();
@ -27,15 +25,4 @@ impl Mgr {
tasks.prework_sorted(&self.current().files);
}
fn title(&self) -> String {
let home = dirs::home_dir().unwrap_or_default();
let cwd = if let Ok(p) = self.cwd().strip_prefix(home) {
format!("~{}{}", MAIN_SEPARATOR, p.display())
} else {
format!("{}", self.cwd().display())
};
YAZI.mgr.title_format.replace("{cwd}", &cwd)
}
}

View file

@ -25,6 +25,7 @@ yazi-macro = { path = "../yazi-macro", version = "25.4.8" }
yazi-plugin = { path = "../yazi-plugin", version = "25.4.8" }
yazi-proxy = { path = "../yazi-proxy", version = "25.4.8" }
yazi-shared = { path = "../yazi-shared", version = "25.4.8" }
yazi-term = { path = "../yazi-term", version = "25.4.8" }
yazi-widgets = { path = "../yazi-widgets", version = "25.4.8" }
# External dependencies

View file

@ -1,16 +1,13 @@
use std::{io, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, AtomicU8, Ordering}};
use std::{io, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, Ordering}};
use anyhow::Result;
use crossterm::{Command, event::{DisableBracketedPaste, EnableBracketedPaste, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, style::Print, terminal::{LeaveAlternateScreen, SetTitle, disable_raw_mode, enable_raw_mode}};
use cursor::RestoreCursor;
use crossterm::{Command, event::{DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, 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_adapter::{Emulator, Mux};
use yazi_adapter::{Emulator, Mux, TMUX};
use yazi_config::YAZI;
use yazi_shared::{SyncCell, tty::{TTY, TtyWriter}};
static CSI_U: AtomicBool = AtomicBool::new(false);
static BLINK: AtomicBool = AtomicBool::new(false);
static SHAPE: AtomicU8 = AtomicU8::new(0);
pub(super) struct Term {
inner: Terminal<CrosstermBackend<TtyWriter<'static>>>,
@ -34,30 +31,21 @@ impl Term {
execute!(
TTY.writer(),
screen::SetScreen(true),
yazi_term::If(TMUX.get(), EnterAlternateScreen),
Print("\x1bP$q q\x1b\\"), // Request cursor shape (DECRQSS query for DECSCUSR)
Print(Mux::csi("\x1b[?12$p")), // Request cursor blink status (DECSET)
Print("\x1b[?u"), // Request keyboard enhancement flags (CSI u)
Print(Mux::csi("\x1b[0c")), // Request device attributes
screen::SetScreen(false),
yazi_term::If(!TMUX.get(), EnterAlternateScreen),
EnableBracketedPaste,
mouse::SetMouse(true),
yazi_term::If(!YAZI.mgr.mouse_events.is_empty(), EnableMouseCapture),
)?;
let resp = Emulator::read_until_da1();
Mux::tmux_drain()?;
yazi_term::RestoreCursor::store(&resp);
CSI_U.store(resp.contains("\x1b[?0u"), Ordering::Relaxed);
BLINK.store(resp.contains("\x1b[?12;1$y"), Ordering::Relaxed);
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,
);
if CSI_U.load(Ordering::Relaxed) {
PushKeyboardEnhancementFlags(
KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
@ -66,6 +54,10 @@ impl Term {
.write_ansi(&mut TTY.writer())?;
}
if let Some(s) = YAZI.mgr.title() {
queue!(TTY.writer(), SetTitle(s)).ok();
}
term.hide_cursor()?;
term.clear()?;
term.flush()?;
@ -77,10 +69,14 @@ impl Term {
PopKeyboardEnhancementFlags.write_ansi(&mut TTY.writer())?;
}
if !YAZI.mgr.title_format.is_empty() {
queue!(TTY.writer(), SetTitle("")).ok();
}
execute!(
TTY.writer(),
mouse::SetMouse(false),
RestoreCursor,
yazi_term::If(!YAZI.mgr.mouse_events.is_empty(), DisableMouseCapture),
yazi_term::RestoreCursor,
DisableBracketedPaste,
LeaveAlternateScreen,
)?;
@ -95,14 +91,13 @@ impl Term {
}
if !YAZI.mgr.title_format.is_empty() {
execute!(TTY.writer(), SetTitle("")).ok();
queue!(TTY.writer(), SetTitle("")).ok();
}
execute!(
TTY.writer(),
mouse::SetMouse(false),
RestoreCursor,
SetTitle(""),
yazi_term::If(!YAZI.mgr.mouse_events.is_empty(), DisableMouseCapture),
yazi_term::RestoreCursor,
DisableBracketedPaste,
LeaveAlternateScreen,
crossterm::cursor::Show
@ -156,98 +151,3 @@ impl Deref for Term {
impl DerefMut for Term {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
}
// --- Mouse support
mod mouse {
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
use yazi_config::YAZI;
pub struct SetMouse(pub bool);
impl crossterm::Command for SetMouse {
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
if YAZI.mgr.mouse_events.is_empty() {
Ok(())
} else if self.0 {
EnableMouseCapture.write_ansi(f)
} else {
DisableMouseCapture.write_ansi(f)
}
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
if YAZI.mgr.mouse_events.is_empty() {
Ok(())
} else if self.0 {
EnableMouseCapture.execute_winapi()
} else {
DisableMouseCapture.execute_winapi()
}
}
#[cfg(windows)]
fn is_ansi_code_supported(&self) -> bool {
if self.0 {
EnableMouseCapture.is_ansi_code_supported()
} else {
DisableMouseCapture.is_ansi_code_supported()
}
}
}
}
// --- Cursor shape
mod cursor {
use std::sync::atomic::Ordering;
use crossterm::cursor::SetCursorStyle;
use super::{BLINK, SHAPE};
pub struct RestoreCursor;
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) {
u8::MAX => (0, false),
n => (n.max(1).div_ceil(2), n.max(1) & 1 == 1),
};
let blink = BLINK.load(Ordering::Relaxed) ^ shape_blink;
Ok(match shape {
2 if blink => SetCursorStyle::BlinkingUnderScore.write_ansi(f)?,
2 if !blink => SetCursorStyle::SteadyUnderScore.write_ansi(f)?,
3 if blink => SetCursorStyle::BlinkingBar.write_ansi(f)?,
3 if !blink => SetCursorStyle::SteadyBar.write_ansi(f)?,
_ if blink => SetCursorStyle::DefaultUserShape.write_ansi(f)?,
_ if !blink => SetCursorStyle::SteadyBlock.write_ansi(f)?,
_ => unreachable!(),
})
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> { Ok(()) }
}
}
mod screen {
use crossterm::terminal::EnterAlternateScreen;
use yazi_adapter::TMUX;
pub struct SetScreen(pub bool);
impl crossterm::Command for SetScreen {
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
if self.0 == TMUX.get() { Ok(()) } else { EnterAlternateScreen.write_ansi(f) }
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
if self.0 == TMUX.get() { Ok(()) } else { EnterAlternateScreen.execute_winapi() }
}
#[cfg(windows)]
fn is_ansi_code_supported(&self) -> bool { EnterAlternateScreen.is_ansi_code_supported() }
}
}

16
yazi-term/Cargo.toml Normal file
View file

@ -0,0 +1,16 @@
[package]
name = "yazi-term"
version = "25.4.8"
edition = "2024"
license = "MIT"
authors = [ "sxyazi <sxyazi@gmail.com>" ]
description = "Yazi terminal extensions"
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
rust-version = "1.85.0"
[dependencies]
yazi-macro = { path = "../yazi-macro", version = "25.4.8" }
# External dependencies
crossterm = { workspace = true }

45
yazi-term/src/cursor.rs Normal file
View file

@ -0,0 +1,45 @@
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use crossterm::cursor::SetCursorStyle;
static BLINK: AtomicBool = AtomicBool::new(false);
static SHAPE: AtomicU8 = AtomicU8::new(0);
pub struct RestoreCursor;
impl RestoreCursor {
pub fn store(resp: &str) {
BLINK.store(resp.contains("\x1b[?12;1$y"), Ordering::Relaxed);
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,
);
}
}
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) {
u8::MAX => (0, false),
n => (n.max(1).div_ceil(2), n.max(1) & 1 == 1),
};
let blink = BLINK.load(Ordering::Relaxed) ^ shape_blink;
Ok(match shape {
2 if blink => SetCursorStyle::BlinkingUnderScore.write_ansi(f)?,
2 if !blink => SetCursorStyle::SteadyUnderScore.write_ansi(f)?,
3 if blink => SetCursorStyle::BlinkingBar.write_ansi(f)?,
3 if !blink => SetCursorStyle::SteadyBar.write_ansi(f)?,
_ if blink => SetCursorStyle::DefaultUserShape.write_ansi(f)?,
_ if !blink => SetCursorStyle::SteadyBlock.write_ansi(f)?,
_ => unreachable!(),
})
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> { Ok(()) }
}

15
yazi-term/src/if_.rs Normal file
View file

@ -0,0 +1,15 @@
pub struct If<T: crossterm::Command>(pub bool, pub T);
impl<T: crossterm::Command> crossterm::Command for If<T> {
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
if self.0 { self.1.write_ansi(f) } else { Ok(()) }
}
#[cfg(windows)]
fn execute_winapi(&self) -> std::io::Result<()> {
if self.0 { self.1.execute_winapi() } else { Ok(()) }
}
#[cfg(windows)]
fn is_ansi_code_supported(&self) -> bool { self.1.is_ansi_code_supported() }
}

3
yazi-term/src/lib.rs Normal file
View file

@ -0,0 +1,3 @@
#![allow(clippy::unit_arg)]
yazi_macro::mod_flat!(cursor if_);