use tree_sitter_highlight::{HighlightConfiguration, HighlightEvent};
use tree_sitter_highlight::Highlighter as TsHighlighter;
use crate::Language;
type Result<T, E = tree_sitter_highlight::Error> = std::result::Result<T, E>;
pub struct Highlighter<'a> {
language: &'static Language,
highlights: Highlights<'a>,
config: HighlightConfiguration,
inner: TsHighlighter,
}
#[derive(Debug)]
enum Highlights<'a> {
Owned(Vec<String>),
Borrowed(&'a [&'a str]),
}
struct FusedEvents<'a, I> {
highlights: &'a Highlights<'a>,
source: &'a str,
events: I,
done: bool
}
pub enum Highlight<'a> {
Start {
highlight: &'a str,
index: usize,
},
Source {
text: &'a str,
start: usize,
end: usize,
},
End,
}
impl<'c> Highlighter<'c> {
pub fn new(language: &'static Language, highlights: &'c [&'c str]) -> Self {
Self {
language,
highlights: Highlights::Borrowed(highlights),
config: language.highlight_config(highlights),
inner: TsHighlighter::new(),
}
}
pub fn into_owned(self) -> Highlighter<'static> {
Highlighter {
language: self.language,
highlights: self.highlights.into_owned(),
config: self.config,
inner: self.inner,
}
}
pub fn language(&self) -> &'static Language {
self.language
}
pub fn highlight<'a>(
&'a mut self,
source: &'a str,
) -> impl Iterator<Item = Result<Highlight<'a>>> + 'a {
let highlights = &self.highlights;
let events = self.inner.highlight(&self.config, source.as_bytes(), None, move |_| None);
FusedEvents { highlights, source, events, done: false }
}
}
impl<'a, I> Iterator for FusedEvents<'a, Result<I>>
where I: Iterator<Item = Result<HighlightEvent>> + 'a
{
type Item = Result<Highlight<'a>>;
fn next(&mut self) -> Option<Self::Item> {
use tree_sitter_highlight::Error;
if self.done {
return None;
}
match self.events {
Ok(ref mut v) => v.next().map(|e| e.map(|v| match v {
HighlightEvent::Source { start, end } => Highlight::Source {
text: &self.source[start..end],
start, end,
},
HighlightEvent::HighlightStart(h) => Highlight::Start {
highlight: &self.highlights[h.0],
index: h.0,
},
HighlightEvent::HighlightEnd => Highlight::End,
})),
Err(ref e) => {
self.done = true;
Some(Err(match e {
Error::Cancelled => Error::Cancelled,
Error::InvalidLanguage => Error::InvalidLanguage,
Error::Unknown => Error::Unknown,
}))
}
}
}
}
impl Highlights<'_> {
fn into_owned(self) -> Highlights<'static> {
match self {
Highlights::Owned(v) => Highlights::Owned(v),
Highlights::Borrowed(s) => {
Highlights::Owned(s.into_iter().map(|s| s.to_string()).collect())
}
}
}
}
impl<'a> std::ops::Index<usize> for Highlights<'a> {
type Output = str;
fn index(&self, index: usize) -> &Self::Output {
match self {
Highlights::Owned(v) => &v[index],
Highlights::Borrowed(s) => &s[index],
}
}
}