1use std::collections::{BTreeMap, BTreeSet, VecDeque};
2use std::sync::Arc;
3
4use crate::{
5 tree_sitter_adapter, ByteRange, Link, LinkId, LinkMetadata, LinkNetwork, LinkType,
6 ParseConfiguration,
7};
8
9impl LinkNetwork {
10 pub fn apply_edit(&mut self, range: ByteRange, replacement: &str) -> bool {
16 self.apply_edit_with_configuration(range, replacement, ParseConfiguration::default())
17 }
18
19 pub fn apply_edit_with_configuration(
24 &mut self,
25 range: ByteRange,
26 replacement: &str,
27 configuration: ParseConfiguration,
28 ) -> bool {
29 let old_text = self.reconstruct_text();
30 let Some(edited_text) = apply_text_edit(&old_text, range, replacement) else {
31 return false;
32 };
33 let Some(language) = document_language(self).map(ToOwned::to_owned) else {
34 return false;
35 };
36
37 let reparsed = tree_sitter_adapter::parse_incremental(
38 &old_text,
39 range,
40 replacement,
41 &language,
42 configuration,
43 )
44 .unwrap_or_else(|| Self::parse(&edited_text, &language, configuration));
45
46 let edit = AppliedEdit::new(range, replacement.len());
47 *self = remap_reparsed_network(self, &reparsed, edit);
48 true
49 }
50}
51
52fn document_language(network: &LinkNetwork) -> Option<&str> {
53 network
54 .links()
55 .find(|link| link.metadata().link_type() == Some(LinkType::Document))
56 .and_then(|link| link.metadata().language())
57}
58
59fn remap_reparsed_network(
60 old: &LinkNetwork,
61 reparsed: &LinkNetwork,
62 edit: AppliedEdit,
63) -> LinkNetwork {
64 let mut id_map = stable_id_map(old, reparsed, edit);
65 let mut used_targets = id_map.values().copied().collect::<BTreeSet<_>>();
66 let mut next_id = old.next_id.max(reparsed.next_id);
67
68 for link in reparsed.links() {
69 if id_map.contains_key(&link.id()) {
70 continue;
71 }
72
73 let target = if used_targets.contains(&link.id()) {
74 let fresh = next_unused_id(&mut next_id, &used_targets);
75 used_targets.insert(fresh);
76 fresh
77 } else {
78 used_targets.insert(link.id());
79 link.id()
80 };
81 id_map.insert(link.id(), target);
82 }
83
84 let mut links = BTreeMap::new();
85 for link in reparsed.links() {
86 let id = id_map[&link.id()];
87 let references = link
88 .references()
89 .iter()
90 .map(|reference| id_map[reference])
91 .collect::<Vec<_>>();
92 let candidate = Link {
93 id,
94 references: Arc::from(references),
95 metadata: link.metadata().clone(),
96 };
97
98 if let Some(shared) = old.links.get(&id) {
99 if shared.as_ref() == &candidate {
100 links.insert(id, Arc::clone(shared));
101 continue;
102 }
103 }
104
105 links.insert(id, Arc::new(candidate));
106 }
107
108 let terms = reparsed
109 .terms
110 .iter()
111 .filter_map(|(term, id)| id_map.get(id).map(|mapped| (Arc::clone(term), *mapped)))
112 .collect();
113 let next_id = used_targets
114 .iter()
115 .map(|id| id.as_u64())
116 .max()
117 .map_or(1, |id| id + 1);
118
119 LinkNetwork {
120 next_id,
121 links,
122 terms,
123 concept_syntax: reparsed.concept_syntax.clone(),
124 strings: reparsed.strings.clone(),
125 }
126}
127
128fn stable_id_map(
129 old: &LinkNetwork,
130 reparsed: &LinkNetwork,
131 edit: AppliedEdit,
132) -> BTreeMap<LinkId, LinkId> {
133 let mut old_links = OldLinkIndex::new(old, edit);
134 let mut mapped = BTreeMap::new();
135
136 for new_link in reparsed.links() {
137 let key = MatchKey::of(new_link.metadata());
138 let matched = match new_link.metadata().span() {
139 Some(span) => old_links.take_spanned(key, span.byte_range()),
140 None => old_links.take_unspanned(key, new_link),
141 };
142 if let Some(old_id) = matched {
143 mapped.insert(new_link.id(), old_id);
144 }
145 }
146
147 let mut pending = reparsed
151 .links()
152 .filter(|link| link.metadata().span().is_none() && !mapped.contains_key(&link.id()))
153 .collect::<Vec<_>>();
154 while !pending.is_empty() {
155 let remaining = pending.len();
156 let mut unmatched = Vec::with_capacity(remaining);
157 for new_link in pending {
158 let matched = new_link
159 .references()
160 .iter()
161 .map(|reference| mapped.get(reference).copied())
162 .collect::<Option<Vec<_>>>()
163 .and_then(|references| {
164 old_links.take_referencing(MatchKey::of(new_link.metadata()), references)
165 });
166 match matched {
167 Some(old_id) => {
168 mapped.insert(new_link.id(), old_id);
169 }
170 None => unmatched.push(new_link),
171 }
172 }
173 if unmatched.len() == remaining {
174 break;
175 }
176 pending = unmatched;
177 }
178
179 mapped
180}
181
182#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
185struct MatchKey<'a> {
186 link_type: Option<LinkType>,
187 named: bool,
188 term: Option<&'a str>,
189 definition: Option<&'a str>,
190 language: Option<&'a str>,
191 flags: (bool, bool, bool, bool),
194}
195
196impl<'a> MatchKey<'a> {
197 fn of(metadata: &'a LinkMetadata) -> Self {
198 let flags = metadata.flags();
199 Self {
200 link_type: metadata.link_type(),
201 named: metadata.is_named(),
202 term: metadata.term(),
203 definition: metadata.definition(),
204 language: metadata.language(),
205 flags: (
206 flags.is_error(),
207 flags.has_error(),
208 flags.is_missing(),
209 flags.is_extra(),
210 ),
211 }
212 }
213}
214
215struct OldLinkIndex<'a> {
224 spanned: BTreeMap<(MatchKey<'a>, usize, usize), VecDeque<LinkId>>,
226 referencing: BTreeMap<(MatchKey<'a>, Vec<LinkId>), VecDeque<LinkId>>,
228 self_referencing: BTreeMap<MatchKey<'a>, VecDeque<LinkId>>,
230 used: BTreeSet<LinkId>,
232}
233
234impl<'a> OldLinkIndex<'a> {
235 fn new(old: &'a LinkNetwork, edit: AppliedEdit) -> Self {
236 let mut index = Self {
237 spanned: BTreeMap::new(),
238 referencing: BTreeMap::new(),
239 self_referencing: BTreeMap::new(),
240 used: BTreeSet::new(),
241 };
242
243 for link in old.links() {
244 let key = MatchKey::of(link.metadata());
245 if let Some(span) = link.metadata().span() {
246 if let Some(range) = edit.adjusted_range(span.byte_range()) {
248 index
249 .spanned
250 .entry((key, range.start(), range.end()))
251 .or_default()
252 .push_back(link.id());
253 }
254 continue;
255 }
256
257 index
258 .referencing
259 .entry((key, link.references().to_vec()))
260 .or_default()
261 .push_back(link.id());
262 if is_self_reference(link) {
263 index
264 .self_referencing
265 .entry(key)
266 .or_default()
267 .push_back(link.id());
268 }
269 }
270
271 index
272 }
273
274 fn take_spanned(&mut self, key: MatchKey<'a>, range: ByteRange) -> Option<LinkId> {
276 let matched = Self::front(
277 self.spanned.get_mut(&(key, range.start(), range.end())),
278 &self.used,
279 )?;
280 self.used.insert(matched);
281 Some(matched)
282 }
283
284 fn take_unspanned(&mut self, key: MatchKey<'a>, new_link: &Link) -> Option<LinkId> {
288 let by_references = Self::front(
289 self.referencing
290 .get_mut(&(key, new_link.references().to_vec())),
291 &self.used,
292 );
293 let by_self_reference = if is_self_reference(new_link) {
294 Self::front(self.self_referencing.get_mut(&key), &self.used)
295 } else {
296 None
297 };
298
299 let matched = by_references.into_iter().chain(by_self_reference).min()?;
302 self.used.insert(matched);
303 Some(matched)
304 }
305
306 fn take_referencing(&mut self, key: MatchKey<'a>, references: Vec<LinkId>) -> Option<LinkId> {
309 let matched = Self::front(self.referencing.get_mut(&(key, references)), &self.used)?;
310 self.used.insert(matched);
311 Some(matched)
312 }
313
314 fn front(group: Option<&mut VecDeque<LinkId>>, used: &BTreeSet<LinkId>) -> Option<LinkId> {
316 let group = group?;
317 while group.front().is_some_and(|id| used.contains(id)) {
318 group.pop_front();
319 }
320 group.front().copied()
321 }
322}
323
324fn apply_text_edit(old_text: &str, range: ByteRange, replacement: &str) -> Option<String> {
325 if range.end() > old_text.len()
326 || !old_text.is_char_boundary(range.start())
327 || !old_text.is_char_boundary(range.end())
328 {
329 return None;
330 }
331
332 let mut edited =
333 String::with_capacity(old_text.len() - (range.end() - range.start()) + replacement.len());
334 edited.push_str(&old_text[..range.start()]);
335 edited.push_str(replacement);
336 edited.push_str(&old_text[range.end()..]);
337 Some(edited)
338}
339
340fn next_unused_id(next_id: &mut u64, used_targets: &BTreeSet<LinkId>) -> LinkId {
341 loop {
342 let candidate = LinkId(*next_id);
343 *next_id += 1;
344 if !used_targets.contains(&candidate) {
345 return candidate;
346 }
347 }
348}
349
350fn is_self_reference(link: &Link) -> bool {
351 link.references() == [link.id()]
352}
353
354#[derive(Clone, Copy, Debug, PartialEq, Eq)]
355struct AppliedEdit {
356 old_start: usize,
357 old_end: usize,
358 new_end: usize,
359}
360
361impl AppliedEdit {
362 const fn new(range: ByteRange, replacement_len: usize) -> Self {
363 Self {
364 old_start: range.start(),
365 old_end: range.end(),
366 new_end: range.start() + replacement_len,
367 }
368 }
369
370 const fn adjusted_range(self, range: ByteRange) -> Option<ByteRange> {
371 if range.end() <= self.old_start {
372 return Some(range);
373 }
374 if range.start() < self.old_end {
375 return None;
376 }
377
378 let start = shift_byte(range.start(), self.old_end, self.new_end);
379 let end = shift_byte(range.end(), self.old_end, self.new_end);
380 Some(ByteRange::new(start, end))
381 }
382}
383
384const fn shift_byte(byte: usize, old_end: usize, new_end: usize) -> usize {
385 if new_end >= old_end {
386 byte + (new_end - old_end)
387 } else {
388 byte - (old_end - new_end)
389 }
390}