mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
refactor: move Term to its own yazi-term crate (#3629)
This commit is contained in:
parent
24c60419bb
commit
583345296f
27 changed files with 197 additions and 124 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -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]]
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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"))?;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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!(
|
||||
|
|
|
|||
|
|
@ -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" ] }
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) -> ! {
|
||||
|
|
|
|||
|
|
@ -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<Data> {
|
||||
|
|
|
|||
|
|
@ -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<()> {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::Term;
|
||||
use yazi_term::Term;
|
||||
|
||||
pub(super) struct Panic;
|
||||
|
||||
|
|
|
|||
|
|
@ -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" ] }
|
||||
|
|
|
|||
|
|
@ -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" ] }
|
||||
|
|
|
|||
1
yazi-shim/src/crossterm/mod.rs
Normal file
1
yazi-shim/src/crossterm/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_flat!(r#if restore_background restore_cursor set_background);
|
||||
10
yazi-shim/src/crossterm/restore_background.rs
Normal file
10
yazi-shim/src/crossterm/restore_background.rs
Normal file
|
|
@ -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(()) }
|
||||
}
|
||||
|
|
@ -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)?,
|
||||
10
yazi-shim/src/crossterm/set_background.rs
Normal file
10
yazi-shim/src/crossterm/set_background.rs
Normal file
|
|
@ -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(()) }
|
||||
}
|
||||
|
|
@ -1 +1,3 @@
|
|||
yazi_macro::mod_pub!(crossterm);
|
||||
|
||||
yazi_macro::mod_flat!(twox);
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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(()) }
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(background cursor r#if);
|
||||
yazi_macro::mod_flat!(option state term);
|
||||
|
|
|
|||
17
yazi-term/src/option.rs
Normal file
17
yazi-term/src/option.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
use yazi_config::{THEME, YAZI};
|
||||
|
||||
pub(super) struct TermOption {
|
||||
pub bg: String,
|
||||
pub mouse: bool,
|
||||
pub title: Option<String>,
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
45
yazi-term/src/state.rs
Normal file
45
yazi-term/src/state.rs
Normal file
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TermState> = SyncCell::new(TermState::default());
|
||||
|
||||
pub struct Term {
|
||||
inner: Terminal<CrosstermBackend<TtyWriter<'static>>>,
|
||||
last_area: Rect,
|
||||
last_buffer: Buffer,
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub(super) fn start() -> Result<Self> {
|
||||
pub fn start() -> Result<Self> {
|
||||
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<CompletedFrame<'_>> {
|
||||
pub fn draw(&mut self, f: impl FnOnce(&mut Frame)) -> io::Result<CompletedFrame<'_>> {
|
||||
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<CompletedFrame<'_>> {
|
||||
pub fn draw_partial(&mut self, f: impl FnOnce(&mut Frame)) -> io::Result<CompletedFrame<'_>> {
|
||||
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 }
|
||||
}
|
||||
|
|
@ -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 }
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue