mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
word wrap in preview
This commit is contained in:
parent
189cb81db3
commit
6a30dfc137
6 changed files with 60 additions and 11 deletions
24
Cargo.lock
generated
24
Cargo.lock
generated
|
|
@ -1830,6 +1830,12 @@ version = "1.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smawk"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.5.7"
|
version = "0.5.7"
|
||||||
|
|
@ -1941,6 +1947,17 @@ dependencies = [
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.14.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
|
||||||
|
dependencies = [
|
||||||
|
"smawk",
|
||||||
|
"unicode-linebreak",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.59"
|
version = "1.0.59"
|
||||||
|
|
@ -2242,6 +2259,12 @@ version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-linebreak"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-normalization"
|
name = "unicode-normalization"
|
||||||
version = "0.1.23"
|
version = "0.1.23"
|
||||||
|
|
@ -2864,6 +2887,7 @@ dependencies = [
|
||||||
"shell-escape",
|
"shell-escape",
|
||||||
"shell-words",
|
"shell-words",
|
||||||
"syntect",
|
"syntect",
|
||||||
|
"textwrap",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ mouse_events = [ "click", "scroll" ]
|
||||||
|
|
||||||
[preview]
|
[preview]
|
||||||
tab_size = 2
|
tab_size = 2
|
||||||
|
word_wrap = false
|
||||||
max_width = 600
|
max_width = 600
|
||||||
max_height = 900
|
max_height = 900
|
||||||
cache_dir = ""
|
cache_dir = ""
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use crate::{validation::check_validation, Xdg, MERGED_YAZI};
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct Preview {
|
pub struct Preview {
|
||||||
pub tab_size: u8,
|
pub tab_size: u8,
|
||||||
|
pub word_wrap: bool,
|
||||||
pub max_width: u32,
|
pub max_width: u32,
|
||||||
pub max_height: u32,
|
pub max_height: u32,
|
||||||
|
|
||||||
|
|
@ -31,6 +32,7 @@ impl Default for Preview {
|
||||||
#[derive(Deserialize, Validate)]
|
#[derive(Deserialize, Validate)]
|
||||||
struct Shadow {
|
struct Shadow {
|
||||||
tab_size: u8,
|
tab_size: u8,
|
||||||
|
word_wrap: bool,
|
||||||
max_width: u32,
|
max_width: u32,
|
||||||
max_height: u32,
|
max_height: u32,
|
||||||
|
|
||||||
|
|
@ -55,6 +57,7 @@ impl Default for Preview {
|
||||||
|
|
||||||
Preview {
|
Preview {
|
||||||
tab_size: preview.tab_size,
|
tab_size: preview.tab_size,
|
||||||
|
word_wrap: preview.word_wrap,
|
||||||
max_width: preview.max_width,
|
max_width: preview.max_width,
|
||||||
max_height: preview.max_height,
|
max_height: preview.max_height,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ tokio = { version = "1.38.0", features = [ "full" ] }
|
||||||
tokio-stream = "0.1.15"
|
tokio-stream = "0.1.15"
|
||||||
tokio-util = "0.7.11"
|
tokio-util = "0.7.11"
|
||||||
unicode-width = "0.1.13"
|
unicode-width = "0.1.13"
|
||||||
|
textwrap = "0.14.2"
|
||||||
yazi-prebuild = "0.1.2"
|
yazi-prebuild = "0.1.2"
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
|
|
|
||||||
39
yazi-plugin/src/external/highlighter.rs
vendored
39
yazi-plugin/src/external/highlighter.rs
vendored
|
|
@ -6,6 +6,8 @@ use syntect::{dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}
|
||||||
use tokio::{fs::File, io::{AsyncBufReadExt, BufReader}};
|
use tokio::{fs::File, io::{AsyncBufReadExt, BufReader}};
|
||||||
use yazi_config::{PREVIEW, THEME};
|
use yazi_config::{PREVIEW, THEME};
|
||||||
use yazi_shared::PeekError;
|
use yazi_shared::PeekError;
|
||||||
|
use ratatui::prelude::Rect;
|
||||||
|
use textwrap::wrap as textwrap_wrap;
|
||||||
|
|
||||||
static INCR: AtomicUsize = AtomicUsize::new(0);
|
static INCR: AtomicUsize = AtomicUsize::new(0);
|
||||||
static SYNTECT_SYNTAX: OnceLock<SyntaxSet> = OnceLock::new();
|
static SYNTECT_SYNTAX: OnceLock<SyntaxSet> = OnceLock::new();
|
||||||
|
|
@ -56,20 +58,20 @@ impl Highlighter {
|
||||||
syntaxes.find_syntax_by_first_line(&line).ok_or_else(|| anyhow!("No syntax found"))
|
syntaxes.find_syntax_by_first_line(&line).ok_or_else(|| anyhow!("No syntax found"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn highlight(&self, skip: usize, limit: usize) -> Result<Text<'static>, PeekError> {
|
pub async fn highlight(&self, skip: usize, area: Rect, wrap: bool) -> Result<Text<'static>, PeekError> {
|
||||||
let mut reader = BufReader::new(File::open(&self.path).await?);
|
let mut reader = BufReader::new(File::open(&self.path).await?);
|
||||||
|
|
||||||
let syntax = Self::find_syntax(&self.path).await;
|
let syntax = Self::find_syntax(&self.path).await;
|
||||||
let mut plain = syntax.is_err();
|
let mut plain = syntax.is_err();
|
||||||
|
|
||||||
let mut before = Vec::with_capacity(if plain { 0 } else { skip });
|
let mut before = Vec::with_capacity(if plain { 0 } else { skip });
|
||||||
let mut after = Vec::with_capacity(limit);
|
let mut after = Vec::with_capacity(area.height as usize);
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
while reader.read_until(b'\n', &mut buf).await.is_ok() {
|
while reader.read_until(b'\n', &mut buf).await.is_ok() {
|
||||||
i += 1;
|
i += 1;
|
||||||
if buf.is_empty() || i > skip + limit {
|
if buf.is_empty() || i > skip + area.height as usize {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,23 +86,40 @@ impl Highlighter {
|
||||||
buf.push(b'\n');
|
buf.push(b'\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let line = String::from_utf8_lossy(&buf).into_owned();
|
||||||
|
|
||||||
if i > skip {
|
if i > skip {
|
||||||
after.push(String::from_utf8_lossy(&buf).into_owned());
|
after.push(line);
|
||||||
} else if !plain {
|
} else if !plain {
|
||||||
before.push(String::from_utf8_lossy(&buf).into_owned());
|
before.push(line);
|
||||||
}
|
}
|
||||||
buf.clear();
|
buf.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if skip > 0 && i < skip + limit {
|
if skip > 0 && i < skip + area.height as usize {
|
||||||
return Err(PeekError::Exceed(i.saturating_sub(limit)));
|
return Err(PeekError::Exceed(i.saturating_sub(area.height as usize)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let indent = " ".repeat(PREVIEW.tab_size as usize);
|
||||||
|
let after_text = after.join("").replace('\t', &indent);
|
||||||
|
|
||||||
if plain {
|
if plain {
|
||||||
let indent = " ".repeat(PREVIEW.tab_size as usize);
|
let wrapped_text = if wrap {
|
||||||
Ok(Text::from(after.join("").replace('\t', &indent)))
|
textwrap_wrap(&after_text, area.width as usize).join("\n")
|
||||||
|
} else {
|
||||||
|
after_text
|
||||||
|
};
|
||||||
|
Ok(Text::from(wrapped_text))
|
||||||
} else {
|
} else {
|
||||||
Self::highlight_with(before, after, syntax.unwrap()).await
|
let wrapped_after: Vec<String> = if wrap {
|
||||||
|
textwrap_wrap(&after_text, area.width as usize)
|
||||||
|
.into_iter()
|
||||||
|
.map(|cow| cow.trim_end_matches('\n').to_string())
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
after
|
||||||
|
};
|
||||||
|
Self::highlight_with(before, wrapped_after, syntax.unwrap()).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use mlua::{AnyUserData, IntoLuaMulti, Lua, Table, Value};
|
||||||
use yazi_shared::{emit, event::Cmd, Layer, PeekError};
|
use yazi_shared::{emit, event::Cmd, Layer, PeekError};
|
||||||
|
|
||||||
use super::Utils;
|
use super::Utils;
|
||||||
|
use yazi_config::PREVIEW;
|
||||||
use crate::{bindings::{FileRef, Window}, cast_to_renderable, elements::{Paragraph, RectRef, Renderable}, external::{self, Highlighter}};
|
use crate::{bindings::{FileRef, Window}, cast_to_renderable, elements::{Paragraph, RectRef, Renderable}, external::{self, Highlighter}};
|
||||||
|
|
||||||
pub struct PreviewLock {
|
pub struct PreviewLock {
|
||||||
|
|
@ -37,7 +38,7 @@ impl Utils {
|
||||||
let mut lock = PreviewLock::try_from(t)?;
|
let mut lock = PreviewLock::try_from(t)?;
|
||||||
|
|
||||||
let text =
|
let text =
|
||||||
match Highlighter::new(&lock.url).highlight(lock.skip, area.height as usize).await {
|
match Highlighter::new(&lock.url).highlight(lock.skip, *area, PREVIEW.word_wrap).await {
|
||||||
Ok(text) => text,
|
Ok(text) => text,
|
||||||
Err(PeekError::Exceed(max)) => return (false, max).into_lua_multi(lua),
|
Err(PeekError::Exceed(max)) => return (false, max).into_lua_multi(lua),
|
||||||
Err(_) => return (false, Value::Nil).into_lua_multi(lua),
|
Err(_) => return (false, Value::Nil).into_lua_multi(lua),
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue