1use std::ops::Range;
34
35use rowan::{GreenNode, GreenToken, TextRange, TextSize};
36
37use crate::parser::bracket_balancer::rebalance_brackets;
38use crate::parser::core::ParseOptions;
39use crate::parser::diagnostics::ParseDiagnostic;
40use crate::parser::expr::parse_expr;
41use crate::parser::lexer::lex_with_md;
42use crate::parser::tree_builder::{build_tree, syntax_kind_for};
43use crate::syntax::{SyntaxKind, SyntaxNode};
44
45#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct Edit {
49 pub range: Range<usize>,
50 pub insert: String,
51}
52
53impl Edit {
54 fn delta(&self) -> isize {
56 self.insert.len() as isize - (self.range.end - self.range.start) as isize
57 }
58
59 pub fn apply(&self, old: &str) -> String {
61 let mut out =
62 String::with_capacity(old.len().saturating_sub(self.range.len()) + self.insert.len());
63 out.push_str(&old[..self.range.start]);
64 out.push_str(&self.insert);
65 out.push_str(&old[self.range.end..]);
66 out
67 }
68}
69
70pub fn apply_edits(old: &str, edits: &[Edit]) -> String {
79 edits.iter().fold(old.to_string(), |t, e| e.apply(&t))
80}
81
82pub fn map_range_through_edit(range: TextRange, edit: &Edit) -> Option<TextRange> {
101 let node_start = usize::from(range.start());
102 let node_end = usize::from(range.end());
103
104 if node_end <= edit.range.start {
105 Some(range)
107 } else if node_start >= edit.range.end {
108 let start = (node_start as isize + edit.delta()) as usize;
110 let end = (node_end as isize + edit.delta()) as usize;
111 Some(text_range(start, end))
112 } else {
113 None
115 }
116}
117
118pub fn map_range_through_edits(range: TextRange, edits: &[Edit]) -> Option<TextRange> {
123 edits.iter().try_fold(range, map_range_through_edit)
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128pub enum ReparseKind {
129 Token,
130 Block,
131 TopLevel,
132 Multi,
136}
137
138#[derive(Debug, Clone)]
141pub struct Reparsed {
142 pub green: GreenNode,
143 pub diagnostics: Vec<ParseDiagnostic>,
144 pub kind: ReparseKind,
145}
146
147pub fn diff_edit(old: &str, new: &str) -> Edit {
152 let ob = old.as_bytes();
153 let nb = new.as_bytes();
154
155 let mut prefix = 0;
156 let max_prefix = ob.len().min(nb.len());
157 while prefix < max_prefix && ob[prefix] == nb[prefix] {
158 prefix += 1;
159 }
160 while prefix > 0 && !old.is_char_boundary(prefix) {
161 prefix -= 1;
162 }
163
164 let mut suffix = 0;
165 let max_suffix = (ob.len() - prefix).min(nb.len() - prefix);
166 while suffix < max_suffix && ob[ob.len() - 1 - suffix] == nb[nb.len() - 1 - suffix] {
167 suffix += 1;
168 }
169 while suffix > 0
170 && (!old.is_char_boundary(old.len() - suffix) || !new.is_char_boundary(new.len() - suffix))
171 {
172 suffix -= 1;
173 }
174
175 Edit {
176 range: prefix..(old.len() - suffix),
177 insert: new[prefix..(new.len() - suffix)].to_string(),
178 }
179}
180
181pub fn reparse(
185 old_root: &SyntaxNode,
186 old_text: &str,
187 old_diags: &[ParseDiagnostic],
188 edit: &Edit,
189) -> Option<Reparsed> {
190 reparse_with_options(
191 old_root,
192 old_text,
193 old_diags,
194 edit,
195 &ParseOptions::default(),
196 )
197}
198
199pub fn reparse_with_options(
205 old_root: &SyntaxNode,
206 old_text: &str,
207 old_diags: &[ParseDiagnostic],
208 edit: &Edit,
209 options: &ParseOptions,
210) -> Option<Reparsed> {
211 let md = options.roxygen_markdown_default;
212 let result = reparse_token(old_root, old_text, old_diags, edit, md)
213 .or_else(|| reparse_block(old_root, old_text, old_diags, edit, md))
214 .or_else(|| reparse_toplevel(old_root, old_text, old_diags, edit, md));
215
216 if let Some(reparsed) = &result {
217 debug_assert_eq!(
218 SyntaxNode::new_root(reparsed.green.clone())
219 .text()
220 .to_string(),
221 edit.apply(old_text),
222 "incremental reparse ({:?}) is not lossless",
223 reparsed.kind,
224 );
225 }
226 result
227}
228
229pub fn reparse_edits(
245 old_root: &SyntaxNode,
246 old_text: &str,
247 old_diags: &[ParseDiagnostic],
248 edits: &[Edit],
249 target: &str,
250) -> Option<Reparsed> {
251 reparse_edits_with_options(
252 old_root,
253 old_text,
254 old_diags,
255 edits,
256 target,
257 &ParseOptions::default(),
258 )
259}
260
261pub fn reparse_edits_with_options(
264 old_root: &SyntaxNode,
265 old_text: &str,
266 old_diags: &[ParseDiagnostic],
267 edits: &[Edit],
268 target: &str,
269 options: &ParseOptions,
270) -> Option<Reparsed> {
271 let (first, rest) = edits.split_first()?;
272
273 let mut reparsed = reparse_with_options(old_root, old_text, old_diags, first, options)?;
275 let mut text = first.apply(old_text);
276 for edit in rest {
277 let root = SyntaxNode::new_root(reparsed.green.clone());
278 reparsed = reparse_with_options(&root, &text, &reparsed.diagnostics, edit, options)?;
279 text = edit.apply(&text);
280 }
281
282 if text != target {
284 return None;
285 }
286 reparsed.kind = ReparseKind::Multi;
287 Some(reparsed)
288}
289
290const TOKEN_REPARSE_KINDS: &[SyntaxKind] = &[
291 SyntaxKind::IDENT,
292 SyntaxKind::STRING,
293 SyntaxKind::COMMENT,
294 SyntaxKind::WHITESPACE,
295 ];
301
302fn reparse_token(
303 old_root: &SyntaxNode,
304 old_text: &str,
305 old_diags: &[ParseDiagnostic],
306 edit: &Edit,
307 md_default: bool,
308) -> Option<Reparsed> {
309 let (s, e) = (edit.range.start, edit.range.end);
310 let elem = old_root.covering_element(text_range(s, e));
311 let token = elem.into_token()?;
312 if !TOKEN_REPARSE_KINDS.contains(&token.kind()) {
313 return None;
314 }
315
316 let tr = token.text_range();
317 let (t0, t1) = (usize::from(tr.start()), usize::from(tr.end()));
318 if s == e && !(t0 < s && s < t1) {
323 return None;
324 }
325
326 let mut new_text = token.text().to_string();
328 new_text.replace_range((s - t0)..(e - t0), &edit.insert);
329
330 let relexed = lex_with_md(&new_text, md_default);
332 let [only] = relexed.as_slice() else {
333 return None;
334 };
335 if only.start != 0 || only.end != new_text.len() || syntax_kind_for(&only.kind) != token.kind()
336 {
337 return None;
338 }
339
340 if let Some(next_char) = old_text[t1..].chars().next() {
344 let mut probe = new_text.clone();
345 probe.push(next_char);
346 let probe_toks = lex_with_md(&probe, md_default);
347 let first = probe_toks.first()?;
348 if first.end != new_text.len() || syntax_kind_for(&first.kind) != token.kind() {
349 return None;
350 }
351 }
352
353 if old_diags.iter().any(|d| d.start < t1 && d.end > t0) {
356 return None;
357 }
358 let delta = edit.delta();
359 let diagnostics = old_diags
360 .iter()
361 .map(|d| {
362 if d.start >= t1 {
363 shift(d, delta)
364 } else {
365 d.clone()
366 }
367 })
368 .collect();
369
370 let green_token = GreenToken::new(rowan::SyntaxKind::from(token.kind()), &new_text);
371 let green = token.replace_with(green_token);
372 Some(Reparsed {
373 green,
374 diagnostics,
375 kind: ReparseKind::Token,
376 })
377}
378
379fn reparse_block(
380 old_root: &SyntaxNode,
381 old_text: &str,
382 old_diags: &[ParseDiagnostic],
383 edit: &Edit,
384 md_default: bool,
385) -> Option<Reparsed> {
386 let (s, e) = (edit.range.start, edit.range.end);
387 let elem = old_root.covering_element(text_range(s, e));
388 let start_node = match elem {
389 rowan::NodeOrToken::Node(n) => n,
390 rowan::NodeOrToken::Token(t) => t.parent()?,
391 };
392
393 let block = start_node.ancestors().find(|node| {
396 if node.kind() != SyntaxKind::BLOCK_EXPR {
397 return false;
398 }
399 let r = node.text_range();
400 let (bstart, bend) = (usize::from(r.start()), usize::from(r.end()));
401 bend.saturating_sub(bstart) >= 2 && s > bstart && e < bend
403 })?;
404
405 let r = block.text_range();
406 let (bstart, bend) = (usize::from(r.start()), usize::from(r.end()));
407
408 let mut block_text = old_text[bstart..bend].to_string();
411 block_text.replace_range((s - bstart)..(e - bstart), &edit.insert);
412
413 let (block_green, block_diags) = parse_block_in_isolation(&block_text, md_default)?;
414
415 let delta = edit.delta();
416 let mut diagnostics: Vec<ParseDiagnostic> =
417 Vec::with_capacity(old_diags.len() + block_diags.len());
418 for d in old_diags {
419 if d.end <= bstart {
420 diagnostics.push(d.clone());
421 } else if d.start >= bend {
422 diagnostics.push(shift(d, delta));
423 }
424 }
426 for d in &block_diags {
427 diagnostics.push(ParseDiagnostic {
428 message: d.message.clone(),
429 start: d.start + bstart,
430 end: d.end + bstart,
431 });
432 }
433 diagnostics.sort_by_key(|d| (d.start, d.end));
434
435 let green = block.replace_with(block_green);
436 Some(Reparsed {
437 green,
438 diagnostics,
439 kind: ReparseKind::Block,
440 })
441}
442
443fn reparse_toplevel(
446 old_root: &SyntaxNode,
447 old_text: &str,
448 old_diags: &[ParseDiagnostic],
449 edit: &Edit,
450 md_default: bool,
451) -> Option<Reparsed> {
452 let (s, e) = (edit.range.start, edit.range.end);
453 let elem = old_root.covering_element(text_range(s, e));
454 let start_node = match elem {
455 rowan::NodeOrToken::Node(n) => n,
456 rowan::NodeOrToken::Token(t) => t.parent()?,
457 };
458
459 let stmt = start_node
464 .ancestors()
465 .find(|node| node.parent().map(|p| p.kind()) == Some(SyntaxKind::ROOT))?;
466
467 if stmt.kind() == SyntaxKind::ROXYGEN_BLOCK {
471 return None;
472 }
473
474 let r = stmt.text_range();
475 let (ss, se) = (usize::from(r.start()), usize::from(r.end()));
476 debug_assert!(ss <= s && e <= se);
478
479 let mut stmt_text = old_text[ss..se].to_string();
483 stmt_text.replace_range((s - ss)..(e - ss), &edit.insert);
484
485 let (stmt_green, stmt_diags) = parse_stmt_in_isolation(&stmt_text, stmt.kind(), md_default)?;
489
490 let forward_end = match stmt.next_sibling() {
498 Some(next) => usize::from(next.text_range().end()),
499 None => old_text.len(),
500 };
501 if !toplevel_boundary_stable(&stmt_text, &old_text[se..forward_end], md_default) {
502 return None;
503 }
504
505 let delta = edit.delta();
506 let mut diagnostics: Vec<ParseDiagnostic> =
507 Vec::with_capacity(old_diags.len() + stmt_diags.len());
508 for d in old_diags {
509 if d.end <= ss {
510 diagnostics.push(d.clone());
511 } else if d.start >= se {
512 diagnostics.push(shift(d, delta));
513 }
514 }
516 for d in &stmt_diags {
517 diagnostics.push(ParseDiagnostic {
518 message: d.message.clone(),
519 start: d.start + ss,
520 end: d.end + ss,
521 });
522 }
523 diagnostics.sort_by_key(|d| (d.start, d.end));
524
525 let green = stmt.replace_with(stmt_green);
526 Some(Reparsed {
527 green,
528 diagnostics,
529 kind: ReparseKind::TopLevel,
530 })
531}
532
533fn parse_stmt_in_isolation(
538 stmt_text: &str,
539 expected_kind: SyntaxKind,
540 md_default: bool,
541) -> Option<(GreenNode, Vec<ParseDiagnostic>)> {
542 let tokens = rebalance_brackets(lex_with_md(stmt_text, md_default));
543 let mut diagnostics = Vec::new();
544 let expr = parse_expr(&tokens, 0, 0, &mut diagnostics, md_default)?;
545 if expr.start != 0 || expr.end != tokens.len() {
546 return None;
547 }
548
549 let root = build_tree(&tokens, &expr.events);
550 let mut children = root.children();
551 let node = children.next()?;
552 if children.next().is_some() || node.kind() != expected_kind {
553 return None;
554 }
555 Some((node.green().to_owned(), diagnostics))
556}
557
558fn toplevel_boundary_stable(stmt_text: &str, forward_ctx: &str, md_default: bool) -> bool {
564 if forward_ctx.is_empty() {
565 return true;
566 }
567 let mut combined = String::with_capacity(stmt_text.len() + forward_ctx.len());
568 combined.push_str(stmt_text);
569 combined.push_str(forward_ctx);
570
571 let tokens = rebalance_brackets(lex_with_md(&combined, md_default));
572 let mut diagnostics = Vec::new();
573 let Some(expr) = parse_expr(&tokens, 0, 0, &mut diagnostics, md_default) else {
574 return false;
575 };
576 expr.end
579 .checked_sub(1)
580 .and_then(|last| tokens.get(last))
581 .is_some_and(|last| last.end == stmt_text.len())
582}
583
584fn parse_block_in_isolation(
589 block_text: &str,
590 md_default: bool,
591) -> Option<(GreenNode, Vec<ParseDiagnostic>)> {
592 let tokens = rebalance_brackets(lex_with_md(block_text, md_default));
593 let mut diagnostics = Vec::new();
594 let expr = parse_expr(&tokens, 0, 0, &mut diagnostics, md_default)?;
595 if expr.start != 0 || expr.end != tokens.len() {
596 return None;
597 }
598
599 let root = build_tree(&tokens, &expr.events);
600 let mut children = root.children();
601 let block = children.next()?;
602 if children.next().is_some() || block.kind() != SyntaxKind::BLOCK_EXPR {
603 return None;
604 }
605
606 let first = block.first_child_or_token().map(|e| e.kind());
612 let last = block.last_child_or_token().map(|e| e.kind());
613 if first != Some(SyntaxKind::LBRACE) || last != Some(SyntaxKind::RBRACE) {
614 return None;
615 }
616
617 Some((block.green().to_owned(), diagnostics))
618}
619
620fn shift(d: &ParseDiagnostic, delta: isize) -> ParseDiagnostic {
621 ParseDiagnostic {
622 message: d.message.clone(),
623 start: (d.start as isize + delta) as usize,
624 end: (d.end as isize + delta) as usize,
625 }
626}
627
628fn text_range(start: usize, end: usize) -> TextRange {
629 TextRange::new(TextSize::new(start as u32), TextSize::new(end as u32))
630}
631
632#[cfg(test)]
633mod map_range_tests {
634 use super::*;
635
636 fn edit(start: usize, end: usize, insert: &str) -> Edit {
637 Edit {
638 range: start..end,
639 insert: insert.to_string(),
640 }
641 }
642
643 #[test]
644 fn node_before_edit_is_unchanged() {
645 let node = text_range(0, 5);
646 assert_eq!(
647 map_range_through_edit(node, &edit(10, 12, "xxxx")),
648 Some(node)
649 );
650 }
651
652 #[test]
653 fn node_after_insertion_shifts_by_delta() {
654 let node = text_range(10, 15);
655 assert_eq!(
657 map_range_through_edit(node, &edit(0, 2, "xxxx")),
658 Some(text_range(12, 17))
659 );
660 }
661
662 #[test]
663 fn node_after_deletion_shifts_back() {
664 let node = text_range(10, 15);
665 assert_eq!(
666 map_range_through_edit(node, &edit(0, 4, "")),
667 Some(text_range(6, 11))
668 );
669 }
670
671 #[test]
672 fn edit_ending_at_node_start_shifts_node() {
673 let node = text_range(10, 15);
675 assert_eq!(
676 map_range_through_edit(node, &edit(8, 10, "abcd")),
677 Some(text_range(12, 17))
678 );
679 }
680
681 #[test]
682 fn insertion_at_node_start_shifts_node() {
683 let node = text_range(10, 15);
684 assert_eq!(
685 map_range_through_edit(node, &edit(10, 10, "ab")),
686 Some(text_range(12, 17))
687 );
688 }
689
690 #[test]
691 fn insertion_at_node_end_leaves_node_unchanged() {
692 let node = text_range(10, 15);
693 assert_eq!(
694 map_range_through_edit(node, &edit(15, 15, "ab")),
695 Some(node)
696 );
697 }
698
699 #[test]
700 fn insertion_strictly_inside_invalidates() {
701 let node = text_range(10, 15);
702 assert_eq!(map_range_through_edit(node, &edit(12, 12, "z")), None);
703 }
704
705 #[test]
706 fn overlapping_deletion_invalidates() {
707 let node = text_range(10, 15);
708 assert_eq!(map_range_through_edit(node, &edit(12, 18, "")), None);
709 }
710
711 #[test]
712 fn zero_length_node_at_insertion_point_is_unchanged() {
713 let node = text_range(10, 10);
716 assert_eq!(
717 map_range_through_edit(node, &edit(10, 10, "ab")),
718 Some(node)
719 );
720 }
721
722 #[test]
723 fn append_at_eof_leaves_earlier_node_unchanged() {
724 let node = text_range(0, 5);
725 assert_eq!(
726 map_range_through_edit(node, &edit(5, 5, "\n# trailing")),
727 Some(node)
728 );
729 }
730
731 #[test]
732 fn map_through_edits_folds_in_order() {
733 let node = text_range(10, 15);
734 let edits = [edit(0, 0, "ab"), edit(0, 0, "cd")];
736 assert_eq!(
737 map_range_through_edits(node, &edits),
738 Some(text_range(14, 19))
739 );
740 }
741
742 #[test]
743 fn map_through_edits_invalidates_on_any_overlap() {
744 let node = text_range(10, 15);
745 let edits = [edit(0, 0, "ab"), edit(13, 13, "z")];
747 assert_eq!(map_range_through_edits(node, &edits), None);
748 }
749
750 #[test]
751 fn apply_edits_folds_each_against_prior_text() {
752 let edits = [edit(0, 0, "ab"), edit(4, 4, "Z")];
756 assert_eq!(apply_edits("cdef", &edits), "abcdZef");
757 }
758
759 #[test]
760 fn apply_edits_empty_is_identity() {
761 assert_eq!(apply_edits("xyz", &[]), "xyz");
762 }
763}