mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
perf: only scan the first 1024 bytes to detect if it's binary, apply \r fixes only to content within the visible range, avoid unnecessary allocations during natural sorting (#1551)
This commit is contained in:
parent
9cf85c09d2
commit
58ba459b63
4 changed files with 91 additions and 78 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
|
@ -2131,9 +2131,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.208"
|
||||
version = "1.0.209"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
|
||||
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
|
@ -2150,9 +2150,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.208"
|
||||
version = "1.0.209"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
|
||||
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2161,9 +2161,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.125"
|
||||
version = "1.0.127"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
|
||||
checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ parking_lot = "0.12.3"
|
|||
ratatui = { version = "0.27.0", features = [ "unstable-rendered-line-info" ] }
|
||||
regex = "1.10.6"
|
||||
scopeguard = "1.2.0"
|
||||
serde = { version = "1.0.208", features = [ "derive" ] }
|
||||
serde_json = "1.0.125"
|
||||
serde = { version = "1.0.209", features = [ "derive" ] }
|
||||
serde_json = "1.0.127"
|
||||
shell-words = "1.1.0"
|
||||
tokio = { version = "1.39.3", features = [ "full" ] }
|
||||
tokio-stream = "0.1.15"
|
||||
|
|
|
|||
|
|
@ -68,25 +68,25 @@ impl FilesSorter {
|
|||
}
|
||||
|
||||
fn sort_naturally(&self, items: &mut Vec<File>) {
|
||||
let mut indices = Vec::with_capacity(items.len());
|
||||
let mut entities = Vec::with_capacity(items.len());
|
||||
for (i, file) in items.iter().enumerate() {
|
||||
indices.push(i);
|
||||
entities.push(file.url.as_os_str().as_encoded_bytes());
|
||||
}
|
||||
|
||||
let mut indices: Vec<usize> = (0..items.len()).collect();
|
||||
indices.sort_unstable_by(|&a, &b| {
|
||||
let promote = self.promote(&items[a], &items[b]);
|
||||
let (a, b) = (&items[a], &items[b]);
|
||||
|
||||
let promote = self.promote(a, b);
|
||||
if promote != Ordering::Equal {
|
||||
return promote;
|
||||
}
|
||||
|
||||
let ordering = if !self.translit {
|
||||
natsort(entities[a], entities[b], !self.sensitive)
|
||||
let ordering = if self.translit {
|
||||
natsort(
|
||||
a.url.as_os_str().as_encoded_bytes().transliterate().as_bytes(),
|
||||
b.url.as_os_str().as_encoded_bytes().transliterate().as_bytes(),
|
||||
!self.sensitive,
|
||||
)
|
||||
} else {
|
||||
natsort(
|
||||
entities[a].transliterate().as_bytes(),
|
||||
entities[b].transliterate().as_bytes(),
|
||||
a.url.as_os_str().as_encoded_bytes(),
|
||||
b.url.as_os_str().as_encoded_bytes(),
|
||||
!self.sensitive,
|
||||
)
|
||||
};
|
||||
|
|
|
|||
129
yazi-plugin/src/external/highlighter.rs
vendored
129
yazi-plugin/src/external/highlighter.rs
vendored
|
|
@ -38,23 +38,8 @@ impl Highlighter {
|
|||
(&r.0, &r.1)
|
||||
}
|
||||
|
||||
async fn find_syntax(path: &Path) -> Result<&'static SyntaxReference> {
|
||||
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);
|
||||
}
|
||||
|
||||
let ext = path.extension().map(|e| e.to_string_lossy()).unwrap_or_default();
|
||||
if let Some(s) = syntaxes.find_syntax_by_extension(&ext) {
|
||||
return Ok(s);
|
||||
}
|
||||
|
||||
let mut line = String::new();
|
||||
let mut reader = BufReader::new(File::open(&path).await?);
|
||||
reader.read_line(&mut line).await?;
|
||||
syntaxes.find_syntax_by_first_line(&line).ok_or_else(|| anyhow!("No syntax found"))
|
||||
}
|
||||
#[inline]
|
||||
pub fn abort() { INCR.fetch_add(1, Ordering::Relaxed); }
|
||||
|
||||
pub async fn highlight(&self, skip: usize, limit: usize) -> Result<Text<'static>, PeekError> {
|
||||
let mut reader = BufReader::new(File::open(&self.path).await?);
|
||||
|
|
@ -67,10 +52,13 @@ impl Highlighter {
|
|||
|
||||
let mut i = 0;
|
||||
let mut buf = vec![];
|
||||
let mut inspected = 0u16;
|
||||
while reader.read_until(b'\n', &mut buf).await.is_ok() {
|
||||
i += 1;
|
||||
if buf.is_empty() || i > skip + limit {
|
||||
break;
|
||||
} else if Self::is_binary(&buf, &mut inspected) {
|
||||
return Err("Binary file".into());
|
||||
}
|
||||
|
||||
if !plain && (buf.len() > 5000 || buf.contains(&0x1b)) {
|
||||
|
|
@ -84,15 +72,8 @@ impl Highlighter {
|
|||
buf.push(b'\n');
|
||||
}
|
||||
|
||||
for b in &mut buf {
|
||||
match *b {
|
||||
b'\0' => return Err("Binary file".into()),
|
||||
b'\r' => *b = b'\n', // '\r' occurs in the middle of a line
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if i > skip {
|
||||
buf.iter_mut().for_each(Self::carriage_return_to_line_feed);
|
||||
after.push(String::from_utf8_lossy(&buf).into_owned());
|
||||
} else if !plain {
|
||||
before.push(String::from_utf8_lossy(&buf).into_owned());
|
||||
|
|
@ -144,13 +125,75 @@ impl Highlighter {
|
|||
.await?
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn abort() { INCR.fetch_add(1, Ordering::Relaxed); }
|
||||
async fn find_syntax(path: &Path) -> Result<&'static SyntaxReference> {
|
||||
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);
|
||||
}
|
||||
|
||||
let ext = path.extension().map(|e| e.to_string_lossy()).unwrap_or_default();
|
||||
if let Some(s) = syntaxes.find_syntax_by_extension(&ext) {
|
||||
return Ok(s);
|
||||
}
|
||||
|
||||
let mut line = String::new();
|
||||
let mut reader = BufReader::new(File::open(&path).await?);
|
||||
reader.read_line(&mut line).await?;
|
||||
syntaxes.find_syntax_by_first_line(&line).ok_or_else(|| anyhow!("No syntax found"))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_binary(buf: &[u8], inspected: &mut u16) -> bool {
|
||||
if let Some(n) = 1024u16.checked_sub(*inspected) {
|
||||
*inspected += n.min(buf.len() as u16);
|
||||
buf.iter().take(n as usize).any(|&b| b == 0)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn carriage_return_to_line_feed(c: &mut u8) {
|
||||
if *c == b'\r' {
|
||||
*c = b'\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Highlighter {
|
||||
pub fn to_line_widget(regions: Vec<(highlighting::Style, &str)>, indent: &str) -> Line<'static> {
|
||||
let spans: Vec<_> = regions
|
||||
.into_iter()
|
||||
.map(|(style, s)| {
|
||||
let mut modifier = ratatui::style::Modifier::empty();
|
||||
if style.font_style.contains(highlighting::FontStyle::BOLD) {
|
||||
modifier |= ratatui::style::Modifier::BOLD;
|
||||
}
|
||||
if style.font_style.contains(highlighting::FontStyle::ITALIC) {
|
||||
modifier |= ratatui::style::Modifier::ITALIC;
|
||||
}
|
||||
if style.font_style.contains(highlighting::FontStyle::UNDERLINE) {
|
||||
modifier |= ratatui::style::Modifier::UNDERLINED;
|
||||
}
|
||||
|
||||
Span {
|
||||
content: s.replace('\t', indent).into(),
|
||||
style: ratatui::style::Style {
|
||||
fg: Self::to_ansi_color(style.foreground),
|
||||
// bg: Self::to_ansi_color(style.background),
|
||||
add_modifier: modifier,
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Line::from(spans)
|
||||
}
|
||||
|
||||
// Copy from https://github.com/sharkdp/bat/blob/master/src/terminal.rs
|
||||
pub fn to_ansi_color(color: highlighting::Color) -> Option<ratatui::style::Color> {
|
||||
fn to_ansi_color(color: highlighting::Color) -> Option<ratatui::style::Color> {
|
||||
if color.a == 0 {
|
||||
// Themes can specify one of the user-configurable terminal colors by
|
||||
// encoding them as #RRGGBBAA with AA set to 00 (transparent) and RR set
|
||||
|
|
@ -188,34 +231,4 @@ impl Highlighter {
|
|||
Some(ratatui::style::Color::Rgb(color.r, color.g, color.b))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_line_widget(regions: Vec<(highlighting::Style, &str)>, indent: &str) -> Line<'static> {
|
||||
let spans: Vec<_> = regions
|
||||
.into_iter()
|
||||
.map(|(style, s)| {
|
||||
let mut modifier = ratatui::style::Modifier::empty();
|
||||
if style.font_style.contains(highlighting::FontStyle::BOLD) {
|
||||
modifier |= ratatui::style::Modifier::BOLD;
|
||||
}
|
||||
if style.font_style.contains(highlighting::FontStyle::ITALIC) {
|
||||
modifier |= ratatui::style::Modifier::ITALIC;
|
||||
}
|
||||
if style.font_style.contains(highlighting::FontStyle::UNDERLINE) {
|
||||
modifier |= ratatui::style::Modifier::UNDERLINED;
|
||||
}
|
||||
|
||||
Span {
|
||||
content: s.replace('\t', indent).into(),
|
||||
style: ratatui::style::Style {
|
||||
fg: Self::to_ansi_color(style.foreground),
|
||||
// bg: Self::to_ansi_color(style.background),
|
||||
add_modifier: modifier,
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Line::from(spans)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue