pub mod cache;
pub mod engine;
pub mod languages;
use std::path::Path;
use std::sync::OnceLock;
pub use cache::HighlightCache;
pub use engine::{BlockState, Highlighter};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HighlightKind {
Keyword,
Type,
Function,
String,
Number,
Comment,
Constant,
Operator,
Punctuation,
Attribute,
Macro,
Heading,
Emphasis,
Link,
DiffAdded,
DiffRemoved,
DiffHunk,
DiffMeta,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Highlight {
pub start: usize,
pub end: usize,
pub kind: HighlightKind,
}
pub struct Language {
pub name: &'static str,
pub extensions: &'static [&'static str],
pub filenames: &'static [&'static str],
pub keywords: &'static [&'static str],
pub types: &'static [&'static str],
pub constants: &'static [&'static str],
pub line_comment: Option<&'static str>,
pub block_comment: Option<(&'static str, &'static str)>,
pub nested_block_comments: bool,
pub macro_suffix: bool,
pub capitalised_types: bool,
pub extra_rules: &'static [(&'static str, HighlightKind)],
pub prose: bool,
}
fn registry() -> &'static [Highlighter] {
static REGISTRY: OnceLock<Vec<Highlighter>> = OnceLock::new();
REGISTRY.get_or_init(|| languages::all().iter().map(Highlighter::new).collect())
}
#[must_use]
pub fn detect(path: &Path) -> Option<&'static Highlighter> {
let name = path.file_name()?.to_str()?.to_ascii_lowercase();
if let Some(found) = registry()
.iter()
.find(|h| h.language.filenames.contains(&name.as_str()))
{
return Some(found);
}
let extension = path.extension()?.to_str()?.to_ascii_lowercase();
registry()
.iter()
.find(|h| h.language.extensions.contains(&extension.as_str()))
}
#[must_use]
pub fn by_name(name: &str) -> Option<&'static Highlighter> {
registry()
.iter()
.find(|h| h.language.name.eq_ignore_ascii_case(name))
}