Skip to content

Commit 2bd5f00

Browse files
committed
Use fancy-regex instead of onig
1 parent 5e19db8 commit 2bd5f00

20 files changed

Lines changed: 1089 additions & 318 deletions

‎Cargo.toml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "giallo"
3-
version = "0.5.2"
3+
version = "0.6.0"
44
edition = "2024"
55
license = "EUPL-1.2"
66
description = "A code highlighter giving the same output as VSCode"
@@ -19,16 +19,16 @@ include = [
1919

2020

2121
[dependencies]
22-
onig = { package = "onig-regset", version = "6", default-features = false }
2322
papaya = "0.2"
2423
serde = { version = "1", features = ["derive"] }
2524
serde_json = "1"
2625

2726
# Optional dependencies for serialization
2827
bitcode = { version = "0.6.9", optional = true, features = ["serde"] }
29-
zstd = { version = "0.13.3", optional = true }
30-
28+
zstd = { version = "0.14", optional = true }
29+
fancy-regex = "0.19"
3130
log = { version = "0.4", optional = true }
31+
regex-syntax = "0.8.11"
3232

3333
[features]
3434
tools = ["dump"]

‎benches/highlight_benchmark.rs‎

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn highlight_jquery_benchmark(c: &mut Criterion) {
1616

1717
c.bench_function("highlight jquery.js", |b| {
1818
b.iter(|| {
19-
registry.clear_pattern_cache();
19+
registry.clear_caches();
2020
let result = registry.highlight(&jquery_content, &options).unwrap();
2121
std::hint::black_box(result);
2222
})
@@ -34,7 +34,7 @@ fn highlight_simple_benchmark(c: &mut Criterion) {
3434

3535
c.bench_function("highlight simple.ts", |b| {
3636
b.iter(|| {
37-
registry.clear_pattern_cache();
37+
registry.clear_caches();
3838
let result = registry.highlight(&ts_content, &options).unwrap();
3939
std::hint::black_box(result);
4040
})
@@ -53,7 +53,7 @@ fn highlight_multiple_simple_benchmark(c: &mut Criterion) {
5353
c.bench_function("highlight multiple simple.ts", |b| {
5454
b.iter(|| {
5555
// should not be 5x slower than "highlight simple.ts"
56-
registry.clear_pattern_cache();
56+
registry.clear_caches();
5757
let result = registry.highlight(&ts_content, &options).unwrap();
5858
std::hint::black_box(result);
5959
let result = registry.highlight(&ts_content, &options).unwrap();
@@ -81,7 +81,7 @@ fn highlight_sample_benchmark(c: &mut Criterion, grammar: &str) {
8181

8282
c.bench_function(&format!("highlight {grammar}.sample"), |b| {
8383
b.iter(|| {
84-
registry.clear_pattern_cache();
84+
registry.clear_caches();
8585
let result = registry.highlight(&content, &options).unwrap();
8686
std::hint::black_box(result);
8787
})
@@ -100,13 +100,75 @@ fn highlight_javascript_sample_benchmark(c: &mut Criterion) {
100100
highlight_sample_benchmark(c, "javascript");
101101
}
102102

103+
fn highlight_go_sample_benchmark(c: &mut Criterion) {
104+
highlight_sample_benchmark(c, "go");
105+
}
106+
107+
fn highlight_ruby_sample_benchmark(c: &mut Criterion) {
108+
highlight_sample_benchmark(c, "ruby");
109+
}
110+
111+
fn highlight_c_sample_benchmark(c: &mut Criterion) {
112+
highlight_sample_benchmark(c, "c");
113+
}
114+
115+
fn highlight_html_sample_benchmark(c: &mut Criterion) {
116+
highlight_sample_benchmark(c, "html");
117+
}
118+
119+
fn highlight_css_sample_benchmark(c: &mut Criterion) {
120+
highlight_sample_benchmark(c, "css");
121+
}
122+
123+
fn highlight_python_sample_benchmark(c: &mut Criterion) {
124+
highlight_sample_benchmark(c, "python");
125+
}
126+
127+
fn highlight_json_sample_benchmark(c: &mut Criterion) {
128+
highlight_sample_benchmark(c, "json");
129+
}
130+
131+
fn highlight_tsx_sample_benchmark(c: &mut Criterion) {
132+
highlight_sample_benchmark(c, "tsx");
133+
}
134+
135+
fn highlight_warm_benchmark(c: &mut Criterion) {
136+
let registry = Registry::load_from_file("builtin.zst").unwrap();
137+
let mut group = c.benchmark_group("highlight warm");
138+
for (grammar, path) in [
139+
("typescript", "src/fixtures/samples/simple.ts"),
140+
("javascript", "src/fixtures/samples/jquery.js"),
141+
("rust", "grammars-themes/samples/rust.sample"),
142+
("markdown", "grammars-themes/samples/markdown.sample"),
143+
("python", "grammars-themes/samples/python.sample"),
144+
("tsx", "grammars-themes/samples/tsx.sample"),
145+
] {
146+
let content = fs::read_to_string(path).unwrap();
147+
let options = HighlightOptions::new(grammar, ThemeVariant::Single("vitesse-black"));
148+
registry.highlight(&content, &options).unwrap();
149+
group.bench_function(grammar, |b| {
150+
b.iter(|| std::hint::black_box(registry.highlight(&content, &options).unwrap()))
151+
});
152+
}
153+
group.finish();
154+
}
155+
103156
criterion_group!(
104157
benches,
105158
highlight_jquery_benchmark,
106159
highlight_simple_benchmark,
107160
highlight_multiple_simple_benchmark,
108161
highlight_rust_sample_benchmark,
109162
highlight_markdown_sample_benchmark,
110-
highlight_javascript_sample_benchmark
163+
highlight_javascript_sample_benchmark,
164+
highlight_go_sample_benchmark,
165+
highlight_ruby_sample_benchmark,
166+
highlight_c_sample_benchmark,
167+
highlight_html_sample_benchmark,
168+
highlight_css_sample_benchmark,
169+
highlight_python_sample_benchmark,
170+
highlight_json_sample_benchmark,
171+
highlight_tsx_sample_benchmark,
172+
highlight_warm_benchmark
111173
);
112174
criterion_main!(benches);

‎examples/html_classes.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use giallo::{HighlightOptions, HtmlRenderer, Registry, RenderOptions, ThemeVariant};
22
use std::fs;
33

4+
#[allow(clippy::single_range_in_vec_init)]
45
fn main() -> Result<(), Box<dyn std::error::Error>> {
56
let mut registry = Registry::load_from_file("builtin.zst")?;
67
registry.link_grammars();

‎grammars-themes‎

Submodule grammars-themes updated 55 files
Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use onig::SearchOptions;
21
use std::fmt;
32

43
/// We use that as a way to convey both the rule and which anchors should be active
@@ -36,15 +35,21 @@ impl AnchorActive {
3635
}
3736
}
3837

39-
pub fn to_search_options(self) -> SearchOptions {
38+
pub fn allow_a(&self) -> bool {
39+
matches!(self, AnchorActive::A | AnchorActive::AG)
40+
}
41+
42+
pub fn allow_g(&self) -> bool {
43+
matches!(self, AnchorActive::G | AnchorActive::AG)
44+
}
45+
46+
/// The same anchor context with \G deactivated.
47+
/// RegexSet handles it on its own (inside fancy-regex) but not the normal Regex
48+
/// we walk through
49+
pub fn without_g(&self) -> Self {
4050
match self {
41-
AnchorActive::AG => SearchOptions::SEARCH_OPTION_NONE,
42-
AnchorActive::A => SearchOptions::SEARCH_OPTION_NOT_BEGIN_POSITION,
43-
AnchorActive::G => SearchOptions::SEARCH_OPTION_NOT_BEGIN_STRING,
44-
AnchorActive::None => {
45-
SearchOptions::SEARCH_OPTION_NOT_BEGIN_STRING
46-
| SearchOptions::SEARCH_OPTION_NOT_BEGIN_POSITION
47-
}
51+
AnchorActive::A | AnchorActive::AG => AnchorActive::A,
52+
AnchorActive::G | AnchorActive::None => AnchorActive::None,
4853
}
4954
}
5055
}

‎src/grammars/caches.rs‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use std::sync::{Arc, OnceLock};
2+
3+
use crate::grammars::engine;
4+
use crate::grammars::prefilter::{ByteSet, get_byte_set_from_pattern};
5+
6+
/// We want to compile only one of each regex/regexset as they are expensive.
7+
/// We key by the patterns so they can be reused in multiple grammars/context.
8+
#[derive(Default, Debug)]
9+
pub(crate) struct RegexCache {
10+
/// We cache the fancy regexes across all grammars
11+
regexes: papaya::HashMap<String, OnceLock<Arc<engine::Regex>>>,
12+
/// And the sets are cached by the patterns
13+
sets: papaya::HashMap<Vec<String>, OnceLock<Arc<engine::RegexSet>>>,
14+
/// And the generated byteset for the prefilter
15+
first_bytes: papaya::HashMap<String, OnceLock<Option<ByteSet>>>,
16+
}
17+
18+
impl RegexCache {
19+
#[doc(hidden)]
20+
pub(crate) fn clear(&self) {
21+
self.sets.pin().clear();
22+
self.regexes.pin().clear();
23+
self.first_bytes.pin().clear();
24+
}
25+
26+
pub fn get_regex(&self, pattern: &str) -> Arc<engine::Regex> {
27+
self.regexes
28+
.pin()
29+
.get_or_insert_with(pattern.to_string(), OnceLock::new)
30+
.get_or_init(|| Arc::new(engine::Regex::new(pattern)))
31+
.clone()
32+
}
33+
34+
pub fn get_set(&self, patterns: &[String]) -> Arc<engine::RegexSet> {
35+
self.sets
36+
.pin()
37+
.get_or_insert_with(patterns.to_vec(), OnceLock::new)
38+
.get_or_init(|| Arc::new(engine::RegexSet::new(patterns)))
39+
.clone()
40+
}
41+
42+
pub(crate) fn get_first_bytes(&self, pattern: &str) -> Option<ByteSet> {
43+
self.first_bytes
44+
.pin()
45+
.get_or_insert_with(pattern.to_string(), OnceLock::new)
46+
.get_or_init(|| get_byte_set_from_pattern(pattern))
47+
.clone()
48+
}
49+
}

0 commit comments

Comments
 (0)