Skip to main content

comfit_core/
engine.rs

1//! Register languages and highlight source code, including embedded languages.
2
3use crate::{
4    Error, HighlightConfig, Highlighted, backend::Backend, document::Document, highlight,
5    language::Registry,
6};
7
8pub use crate::language::Queries;
9
10/// A highlighter with a parser backend and a language registry.
11///
12/// Queries are compiled during registration or on first use, then reused.
13/// Results borrow the source and capture names, not the temporary syntax trees.
14/// One-shot highlighting releases each layer's tree before parsing the next layer.
15pub struct Highlighter<B: Backend> {
16    backend: B,
17    registry: Registry<B>,
18}
19
20impl<B: Backend> Highlighter<B> {
21    /// Create an empty registry using the supplied backend.
22    pub fn new(backend: B) -> Self {
23        Self {
24            backend,
25            registry: Registry::new(),
26        }
27    }
28
29    /// Register a language and compile its queries.
30    ///
31    /// IDs and aliases share one namespace. Registration leaves the registry unchanged
32    /// if validation or compilation fails. Query sources are borrowed only during loading.
33    ///
34    /// # Errors
35    ///
36    /// Returns [`Error::DuplicateLanguage`] for empty or conflicting names, or a
37    /// backend/query error if compilation fails.
38    pub fn load(
39        &mut self,
40        id: &str,
41        aliases: &[&str],
42        language: B::Language,
43        queries: Queries<'_>,
44    ) -> Result<(), Error> {
45        self.registry
46            .load(&mut self.backend, id, aliases, language, queries)
47    }
48
49    /// Register static queries to compile when the language is first used.
50    /// Documents share uncompiled query sources and compiled queries with this registry.
51    /// Query compilation errors are reported when the language is first used.
52    ///
53    /// # Errors
54    ///
55    /// Returns [`Error::DuplicateLanguage`] for empty or conflicting names.
56    pub fn load_lazy(
57        &mut self,
58        id: &str,
59        aliases: &[&str],
60        language: B::Language,
61        queries: Queries<'static>,
62    ) -> Result<(), Error> {
63        self.registry.load_lazy(id, aliases, language, queries)
64    }
65
66    /// Return whether a language ID or alias is registered.
67    pub fn contains(&self, name: &str) -> bool {
68        self.registry.get(name).is_some()
69    }
70
71    /// Highlight source text using a registered language ID or alias.
72    ///
73    /// Injections into unregistered languages are skipped. Each syntax layer is parsed
74    /// once. Results borrow the source and capture names from this registry.
75    ///
76    /// # Errors
77    ///
78    /// Returns an error for an unloaded root language, a parser or query failure, an
79    /// invalid UTF-8 range, or injection nesting beyond 32 layers.
80    pub fn highlight<'a>(
81        &'a mut self,
82        source: &'a str,
83        name: &str,
84        config: &HighlightConfig,
85    ) -> Result<Highlighted<'a>, Error> {
86        highlight::source(&mut self.backend, &self.registry, source, name, config)
87    }
88
89    /// Create an independent document using a fresh backend and shared compiled queries.
90    pub fn create_document(
91        &self,
92        name: &str,
93        source: &str,
94        backend: B,
95    ) -> Result<Document<B>, Error> {
96        Document::new(self.registry.clone(), backend, name, source)
97    }
98}