From cc50f94de64b2106f9d5a4f4dff05342559bc780 Mon Sep 17 00:00:00 2001 From: Xerxes-2 Date: Mon, 5 Aug 2024 15:21:28 +1000 Subject: [PATCH] fix: preview files containing special `\x1b` characters as plain text and escape (#1395) Co-authored-by: sxyazi --- yazi-adapter/src/emulator.rs | 4 +- yazi-fm/src/input/input.rs | 6 +-- yazi-plugin/src/external/highlighter.rs | 55 ++++++++++++------------- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/yazi-adapter/src/emulator.rs b/yazi-adapter/src/emulator.rs index 73908dc8..9e449527 100644 --- a/yazi-adapter/src/emulator.rs +++ b/yazi-adapter/src/emulator.rs @@ -199,10 +199,10 @@ impl Emulator { bail!("unexpected EOF"); } buf.push(c[0]); - if c[0] != b'c' || !buf.contains(&b'\x1b') { + if c[0] != b'c' || !buf.contains(&0x1b) { continue; } - if buf.rsplitn(2, |&b| b == b'\x1b').next().is_some_and(|s| s.starts_with(b"[?")) { + if buf.rsplitn(2, |&b| b == 0x1b).next().is_some_and(|s| s.starts_with(b"[?")) { break; } } diff --git a/yazi-fm/src/input/input.rs b/yazi-fm/src/input/input.rs index f4ad39cf..c4b7cf8c 100644 --- a/yazi-fm/src/input/input.rs +++ b/yazi-fm/src/input/input.rs @@ -3,7 +3,7 @@ use std::ops::Range; use anyhow::{bail, Result}; use ratatui::{buffer::Buffer, layout::Rect, text::Line, widgets::{Block, BorderType, Paragraph, Widget}}; use syntect::easy::HighlightLines; -use yazi_config::THEME; +use yazi_config::{PREVIEW, THEME}; use yazi_core::input::InputMode; use yazi_plugin::external::Highlighter; @@ -21,11 +21,11 @@ impl<'a> Input<'a> { bail!("Highlighting is disabled"); } - let (theme, syntaxes) = Highlighter::init(); + let (theme, syntaxes) = futures::executor::block_on(Highlighter::init()); if let Some(syntax) = syntaxes.find_syntax_by_name("Bourne Again Shell (bash)") { let mut h = HighlightLines::new(syntax, theme); let regions = h.highlight_line(self.cx.input.value(), syntaxes)?; - return Ok(Highlighter::to_line_widget(regions)); + return Ok(Highlighter::to_line_widget(regions, &" ".repeat(PREVIEW.tab_size as usize))); } bail!("Failed to find syntax") } diff --git a/yazi-plugin/src/external/highlighter.rs b/yazi-plugin/src/external/highlighter.rs index 0e681b96..3def21ff 100644 --- a/yazi-plugin/src/external/highlighter.rs +++ b/yazi-plugin/src/external/highlighter.rs @@ -1,15 +1,14 @@ -use std::{io::Cursor, mem, path::{Path, PathBuf}, sync::{atomic::{AtomicUsize, Ordering}, OnceLock}}; +use std::{io::Cursor, mem, path::{Path, PathBuf}, sync::atomic::{AtomicUsize, Ordering}}; use anyhow::{anyhow, Result}; use ratatui::text::{Line, Span, Text}; -use syntect::{dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}}; -use tokio::{fs::File, io::{AsyncBufReadExt, BufReader}}; +use syntect::{dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}, LoadingError}; +use tokio::{fs::File, io::{AsyncBufReadExt, BufReader}, sync::OnceCell}; use yazi_config::{PREVIEW, THEME}; use yazi_shared::PeekError; static INCR: AtomicUsize = AtomicUsize::new(0); -static SYNTECT_SYNTAX: OnceLock = OnceLock::new(); -static SYNTECT_THEME: OnceLock = OnceLock::new(); +static SYNTECT: OnceCell<(Theme, SyntaxSet)> = OnceCell::const_new(); pub struct Highlighter { path: PathBuf, @@ -19,27 +18,28 @@ impl Highlighter { #[inline] pub fn new(path: &Path) -> Self { Self { path: path.to_owned() } } - pub fn init() -> (&'static Theme, &'static SyntaxSet) { - #[inline] - fn from_file() -> Result { - let file = std::fs::File::open(&THEME.manager.syntect_theme)?; - Ok(ThemeSet::load_from_reader(&mut std::io::BufReader::new(file))?) - } + pub async fn init() -> (&'static Theme, &'static SyntaxSet) { + let fut = async { + tokio::task::spawn_blocking(|| { + let theme = std::fs::File::open(&THEME.manager.syntect_theme) + .map_err(LoadingError::Io) + .and_then(|f| ThemeSet::load_from_reader(&mut std::io::BufReader::new(f))) + .or_else(|_| ThemeSet::load_from_reader(&mut Cursor::new(yazi_prebuild::ansi_theme()))); - let theme = SYNTECT_THEME.get_or_init(|| { - from_file().unwrap_or_else(|_| { - ThemeSet::load_from_reader(&mut Cursor::new(yazi_prebuild::ansi_theme())).unwrap() + let syntaxes = dumps::from_uncompressed_data(yazi_prebuild::syntaxes()); + + (theme.unwrap(), syntaxes.unwrap()) }) - }); + .await + .unwrap() + }; - let syntaxes = SYNTECT_SYNTAX - .get_or_init(|| dumps::from_uncompressed_data(yazi_prebuild::syntaxes()).unwrap()); - - (theme, syntaxes) + let r = SYNTECT.get_or_init(|| fut).await; + (&r.0, &r.1) } async fn find_syntax(path: &Path) -> Result<&'static SyntaxReference> { - let (_, syntaxes) = Self::init(); + let (_, syntaxes) = Self::init().await; let name = path.file_name().map(|n| n.to_string_lossy()).unwrap_or_default(); if let Some(s) = syntaxes.find_syntax_by_extension(&name) { return Ok(s); @@ -73,7 +73,7 @@ impl Highlighter { break; } - if !plain && buf.len() > 6000 { + if !plain && (buf.len() > 5000 || buf.contains(&0x1b)) { plain = true; drop(mem::take(&mut before)); } @@ -98,7 +98,7 @@ impl Highlighter { if plain { let indent = " ".repeat(PREVIEW.tab_size as usize); - Ok(Text::from(after.join("").replace('\t', &indent))) + Ok(Text::from(after.join("").replace('\x1b', "^[").replace('\t', &indent))) } else { Self::highlight_with(before, after, syntax.unwrap()).await } @@ -110,11 +110,10 @@ impl Highlighter { syntax: &'static SyntaxReference, ) -> Result, PeekError> { let ticket = INCR.load(Ordering::Relaxed); + let (theme, syntaxes) = Self::init().await; tokio::task::spawn_blocking(move || { - let (theme, syntaxes) = Self::init(); let mut h = HighlightLines::new(syntax, theme); - for line in before { if ticket != INCR.load(Ordering::Relaxed) { return Err("Highlighting cancelled".into()); @@ -122,6 +121,7 @@ impl Highlighter { h.highlight_line(&line, syntaxes).map_err(|e| anyhow!(e))?; } + let indent = " ".repeat(PREVIEW.tab_size as usize); let mut lines = Vec::with_capacity(after.len()); for line in after { if ticket != INCR.load(Ordering::Relaxed) { @@ -129,7 +129,7 @@ impl Highlighter { } let regions = h.highlight_line(&line, syntaxes).map_err(|e| anyhow!(e))?; - lines.push(Self::to_line_widget(regions)); + lines.push(Self::to_line_widget(regions, &indent)); } Ok(Text::from(lines)) @@ -182,8 +182,7 @@ impl Highlighter { } } - pub fn to_line_widget(regions: Vec<(highlighting::Style, &str)>) -> Line<'static> { - let indent = " ".repeat(PREVIEW.tab_size as usize); + pub fn to_line_widget(regions: Vec<(highlighting::Style, &str)>, indent: &str) -> Line<'static> { let spans: Vec<_> = regions .into_iter() .map(|(style, s)| { @@ -199,7 +198,7 @@ impl Highlighter { } Span { - content: s.replace('\t', &indent).into(), + content: s.replace('\t', indent).into(), style: ratatui::style::Style { fg: Self::to_ansi_color(style.foreground), // bg: Self::to_ansi_color(style.background),