Skip to content

Commit 8696965

Browse files
committed
Add an enum for mode strategy
1 parent e4fc290 commit 8696965

4 files changed

Lines changed: 54 additions & 11 deletions

File tree

‎src/grammars/matcher.rs‎

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,30 @@
1-
use std::sync::{Arc, OnceLock};
2-
31
use crate::grammars::anchors::AnchorActive;
42
use crate::grammars::caches::RegexCache;
53
use crate::grammars::engine::CaptureSpans;
64
use crate::grammars::prefilter::Prefilter;
75
use crate::grammars::{GlobalRuleRef, engine};
6+
use std::sync::{Arc, OnceLock};
7+
8+
/// How will giallo find the next matching pattern
9+
#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
10+
pub enum MatchStrategy {
11+
/// giallo will check every pattern individually when it can and fallback to a RegexSet when it cannot
12+
///
13+
/// This is fast when giallo is cold and uses less memory.
14+
/// This should be used by CLIs highlighting a few things and exiting or if you are memory constrained.
15+
#[default]
16+
Walk,
17+
/// giallo will only use a RegexSet
18+
///
19+
/// Compared to [MatchStrategy::Walk]:
20+
/// 1. Initial RegexSet compilation is _very_ slow: first highlight will likely be around 10x slower
21+
/// 2. It will use *much* more memory (2-10x more) growing with the number of languages that have highlighted
22+
/// 3. Warm highlights will be between 15-60% faster, dependent on language
23+
///
24+
/// This should only be used if you're using giallo in a long-lived process and you're okay with
25+
/// potentially 10GB+ of ram used just for giallo with a few dozen languages.
26+
Set,
27+
}
828

