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,
fmt::{Display, Formatter},
str::FromStr,
sync::{Arc, Mutex, RwLock},
sync::{Arc, LazyLock, Mutex, RwLock},
thread::sleep,
time::{Duration, Instant},
};
use clap::Parser;
use hyprland::data::Client;
use hyprland::dispatch::WindowIdentifier;
use hyprland::{
data::{Workspace, Workspaces},
dispatch::{Dispatch, DispatchType, WorkspaceIdentifierWithSpecial},
data::{Client, Workspace, Workspaces},
dispatch::{Dispatch, DispatchType, WindowIdentifier, WorkspaceIdentifierWithSpecial},
prelude::HyprData,
shared::HyprDataActive,
};
@ -478,12 +476,14 @@ fn main() -> Result<(), String> {
cfg.worf.set_prompt(cfg.hypr_space_mode().to_string());
}
static PATTERN_RE: LazyLock<Regex> = LazyLock::new(|| {
let pattern = Mode::iter()
.map(|m| regex::escape(&m.to_string().to_lowercase()))
.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(
&cfg,

View file

@ -6,9 +6,11 @@ use std::{
os::unix::{fs::PermissionsExt, prelude::CommandExt},
path::{Path, PathBuf},
process::{Command, Stdio},
sync::LazyLock,
time::Instant,
};
// re-export freedesktop_file_parser for easier access
pub use freedesktop_file_parser::{DesktopFile, EntryType};
use notify_rust::Notification;
use rayon::prelude::*;
@ -26,8 +28,9 @@ use crate::{
/// When it cannot parse the internal regex
#[must_use]
pub fn known_image_extension_regex_pattern() -> Regex {
Regex::new(r"(?i).*\.(png|jpg|gif|svg|jpeg)$")
.expect("Internal image regex is not valid anymore.")
static RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"(?i).*\.(png|jpg|gif|svg|jpeg)$").unwrap());
RE.clone()
}
/// 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
#[must_use]
pub fn find_desktop_files() -> Vec<DesktopFile> {
static DESKTOP_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(?i).*\.desktop$").unwrap());
let mut paths = vec![
PathBuf::from("/usr/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
.into_par_iter()
.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| {
desktop_files.into_par_iter().filter_map(|desktop_file| {
fs::read_to_string(desktop_file)
@ -160,7 +163,9 @@ pub fn fork_if_configured(config: &Config) {
/// # Panics
/// 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> {
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
.captures_iter(cmd)
.map(|cap| {

View file

@ -1,12 +1,14 @@
use regex::Regex;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::{Arc, LazyLock, Mutex, RwLock};
use regex::Regex;
use crate::gui::ArcProvider;
use crate::{
Error,
config::Config,
desktop::spawn_fork,
gui::{self, DefaultItemFactory, ExpandMode, ItemProvider, MenuItem, ProviderData},
gui::{
self, ArcProvider, DefaultItemFactory, ExpandMode, ItemProvider, MenuItem, ProviderData,
},
modes::{
drun::{DRunProvider, update_drun_cache_and_run},
file::FileItemProvider,
@ -74,15 +76,15 @@ impl AutoItemProvider {
fn contains_math_functions_or_starts_with_number(input: &str) -> bool {
// 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)
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();
let number_regex = Regex::new(starts_with_number).unwrap();
math_regex.is_match(input) || number_regex.is_match(input)
MATH_REGEX.is_match(input) || NUMBER_REGEX.is_match(input)
}
impl ItemProvider<AutoRunType> for AutoItemProvider {
@ -99,7 +101,8 @@ impl ItemProvider<AutoRunType> for AutoItemProvider {
} else if search.starts_with("ssh") {
(AutoRunType::Ssh, self.ssh.get_elements(search_opt))
} 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, "");
(
AutoRunType::WebSearch,
@ -151,7 +154,7 @@ pub fn show(config: &Arc<RwLock<Config>>) -> Result<(), Error> {
Some(
vec!["ssh", "emoji", "^\\$\\w+", "^\\?\\s*"]
.into_iter()
.map(|s| Regex::new(s).unwrap())
.map(|s| Regex::new(s).unwrap()) // Consider precompiling if s is constant
.collect(),
),
ExpandMode::Verbatim,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,13 +1,15 @@
use regex::Regex;
use std::fs;
use std::sync::{Arc, Mutex, RwLock};
use std::{
fs,
sync::{Arc, LazyLock, Mutex, RwLock},
};
use regex::Regex;
use crate::gui::{ExpandMode, ProviderData};
use crate::{
Error,
config::{Config, SortOrder},
desktop::spawn_fork,
gui::{self, ItemProvider, MenuItem},
gui::{self, ExpandMode, ItemProvider, MenuItem, ProviderData},
};
#[derive(Clone)]
@ -17,7 +19,8 @@ pub(crate) struct SshProvider<T: Clone> {
impl<T: Clone> SshProvider<T> {
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()
.map(|home| home.join(".ssh").join("config"))
.filter(|path| path.exists())

View file

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

View file

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