From a70374fced7f72fdd929e58d43ef37ea50dee921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Sun, 14 Apr 2024 11:11:31 +0800 Subject: [PATCH] perf: switch to `globset` to reduce CPU time spent on matching icons (#908) --- Cargo.lock | 15 +++++++--- cspell.json | 2 +- yazi-config/Cargo.toml | 2 +- yazi-config/preset/theme.toml | 16 +++-------- yazi-config/preset/yazi.toml | 33 ++++++++-------------- yazi-config/src/open/open.rs | 4 +-- yazi-config/src/pattern.rs | 46 ++++++++++++------------------- yazi-config/src/plugin/plugin.rs | 8 +++--- yazi-config/src/theme/filetype.rs | 4 +-- 9 files changed, 53 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 95129855..1d5eecfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -852,10 +852,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] -name = "glob" -version = "0.3.1" +name = "globset" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] [[package]] name = "half" @@ -2719,7 +2726,7 @@ dependencies = [ "anyhow", "arc-swap", "crossterm", - "glob", + "globset", "indexmap", "ratatui", "serde", diff --git a/cspell.json b/cspell.json index 2ee0789c..1d39b6a6 100644 --- a/cspell.json +++ b/cspell.json @@ -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"} \ No newline at end of file +{"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"]} \ No newline at end of file diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 4e817496..f76f69aa 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -15,7 +15,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.4" } anyhow = "1.0.82" arc-swap = "1.7.1" crossterm = "0.27.0" -glob = "0.3.1" +globset = "0.4.14" indexmap = "2.2.6" ratatui = "0.26.1" serde = { version = "1.0.197", features = [ "derive" ] } diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index 5e09d4e9..af7f542b 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -173,22 +173,14 @@ rules = [ { mime = "image/*", fg = "cyan" }, # Media - { mime = "video/*", fg = "yellow" }, - { mime = "audio/*", fg = "yellow" }, + { mime = "{audio,video}/*", fg = "yellow" }, # Archives - { mime = "application/*zip", fg = "magenta" }, - { mime = "application/x-tar", 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" }, + { mime = "application/*zip", fg = "magenta" }, + { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", fg = "magenta" }, # Documents - { mime = "application/doc", fg = "green" }, - { mime = "application/pdf", fg = "green" }, - { mime = "application/rtf", fg = "green" }, - { mime = "application/vnd.*", fg = "green" }, + { mime = "application/{pdf,doc,rtf,vnd.*}", fg = "green" }, # Fallback # { name = "*", fg = "white" }, diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index 81ce4688..f3dac472 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -55,22 +55,17 @@ play = [ rules = [ { name = "*/", use = [ "edit", "open", "reveal" ] }, - { mime = "text/*", use = [ "edit", "reveal" ] }, - { mime = "image/*", use = [ "open", "reveal" ] }, - { mime = "video/*", use = [ "play", "reveal" ] }, - { mime = "audio/*", use = [ "play", "reveal" ] }, - { mime = "inode/x-empty", use = [ "edit", "reveal" ] }, + { mime = "text/*", use = [ "edit", "reveal" ] }, + { mime = "image/*", use = [ "open", "reveal" ] }, + { mime = "{audio,video}/*", use = [ "play", "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 = "*/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" ] }, ] @@ -96,10 +91,8 @@ preloaders = [ previewers = [ { name = "*/", run = "folder", sync = true }, # Code - { mime = "text/*", run = "code" }, - { mime = "*/xml", run = "code" }, - { mime = "*/javascript", run = "code" }, - { mime = "*/x-wine-extension-ini", run = "code" }, + { mime = "text/*", run = "code" }, + { mime = "*/{xml,javascript,x-wine-extension-ini}", run = "code" }, # JSON { mime = "application/json", run = "json" }, # Image @@ -110,12 +103,8 @@ previewers = [ # PDF { mime = "application/pdf", run = "pdf" }, # Archive - { mime = "application/*zip", run = "archive" }, - { mime = "application/x-tar", 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" }, + { mime = "application/*zip", run = "archive" }, + { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", run = "archive" }, # Fallback { name = "*", run = "file" }, ] diff --git a/yazi-config/src/open/open.rs b/yazi-config/src/open/open.rs index 0ccd8983..610bdc62 100644 --- a/yazi-config/src/open/open.rs +++ b/yazi-config/src/open/open.rs @@ -25,8 +25,8 @@ impl Open { { let is_folder = mime.as_ref() == MIME_DIR; self.rules.iter().find_map(|rule| { - if rule.mime.as_ref().is_some_and(|m| m.matches(&mime)) - || rule.name.as_ref().is_some_and(|n| n.match_path(&path, is_folder)) + if rule.mime.as_ref().is_some_and(|p| p.match_mime(&mime)) + || rule.name.as_ref().is_some_and(|p| p.match_path(&path, is_folder)) { let openers = rule .use_ diff --git a/yazi-config/src/pattern.rs b/yazi-config/src/pattern.rs index 922f8394..74096b1d 100644 --- a/yazi-config/src/pattern.rs +++ b/yazi-config/src/pattern.rs @@ -1,46 +1,30 @@ use std::path::Path; -use glob::MatchOptions; +use globset::GlobBuilder; use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(try_from = "String")] pub struct Pattern { - inner: glob::Pattern, - sensitive: bool, + inner: globset::GlobMatcher, + is_star: bool, is_folder: bool, - full_path: bool, } impl Pattern { #[inline] - pub fn matches(&self, str: impl AsRef) -> bool { - self.inner.matches_with(str.as_ref(), MatchOptions { - case_sensitive: self.sensitive, - require_literal_separator: self.full_path, - require_literal_leading_dot: false, - }) - } + pub fn match_mime(&self, str: impl AsRef) -> bool { self.inner.is_match(str.as_ref()) } #[inline] pub fn match_path(&self, path: impl AsRef, is_folder: bool) -> bool { - if is_folder != self.is_folder { - 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()) - }) + is_folder == self.is_folder && (self.is_star || self.inner.is_match(path)) } #[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] - 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 { @@ -49,12 +33,16 @@ impl TryFrom<&str> for Pattern { fn try_from(s: &str) -> Result { let a = s.trim_start_matches("\\s"); let b = a.trim_end_matches('/'); - Ok(Self { - inner: glob::Pattern::new(b)?, - sensitive: a.len() < s.len(), - is_folder: b.len() < a.len(), - full_path: b.contains('/'), - }) + + let inner = GlobBuilder::new(b) + .case_insensitive(a.len() == s.len()) + .literal_separator(b.contains('/')) + .backslash_escape(false) + .empty_alternates(false) + .build()? + .compile_matcher(); + + Ok(Self { inner, is_star: b == "*", is_folder: b.len() < a.len() }) } } diff --git a/yazi-config/src/plugin/plugin.rs b/yazi-config/src/plugin/plugin.rs index 242df770..0d226037 100644 --- a/yazi-config/src/plugin/plugin.rs +++ b/yazi-config/src/plugin/plugin.rs @@ -73,8 +73,8 @@ impl Plugin { .iter() .filter(|&rule| { 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.name.as_ref().is_some_and(|n| n.match_path(path, is_folder))) + && (rule.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m)) + || rule.name.as_ref().is_some_and(|p| p.match_path(path, is_folder))) }) .collect() } @@ -82,8 +82,8 @@ impl Plugin { pub fn previewer(&self, path: &Path, mime: &str) -> Option<&PluginRule> { let is_folder = mime == MIME_DIR; self.previewers.iter().find(|&rule| { - rule.mime.as_ref().is_some_and(|m| m.matches(mime)) - || rule.name.as_ref().is_some_and(|n| n.match_path(path, is_folder)) + rule.mime.as_ref().is_some_and(|p| p.match_mime(mime)) + || rule.name.as_ref().is_some_and(|p| p.match_path(path, is_folder)) }) } } diff --git a/yazi-config/src/theme/filetype.rs b/yazi-config/src/theme/filetype.rs index e61069f5..cac982d2 100644 --- a/yazi-config/src/theme/filetype.rs +++ b/yazi-config/src/theme/filetype.rs @@ -17,8 +17,8 @@ impl Filetype { 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, |(m, s)| m.matches(s)) + self.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m)) + || self.name.as_ref().is_some_and(|n| n.match_path(&file.url, file.is_dir())) } }