perf: switch to globset to reduce CPU time spent on matching icons (#908)

This commit is contained in:
三咲雅 · Misaki Masa 2024-04-14 11:11:31 +08:00 committed by GitHub
parent 0cc14f44d7
commit a70374fced
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 53 additions and 77 deletions

15
Cargo.lock generated
View file

@ -852,10 +852,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]] [[package]]
name = "glob" name = "globset"
version = "0.3.1" version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata",
"regex-syntax",
]
[[package]] [[package]]
name = "half" name = "half"
@ -2719,7 +2726,7 @@ dependencies = [
"anyhow", "anyhow",
"arc-swap", "arc-swap",
"crossterm", "crossterm",
"glob", "globset",
"indexmap", "indexmap",
"ratatui", "ratatui",
"serde", "serde",

View file

@ -1 +1 @@
{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp"," Überzug"," Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG"],"version":"0.2"} {"flagWords":[],"language":"en","version":"0.2","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp"," Überzug"," Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset"]}

View file

@ -15,7 +15,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.4" }
anyhow = "1.0.82" anyhow = "1.0.82"
arc-swap = "1.7.1" arc-swap = "1.7.1"
crossterm = "0.27.0" crossterm = "0.27.0"
glob = "0.3.1" globset = "0.4.14"
indexmap = "2.2.6" indexmap = "2.2.6"
ratatui = "0.26.1" ratatui = "0.26.1"
serde = { version = "1.0.197", features = [ "derive" ] } serde = { version = "1.0.197", features = [ "derive" ] }

View file

@ -173,22 +173,14 @@ rules = [
{ mime = "image/*", fg = "cyan" }, { mime = "image/*", fg = "cyan" },
# Media # Media
{ mime = "video/*", fg = "yellow" }, { mime = "{audio,video}/*", fg = "yellow" },
{ mime = "audio/*", fg = "yellow" },
# Archives # Archives
{ mime = "application/*zip", fg = "magenta" }, { mime = "application/*zip", fg = "magenta" },
{ mime = "application/x-tar", fg = "magenta" }, { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", fg = "magenta" },
{ mime = "application/x-bzip*", fg = "magenta" },
{ mime = "application/x-7z-compressed", fg = "magenta" },
{ mime = "application/x-rar", fg = "magenta" },
{ mime = "application/x-xz", fg = "magenta" },
# Documents # Documents
{ mime = "application/doc", fg = "green" }, { mime = "application/{pdf,doc,rtf,vnd.*}", fg = "green" },
{ mime = "application/pdf", fg = "green" },
{ mime = "application/rtf", fg = "green" },
{ mime = "application/vnd.*", fg = "green" },
# Fallback # Fallback
# { name = "*", fg = "white" }, # { name = "*", fg = "white" },

View file

@ -55,22 +55,17 @@ play = [
rules = [ rules = [
{ name = "*/", use = [ "edit", "open", "reveal" ] }, { name = "*/", use = [ "edit", "open", "reveal" ] },
{ mime = "text/*", use = [ "edit", "reveal" ] }, { mime = "text/*", use = [ "edit", "reveal" ] },
{ mime = "image/*", use = [ "open", "reveal" ] }, { mime = "image/*", use = [ "open", "reveal" ] },
{ mime = "video/*", use = [ "play", "reveal" ] }, { mime = "{audio,video}/*", use = [ "play", "reveal" ] },
{ mime = "audio/*", use = [ "play", "reveal" ] }, { mime = "inode/x-empty", use = [ "edit", "reveal" ] },
{ mime = "inode/x-empty", use = [ "edit", "reveal" ] },
{ mime = "application/*zip", use = [ "extract", "reveal" ] },
{ mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", use = [ "extract", "reveal" ] },
{ mime = "application/json", use = [ "edit", "reveal" ] }, { mime = "application/json", use = [ "edit", "reveal" ] },
{ mime = "*/javascript", use = [ "edit", "reveal" ] }, { mime = "*/javascript", use = [ "edit", "reveal" ] },
{ mime = "application/*zip", use = [ "extract", "reveal" ] },
{ mime = "application/x-tar", use = [ "extract", "reveal" ] },
{ mime = "application/x-bzip*", use = [ "extract", "reveal" ] },
{ mime = "application/x-7z-compressed", use = [ "extract", "reveal" ] },
{ mime = "application/x-rar", use = [ "extract", "reveal" ] },
{ mime = "application/x-xz", use = [ "extract", "reveal" ] },
{ mime = "*", use = [ "open", "reveal" ] }, { mime = "*", use = [ "open", "reveal" ] },
] ]
@ -96,10 +91,8 @@ preloaders = [
previewers = [ previewers = [
{ name = "*/", run = "folder", sync = true }, { name = "*/", run = "folder", sync = true },
# Code # Code
{ mime = "text/*", run = "code" }, { mime = "text/*", run = "code" },
{ mime = "*/xml", run = "code" }, { mime = "*/{xml,javascript,x-wine-extension-ini}", run = "code" },
{ mime = "*/javascript", run = "code" },
{ mime = "*/x-wine-extension-ini", run = "code" },
# JSON # JSON
{ mime = "application/json", run = "json" }, { mime = "application/json", run = "json" },
# Image # Image
@ -110,12 +103,8 @@ previewers = [
# PDF # PDF
{ mime = "application/pdf", run = "pdf" }, { mime = "application/pdf", run = "pdf" },
# Archive # Archive
{ mime = "application/*zip", run = "archive" }, { mime = "application/*zip", run = "archive" },
{ mime = "application/x-tar", run = "archive" }, { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", run = "archive" },
{ mime = "application/x-bzip*", run = "archive" },
{ mime = "application/x-7z-compressed", run = "archive" },
{ mime = "application/x-rar", run = "archive" },
{ mime = "application/x-xz", run = "archive" },
# Fallback # Fallback
{ name = "*", run = "file" }, { name = "*", run = "file" },
] ]

View file

@ -25,8 +25,8 @@ impl Open {
{ {
let is_folder = mime.as_ref() == MIME_DIR; let is_folder = mime.as_ref() == MIME_DIR;
self.rules.iter().find_map(|rule| { self.rules.iter().find_map(|rule| {
if rule.mime.as_ref().is_some_and(|m| m.matches(&mime)) if rule.mime.as_ref().is_some_and(|p| p.match_mime(&mime))
|| rule.name.as_ref().is_some_and(|n| n.match_path(&path, is_folder)) || rule.name.as_ref().is_some_and(|p| p.match_path(&path, is_folder))
{ {
let openers = rule let openers = rule
.use_ .use_

View file

@ -1,46 +1,30 @@
use std::path::Path; use std::path::Path;
use glob::MatchOptions; use globset::GlobBuilder;
use serde::Deserialize; use serde::Deserialize;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(try_from = "String")] #[serde(try_from = "String")]
pub struct Pattern { pub struct Pattern {
inner: glob::Pattern, inner: globset::GlobMatcher,
sensitive: bool, is_star: bool,
is_folder: bool, is_folder: bool,
full_path: bool,
} }
impl Pattern { impl Pattern {
#[inline] #[inline]
pub fn matches(&self, str: impl AsRef<str>) -> bool { pub fn match_mime(&self, str: impl AsRef<str>) -> bool { self.inner.is_match(str.as_ref()) }
self.inner.matches_with(str.as_ref(), MatchOptions {
case_sensitive: self.sensitive,
require_literal_separator: self.full_path,
require_literal_leading_dot: false,
})
}
#[inline] #[inline]
pub fn match_path(&self, path: impl AsRef<Path>, is_folder: bool) -> bool { pub fn match_path(&self, path: impl AsRef<Path>, is_folder: bool) -> bool {
if is_folder != self.is_folder { is_folder == self.is_folder && (self.is_star || self.inner.is_match(path))
return false;
}
let path = path.as_ref();
self.matches(if self.full_path {
path.to_string_lossy()
} else {
path.file_name().map_or_else(|| path.to_string_lossy(), |n| n.to_string_lossy())
})
} }
#[inline] #[inline]
pub fn any_file(&self) -> bool { !self.is_folder && self.inner.as_str() == "*" } pub fn any_file(&self) -> bool { self.is_star && !self.is_folder }
#[inline] #[inline]
pub fn any_dir(&self) -> bool { self.is_folder && self.inner.as_str() == "*" } pub fn any_dir(&self) -> bool { self.is_star && self.is_folder }
} }
impl TryFrom<&str> for Pattern { impl TryFrom<&str> for Pattern {
@ -49,12 +33,16 @@ impl TryFrom<&str> for Pattern {
fn try_from(s: &str) -> Result<Self, Self::Error> { fn try_from(s: &str) -> Result<Self, Self::Error> {
let a = s.trim_start_matches("\\s"); let a = s.trim_start_matches("\\s");
let b = a.trim_end_matches('/'); let b = a.trim_end_matches('/');
Ok(Self {
inner: glob::Pattern::new(b)?, let inner = GlobBuilder::new(b)
sensitive: a.len() < s.len(), .case_insensitive(a.len() == s.len())
is_folder: b.len() < a.len(), .literal_separator(b.contains('/'))
full_path: b.contains('/'), .backslash_escape(false)
}) .empty_alternates(false)
.build()?
.compile_matcher();
Ok(Self { inner, is_star: b == "*", is_folder: b.len() < a.len() })
} }
} }

View file

@ -73,8 +73,8 @@ impl Plugin {
.iter() .iter()
.filter(|&rule| { .filter(|&rule| {
rule.cond.as_ref().and_then(|c| c.eval(f)) != Some(false) rule.cond.as_ref().and_then(|c| c.eval(f)) != Some(false)
&& (rule.mime.as_ref().zip(mime).map_or(false, |(m, s)| m.matches(s)) && (rule.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m))
|| rule.name.as_ref().is_some_and(|n| n.match_path(path, is_folder))) || rule.name.as_ref().is_some_and(|p| p.match_path(path, is_folder)))
}) })
.collect() .collect()
} }
@ -82,8 +82,8 @@ impl Plugin {
pub fn previewer(&self, path: &Path, mime: &str) -> Option<&PluginRule> { pub fn previewer(&self, path: &Path, mime: &str) -> Option<&PluginRule> {
let is_folder = mime == MIME_DIR; let is_folder = mime == MIME_DIR;
self.previewers.iter().find(|&rule| { self.previewers.iter().find(|&rule| {
rule.mime.as_ref().is_some_and(|m| m.matches(mime)) rule.mime.as_ref().is_some_and(|p| p.match_mime(mime))
|| rule.name.as_ref().is_some_and(|n| n.match_path(path, is_folder)) || rule.name.as_ref().is_some_and(|p| p.match_path(path, is_folder))
}) })
} }
} }

View file

@ -17,8 +17,8 @@ impl Filetype {
return false; return false;
} }
self.name.as_ref().is_some_and(|n| n.match_path(&file.url, file.is_dir())) self.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m))
|| self.mime.as_ref().zip(mime).map_or(false, |(m, s)| m.matches(s)) || self.name.as_ref().is_some_and(|n| n.match_path(&file.url, file.is_dir()))
} }
} }