Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-syntax/src/highlighter2.rs')
-rw-r--r--helix-syntax/src/highlighter2.rs206
1 files changed, 206 insertions, 0 deletions
diff --git a/helix-syntax/src/highlighter2.rs b/helix-syntax/src/highlighter2.rs
new file mode 100644
index 00000000..9abe5726
--- /dev/null
+++ b/helix-syntax/src/highlighter2.rs
@@ -0,0 +1,206 @@
+use std::borrow::Cow;
+use std::iter::{self, Peekable};
+use std::mem::{replace, take};
+use std::slice;
+
+use hashbrown::HashMap;
+
+use crate::query_iter::{MatchedNode, QueryIter, QueryIterEvent};
+use crate::{Injection, LayerId, Range, Syntax};
+
+/// Indicates which highlight should be applied to a region of source code.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct Highlight(pub u32);
+impl Highlight{
+ pub(crate) const NONE = Highlight(u32::MAX);
+}
+
+#[derive(Debug)]
+struct LocalDef<'a> {
+ name: Cow<'a, str>,
+ value_range: Range,
+ highlight: Option<Highlight>,
+}
+
+#[derive(Debug)]
+struct LocalScope<'a> {
+ inherits: bool,
+ range: Range,
+ local_defs: Vec<LocalDef<'a>>,
+}
+
+#[derive(Debug)]
+struct HighlightedNode {
+ end: u32,
+ highlight: Highlight,
+}
+
+#[derive(Debug, Default)]
+struct LayerData<'a> {
+ parent_highlights: usize,
+ dormant_highlights: Vec<HighlightedNode>,
+ scope_stack: Vec<LocalDef<'a>>,
+}
+
+struct HighlighterConfig<'a> {
+ new_precedance: bool,
+ highlight_indices: &'a [Highlight],
+}
+
+pub struct Highligther<'a> {
+ query: QueryIter<'a, LayerData<'a>>,
+ next_query_event: Option<QueryIterEvent<LayerData<'a>>>,
+ active_highlights: Vec<HighlightedNode>,
+ next_highlight_end: u32,
+ next_highlight_start: u32,
+ config: HighlighterConfig<'a>,
+}
+
+pub struct HighlightList<'a>(slice::Iter<'a, HighlightedNode>);
+
+impl<'a> Iterator for HighlightList<'a> {
+ type Item = Highlight;
+
+ fn next(&mut self) -> Option<Highlight> {
+ self.0.next().map(|node| node.highlight)
+ }
+}
+
+pub enum HighlighEvent<'a> {
+ RefreshHiglights(HighlightList<'a>),
+ PushHighlights(HighlightList<'a>),
+}
+
+impl<'a> Highligther<'a> {
+ pub fn active_highlights(&self) -> HighlightList<'_> {
+ HighlightList(self.active_highlights.iter())
+ }
+
+ pub fn next_event_offset(&self) -> u32 {
+ self.next_highlight_start.min(self.next_highlight_end)
+ }
+
+ pub fn advance(&mut self) -> HighlighEvent<'_> {
+ let mut refresh = false;
+ let prev_stack_size = self.active_highlights.len();
+
+ let pos = self.next_event_offset();
+ if self.next_highlight_end == pos {
+ self.process_injection_ends();
+ self.process_higlight_end();
+ refresh = true;
+ }
+
+ let mut first_highlight = true;
+ while self.next_highlight_start == pos {
+ let Some(query_event) = self.adance_query_iter() else {
+ break;
+ };
+ match query_event {
+ QueryIterEvent::EnterInjection(_) => self.enter_injection(),
+ QueryIterEvent::Match(node) => self.start_highlight(node, &mut first_highlight),
+ QueryIterEvent::ExitInjection { injection, state } => {
+ // state is returned if the layer is finifhed, if it isn't we have
+ // a combined injection and need to deactive its highlights
+ if state.is_none() {
+ self.deactive_layer(injection.layer);
+ refresh = true;
+ }
+ }
+ }
+ }
+ self.next_highlight_end = self
+ .active_highlights
+ .last()
+ .map_or(u32::MAX, |node| node.end);
+
+ if refresh {
+ HighlighEvent::RefreshHiglights(HighlightList(self.active_highlights.iter()))
+ } else {
+ HighlighEvent::PushHighlights(HighlightList(
+ self.active_highlights[prev_stack_size..].iter(),
+ ))
+ }
+ }
+
+ fn adance_query_iter(&mut self) -> Option<QueryIterEvent<LayerData<'a>>> {
+ let event = replace(&mut self.next_query_event, self.query.next());
+ self.next_highlight_start = self
+ .next_query_event
+ .as_ref()
+ .map_or(u32::MAX, |event| event.start());
+ event
+ }
+
+ fn process_higlight_end(&mut self) {
+ let i = self
+ .active_highlights
+ .iter()
+ .rposition(|highlight| highlight.end != self.next_highlight_end)
+ .unwrap();
+ self.active_highlights.truncate(i);
+ }
+
+ /// processes injections that end at the same position as highlights first.
+ fn process_injection_ends(&mut self) {
+ while self.next_highlight_end == self.next_highlight_start {
+ match self.next_query_event.as_ref() {
+ Some(QueryIterEvent::ExitInjection { injection, state }) => {
+ if state.is_none() {
+ self.deactive_layer(injection.layer);
+ }
+ }
+ Some(QueryIterEvent::Match(matched_node)) if matched_node.byte_range.is_empty() => {
+ }
+ _ => break,
+ }
+ }
+ }
+
+ fn enter_injection(&mut self) {
+ self.query.current_layer_state().parent_highlights = self.active_highlights.len();
+ }
+
+ fn deactive_layer(&mut self, layer: LayerId) {
+ let LayerData {
+ parent_highlights,
+ ref mut dormant_highlights,
+ ..
+ } = *self.query.layer_state(layer);
+ let i = self.active_highlights[parent_highlights..]
+ .iter()
+ .rposition(|highlight| highlight.end != self.next_highlight_end)
+ .unwrap();
+ self.active_highlights.truncate(parent_highlights + i);
+ dormant_highlights.extend(self.active_highlights.drain(parent_highlights..))
+ }
+
+ fn start_highlight(&mut self, node: MatchedNode, first_highlight: &mut bool) {
+ if node.byte_range.is_empty() {
+ return;
+ }
+
+ // if there are multiple matches for the exact same node
+ // only use one of the (the last with new/nvim precedance)
+ if !*first_highlight
+ && self.active_highlights.last().map_or(false, |prev_node| {
+ prev_node.end == node.byte_range.end as u32
+ })
+ {
+ if self.config.new_precedance {
+ self.active_highlights.pop();
+ } else {
+ return;
+ }
+ }
+ let highlight = self.config.highlight_indices[node.capture.idx()];
+ if highlight.0 == u32::MAX {
+ return;
+ }
+ self.active_highlights.push(HighlightedNode {
+ end: node.byte_range.end as u32,
+ highlight,
+ });
+ *first_highlight = false;
+ }
+}