929
#[derive(Debug, PartialEq, Eq, Clone)]
1030
pub struct RuleMatch {
@@ -132,20 +152,27 @@ pub struct RuleMatcher {
132152
}
133153

134154
impl RuleMatcher {
135-
pub fn new(items: Vec<(GlobalRuleRef, String)>, cache: Arc<RegexCache>) -> Self {
155+
pub fn new(
156+
items: Vec<(GlobalRuleRef, String)>,
157+
cache: Arc<RegexCache>,
158+
strategy: MatchStrategy,
159+
) -> Self {
136160
if items.is_empty() {
137161
return RuleMatcher::default();
138162
}
139163

140164
let (rule_refs, patterns): (Vec<_>, Vec<_>) = items.into_iter().unzip();
141-
let sets: Vec<_> = patterns.iter().map(|p| cache.get_first_bytes(p)).collect();
142165

143-
let (prefilter, remaining) =
166+
let (prefilter, remaining) = if strategy == MatchStrategy::Walk {
167+
let sets: Vec<_> = patterns.iter().map(|p| cache.get_first_bytes(p)).collect();
144168
if let Some((prefilter, remaining)) = Prefilter::from_byte_sets(&sets) {
145169
(prefilter, remaining)
146170
} else {
147171
(Prefilter::default(), (0..patterns.len()).collect())
148-
};
172+
}
173+
} else {
174+
(Prefilter::default(), (0..patterns.len()).collect())
175+
};
149176

150177
let has_walk = remaining.len() < patterns.len();
151178
let remainder = (!remaining.is_empty()).then(|| {

‎src/grammars/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ mod raw;
1010

1111
pub use compiled::*;
1212
pub use injections::InjectionPrecedence;
13-
pub use matcher::{RuleMatch, RuleMatcher};
13+
pub use matcher::{MatchStrategy, RuleMatch, RuleMatcher};
1414
pub use pattern::resolve_backreferences;
1515
pub use raw::RawGrammar;

‎src/lib.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ mod renderers;
3434
mod tokenizer;
3535

3636
pub use error::Error;
37+
pub use grammars::MatchStrategy;
3738
pub use highlight::HighlightedText;
3839
pub use markdown_fence::{ParsedFence, parse_markdown_fence};
3940
pub use registry::{HighlightOptions, HighlightedCode, PLAIN_GRAMMAR_NAME, Registry};

‎src/registry.rs‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet};
22
use std::path::Path;
33
use std::sync::Arc;
44

5+
use crate::MatchStrategy;
56
use crate::error::{Error, GialloResult};
67
use crate::grammars::caches::RegexCache;
78
use crate::grammars::{
@@ -10,14 +11,13 @@ use crate::grammars::{
1011
resolve_external_references,
1112
};
1213
use crate::highlight::{HighlightedText, Highlighter, MergingOptions};
13-
use serde::{Deserialize, Serialize};
14-
1514
#[cfg(feature = "dump")]
1615
use crate::scope::ScopeRepository;
1716
use crate::scope::{Scope, ScopeInterner};
1817
use crate::themes::css::{DARK_SUFFIX, LIGHT_SUFFIX};
1918
use crate::themes::{CompiledTheme, RawTheme, ThemeVariant};
2019
use crate::tokenizer::{Token, Tokenizer};
20+
use serde::{Deserialize, Serialize};
2121

2222
#[cfg(feature = "dump")]
2323
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
@@ -149,6 +149,8 @@ pub struct Registry {
149149
// highlight. To do that we had to check the end regex in the tokenizer separately from the
150150
// regset.
151151
matcher_cache: papaya::HashMap<(GrammarId, GlobalRuleRef), Arc<RuleMatcher>>,
152+
// whether to walk or only use regset
153+
match_strategy: MatchStrategy,
152154
}
153155

154156
impl Clone for Registry {
@@ -162,6 +164,7 @@ impl Clone for Registry {
162164
linked: self.linked,
163165
regex_cache: self.regex_cache.clone(),
164166
matcher_cache: papaya::HashMap::new(),
167+
match_strategy: self.match_strategy,
165168
}
166169
}
167170
}
@@ -198,6 +201,7 @@ impl Registry {
198201
linked: false,
199202
regex_cache: Arc::new(RegexCache::default()),
200203
matcher_cache: papaya::HashMap::new(),
204+
match_strategy: MatchStrategy::default(),
201205
};
202206
this.link_grammars();
203207

@@ -321,6 +325,12 @@ impl Registry {
321325
Ok((tokens, tokenizer.into_scope_interner()))
322326
}
323327

328+
/// See [MatchStrategy] documentation to see which one to use for your usecase
329+
pub fn set_match_strategy(&mut self, match_strategy: MatchStrategy) {
330+
self.match_strategy = match_strategy;
331+
self.clear_matcher_cache();
332+
}
333+
324334
/// Checks whether the given lang is available in the registry with its grammar name
325335
/// or aliases
326336
pub fn contains_grammar(&self, name: &str) -> bool {
@@ -554,10 +564,11 @@ impl Registry {
554564
pub fn clear_caches(&self) {
555565
self.matcher_cache.pin().clear();
556566
self.regex_cache.clear();
567+
self.matcher_cache.pin().clear();
557568
}
558569

559570
#[doc(hidden)]
560-
pub fn clear_pattern_cache(&self) {
571+
pub fn clear_matcher_cache(&self) {
561572
self.matcher_cache.pin().clear();
562573
}
563574

@@ -578,7 +589,11 @@ impl Registry {
578589
.into_iter()
579590
.map(|(rule, pat)| (rule, pat.to_owned()))
580591
.collect();
581-
let rule_matcher = Arc::new(RuleMatcher::new(patterns, self.regex_cache.clone()));
592+
let rule_matcher = Arc::new(RuleMatcher::new(
593+
patterns,
594+
self.regex_cache.clone(),
595+
self.match_strategy,
596+
));
582597

583598
// Use get_or_insert for concurrent-safe lazy init
584599
let inserted = self

0 commit comments

Comments
 (0)