Skip to main content

comfit_core/
syntax.rs

1//! Parse source and update root and embedded-language trees after edits.
2
3use std::{
4    collections::{HashMap, HashSet, VecDeque},
5    ops::Range,
6};
7
8use crate::{Error, backend::Backend, language::Registry};
9
10mod injection;
11mod stream;
12
13pub(crate) use stream::walk;
14
15/// A source edit covering all changes, using UTF-8 byte offsets and zero-based byte columns.
16#[derive(Debug, Clone, Copy)]
17pub struct Edit {
18    pub start_byte: usize,
19    pub old_end_byte: usize,
20    pub new_end_byte: usize,
21    pub start_position: (usize, usize),
22    pub old_end_position: (usize, usize),
23    pub new_end_position: (usize, usize),
24}
25
26impl Edit {
27    pub(crate) fn between(old: &str, new: &str) -> Self {
28        let mut start = old
29            .bytes()
30            .zip(new.bytes())
31            .take_while(|(a, b)| a == b)
32            .count();
33        while !old.is_char_boundary(start) || !new.is_char_boundary(start) {
34            start -= 1;
35        }
36        let mut suffix = old[start..]
37            .bytes()
38            .rev()
39            .zip(new[start..].bytes().rev())
40            .take_while(|(a, b)| a == b)
41            .count();
42        while !old.is_char_boundary(old.len() - suffix) || !new.is_char_boundary(new.len() - suffix)
43        {
44            suffix -= 1;
45        }
46        let position = |source: &str, end: usize| {
47            let prefix = &source[..end];
48            (
49                prefix.bytes().filter(|byte| *byte == b'\n').count(),
50                end - prefix.rfind('\n').map_or(0, |index| index + 1),
51            )
52        };
53        Self {
54            start_byte: start,
55            old_end_byte: old.len() - suffix,
56            new_end_byte: new.len() - suffix,
57            start_position: position(old, start),
58            old_end_position: position(old, old.len() - suffix),
59            new_end_position: position(new, new.len() - suffix),
60        }
61    }
62}
63
64pub(crate) struct Layer<T> {
65    pub language: String,
66    pub ranges: Vec<Range<usize>>,
67    pub parent: Option<usize>,
68    pub depth: usize,
69    pub tree: T,
70}
71
72/// Borrowed syntax data used by both document and one-shot highlighting.
73pub(crate) struct LayerView<'a, T> {
74    pub language: &'a str,
75    pub ranges: &'a [Range<usize>],
76    pub depth: usize,
77    pub tree: &'a T,
78}
79
80impl<T> Layer<T> {
81    pub fn view(&self) -> LayerView<'_, T> {
82        LayerView {
83            language: &self.language,
84            ranges: &self.ranges,
85            depth: self.depth,
86            tree: &self.tree,
87        }
88    }
89
90    pub(crate) fn adjust_ranges(&mut self, edit: &Edit) -> bool {
91        for range in &mut self.ranges {
92            if edit.old_end_byte < range.start {
93                range.start = range.start - edit.old_end_byte + edit.new_end_byte;
94                range.end = range.end - edit.old_end_byte + edit.new_end_byte;
95            } else if edit.start_byte > range.start && edit.old_end_byte < range.end {
96                range.end = range.end - edit.old_end_byte + edit.new_end_byte;
97            } else if edit.start_byte <= range.end {
98                // Boundary edits can change which language owns the content.
99                return false;
100            }
101        }
102        true
103    }
104}
105
106/// Root and injection layers in parent-first order. The root is always at index zero.
107pub(crate) struct Syntax<B: Backend> {
108    pub layers: Vec<Layer<B::Tree>>,
109}
110
111struct ParseRequest<'a> {
112    language: &'a str,
113    ranges: Vec<Range<usize>>,
114    parent: Option<usize>,
115    previous_parent: Option<usize>,
116    depth: usize,
117}
118
119impl<B: Backend> Syntax<B> {
120    pub fn parse(
121        backend: &mut B,
122        registry: &Registry<B>,
123        name: &str,
124        source: &str,
125        previous: Option<Self>,
126        edit: Option<&Edit>,
127    ) -> Result<Self, Error> {
128        let language = registry.resolve(name)?;
129        backend.prepare_source(source)?;
130        let mut previous: HashMap<_, _> = previous
131            .into_iter()
132            .flat_map(|syntax| syntax.layers)
133            .enumerate()
134            .filter_map(|(index, mut layer)| {
135                if layer.parent.is_none() {
136                    layer.ranges = std::iter::once(0..source.len()).collect();
137                } else if edit.is_some_and(|edit| !layer.adjust_ranges(edit)) {
138                    return None;
139                }
140                Some((
141                    (layer.parent, layer.language, layer.ranges),
142                    (index, layer.tree),
143                ))
144            })
145            .collect();
146        let mut requests = VecDeque::from([ParseRequest {
147            language,
148            ranges: std::iter::once(0..source.len()).collect(),
149            parent: None,
150            previous_parent: None,
151            depth: 0,
152        }]);
153        let mut seen = HashSet::new();
154        let mut layers: Vec<Layer<B::Tree>> = Vec::new();
155
156        while let Some(request) = requests.pop_front() {
157            if request.depth > 32 {
158                return Err(Error::InjectionDepth);
159            }
160            if !seen.insert((request.language, request.ranges.clone())) {
161                continue;
162            }
163            let (raw, language) = registry.language(backend, request.language)?;
164            let previous = if request.parent.is_none() {
165                previous.remove(&(None, request.language.to_owned(), request.ranges.clone()))
166            } else {
167                request.previous_parent.and_then(|parent| {
168                    previous.remove(&(
169                        Some(parent),
170                        request.language.to_owned(),
171                        request.ranges.clone(),
172                    ))
173                })
174            };
175            let previous_index = previous.as_ref().map(|(index, _)| *index);
176            let tree = backend.parse_prepared(
177                raw,
178                source,
179                &request.ranges,
180                previous.map(|(_, tree)| tree),
181                edit,
182            )?;
183            let matches = language.injections.matches(backend, &tree, source)?;
184            let index = layers.len();
185            let parent_language = request
186                .parent
187                .map(|parent| layers[parent].language.as_str());
188            for injection in injection::collect(
189                &language.injections,
190                &matches,
191                source,
192                &request.ranges,
193                request.language,
194                parent_language,
195            ) {
196                if let Some((target, _)) = registry.get(injection.language) {
197                    requests.push_back(ParseRequest {
198                        language: target,
199                        ranges: injection.ranges,
200                        parent: Some(index),
201                        previous_parent: previous_index,
202                        depth: request.depth + 1,
203                    });
204                }
205            }
206            layers.push(Layer {
207                language: request.language.to_owned(),
208                ranges: request.ranges,
209                parent: request.parent,
210                depth: request.depth,
211                tree,
212            });
213        }
214        Ok(Self { layers })
215    }
216}