precompile regex, fixes #84

group imports
This commit is contained in:
Alexander Mohr 2025-07-03 18:00:52 +00:00
parent 172d9dc589
commit f10e9d1794
11 changed files with 65 additions and 52 deletions

View file

@ -2,17 +2,15 @@ use std::{
env, env,
fmt::{Display, Formatter}, fmt::{Display, Formatter},
str::FromStr, str::FromStr,
sync::{Arc, Mutex, RwLock}, sync::{Arc, LazyLock, Mutex, RwLock},
thread::sleep, thread::sleep,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use clap::Parser; use clap::Parser;
use hyprland::data::Client;
use hyprland::dispatch::WindowIdentifier;
use hyprland::{ use hyprland::{
data::{Workspace, Workspaces}, data::{Client, Workspace, Workspaces},
dispatch::{Dispatch, DispatchType, WorkspaceIdentifierWithSpecial}, dispatch::{Dispatch, DispatchType, WindowIdentifier, WorkspaceIdentifierWithSpecial},
prelude::HyprData, prelude::HyprData,
shared::HyprDataActive, shared::HyprDataActive,
}; };
@ -478,12 +476,14 @@ fn main() -> Result<(), String> {
cfg.worf.set_prompt(cfg.hypr_space_mode().to_string()); cfg.worf.set_prompt(cfg.hypr_space_mode().to_string());
} }
let pattern = Mode::iter() static PATTERN_RE: LazyLock<Regex> = LazyLock::new(|| {
.map(|m| regex::escape(&m.to_string().to_lowercase())) let pattern = Mode::iter()
.collect::<Vec<_>>() .map(|m| regex::escape(&m.to_string().to_lowercase()))
.join("|"); .collect::<Vec<_>>()
.join("|");
let pattern = Regex::new(&format!("(?i){pattern}")).map_err(|e| e.to_string())?; Regex::new(&format!("(?i){pattern}")).unwrap()
});
let pattern = PATTERN_RE.clone();
let provider = Arc::new(Mutex::new(HyprspaceProvider::new( let provider = Arc::new(Mutex::new(HyprspaceProvider::new(
&cfg, &cfg,

View file

@ -6,9 +6,11 @@ use std::{
os::unix::{fs::PermissionsExt, prelude::CommandExt}, os::unix::{fs::PermissionsExt, prelude::CommandExt},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::{Command, Stdio}, process::{Command, Stdio},
sync::LazyLock,
time::Instant, time::Instant,
}; };
// re-export freedesktop_file_parser for easier access
pub use freedesktop_file_parser::{DesktopFile, EntryType}; pub use freedesktop_file_parser::{DesktopFile, EntryType};
use notify_rust::Notification; use notify_rust::Notification;
use rayon::prelude::*; use rayon::prelude::*;
@ -26,8 +28,9 @@ use crate::{
/// When it cannot parse the internal regex /// When it cannot parse the internal regex
#[must_use] #[must_use]
pub fn known_image_extension_regex_pattern() -> Regex { pub fn known_image_extension_regex_pattern() -> Regex {
Regex::new(r"(?i).*\.(png|jpg|gif|svg|jpeg)$") static RE: LazyLock<Regex> =
.expect("Internal image regex is not valid anymore.") LazyLock::new(|| Regex::new(r"(?i).*\.(png|jpg|gif|svg|jpeg)$").unwrap());
RE.clone()
} }
/// Helper function to retrieve a file with given regex. /// Helper function to retrieve a file with given regex.
@ -59,6 +62,8 @@ fn find_files(folder: &Path, file_name: &Regex) -> Option<Vec<PathBuf>> {
/// When it cannot parse the internal regex /// When it cannot parse the internal regex
#[must_use] #[must_use]
pub fn find_desktop_files() -> Vec<DesktopFile> { pub fn find_desktop_files() -> Vec<DesktopFile> {
static DESKTOP_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(?i).*\.desktop$").unwrap());
let mut paths = vec![ let mut paths = vec![
PathBuf::from("/usr/share/applications"), PathBuf::from("/usr/share/applications"),
PathBuf::from("/usr/local/share/applications"), PathBuf::from("/usr/local/share/applications"),
@ -81,12 +86,10 @@ pub fn find_desktop_files() -> Vec<DesktopFile> {
} }
} }
let regex = &Regex::new("(?i).*\\.desktop$").expect("invalid internal regex");
let p: Vec<_> = paths let p: Vec<_> = paths
.into_par_iter() .into_par_iter()
.filter(|desktop_dir| desktop_dir.exists()) .filter(|desktop_dir| desktop_dir.exists())
.filter_map(|desktop_dir| find_files(&desktop_dir, regex)) .filter_map(|desktop_dir| find_files(&desktop_dir, &DESKTOP_RE))
.flat_map(|desktop_files| { .flat_map(|desktop_files| {
desktop_files.into_par_iter().filter_map(|desktop_file| { desktop_files.into_par_iter().filter_map(|desktop_file| {
fs::read_to_string(desktop_file) fs::read_to_string(desktop_file)
@ -160,7 +163,9 @@ pub fn fork_if_configured(config: &Config) {
/// # Panics /// # Panics
/// When internal regex unwrapping fails. Should not happen as the regex is static /// When internal regex unwrapping fails. Should not happen as the regex is static
pub fn spawn_fork(cmd: &str, working_dir: Option<&String>) -> Result<(), Error> { pub fn spawn_fork(cmd: &str, working_dir: Option<&String>) -> Result<(), Error> {
let re = Regex::new(r#"'([^']*)'|"([^"]*)"|(\S+)"#).expect("invalid regex in spawn_fork"); static RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r#"'([^']*)'|"([^"]*)"|(\S+)"#).unwrap());
let re = &*RE;
let parts: Vec<String> = re let parts: Vec<String> = re
.captures_iter(cmd) .captures_iter(cmd)
.map(|cap| { .map(|cap| {

View file

@ -1,12 +1,14 @@
use regex::Regex; use std::sync::{Arc, LazyLock, Mutex, RwLock};
use std::sync::{Arc, Mutex, RwLock};
use regex::Regex;
use crate::gui::ArcProvider;
use crate::{ use crate::{
Error, Error,
config::Config, config::Config,
desktop::spawn_fork, desktop::spawn_fork,
gui::{self, DefaultItemFactory, ExpandMode, ItemProvider, MenuItem, ProviderData}, gui::{
self, ArcProvider, DefaultItemFactory, ExpandMode, ItemProvider, MenuItem, ProviderData,
},
modes::{ modes::{
drun::{DRunProvider, update_drun_cache_and_run}, drun::{DRunProvider, update_drun_cache_and_run},
file::FileItemProvider, file::FileItemProvider,
@ -74,15 +76,15 @@ impl AutoItemProvider {
fn contains_math_functions_or_starts_with_number(input: &str) -> bool { fn contains_math_functions_or_starts_with_number(input: &str) -> bool {
// Regex for function names (word boundaries to match whole words) // Regex for function names (word boundaries to match whole words)
let math_functions = r"\b(sqrt|abs|exp|ln|sin|cos|tan|asin|acos|atan|atan2|sinh|cosh|tanh|asinh|acosh|atanh|floor|ceil|round|signum|min|max|pi|e|0x|0b|\||&|<<|>>|\^)\b"; static MATH_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"\b(sqrt|abs|exp|ln|sin|cos|tan|asin|acos|atan|atan2|sinh|cosh|tanh|asinh|acosh|atanh|floor|ceil|round|signum|min|max|pi|e|0x|0b|\||&|<<|>>|\^)\b").unwrap()
});
// Regex for strings that start with a number (including decimals) // Regex for strings that start with a number (including decimals)
let starts_with_number = r"^\s*[+-]?(\d+(\.\d*)?|\.\d+)"; static NUMBER_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^\s*[+-]?(\d+(\.\d*)?|\.\d+)").unwrap());
let math_regex = Regex::new(math_functions).unwrap(); MATH_REGEX.is_match(input) || NUMBER_REGEX.is_match(input)
let number_regex = Regex::new(starts_with_number).unwrap();
math_regex.is_match(input) || number_regex.is_match(input)
} }
impl ItemProvider<AutoRunType> for AutoItemProvider { impl ItemProvider<AutoRunType> for AutoItemProvider {
@ -99,7 +101,8 @@ impl ItemProvider<AutoRunType> for AutoItemProvider {
} else if search.starts_with("ssh") { } else if search.starts_with("ssh") {
(AutoRunType::Ssh, self.ssh.get_elements(search_opt)) (AutoRunType::Ssh, self.ssh.get_elements(search_opt))
} else if search.starts_with('?') { } else if search.starts_with('?') {
let re = Regex::new(r"^\?\s*").unwrap(); static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^\?\s*").unwrap());
let re = &*RE;
let query = re.replace(search, ""); let query = re.replace(search, "");
( (
AutoRunType::WebSearch, AutoRunType::WebSearch,
@ -151,7 +154,7 @@ pub fn show(config: &Arc<RwLock<Config>>) -> Result<(), Error> {
Some( Some(
vec!["ssh", "emoji", "^\\$\\w+", "^\\?\\s*"] vec!["ssh", "emoji", "^\\$\\w+", "^\\?\\s*"]
.into_iter() .into_iter()
.map(|s| Regex::new(s).unwrap()) .map(|s| Regex::new(s).unwrap()) // Consider precompiling if s is constant
.collect(), .collect(),
), ),
ExpandMode::Verbatim, ExpandMode::Verbatim,

View file

@ -9,7 +9,6 @@ use freedesktop_file_parser::EntryType;
use rayon::prelude::*; use rayon::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::gui::ArcProvider;
use crate::{ use crate::{
Error, Error,
config::{Config, SortOrder}, config::{Config, SortOrder},
@ -17,7 +16,7 @@ use crate::{
find_desktop_files, get_locale_variants, lookup_name_with_locale, save_cache_file, find_desktop_files, get_locale_variants, lookup_name_with_locale, save_cache_file,
spawn_fork, spawn_fork,
}, },
gui::{self, ExpandMode, ItemProvider, MenuItem, ProviderData}, gui::{self, ArcProvider, ExpandMode, ItemProvider, MenuItem, ProviderData},
modes::load_cache, modes::load_cache,
}; };

View file

@ -1,12 +1,12 @@
use regex::Regex;
use std::sync::Mutex;
use std::{ use std::{
fs, fs,
os::unix::fs::FileTypeExt, os::unix::fs::FileTypeExt,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{Arc, RwLock}, sync::{Arc, LazyLock, Mutex, RwLock},
}; };
use regex::Regex;
use crate::{ use crate::{
Error, Error,
config::{Config, SortOrder, expand_path}, config::{Config, SortOrder, expand_path},
@ -201,6 +201,8 @@ impl<T: Clone> ItemProvider<T> for FileItemProvider<T> {
/// # Panics /// # Panics
/// In case an internal regex does not parse anymore, this should never happen /// In case an internal regex does not parse anymore, this should never happen
pub fn show(config: &Arc<RwLock<Config>>) -> Result<(), Error> { pub fn show(config: &Arc<RwLock<Config>>) -> Result<(), Error> {
static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^\$\w+").unwrap());
let provider = Arc::new(Mutex::new(FileItemProvider::new( let provider = Arc::new(Mutex::new(FileItemProvider::new(
0, 0,
config.read().unwrap().sort_order(), config.read().unwrap().sort_order(),
@ -211,7 +213,7 @@ pub fn show(config: &Arc<RwLock<Config>>) -> Result<(), Error> {
config, config,
provider, provider,
None, None,
Some(vec![Regex::new("^\\$\\w+").unwrap()]), Some(vec![RE.clone()]),
ExpandMode::Verbatim, ExpandMode::Verbatim,
None, None,
)?; )?;

View file

@ -1,6 +1,6 @@
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
sync::{Arc, Mutex, RwLock}, sync::{Arc, LazyLock, Mutex, RwLock},
}; };
use regex::Regex; use regex::Regex;
@ -79,13 +79,12 @@ enum Value {
/// Normalize base literals like 0x and 0b into decimal format /// Normalize base literals like 0x and 0b into decimal format
fn normalize_bases(expr: &str) -> String { fn normalize_bases(expr: &str) -> String {
let hex_re = Regex::new(r"0x[0-9a-fA-F]+").unwrap(); static HEX_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"0x[0-9a-fA-F]+").unwrap());
let expr = hex_re.replace_all(expr, |caps: &regex::Captures| { static BIN_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"0b[01]+").unwrap());
let expr = HEX_RE.replace_all(expr, |caps: &regex::Captures| {
i64::from_str_radix(&caps[0][2..], 16).unwrap().to_string() i64::from_str_radix(&caps[0][2..], 16).unwrap().to_string()
}); });
BIN_RE
let bin_re = Regex::new(r"0b[01]+").unwrap();
bin_re
.replace_all(&expr, |caps: &regex::Captures| { .replace_all(&expr, |caps: &regex::Captures| {
i64::from_str_radix(&caps[0][2..], 2).unwrap().to_string() i64::from_str_radix(&caps[0][2..], 2).unwrap().to_string()
}) })

View file

@ -7,12 +7,11 @@ use std::{
sync::{Arc, Mutex, RwLock}, sync::{Arc, Mutex, RwLock},
}; };
use crate::gui::ArcProvider;
use crate::{ use crate::{
Error, Error,
config::{Config, SortOrder}, config::{Config, SortOrder},
desktop::{is_executable, save_cache_file}, desktop::{is_executable, save_cache_file},
gui::{self, ExpandMode, ItemProvider, MenuItem, ProviderData}, gui::{self, ArcProvider, ExpandMode, ItemProvider, MenuItem, ProviderData},
modes::load_cache, modes::load_cache,
}; };

View file

@ -1,4 +1,5 @@
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use urlencoding::encode; use urlencoding::encode;
use crate::{ use crate::{

View file

@ -1,13 +1,15 @@
use regex::Regex; use std::{
use std::fs; fs,
use std::sync::{Arc, Mutex, RwLock}; sync::{Arc, LazyLock, Mutex, RwLock},
};
use regex::Regex;
use crate::gui::{ExpandMode, ProviderData};
use crate::{ use crate::{
Error, Error,
config::{Config, SortOrder}, config::{Config, SortOrder},
desktop::spawn_fork, desktop::spawn_fork,
gui::{self, ItemProvider, MenuItem}, gui::{self, ExpandMode, ItemProvider, MenuItem, ProviderData},
}; };
#[derive(Clone)] #[derive(Clone)]
@ -17,7 +19,8 @@ pub(crate) struct SshProvider<T: Clone> {
impl<T: Clone> SshProvider<T> { impl<T: Clone> SshProvider<T> {
pub(crate) fn new(menu_item_data: T, order: &SortOrder) -> Self { pub(crate) fn new(menu_item_data: T, order: &SortOrder) -> Self {
let re = Regex::new(r"(?m)^\s*Host\s+(.+)$").unwrap(); static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(?m)^\s*Host\s+(.+)$").unwrap());
let re = &*RE;
let mut items: Vec<_> = dirs::home_dir() let mut items: Vec<_> = dirs::home_dir()
.map(|home| home.join(".ssh").join("config")) .map(|home| home.join(".ssh").join("config"))
.filter(|path| path.exists()) .filter(|path| path.exists())

View file

@ -1,10 +1,11 @@
use clap::Parser;
use std::fmt::Display;
use std::str::FromStr;
use std::{ use std::{
env, env,
fmt::Display,
str::FromStr,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
use clap::Parser;
use worf::{Error, config, desktop::fork_if_configured, modes}; use worf::{Error, config, desktop::fork_if_configured, modes};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -1,4 +1,5 @@
use std::fmt; use std::fmt;
use thiserror::Error; use thiserror::Error;
/// Configuration and command line parsing /// Configuration and command line parsing