1#![forbid(unsafe_code)]
17#![warn(missing_docs)]
18#![cfg_attr(docsrs, feature(doc_cfg))]
19#![cfg_attr(docsrs, doc(auto_cfg))]
20
21#[cfg(all(
23 doc,
24 feature = "bundled-grammars",
25 feature = "bundled-themes",
26 feature = "html"
27))]
28#[doc = include_str!("../README.md")]
29mod readme_examples {}
30
31#[cfg(all(
32 doc,
33 feature = "bundled-grammars",
34 feature = "bundled-themes",
35 feature = "html",
36 feature = "ansi"
37))]
38#[doc = include_str!("../docs/rendering.md")]
39mod rendering_examples {}
40
41mod catalog;
42#[allow(dead_code, unused_imports)]
43mod engine;
44mod error;
45#[allow(dead_code)]
46mod grammars;
47mod highlighter;
48mod render;
49mod theme;
50mod tokenizer;
51#[allow(dead_code)]
52mod types;
53
54pub use catalog::{AssetLicense, Catalog, CatalogSummary, LanguageInfo};
55pub use error::{
56 BundleError, BundleErrorKind, DiagnosticError, DiagnosticErrorKind, Error, GrammarError,
57 GrammarErrorKind, GrammarResource, JsonError, LimitExceeded, MissingInclude, RegexError,
58 RenderError, RenderErrorKind, Result, ThemeError, ThemeErrorKind,
59};
60pub use highlighter::{HighlightSession, Highlighter, HighlighterOptions};
61pub use highlighter::{
62 HighlightedDocument, HighlightedLine, HighlightedToken, Theme, style_document,
63};
64pub use render::RenderedOutput;
65#[cfg(feature = "ansi")]
66pub use render::{AnsiOptions, render_ansi, render_ansi_to};
67#[cfg(feature = "html")]
68pub use render::{HtmlOptions, html_stylesheet, render_html, render_html_to};
69pub use theme::{FontModifiers, RgbColor, Style};
70#[cfg(feature = "diagnostics")]
71pub use theme::{ResolvedThemeStyle, ThemeMatch, ThemeSelectorScore};
72pub use tokenizer::{
73 CheckpointTable, GrammarId, GrammarLimits, GrammarRegistry, HighlightStatus, PreparedLanguage,
74 PreparedLanguageStats, Scopes, Token, TokenizedDocument, TokenizedLine, Tokenizer,
75 TokenizerState,
76};
77pub(crate) use types::{HighlightScopeTable, ScopeAtomId};
78pub use types::{ScopeStackId, ThemeRule, TokenizerOptions};
79
80pub(crate) use types::{
83 HighlightedLine as EngineHighlightedLine, HighlightedText, LineTextFingerprint, SyntaxClass,
84 SyntaxSegment,
85};
86#[cfg(test)]
87#[path = "../tests/engine_capture_quality.rs"]
88mod engine_capture_quality;
89#[cfg(test)]
90#[path = "../tests/engine_regressions.rs"]
91mod engine_regressions;
92#[cfg(all(test, feature = "bundled-grammars", feature = "bundled-themes"))]
93mod public_api_tests;
94#[cfg(test)]
95#[path = "../tests/textmate_golden.rs"]
96mod textmate_golden;
97#[cfg(test)]
98#[path = "../tests/theme_golden.rs"]
99mod theme_golden;
100
101#[cfg(feature = "diagnostics")]
103pub mod diagnostics {
104 use std::ops::Range;
105
106 pub use crate::engine::counters::{EngineCounters, PatternCompileCount, PatternHotspot};
107 use crate::engine::regex::{
108 AnchorContext, AutomataMatcher, FallbackMatcher, Matcher, RegexMatcher, parse, translate,
109 };
110 use crate::{Error, Result};
111
112 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
114 pub enum RegexEngine {
115 Auto,
117 Dfa,
119 Fallback,
121 }
122
123 #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
125 pub struct RegexAnchorContext {
126 pub allow_start_of_file: bool,
128 pub continuation_position: Option<usize>,
130 }
131
132 #[derive(Debug, Clone, PartialEq, Eq)]
134 pub struct RegexInspection {
135 pub parsed: String,
137 pub translated_pattern: String,
139 pub anchor_strategy: String,
141 pub route: String,
143 }
144
145 #[derive(Debug, Clone, PartialEq, Eq)]
147 pub struct RegexMatchReport {
148 pub engine: &'static str,
150 pub matched: Option<Range<usize>>,
152 pub captures: Vec<Option<Range<usize>>>,
154 pub steps: Option<usize>,
156 }
157
158 pub fn inspect_regex(pattern: &str) -> RegexInspection {
160 let parsed = parse(pattern);
161 let translation = translate(pattern);
162 RegexInspection {
163 parsed: parsed.to_string(),
164 translated_pattern: translation.pattern,
165 anchor_strategy: format!("{:?}", translation.anchor_strategy),
166 route: format!("{:?}", translation.route),
167 }
168 }
169
170 pub fn match_regex(
172 pattern: &str,
173 line: &str,
174 from: usize,
175 anchors: RegexAnchorContext,
176 engine: RegexEngine,
177 fallback_budget: usize,
178 ) -> Result<RegexMatchReport> {
179 let context = AnchorContext {
180 allow_a: anchors.allow_start_of_file,
181 allow_g: anchors.continuation_position.is_some(),
182 g_pos: anchors.continuation_position.unwrap_or(0),
183 };
184 let (engine_name, result, steps) = match engine {
185 RegexEngine::Auto => {
186 let matcher = RegexMatcher::new(pattern);
187 let (result, steps) =
188 matcher.find_report(line, from, context).map_err(|error| {
189 Error::Diagnostic(crate::DiagnosticError::fallback(pattern, error))
190 })?;
191 (matcher.engine_name(), result, steps)
192 }
193 RegexEngine::Dfa => {
194 let matcher = AutomataMatcher::new(pattern).map_err(|error| {
195 Error::Diagnostic(crate::DiagnosticError::build(pattern, error.to_string()))
196 })?;
197 ("dfa", matcher.find(line, from, context), None)
198 }
199 RegexEngine::Fallback => {
200 let matcher = FallbackMatcher::with_budget(pattern, fallback_budget);
201 let report = matcher.try_find(line, from, context).map_err(|error| {
202 Error::Diagnostic(crate::DiagnosticError::fallback(pattern, error))
203 })?;
204 ("fallback", report.result, Some(report.steps))
205 }
206 };
207 Ok(RegexMatchReport {
208 engine: engine_name,
209 matched: result.as_ref().map(|matched| matched.start..matched.end),
210 captures: result.map_or_else(Vec::new, |matched| matched.captures),
211 steps,
212 })
213 }
214}