#![doc = include_str!("readme.md")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HighlightKind {
Keyword,
String,
Number,
Comment,
Identifier,
}
pub trait Highlighter {
fn highlight(&self, text: &str) -> Vec<(usize, usize, HighlightKind)>;
}
pub struct PerlHighlighter {
pub use_parser: bool,
}
impl Default for PerlHighlighter {
fn default() -> Self {
Self { use_parser: false }
}
}
impl PerlHighlighter {
pub fn new() -> Self {
Self::default()
}
pub fn with_parser() -> Self {
Self { use_parser: true }
}
fn highlight_keywords(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
let mut highlights = Vec::new();
let keywords = [
"if", "else", "elsif", "unless", "while", "until", "for", "foreach", "next", "last", "redo", "sub", "my", "our", "local", "use", "require", "package", "return", "eval", "die", "warn", "print", "printf", "say", "shift", "unshift", "push",
"pop", "split", "join", "map", "grep", "sort", "reverse", "keys", "values", "each", "delete", "exists", "defined", "undef", "scalar", "array", "hash", "ref", "bless", "tie", "untie", "package", "strict", "warnings",
];
for keyword in &keywords {
let mut start = 0;
while let Some(pos) = text[start..].find(keyword) {
let absolute_pos = start + pos;
let end_pos = absolute_pos + keyword.len();
let is_word_boundary_before = absolute_pos == 0 || !text.chars().nth(absolute_pos - 1).unwrap_or(' ').is_alphanumeric();
let is_word_boundary_after = end_pos >= text.len() || !text.chars().nth(end_pos).unwrap_or(' ').is_alphanumeric();
if is_word_boundary_before && is_word_boundary_after {
highlights.push((absolute_pos, end_pos, HighlightKind::Keyword));
}
start = absolute_pos + 1;
}
}
highlights
}
}
impl Highlighter for PerlHighlighter {
fn highlight(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
let mut highlights = self.highlight_keywords(text);
highlights.sort_by_key(|h| h.0);
highlights
}
}