mirror of https://github.com/helix-editor/helix
tmp
parent
f7686d7af2
commit
ae0d4189e1
@ -1,439 +1,438 @@
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::atomic::{self, AtomicUsize};
|
||||
use std::{fmt, iter, mem, ops};
|
||||
|
||||
use ropey::RopeSlice;
|
||||
use tree_sitter::{QueryCaptures, QueryCursor, Tree};
|
||||
|
||||
use crate::ropey::RopeProvider;
|
||||
use crate::{
|
||||
byte_range_to_str, Error, HighlightConfiguration, Syntax, PARSER, TREE_SITTER_MATCH_LIMIT,
|
||||
};
|
||||
|
||||
const CANCELLATION_CHECK_INTERVAL: usize = 100;
|
||||
|
||||
/// Indicates which highlight should be applied to a region of source code.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Highlight(pub usize);
|
||||
|
||||
/// Represents a single step in rendering a syntax-highlighted document.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum HighlightEvent {
|
||||
Source { start: usize, end: usize },
|
||||
HighlightStart(Highlight),
|
||||
HighlightEnd,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LocalDef<'a> {
|
||||
name: Cow<'a, str>,
|
||||
value_range: ops::Range<usize>,
|
||||
highlight: Option<Highlight>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LocalScope<'a> {
|
||||
inherits: bool,
|
||||
range: ops::Range<usize>,
|
||||
local_defs: Vec<LocalDef<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct HighlightIter<'a> {
|
||||
source: RopeSlice<'a>,
|
||||
byte_offset: usize,
|
||||
cancellation_flag: Option<&'a AtomicUsize>,
|
||||
layers: Vec<HighlightIterLayer<'a>>,
|
||||
iter_count: usize,
|
||||
next_event: Option<HighlightEvent>,
|
||||
last_highlight_range: Option<(usize, usize, u32)>,
|
||||
}
|
||||
|
||||
struct HighlightIterLayer<'a> {
|
||||
_tree: Option<Tree>,
|
||||
cursor: QueryCursor,
|
||||
captures: RefCell<iter::Peekable<QueryCaptures<'a, 'a, RopeProvider<'a>, &'a [u8]>>>,
|
||||
config: &'a HighlightConfiguration,
|
||||
highlight_end_stack: Vec<usize>,
|
||||
scope_stack: Vec<LocalScope<'a>>,
|
||||
depth: u32,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for HighlightIterLayer<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("HighlightIterLayer").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> HighlightIterLayer<'a> {
|
||||
// First, sort scope boundaries by their byte offset in the document. At a
|
||||
// given position, emit scope endings before scope beginnings. Finally, emit
|
||||
// scope boundaries from deeper layers first.
|
||||
fn sort_key(&self) -> Option<(usize, bool, isize)> {
|
||||
let depth = -(self.depth as isize);
|
||||
let next_start = self
|
||||
.captures
|
||||
.borrow_mut()
|
||||
.peek()
|
||||
.map(|(m, i)| m.captures[*i].node.start_byte());
|
||||
let next_end = self.highlight_end_stack.last().cloned();
|
||||
match (next_start, next_end) {
|
||||
(Some(start), Some(end)) => {
|
||||
if start < end {
|
||||
Some((start, true, depth))
|
||||
} else {
|
||||
Some((end, false, depth))
|
||||
}
|
||||
}
|
||||
(Some(i), None) => Some((i, true, depth)),
|
||||
(None, Some(j)) => Some((j, false, depth)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> HighlightIter<'a> {
|
||||
fn emit_event(
|
||||
&mut self,
|
||||
offset: usize,
|
||||
event: Option<HighlightEvent>,
|
||||
) -> Option<Result<HighlightEvent, Error>> {
|
||||
let result;
|
||||
if self.byte_offset < offset {
|
||||
result = Some(Ok(HighlightEvent::Source {
|
||||
start: self.byte_offset,
|
||||
end: offset,
|
||||
}));
|
||||
self.byte_offset = offset;
|
||||
self.next_event = event;
|
||||
} else {
|
||||
result = event.map(Ok);
|
||||
}
|
||||
self.sort_layers();
|
||||
result
|
||||
}
|
||||
|
||||
fn sort_layers(&mut self) {
|
||||
while !self.layers.is_empty() {
|
||||
if let Some(sort_key) = self.layers[0].sort_key() {
|
||||
let mut i = 0;
|
||||
while i + 1 < self.layers.len() {
|
||||
if let Some(next_offset) = self.layers[i + 1].sort_key() {
|
||||
if next_offset < sort_key {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
let layer = self.layers.remove(i + 1);
|
||||
PARSER.with(|ts_parser| {
|
||||
let highlighter = &mut ts_parser.borrow_mut();
|
||||
highlighter.cursors.push(layer.cursor);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
if i > 0 {
|
||||
self.layers[0..(i + 1)].rotate_left(1);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
let layer = self.layers.remove(0);
|
||||
PARSER.with(|ts_parser| {
|
||||
let highlighter = &mut ts_parser.borrow_mut();
|
||||
highlighter.cursors.push(layer.cursor);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for HighlightIter<'a> {
|
||||
type Item = Result<HighlightEvent, Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
'main: loop {
|
||||
// If we've already determined the next highlight boundary, just return it.
|
||||
if let Some(e) = self.next_event.take() {
|
||||
return Some(Ok(e));
|
||||
}
|
||||
|
||||
// Periodically check for cancellation, returning `Cancelled` error if the
|
||||
// cancellation flag was flipped.
|
||||
if let Some(cancellation_flag) = self.cancellation_flag {
|
||||
self.iter_count += 1;
|
||||
if self.iter_count >= CANCELLATION_CHECK_INTERVAL {
|
||||
self.iter_count = 0;
|
||||
if cancellation_flag.load(atomic::Ordering::Relaxed) != 0 {
|
||||
return Some(Err(Error::Cancelled));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If none of the layers have any more highlight boundaries, terminate.
|
||||
if self.layers.is_empty() {
|
||||
let len = self.source.len_bytes();
|
||||
return if self.byte_offset < len {
|
||||
let result = Some(Ok(HighlightEvent::Source {
|
||||
start: self.byte_offset,
|
||||
end: len,
|
||||
}));
|
||||
self.byte_offset = len;
|
||||
result
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
// Get the next capture from whichever layer has the earliest highlight boundary.
|
||||
let range;
|
||||
let layer = &mut self.layers[0];
|
||||
let captures = layer.captures.get_mut();
|
||||
if let Some((next_match, capture_index)) = captures.peek() {
|
||||
let next_capture = next_match.captures[*capture_index];
|
||||
range = next_capture.node.byte_range();
|
||||
|
||||
// If any previous highlight ends before this node starts, then before
|
||||
// processing this capture, emit the source code up until the end of the
|
||||
// previous highlight, and an end event for that highlight.
|
||||
if let Some(end_byte) = layer.highlight_end_stack.last().cloned() {
|
||||
if end_byte <= range.start {
|
||||
layer.highlight_end_stack.pop();
|
||||
return self.emit_event(end_byte, Some(HighlightEvent::HighlightEnd));
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there are no more captures, then emit any remaining highlight end events.
|
||||
// And if there are none of those, then just advance to the end of the document.
|
||||
else if let Some(end_byte) = layer.highlight_end_stack.last().cloned() {
|
||||
layer.highlight_end_stack.pop();
|
||||
return self.emit_event(end_byte, Some(HighlightEvent::HighlightEnd));
|
||||
} else {
|
||||
return self.emit_event(self.source.len_bytes(), None);
|
||||
};
|
||||
|
||||
let (mut match_, capture_index) = captures.next().unwrap();
|
||||
let mut capture = match_.captures[capture_index];
|
||||
|
||||
// Remove from the local scope stack any local scopes that have already ended.
|
||||
while range.start > layer.scope_stack.last().unwrap().range.end {
|
||||
layer.scope_stack.pop();
|
||||
}
|
||||
|
||||
// If this capture is for tracking local variables, then process the
|
||||
// local variable info.
|
||||
let mut reference_highlight = None;
|
||||
let mut definition_highlight = None;
|
||||
while match_.pattern_index < layer.config.highlights_pattern_index {
|
||||
// If the node represents a local scope, push a new local scope onto
|
||||
// the scope stack.
|
||||
if Some(capture.index) == layer.config.local_scope_capture_index {
|
||||
definition_highlight = None;
|
||||
let mut scope = LocalScope {
|
||||
inherits: true,
|
||||
range: range.clone(),
|
||||
local_defs: Vec::new(),
|
||||
};
|
||||
for prop in layer.config.query.property_settings(match_.pattern_index) {
|
||||
if let "local.scope-inherits" = prop.key.as_ref() {
|
||||
scope.inherits =
|
||||
prop.value.as_ref().map_or(true, |r| r.as_ref() == "true");
|
||||
}
|
||||
}
|
||||
layer.scope_stack.push(scope);
|
||||
}
|
||||
// If the node represents a definition, add a new definition to the
|
||||
// local scope at the top of the scope stack.
|
||||
else if Some(capture.index) == layer.config.local_def_capture_index {
|
||||
reference_highlight = None;
|
||||
let scope = layer.scope_stack.last_mut().unwrap();
|
||||
|
||||
let mut value_range = 0..0;
|
||||
for capture in match_.captures {
|
||||
if Some(capture.index) == layer.config.local_def_value_capture_index {
|
||||
value_range = capture.node.byte_range();
|
||||
}
|
||||
}
|
||||
|
||||
let name = byte_range_to_str(range.clone(), self.source);
|
||||
scope.local_defs.push(LocalDef {
|
||||
name,
|
||||
value_range,
|
||||
highlight: None,
|
||||
});
|
||||
definition_highlight = scope.local_defs.last_mut().map(|s| &mut s.highlight);
|
||||
}
|
||||
// If the node represents a reference, then try to find the corresponding
|
||||
// definition in the scope stack.
|
||||
else if Some(capture.index) == layer.config.local_ref_capture_index
|
||||
&& definition_highlight.is_none()
|
||||
{
|
||||
definition_highlight = None;
|
||||
let name = byte_range_to_str(range.clone(), self.source);
|
||||
for scope in layer.scope_stack.iter().rev() {
|
||||
if let Some(highlight) = scope.local_defs.iter().rev().find_map(|def| {
|
||||
if def.name == name && range.start >= def.value_range.end {
|
||||
Some(def.highlight)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
reference_highlight = highlight;
|
||||
break;
|
||||
}
|
||||
if !scope.inherits {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Continue processing any additional matches for the same node.
|
||||
if let Some((next_match, next_capture_index)) = captures.peek() {
|
||||
let next_capture = next_match.captures[*next_capture_index];
|
||||
if next_capture.node == capture.node {
|
||||
capture = next_capture;
|
||||
match_ = captures.next().unwrap().0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
self.sort_layers();
|
||||
continue 'main;
|
||||
}
|
||||
|
||||
// Otherwise, this capture must represent a highlight.
|
||||
// If this exact range has already been highlighted by an earlier pattern, or by
|
||||
// a different layer, then skip over this one.
|
||||
if let Some((last_start, last_end, last_depth)) = self.last_highlight_range {
|
||||
if range.start == last_start && range.end == last_end && layer.depth < last_depth {
|
||||
self.sort_layers();
|
||||
continue 'main;
|
||||
}
|
||||
}
|
||||
|
||||
// If the current node was found to be a local variable, then skip over any
|
||||
// highlighting patterns that are disabled for local variables.
|
||||
if definition_highlight.is_some() || reference_highlight.is_some() {
|
||||
while layer.config.non_local_variable_patterns[match_.pattern_index] {
|
||||
match_.remove();
|
||||
if let Some((next_match, next_capture_index)) = captures.peek() {
|
||||
let next_capture = next_match.captures[*next_capture_index];
|
||||
if next_capture.node == capture.node {
|
||||
capture = next_capture;
|
||||
match_ = captures.next().unwrap().0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
self.sort_layers();
|
||||
continue 'main;
|
||||
}
|
||||
}
|
||||
|
||||
// Once a highlighting pattern is found for the current node, skip over
|
||||
// any later highlighting patterns that also match this node. Captures
|
||||
// for a given node are ordered by pattern index, so these subsequent
|
||||
// captures are guaranteed to be for highlighting, not injections or
|
||||
// local variables.
|
||||
while let Some((next_match, next_capture_index)) = captures.peek() {
|
||||
let next_capture = next_match.captures[*next_capture_index];
|
||||
if next_capture.node == capture.node {
|
||||
captures.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let current_highlight = layer.config.highlight_indices.load()[capture.index as usize];
|
||||
|
||||
// If this node represents a local definition, then store the current
|
||||
// highlight value on the local scope entry representing this node.
|
||||
if let Some(definition_highlight) = definition_highlight {
|
||||
*definition_highlight = current_highlight;
|
||||
}
|
||||
|
||||
// Emit a scope start event and push the node's end position to the stack.
|
||||
if let Some(highlight) = reference_highlight.or(current_highlight) {
|
||||
self.last_highlight_range = Some((range.start, range.end, layer.depth));
|
||||
layer.highlight_end_stack.push(range.end);
|
||||
return self
|
||||
.emit_event(range.start, Some(HighlightEvent::HighlightStart(highlight)));
|
||||
}
|
||||
|
||||
self.sort_layers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Syntax {
|
||||
/// Iterate over the highlighted regions for a given slice of source code.
|
||||
pub fn highlight_iter<'a>(
|
||||
&'a self,
|
||||
source: RopeSlice<'a>,
|
||||
range: Option<std::ops::Range<usize>>,
|
||||
cancellation_flag: Option<&'a AtomicUsize>,
|
||||
) -> impl Iterator<Item = Result<HighlightEvent, Error>> + 'a {
|
||||
let mut layers = self
|
||||
.layers
|
||||
.iter()
|
||||
.filter_map(|(_, layer)| {
|
||||
// TODO: if range doesn't overlap layer range, skip it
|
||||
|
||||
// Reuse a cursor from the pool if available.
|
||||
let mut cursor = PARSER.with(|ts_parser| {
|
||||
let highlighter = &mut ts_parser.borrow_mut();
|
||||
highlighter.cursors.pop().unwrap_or_else(QueryCursor::new)
|
||||
});
|
||||
|
||||
// The `captures` iterator borrows the `Tree` and the `QueryCursor`, which
|
||||
// prevents them from being moved. But both of these values are really just
|
||||
// pointers, so it's actually ok to move them.
|
||||
let cursor_ref =
|
||||
unsafe { mem::transmute::<_, &'static mut QueryCursor>(&mut cursor) };
|
||||
|
||||
// if reusing cursors & no range this resets to whole range
|
||||
cursor_ref.set_byte_range(range.clone().unwrap_or(0..usize::MAX));
|
||||
cursor_ref.set_match_limit(TREE_SITTER_MATCH_LIMIT);
|
||||
|
||||
let mut captures = cursor_ref
|
||||
.captures(
|
||||
&layer.config.query,
|
||||
layer.tree().root_node(),
|
||||
RopeProvider(source),
|
||||
)
|
||||
.peekable();
|
||||
|
||||
// If there's no captures, skip the layer
|
||||
captures.peek()?;
|
||||
|
||||
Some(HighlightIterLayer {
|
||||
highlight_end_stack: Vec::new(),
|
||||
scope_stack: vec![LocalScope {
|
||||
inherits: false,
|
||||
range: 0..usize::MAX,
|
||||
local_defs: Vec::new(),
|
||||
}],
|
||||
cursor,
|
||||
_tree: None,
|
||||
captures: RefCell::new(captures),
|
||||
config: layer.config.as_ref(), // TODO: just reuse `layer`
|
||||
depth: layer.depth, // TODO: just reuse `layer`
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
layers.sort_unstable_by_key(|layer| layer.sort_key());
|
||||
|
||||
let mut result = HighlightIter {
|
||||
source,
|
||||
byte_offset: range.map_or(0, |r| r.start),
|
||||
cancellation_flag,
|
||||
iter_count: 0,
|
||||
layers,
|
||||
next_event: None,
|
||||
last_highlight_range: None,
|
||||
};
|
||||
result.sort_layers();
|
||||
result
|
||||
}
|
||||
}
|
||||
pub use super::highlighter2::*;
|
||||
|
||||
// use std::borrow::Cow;
|
||||
// use std::cell::RefCell;
|
||||
// use std::sync::atomic::{self, AtomicUsize};
|
||||
// use std::{fmt, iter, mem, ops};
|
||||
|
||||
// use ropey::RopeSlice;
|
||||
// use tree_sitter::{QueryCaptures, QueryCursor, Tree};
|
||||
|
||||
// use crate::{byte_range_to_str, Error, HighlightConfiguration, Syntax, TREE_SITTER_MATCH_LIMIT};
|
||||
|
||||
// const CANCELLATION_CHECK_INTERVAL: usize = 100;
|
||||
|
||||
// /// Indicates which highlight should be applied to a region of source code.
|
||||
// #[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
// pub struct Highlight(pub usize);
|
||||
|
||||
// /// Represents a single step in rendering a syntax-highlighted document.
|
||||
// #[derive(Copy, Clone, Debug)]
|
||||
// pub enum HighlightEvent {
|
||||
// Source { start: usize, end: usize },
|
||||
// HighlightStart(Highlight),
|
||||
// HighlightEnd,
|
||||
// }
|
||||
|
||||
// #[derive(Debug)]
|
||||
// struct LocalDef<'a> {
|
||||
// name: Cow<'a, str>,
|
||||
// value_range: ops::Range<usize>,
|
||||
// highlight: Option<Highlight>,
|
||||
// }
|
||||
|
||||
// #[derive(Debug)]
|
||||
// struct LocalScope<'a> {
|
||||
// inherits: bool,
|
||||
// range: ops::Range<usize>,
|
||||
// local_defs: Vec<LocalDef<'a>>,
|
||||
// }
|
||||
|
||||
// #[derive(Debug)]
|
||||
// struct HighlightIter<'a> {
|
||||
// source: RopeSlice<'a>,
|
||||
// byte_offset: usize,
|
||||
// cancellation_flag: Option<&'a AtomicUsize>,
|
||||
// layers: Vec<HighlightIterLayer<'a>>,
|
||||
// iter_count: usize,
|
||||
// next_event: Option<HighlightEvent>,
|
||||
// last_highlight_range: Option<(usize, usize, u32)>,
|
||||
// }
|
||||
|
||||
// struct HighlightIterLayer<'a> {
|
||||
// _tree: Option<Tree>,
|
||||
// cursor: QueryCursor,
|
||||
// captures: RefCell<iter::Peekable<QueryCaptures<'a, 'a, RopeProvider<'a>, &'a [u8]>>>,
|
||||
// config: &'a HighlightConfiguration,
|
||||
// highlight_end_stack: Vec<usize>,
|
||||
// scope_stack: Vec<LocalScope<'a>>,
|
||||
// depth: u32,
|
||||
// }
|
||||
|
||||
// impl<'a> fmt::Debug for HighlightIterLayer<'a> {
|
||||
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// f.debug_struct("HighlightIterLayer").finish()
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<'a> HighlightIterLayer<'a> {
|
||||
// // First, sort scope boundaries by their byte offset in the document. At a
|
||||
// // given position, emit scope endings before scope beginnings. Finally, emit
|
||||
// // scope boundaries from deeper layers first.
|
||||
// fn sort_key(&self) -> Option<(usize, bool, isize)> {
|
||||
// let depth = -(self.depth as isize);
|
||||
// let next_start = self
|
||||
// .captures
|
||||
// .borrow_mut()
|
||||
// .peek()
|
||||
// .map(|(m, i)| m.captures[*i].node.start_byte());
|
||||
// let next_end = self.highlight_end_stack.last().cloned();
|
||||
// match (next_start, next_end) {
|
||||
// (Some(start), Some(end)) => {
|
||||
// if start < end {
|
||||
// Some((start, true, depth))
|
||||
// } else {
|
||||
// Some((end, false, depth))
|
||||
// }
|
||||
// }
|
||||
// (Some(i), None) => Some((i, true, depth)),
|
||||
// (None, Some(j)) => Some((j, false, depth)),
|
||||
// _ => None,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<'a> HighlightIter<'a> {
|
||||
// fn emit_event(
|
||||
// &mut self,
|
||||
// offset: usize,
|
||||
// event: Option<HighlightEvent>,
|
||||
// ) -> Option<Result<HighlightEvent, Error>> {
|
||||
// let result;
|
||||
// if self.byte_offset < offset {
|
||||
// result = Some(Ok(HighlightEvent::Source {
|
||||
// start: self.byte_offset,
|
||||
// end: offset,
|
||||
// }));
|
||||
// self.byte_offset = offset;
|
||||
// self.next_event = event;
|
||||
// } else {
|
||||
// result = event.map(Ok);
|
||||
// }
|
||||
// self.sort_layers();
|
||||
// result
|
||||
// }
|
||||
|
||||
// fn sort_layers(&mut self) {
|
||||
// while !self.layers.is_empty() {
|
||||
// if let Some(sort_key) = self.layers[0].sort_key() {
|
||||
// let mut i = 0;
|
||||
// while i + 1 < self.layers.len() {
|
||||
// if let Some(next_offset) = self.layers[i + 1].sort_key() {
|
||||
// if next_offset < sort_key {
|
||||
// i += 1;
|
||||
// continue;
|
||||
// }
|
||||
// } else {
|
||||
// let layer = self.layers.remove(i + 1);
|
||||
// PARSER.with(|ts_parser| {
|
||||
// let highlighter = &mut ts_parser.borrow_mut();
|
||||
// highlighter.cursors.push(layer.cursor);
|
||||
// });
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// if i > 0 {
|
||||
// self.layers[0..(i + 1)].rotate_left(1);
|
||||
// }
|
||||
// break;
|
||||
// } else {
|
||||
// let layer = self.layers.remove(0);
|
||||
// PARSER.with(|ts_parser| {
|
||||
// let highlighter = &mut ts_parser.borrow_mut();
|
||||
// highlighter.cursors.push(layer.cursor);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<'a> Iterator for HighlightIter<'a> {
|
||||
// type Item = Result<HighlightEvent, Error>;
|
||||
|
||||
// fn next(&mut self) -> Option<Self::Item> {
|
||||
// 'main: loop {
|
||||
// // If we've already determined the next highlight boundary, just return it.
|
||||
// if let Some(e) = self.next_event.take() {
|
||||
// return Some(Ok(e));
|
||||
// }
|
||||
|
||||
// // Periodically check for cancellation, returning `Cancelled` error if the
|
||||
// // cancellation flag was flipped.
|
||||
// if let Some(cancellation_flag) = self.cancellation_flag {
|
||||
// self.iter_count += 1;
|
||||
// if self.iter_count >= CANCELLATION_CHECK_INTERVAL {
|
||||
// self.iter_count = 0;
|
||||
// if cancellation_flag.load(atomic::Ordering::Relaxed) != 0 {
|
||||
// return Some(Err(Error::Cancelled));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // If none of the layers have any more highlight boundaries, terminate.
|
||||
// if self.layers.is_empty() {
|
||||
// let len = self.source.len_bytes();
|
||||
// return if self.byte_offset < len {
|
||||
// let result = Some(Ok(HighlightEvent::Source {
|
||||
// start: self.byte_offset,
|
||||
// end: len,
|
||||
// }));
|
||||
// self.byte_offset = len;
|
||||
// result
|
||||
// } else {
|
||||
// None
|
||||
// };
|
||||
// }
|
||||
|
||||
// // Get the next capture from whichever layer has the earliest highlight boundary.
|
||||
// let range;
|
||||
// let layer = &mut self.layers[0];
|
||||
// let captures = layer.captures.get_mut();
|
||||
// if let Some((next_match, capture_index)) = captures.peek() {
|
||||
// let next_capture = next_match.captures[*capture_index];
|
||||
// range = next_capture.node.byte_range();
|
||||
|
||||
// // If any previous highlight ends before this node starts, then before
|
||||
// // processing this capture, emit the source code up until the end of the
|
||||
// // previous highlight, and an end event for that highlight.
|
||||
// if let Some(end_byte) = layer.highlight_end_stack.last().cloned() {
|
||||
// if end_byte <= range.start {
|
||||
// layer.highlight_end_stack.pop();
|
||||
// return self.emit_event(end_byte, Some(HighlightEvent::HighlightEnd));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // If there are no more captures, then emit any remaining highlight end events.
|
||||
// // And if there are none of those, then just advance to the end of the document.
|
||||
// else if let Some(end_byte) = layer.highlight_end_stack.last().cloned() {
|
||||
// layer.highlight_end_stack.pop();
|
||||
// return self.emit_event(end_byte, Some(HighlightEvent::HighlightEnd));
|
||||
// } else {
|
||||
// return self.emit_event(self.source.len_bytes(), None);
|
||||
// };
|
||||
|
||||
// let (mut match_, capture_index) = captures.next().unwrap();
|
||||
// let mut capture = match_.captures[capture_index];
|
||||
|
||||
// // Remove from the local scope stack any local scopes that have already ended.
|
||||
// while range.start > layer.scope_stack.last().unwrap().range.end {
|
||||
// layer.scope_stack.pop();
|
||||
// }
|
||||
|
||||
// // If this capture is for tracking local variables, then process the
|
||||
// // local variable info.
|
||||
// let mut reference_highlight = None;
|
||||
// let mut definition_highlight = None;
|
||||
// while match_.pattern_index < layer.config.highlights_pattern_index {
|
||||
// // If the node represents a local scope, push a new local scope onto
|
||||
// // the scope stack.
|
||||
// if Some(capture.index) == layer.config.local_scope_capture_index {
|
||||
// definition_highlight = None;
|
||||
// let mut scope = LocalScope {
|
||||
// inherits: true,
|
||||
// range: range.clone(),
|
||||
// local_defs: Vec::new(),
|
||||
// };
|
||||
// for prop in layer.config.query.property_settings(match_.pattern_index) {
|
||||
// if let "local.scope-inherits" = prop.key.as_ref() {
|
||||
// scope.inherits =
|
||||
// prop.value.as_ref().map_or(true, |r| r.as_ref() == "true");
|
||||
// }
|
||||
// }
|
||||
// layer.scope_stack.push(scope);
|
||||
// }
|
||||
// // If the node represents a definition, add a new definition to the
|
||||
// // local scope at the top of the scope stack.
|
||||
// else if Some(capture.index) == layer.config.local_def_capture_index {
|
||||
// reference_highlight = None;
|
||||
// let scope = layer.scope_stack.last_mut().unwrap();
|
||||
|
||||
// let mut value_range = 0..0;
|
||||
// for capture in match_.captures {
|
||||
// if Some(capture.index) == layer.config.local_def_value_capture_index {
|
||||
// value_range = capture.node.byte_range();
|
||||
// }
|
||||
// }
|
||||
|
||||
// let name = byte_range_to_str(range.clone(), self.source);
|
||||
// scope.local_defs.push(LocalDef {
|
||||
// name,
|
||||
// value_range,
|
||||
// highlight: None,
|
||||
// });
|
||||
// definition_highlight = scope.local_defs.last_mut().map(|s| &mut s.highlight);
|
||||
// }
|
||||
// // If the node represents a reference, then try to find the corresponding
|
||||
// // definition in the scope stack.
|
||||
// else if Some(capture.index) == layer.config.local_ref_capture_index
|
||||
// && definition_highlight.is_none()
|
||||
// {
|
||||
// definition_highlight = None;
|
||||
// let name = byte_range_to_str(range.clone(), self.source);
|
||||
// for scope in layer.scope_stack.iter().rev() {
|
||||
// if let Some(highlight) = scope.local_defs.iter().rev().find_map(|def| {
|
||||
// if def.name == name && range.start >= def.value_range.end {
|
||||
// Some(def.highlight)
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// }) {
|
||||
// reference_highlight = highlight;
|
||||
// break;
|
||||
// }
|
||||
// if !scope.inherits {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Continue processing any additional matches for the same node.
|
||||
// if let Some((next_match, next_capture_index)) = captures.peek() {
|
||||
// let next_capture = next_match.captures[*next_capture_index];
|
||||
// if next_capture.node == capture.node {
|
||||
// capture = next_capture;
|
||||
// match_ = captures.next().unwrap().0;
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
|
||||
// self.sort_layers();
|
||||
// continue 'main;
|
||||
// }
|
||||
|
||||
// // Otherwise, this capture must represent a highlight.
|
||||
// // If this exact range has already been highlighted by an earlier pattern, or by
|
||||
// // a different layer, then skip over this one.
|
||||
// if let Some((last_start, last_end, last_depth)) = self.last_highlight_range {
|
||||
// if range.start == last_start && range.end == last_end && layer.depth < last_depth {
|
||||
// self.sort_layers();
|
||||
// continue 'main;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // If the current node was found to be a local variable, then skip over any
|
||||
// // highlighting patterns that are disabled for local variables.
|
||||
// if definition_highlight.is_some() || reference_highlight.is_some() {
|
||||
// while layer.config.non_local_variable_patterns[match_.pattern_index] {
|
||||
// match_.remove();
|
||||
// if let Some((next_match, next_capture_index)) = captures.peek() {
|
||||
// let next_capture = next_match.captures[*next_capture_index];
|
||||
// if next_capture.node == capture.node {
|
||||
// capture = next_capture;
|
||||
// match_ = captures.next().unwrap().0;
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
|
||||
// self.sort_layers();
|
||||
// continue 'main;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Once a highlighting pattern is found for the current node, skip over
|
||||
// // any later highlighting patterns that also match this node. Captures
|
||||
// // for a given node are ordered by pattern index, so these subsequent
|
||||
// // captures are guaranteed to be for highlighting, not injections or
|
||||
// // local variables.
|
||||
// while let Some((next_match, next_capture_index)) = captures.peek() {
|
||||
// let next_capture = next_match.captures[*next_capture_index];
|
||||
// if next_capture.node == capture.node {
|
||||
// captures.next();
|
||||
// } else {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// let current_highlight = layer.config.highlight_indices.load()[capture.index as usize];
|
||||
|
||||
// // If this node represents a local definition, then store the current
|
||||
// // highlight value on the local scope entry representing this node.
|
||||
// if let Some(definition_highlight) = definition_highlight {
|
||||
// *definition_highlight = current_highlight;
|
||||
// }
|
||||
|
||||
// // Emit a scope start event and push the node's end position to the stack.
|
||||
// if let Some(highlight) = reference_highlight.or(current_highlight) {
|
||||
// self.last_highlight_range = Some((range.start, range.end, layer.depth));
|
||||
// layer.highlight_end_stack.push(range.end);
|
||||
// return self
|
||||
// .emit_event(range.start, Some(HighlightEvent::HighlightStart(highlight)));
|
||||
// }
|
||||
|
||||
// self.sort_layers();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Syntax {
|
||||
// /// Iterate over the highlighted regions for a given slice of source code.
|
||||
// pub fn highlight_iter<'a>(
|
||||
// &'a self,
|
||||
// source: RopeSlice<'a>,
|
||||
// range: Option<std::ops::Range<usize>>,
|
||||
// cancellation_flag: Option<&'a AtomicUsize>,
|
||||
// ) -> impl Iterator<Item = Result<HighlightEvent, Error>> + 'a {
|
||||
// let mut layers = self
|
||||
// .layers
|
||||
// .iter()
|
||||
// .filter_map(|(_, layer)| {
|
||||
// // TODO: if range doesn't overlap layer range, skip it
|
||||
|
||||
// // Reuse a cursor from the pool if available.
|
||||
// let mut cursor = PARSER.with(|ts_parser| {
|
||||
// let highlighter = &mut ts_parser.borrow_mut();
|
||||
// highlighter.cursors.pop().unwrap_or_else(QueryCursor::new)
|
||||
// });
|
||||
|
||||
// // The `captures` iterator borrows the `Tree` and the `QueryCursor`, which
|
||||
// // prevents them from being moved. But both of these values are really just
|
||||
// // pointers, so it's actually ok to move them.
|
||||
// let cursor_ref =
|
||||
// unsafe { mem::transmute::<_, &'static mut QueryCursor>(&mut cursor) };
|
||||
|
||||
// // if reusing cursors & no range this resets to whole range
|
||||
// cursor_ref.set_byte_range(range.clone().unwrap_or(0..usize::MAX));
|
||||
// cursor_ref.set_match_limit(TREE_SITTER_MATCH_LIMIT);
|
||||
|
||||
// let mut captures = cursor_ref
|
||||
// .captures(
|
||||
// &layer.config.query,
|
||||
// layer.tree().root_node(),
|
||||
// RopeProvider(source),
|
||||
// )
|
||||
// .peekable();
|
||||
|
||||
// // If there's no captures, skip the layer
|
||||
// captures.peek()?;
|
||||
|
||||
// Some(HighlightIterLayer {
|
||||
// highlight_end_stack: Vec::new(),
|
||||
// scope_stack: vec![LocalScope {
|
||||
// inherits: false,
|
||||
// range: 0..usize::MAX,
|
||||
// local_defs: Vec::new(),
|
||||
// }],
|
||||
// cursor,
|
||||
// _tree: None,
|
||||
// captures: RefCell::new(captures),
|
||||
// config: layer.config.as_ref(), // TODO: just reuse `layer`
|
||||
// depth: layer.depth, // TODO: just reuse `layer`
|
||||
// })
|
||||
// })
|
||||
// .collect::<Vec<_>>();
|
||||
|
||||
// layers.sort_unstable_by_key(|layer| layer.sort_key());
|
||||
|
||||
// let mut result = HighlightIter {
|
||||
// source,
|
||||
// byte_offset: range.map_or(0, |r| r.start),
|
||||
// cancellation_flag,
|
||||
// iter_count: 0,
|
||||
// layers,
|
||||
// next_event: None,
|
||||
// last_highlight_range: None,
|
||||
// };
|
||||
// result.sort_layers();
|
||||
// result
|
||||
// }
|
||||
// }
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -1,268 +0,0 @@
|
||||
use core::slice;
|
||||
use std::cell::RefCell;
|
||||
use std::iter::Peekable;
|
||||
use std::mem::replace;
|
||||
use std::sync::Arc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use ropey::RopeSlice;
|
||||
use slotmap::{new_key_type, SlotMap};
|
||||
|
||||
use crate::parse::LayerUpdateFlags;
|
||||
use crate::tree_sitter::{
|
||||
self, Capture, InactiveQueryCursor, Parser, Query, QueryCursor, RopeTsInput, SyntaxTree,
|
||||
SyntaxTreeNode,
|
||||
};
|
||||
use crate::HighlightConfiguration;
|
||||
|
||||
// TODO(perf): replace std::ops::Range<usize> with helix_stdx::Range<u32> once added
|
||||
type Range = std::ops::Range<usize>;
|
||||
|
||||
new_key_type! {
|
||||
/// The default slot map key type.
|
||||
pub struct LayerId;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LanguageLayer {
|
||||
pub config: Arc<HighlightConfiguration>,
|
||||
pub(crate) parse_tree: Option<SyntaxTree>,
|
||||
/// internal flags used during parsing to track incremental invalidation
|
||||
pub(crate) flags: LayerUpdateFlags,
|
||||
ranges: Vec<tree_sitter::Range>,
|
||||
pub(crate) parent: Option<LayerId>,
|
||||
/// a list of **sorted** non-overlapping injection ranges. Note that
|
||||
/// injection ranges are not relative to the start of this layer but the
|
||||
/// start of the root layer
|
||||
pub(crate) injections: Box<[Injection]>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Injection {
|
||||
pub byte_range: Range,
|
||||
pub layer: LayerId,
|
||||
}
|
||||
|
||||
impl LanguageLayer {
|
||||
/// Returns the injection range **within this layers** that contains `idx`.
|
||||
/// This function will not descend into nested injections
|
||||
pub(crate) fn injection_at_byte_idx(&self, idx: usize) -> Option<&Injection> {
|
||||
let i = self
|
||||
.injections
|
||||
.partition_point(|range| range.byte_range.start <= idx);
|
||||
self.injections
|
||||
.get(i)
|
||||
.filter(|injection| injection.byte_range.end > idx)
|
||||
}
|
||||
}
|
||||
|
||||
struct InjectionTree {
|
||||
layers: SlotMap<LayerId, LanguageLayer>,
|
||||
root: LayerId,
|
||||
}
|
||||
|
||||
impl InjectionTree {
|
||||
pub fn layer_for_byte_range(&self, start: usize, end: usize) -> LayerId {
|
||||
let mut cursor = self.root;
|
||||
loop {
|
||||
let layer = &self.layers[cursor];
|
||||
let Some(start_injection) = layer.injection_at_byte_idx(start) else {
|
||||
break;
|
||||
};
|
||||
let Some(end_injection) = layer.injection_at_byte_idx(end) else {
|
||||
break;
|
||||
};
|
||||
if start_injection.layer == end_injection.layer {
|
||||
cursor = start_injection.layer;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
cursor
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MatchedNode {
|
||||
pub capture: Capture,
|
||||
pub byte_range: Range,
|
||||
}
|
||||
|
||||
struct LayerQueryIter<'a> {
|
||||
cursor: QueryCursor<'a, 'a, RopeTsInput<'a>>,
|
||||
peeked: Option<MatchedNode>,
|
||||
}
|
||||
|
||||
impl<'a> LayerQueryIter<'a> {
|
||||
fn peek(&mut self) -> Option<&MatchedNode> {
|
||||
if self.peeked.is_none() {
|
||||
let (query_match, node_idx) = self.cursor.next_matched_node()?;
|
||||
let matched_node = query_match.matched_node(node_idx);
|
||||
self.peeked = Some(MatchedNode {
|
||||
capture: matched_node.capture,
|
||||
byte_range: matched_node.syntax_node.byte_range(),
|
||||
});
|
||||
}
|
||||
self.peeked.as_ref()
|
||||
}
|
||||
|
||||
fn consume(&mut self) -> MatchedNode {
|
||||
self.peeked.take().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
struct ActiveLayer<'a> {
|
||||
query_iter: LayerQueryIter<'a>,
|
||||
injections: Peekable<slice::Iter<'a, Injection>>,
|
||||
}
|
||||
|
||||
struct QueryBuilder<'a, 'tree> {
|
||||
query: &'a Query,
|
||||
node: &'a SyntaxTreeNode<'tree>,
|
||||
src: RopeSlice<'a>,
|
||||
injection_tree: &'a InjectionTree,
|
||||
}
|
||||
|
||||
pub struct QueryIter<'a, 'tree> {
|
||||
query_builder: Box<QueryBuilder<'a, 'tree>>,
|
||||
active_layers: HashMap<LayerId, ActiveLayer<'a>>,
|
||||
active_injections: Vec<Injection>,
|
||||
current_injection: Injection,
|
||||
}
|
||||
|
||||
impl<'a> QueryIter<'a, '_> {
|
||||
fn enter_injection(&mut self, injection: Injection) -> bool {
|
||||
self.active_layers
|
||||
.entry(injection.layer)
|
||||
.or_insert_with(|| {
|
||||
let layer = &self.query_builder.injection_tree.layers[injection.layer];
|
||||
let injection_start = layer
|
||||
.injections
|
||||
.partition_point(|child| child.byte_range.start < injection.byte_range.start);
|
||||
let cursor = get_cursor().execute_query(
|
||||
self.query_builder.query,
|
||||
self.query_builder.node,
|
||||
RopeTsInput::new(self.query_builder.src),
|
||||
);
|
||||
ActiveLayer {
|
||||
query_iter: LayerQueryIter {
|
||||
cursor,
|
||||
peeked: None,
|
||||
},
|
||||
injections: layer.injections[injection_start..].iter().peekable(),
|
||||
}
|
||||
});
|
||||
let old_injection = replace(&mut self.current_injection, injection);
|
||||
self.active_injections.push(old_injection);
|
||||
true
|
||||
}
|
||||
|
||||
fn exit_injection(&mut self) -> Option<Injection> {
|
||||
let injection = replace(&mut self.current_injection, self.active_injections.pop()?);
|
||||
let finished_layer = self.active_layers[&injection.layer]
|
||||
.query_iter
|
||||
.peeked
|
||||
.is_none();
|
||||
if finished_layer {
|
||||
let layer = self.active_layers.remove(&injection.layer).unwrap();
|
||||
reuse_cursor(layer.query_iter.cursor.reuse());
|
||||
}
|
||||
Some(injection)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum QueryIterEvent {
|
||||
EnterInjection(Injection),
|
||||
Match(MatchedNode),
|
||||
ExitInjection(Injection),
|
||||
}
|
||||
|
||||
impl<'a> Iterator for QueryIter<'a, '_> {
|
||||
type Item = QueryIterEvent;
|
||||
|
||||
fn next(&mut self) -> Option<QueryIterEvent> {
|
||||
loop {
|
||||
let active_layer = self
|
||||
.active_layers
|
||||
.get_mut(&self.current_injection.layer)
|
||||
.unwrap();
|
||||
let next_injection = active_layer.injections.peek().filter(|injection| {
|
||||
injection.byte_range.start < self.current_injection.byte_range.end
|
||||
});
|
||||
let next_match = active_layer.query_iter.peek().filter(|matched_node| {
|
||||
matched_node.byte_range.start < self.current_injection.byte_range.end
|
||||
});
|
||||
|
||||
match (next_match, next_injection) {
|
||||
(None, None) => {
|
||||
return self.exit_injection().map(QueryIterEvent::ExitInjection);
|
||||
}
|
||||
(Some(_), None) => {
|
||||
// consume match
|
||||
let matched_node = active_layer.query_iter.consume();
|
||||
return Some(QueryIterEvent::Match(matched_node));
|
||||
}
|
||||
(Some(matched_node), Some(injection))
|
||||
if matched_node.byte_range.start <= injection.byte_range.end =>
|
||||
{
|
||||
// consume match
|
||||
let matched_node = active_layer.query_iter.consume();
|
||||
// ignore nodes that are overlapped by the injection
|
||||
if matched_node.byte_range.start <= injection.byte_range.start {
|
||||
return Some(QueryIterEvent::Match(matched_node));
|
||||
}
|
||||
}
|
||||
(Some(_), Some(_)) | (None, Some(_)) => {
|
||||
// consume injection
|
||||
let injection = active_layer.injections.next().unwrap();
|
||||
if self.enter_injection(injection.clone()) {
|
||||
return Some(QueryIterEvent::EnterInjection(injection.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TsParser {
|
||||
parser: crate::tree_sitter::Parser,
|
||||
pub cursors: Vec<crate::tree_sitter::InactiveQueryCursor>,
|
||||
}
|
||||
|
||||
// could also just use a pool, or a single instance?
|
||||
thread_local! {
|
||||
static PARSER: RefCell<TsParser> = RefCell::new(TsParser {
|
||||
parser: Parser::new(),
|
||||
cursors: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_cursor<T>(f: impl FnOnce(&mut InactiveQueryCursor) -> T) -> T {
|
||||
PARSER.with(|parser| {
|
||||
let mut parser = parser.borrow_mut();
|
||||
let mut cursor = parser
|
||||
.cursors
|
||||
.pop()
|
||||
.unwrap_or_else(InactiveQueryCursor::new);
|
||||
let res = f(&mut cursor);
|
||||
parser.cursors.push(cursor);
|
||||
res
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_cursor() -> InactiveQueryCursor {
|
||||
PARSER.with(|parser| {
|
||||
let mut parser = parser.borrow_mut();
|
||||
parser
|
||||
.cursors
|
||||
.pop()
|
||||
.unwrap_or_else(InactiveQueryCursor::new)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reuse_cursor(cursor: InactiveQueryCursor) {
|
||||
PARSER.with(|parser| {
|
||||
let mut parser = parser.borrow_mut();
|
||||
parser.cursors.push(cursor)
|
||||
})
|
||||
}
|
@ -0,0 +1,236 @@
|
||||
use core::slice;
|
||||
use std::iter::Peekable;
|
||||
use std::mem::replace;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use ropey::RopeSlice;
|
||||
|
||||
use crate::tree_sitter::{
|
||||
Capture, InactiveQueryCursor, Query, QueryCursor, RopeTsInput, SyntaxTreeNode,
|
||||
};
|
||||
use crate::{Injection, LayerId, Range, Syntax};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MatchedNode {
|
||||
pub capture: Capture,
|
||||
pub byte_range: Range,
|
||||
}
|
||||
|
||||
struct LayerQueryIter<'a> {
|
||||
cursor: QueryCursor<'a, 'a, RopeTsInput<'a>>,
|
||||
peeked: Option<MatchedNode>,
|
||||
}
|
||||
|
||||
impl<'a> LayerQueryIter<'a> {
|
||||
fn peek(&mut self) -> Option<&MatchedNode> {
|
||||
if self.peeked.is_none() {
|
||||
let (query_match, node_idx) = self.cursor.next_matched_node()?;
|
||||
let matched_node = query_match.matched_node(node_idx);
|
||||
self.peeked = Some(MatchedNode {
|
||||
capture: matched_node.capture,
|
||||
byte_range: matched_node.syntax_node.byte_range(),
|
||||
});
|
||||
}
|
||||
self.peeked.as_ref()
|
||||
}
|
||||
|
||||
fn consume(&mut self) -> MatchedNode {
|
||||
self.peeked.take().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
struct ActiveLayer<'a, S> {
|
||||
state: S,
|
||||
query_iter: LayerQueryIter<'a>,
|
||||
injections: Peekable<slice::Iter<'a, Injection>>,
|
||||
}
|
||||
|
||||
// data only needed when entering and exiting injections
|
||||
// seperate struck to keep the QueryIter reasonably small
|
||||
struct QueryIterLayerManager<'a, S> {
|
||||
query: &'a Query,
|
||||
node: SyntaxTreeNode<'a>,
|
||||
src: RopeSlice<'a>,
|
||||
syntax: &'a Syntax,
|
||||
active_layers: HashMap<LayerId, Box<ActiveLayer<'a, S>>>,
|
||||
active_injections: Vec<Injection>,
|
||||
}
|
||||
|
||||
impl<'a, S: Default> QueryIterLayerManager<'a, S> {
|
||||
fn init_layer(&mut self, injection: Injection) -> Box<ActiveLayer<'a, S>> {
|
||||
self.active_layers
|
||||
.remove(&injection.layer)
|
||||
.unwrap_or_else(|| {
|
||||
let layer = &self.syntax.layers[injection.layer];
|
||||
let injection_start = layer
|
||||
.injections
|
||||
.partition_point(|child| child.byte_range.start < injection.byte_range.start);
|
||||
let cursor = InactiveQueryCursor::new().execute_query(
|
||||
self.query,
|
||||
&self.node,
|
||||
RopeTsInput::new(self.src),
|
||||
);
|
||||
Box::new(ActiveLayer {
|
||||
state: S::default(),
|
||||
query_iter: LayerQueryIter {
|
||||
cursor,
|
||||
peeked: None,
|
||||
},
|
||||
injections: layer.injections[injection_start..].iter().peekable(),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QueryIter<'a, LayerState: Default = ()> {
|
||||
layer_manager: Box<QueryIterLayerManager<'a, LayerState>>,
|
||||
current_layer: Box<ActiveLayer<'a, LayerState>>,
|
||||
current_injection: Injection,
|
||||
}
|
||||
|
||||
impl<'a, LayerState: Default> QueryIter<'a, LayerState> {
|
||||
pub fn new(syntax: &'a Syntax, src: RopeSlice<'a>, query: &'a Query) -> Self {
|
||||
Self::at(syntax, src, query, syntax.tree().root_node(), syntax.root)
|
||||
}
|
||||
|
||||
pub fn at(
|
||||
syntax: &'a Syntax,
|
||||
src: RopeSlice<'a>,
|
||||
query: &'a Query,
|
||||
node: SyntaxTreeNode<'a>,
|
||||
layer: LayerId,
|
||||
) -> Self {
|
||||
// create fake injection for query root
|
||||
let injection = Injection {
|
||||
byte_range: node.byte_range(),
|
||||
layer,
|
||||
};
|
||||
let mut layer_manager = Box::new(QueryIterLayerManager {
|
||||
query,
|
||||
node,
|
||||
src,
|
||||
syntax,
|
||||
// TODO: reuse allocations with an allocation pool
|
||||
active_layers: HashMap::with_capacity(8),
|
||||
active_injections: Vec::with_capacity(8),
|
||||
});
|
||||
Self {
|
||||
current_layer: layer_manager.init_layer(injection),
|
||||
current_injection: injection,
|
||||
layer_manager,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_layer_state(&mut self) -> &mut LayerState {
|
||||
&mut self.current_layer.state
|
||||
}
|
||||
|
||||
pub fn layer_state(&mut self, layer: LayerId) -> &mut LayerState {
|
||||
if layer == self.current_injection.layer {
|
||||
self.current_layer_state()
|
||||
} else {
|
||||
&mut self
|
||||
.layer_manager
|
||||
.active_layers
|
||||
.get_mut(&layer)
|
||||
.unwrap()
|
||||
.state
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_injection(&mut self, injection: Injection) {
|
||||
let active_layer = self.layer_manager.init_layer(injection);
|
||||
let old_injection = replace(&mut self.current_injection, injection);
|
||||
let old_layer = replace(&mut self.current_layer, active_layer);
|
||||
self.layer_manager
|
||||
.active_layers
|
||||
.insert(old_injection.layer, old_layer);
|
||||
self.layer_manager.active_injections.push(old_injection);
|
||||
}
|
||||
|
||||
fn exit_injection(&mut self) -> Option<(Injection, Option<LayerState>)> {
|
||||
let injection = replace(
|
||||
&mut self.current_injection,
|
||||
self.layer_manager.active_injections.pop()?,
|
||||
);
|
||||
let layer = replace(
|
||||
&mut self.current_layer,
|
||||
self.layer_manager
|
||||
.active_layers
|
||||
.remove(&self.current_injection.layer)?,
|
||||
);
|
||||
let layer_unfinished = layer.query_iter.peeked.is_some();
|
||||
if layer_unfinished {
|
||||
self.layer_manager
|
||||
.active_layers
|
||||
.insert(injection.layer, layer)
|
||||
.unwrap();
|
||||
Some((injection, None))
|
||||
} else {
|
||||
Some((injection, Some(layer.state)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: Default> Iterator for QueryIter<'a, S> {
|
||||
type Item = QueryIterEvent<S>;
|
||||
|
||||
fn next(&mut self) -> Option<QueryIterEvent<S>> {
|
||||
loop {
|
||||
let next_injection = self.current_layer.injections.peek().filter(|injection| {
|
||||
injection.byte_range.start < self.current_injection.byte_range.end
|
||||
});
|
||||
let next_match = self.current_layer.query_iter.peek().filter(|matched_node| {
|
||||
matched_node.byte_range.start < self.current_injection.byte_range.end
|
||||
});
|
||||
|
||||
match (next_match, next_injection) {
|
||||
(None, None) => {
|
||||
return self.exit_injection().map(|(injection, state)| {
|
||||
QueryIterEvent::ExitInjection { injection, state }
|
||||
});
|
||||
}
|
||||
(Some(_), None) => {
|
||||
// consume match
|
||||
let matched_node = self.current_layer.query_iter.consume();
|
||||
return Some(QueryIterEvent::Match(matched_node));
|
||||
}
|
||||
(Some(matched_node), Some(injection))
|
||||
if matched_node.byte_range.start <= injection.byte_range.end =>
|
||||
{
|
||||
// consume match
|
||||
let matched_node = self.current_layer.query_iter.consume();
|
||||
// ignore nodes that are overlapped by the injection
|
||||
if matched_node.byte_range.start <= injection.byte_range.start {
|
||||
return Some(QueryIterEvent::Match(matched_node));
|
||||
}
|
||||
}
|
||||
(Some(_), Some(_)) | (None, Some(_)) => {
|
||||
// consume injection
|
||||
let injection = self.current_layer.injections.next().unwrap();
|
||||
self.enter_injection(injection.clone());
|
||||
return Some(QueryIterEvent::EnterInjection(injection.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum QueryIterEvent<State = ()> {
|
||||
EnterInjection(Injection),
|
||||
Match(MatchedNode),
|
||||
ExitInjection {
|
||||
injection: Injection,
|
||||
state: Option<State>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<S> QueryIterEvent<S> {
|
||||
pub fn start(&self) -> u32 {
|
||||
match self {
|
||||
QueryIterEvent::EnterInjection(injection) => injection.byte_range.start as u32,
|
||||
QueryIterEvent::Match(mat) => mat.byte_range.start as u32,
|
||||
QueryIterEvent::ExitInjection { injection, .. } => injection.byte_range.start as u32,
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
// TODO: rework using query iter
|
||||
|
||||
use std::iter;
|
||||
|
||||
use ropey::RopeSlice;
|
||||
|
||||
use crate::tree_sitter::{InactiveQueryCursor, Query, RopeTsInput, SyntaxTreeNode};
|
||||
use crate::TREE_SITTER_MATCH_LIMIT;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CapturedNode<'a> {
|
||||
Single(SyntaxTreeNode<'a>),
|
||||
/// Guaranteed to be not empty
|
||||
Grouped(Vec<SyntaxTreeNode<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> CapturedNode<'a> {
|
||||
pub fn start_byte(&self) -> usize {
|
||||
match self {
|
||||
Self::Single(n) => n.start_byte(),
|
||||
Self::Grouped(ns) => ns[0].start_byte(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_byte(&self) -> usize {
|
||||
match self {
|
||||
Self::Single(n) => n.end_byte(),
|
||||
Self::Grouped(ns) => ns.last().unwrap().end_byte(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TextObjectQuery {
|
||||
pub query: Query,
|
||||
}
|
||||
|
||||
impl TextObjectQuery {
|
||||
/// Run the query on the given node and return sub nodes which match given
|
||||
/// capture ("function.inside", "class.around", etc).
|
||||
///
|
||||
/// Captures may contain multiple nodes by using quantifiers (+, *, etc),
|
||||
/// and support for this is partial and could use improvement.
|
||||
///
|
||||
/// ```query
|
||||
/// (comment)+ @capture
|
||||
///
|
||||
/// ; OR
|
||||
/// (
|
||||
/// (comment)*
|
||||
/// .
|
||||
/// (function)
|
||||
/// ) @capture
|
||||
/// ```
|
||||
pub fn capture_nodes<'a>(
|
||||
&'a self,
|
||||
capture_name: &str,
|
||||
node: SyntaxTreeNode<'a>,
|
||||
slice: RopeSlice<'a>,
|
||||
cursor: InactiveQueryCursor,
|
||||
) -> Option<impl Iterator<Item = CapturedNode<'a>>> {
|
||||
self.capture_nodes_any(&[capture_name], node, slice, cursor)
|
||||
}
|
||||
|
||||
/// Find the first capture that exists out of all given `capture_names`
|
||||
/// and return sub nodes that match this capture.
|
||||
pub fn capture_nodes_any<'a>(
|
||||
&'a self,
|
||||
capture_names: &[&str],
|
||||
node: SyntaxTreeNode<'a>,
|
||||
slice: RopeSlice<'a>,
|
||||
mut cursor: InactiveQueryCursor,
|
||||
) -> Option<impl Iterator<Item = CapturedNode<'a>>> {
|
||||
let capture = capture_names
|
||||
.iter()
|
||||
.find_map(|cap| self.query.get_capture(cap))?;
|
||||
|
||||
cursor.set_match_limit(TREE_SITTER_MATCH_LIMIT);
|
||||
let mut cursor = cursor.execute_query(&self.query, &node, RopeTsInput::new(slice));
|
||||
let capture_node = iter::from_fn(move || {
|
||||
let (mat, _) = cursor.next_matched_node()?;
|
||||
Some(mat.nodes_for_capture(capture).cloned().collect())
|
||||
})
|
||||
.filter_map(move |nodes: Vec<_>| {
|
||||
if nodes.len() > 1 {
|
||||
Some(CapturedNode::Grouped(nodes))
|
||||
} else {
|
||||
nodes.into_iter().map(CapturedNode::Single).next()
|
||||
}
|
||||
});
|
||||
Some(capture_node)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue