use regex::Regex;
use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Language {
Rust,
Python,
JavaScript,
C,
Cpp,
Go,
Plain,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HighlightColor {
Cyan, Yellow, Green, Magenta, Reset, }
pub struct HighlightRule {
pub pattern: Regex,
pub color: HighlightColor,
}
pub struct SyntaxHighlighter {
pub language: Language,
pub rules: Vec<HighlightRule>,
}
impl SyntaxHighlighter {
pub fn from_filename(filename: &str) -> Option<Self> {
let path = Path::new(filename);
let extension = path.extension()?.to_str()?;
let language = match extension {
"rs" => Language::Rust,
"py" => Language::Python,
"js" => Language::JavaScript,
"c" | "h" => Language::C,
"cpp" | "hpp" | "cc" | "cxx" | "hxx" => Language::Cpp,
"go" => Language::Go,
_ => return None,
};
Some(Self::new(language))
}
pub fn new(language: Language) -> Self {
let rules = Self::create_rules(language);
Self { language, rules }
}
fn create_rules(language: Language) -> Vec<HighlightRule> {
match language {
Language::Rust => Self::rust_rules(),
Language::Python => Self::python_rules(),
Language::JavaScript => Self::javascript_rules(),
Language::C => Self::c_rules(),
Language::Cpp => Self::cpp_rules(),
Language::Go => Self::go_rules(),
Language::Plain => Vec::new(),
}
}
fn rust_rules() -> Vec<HighlightRule> {
vec![
HighlightRule {
pattern: Regex::new(r"//.*$").unwrap(),
color: HighlightColor::Green,
},
HighlightRule {
pattern: Regex::new(r"/\*.*?\*/").unwrap(),
color: HighlightColor::Green,
},
HighlightRule {
pattern: Regex::new(r#""([^"\\]|\\.)*""#).unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"'([^'\\]|\\.)*'").unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"\b(fn|let|mut|pub|struct|enum|impl|trait|use|mod|const|static|match|if|else|loop|while|for|in|return|break|continue|as|ref|move|type|where|unsafe|async|await|dyn|crate|super|self|Self)\b").unwrap(),
color: HighlightColor::Cyan,
},
HighlightRule {
pattern: Regex::new(r"\b\d+(\.\d+)?\b").unwrap(),
color: HighlightColor::Magenta,
},
]
}
fn python_rules() -> Vec<HighlightRule> {
vec![
HighlightRule {
pattern: Regex::new(r"#.*$").unwrap(),
color: HighlightColor::Green,
},
HighlightRule {
pattern: Regex::new(r#""""([^"\\]|\\.)*""""#).unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r#""([^"\\]|\\.)*""#).unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"'([^'\\]|\\.)*'").unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"@\w+").unwrap(),
color: HighlightColor::Cyan,
},
HighlightRule {
pattern: Regex::new(r"\b(def|class|if|elif|else|for|while|import|from|return|yield|try|except|finally|with|as|pass|break|continue|raise|assert|lambda|global|nonlocal|del|and|or|not|in|is|None|True|False)\b").unwrap(),
color: HighlightColor::Cyan,
},
HighlightRule {
pattern: Regex::new(r"\b\d+(\.\d+)?\b").unwrap(),
color: HighlightColor::Magenta,
},
]
}
fn javascript_rules() -> Vec<HighlightRule> {
vec![
HighlightRule {
pattern: Regex::new(r"//.*$").unwrap(),
color: HighlightColor::Green,
},
HighlightRule {
pattern: Regex::new(r"/\*.*?\*/").unwrap(),
color: HighlightColor::Green,
},
HighlightRule {
pattern: Regex::new(r#""([^"\\]|\\.)*""#).unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"'([^'\\]|\\.)*'").unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"`([^`\\]|\\.)*`").unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"\b(function|const|let|var|if|else|for|while|return|class|extends|import|export|from|default|async|await|try|catch|finally|throw|new|this|super|static|get|set|typeof|instanceof|delete|void|yield|break|continue|switch|case|do)\b").unwrap(),
color: HighlightColor::Cyan,
},
HighlightRule {
pattern: Regex::new(r"\b\d+(\.\d+)?\b").unwrap(),
color: HighlightColor::Magenta,
},
]
}
fn c_rules() -> Vec<HighlightRule> {
vec![
HighlightRule {
pattern: Regex::new(r"//.*$").unwrap(),
color: HighlightColor::Green,
},
HighlightRule {
pattern: Regex::new(r"/\*.*?\*/").unwrap(),
color: HighlightColor::Green,
},
HighlightRule {
pattern: Regex::new(r#""([^"\\]|\\.)*""#).unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"'([^'\\]|\\.)*'").unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"\b(int|char|float|double|void|long|short|unsigned|signed|struct|union|enum|typedef|sizeof|const|static|extern|auto|register|volatile|if|else|for|while|do|switch|case|default|break|continue|return|goto)\b").unwrap(),
color: HighlightColor::Cyan,
},
HighlightRule {
pattern: Regex::new(r"\b\d+(\.\d+)?\b").unwrap(),
color: HighlightColor::Magenta,
},
]
}
fn cpp_rules() -> Vec<HighlightRule> {
vec![
HighlightRule {
pattern: Regex::new(r"//.*$").unwrap(),
color: HighlightColor::Green,
},
HighlightRule {
pattern: Regex::new(r"/\*.*?\*/").unwrap(),
color: HighlightColor::Green,
},
HighlightRule {
pattern: Regex::new(r#""([^"\\]|\\.)*""#).unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"'([^'\\]|\\.)*'").unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"\b(int|char|float|double|void|long|short|unsigned|signed|struct|union|enum|typedef|sizeof|const|static|extern|auto|register|volatile|if|else|for|while|do|switch|case|default|break|continue|return|goto|class|public|private|protected|virtual|namespace|using|template|typename|try|catch|throw|new|delete|this|operator|friend|inline|explicit|mutable|constexpr|nullptr)\b").unwrap(),
color: HighlightColor::Cyan,
},
HighlightRule {
pattern: Regex::new(r"\b\d+(\.\d+)?\b").unwrap(),
color: HighlightColor::Magenta,
},
]
}
fn go_rules() -> Vec<HighlightRule> {
vec![
HighlightRule {
pattern: Regex::new(r"//.*$").unwrap(),
color: HighlightColor::Green,
},
HighlightRule {
pattern: Regex::new(r"/\*.*?\*/").unwrap(),
color: HighlightColor::Green,
},
HighlightRule {
pattern: Regex::new(r#""([^"\\]|\\.)*""#).unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"`[^`]*`").unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"'([^'\\]|\\.)*'").unwrap(),
color: HighlightColor::Yellow,
},
HighlightRule {
pattern: Regex::new(r"\b(func|var|const|type|struct|interface|package|import|if|else|for|range|return|break|continue|switch|case|default|go|defer|chan|select|fallthrough|goto|map)\b").unwrap(),
color: HighlightColor::Cyan,
},
HighlightRule {
pattern: Regex::new(r"\b\d+(\.\d+)?\b").unwrap(),
color: HighlightColor::Magenta,
},
]
}
pub fn highlight_line(&self, line: &str) -> Vec<(String, HighlightColor)> {
if self.language == Language::Plain {
return vec![(line.to_string(), HighlightColor::Reset)];
}
let mut segments = Vec::new();
let mut last_end = 0;
let mut matches: Vec<(usize, usize, HighlightColor)> = Vec::new();
for rule in &self.rules {
for mat in rule.pattern.find_iter(line) {
matches.push((mat.start(), mat.end(), rule.color));
}
}
matches.sort_by(|a, b| {
if a.0 == b.0 {
(b.1 - b.0).cmp(&(a.1 - a.0))
} else {
a.0.cmp(&b.0)
}
});
let mut filtered_matches = Vec::new();
let mut covered_end = 0;
for (start, end, color) in matches {
if start >= covered_end {
filtered_matches.push((start, end, color));
covered_end = end;
}
}
for (start, end, color) in filtered_matches {
if start > last_end {
segments.push((line[last_end..start].to_string(), HighlightColor::Reset));
}
segments.push((line[start..end].to_string(), color));
last_end = end;
}
if last_end < line.len() {
segments.push((line[last_end..].to_string(), HighlightColor::Reset));
}
if segments.is_empty() {
segments.push((line.to_string(), HighlightColor::Reset));
}
segments
}
}