Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-syntax/src/injections_tree.rs')
| -rw-r--r-- | helix-syntax/src/injections_tree.rs | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/helix-syntax/src/injections_tree.rs b/helix-syntax/src/injections_tree.rs new file mode 100644 index 00000000..e181a775 --- /dev/null +++ b/helix-syntax/src/injections_tree.rs @@ -0,0 +1,94 @@ +use core::slice; +use std::iter::Peekable; +use std::sync::Arc; + +use hashbrown::HashMap; +use slotmap::{new_key_type, HopSlotMap, SlotMap}; +use tree_sitter::Tree; + +use crate::parse::LayerUpdateFlags; +use crate::{HighlightConfiguration, RopeProvider}; + +// TODO(perf): replace std::ops::Range with helix_core::Range 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<Tree>, + /// internal flags used during parsing to track incremental invalidation + pub(crate) flags: LayerUpdateFlags, + 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) injection_ranges: Box<[InjectionRange]>, +} + +#[derive(Debug)] +pub(crate) struct InjectionRange { + 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<&InjectionRange> { + let i = self + .injection_ranges + .partition_point(|range| range.byte_range.start <= idx); + self.injection_ranges + .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 + } +} + +struct ActiveInjection<'a> { + injections: Peekable<slice::Iter<'a, InjectionTree>>, + range: InjectionRange, +} + +struct ActiveLayer<'a, State> { + state: State, + /// the query captures just for this layer + layer_captures: Peekable<LayerQueryCaptures<'a>>, +} + +type LayerQueryCaptures<'a> = tree_sitter::QueryCaptures<'a, 'a, RopeProvider<'a>, &'a [u8]>; + +pub struct QueryCaptures<'a> { + active_layers: HashMap<LayerId, ActiveLayer<'a, ()>>, + active_injections: Vec<ActiveInjection<'a>>, +} |