|
1 | 1 | //! Implementation of applying changes to a syntax tree.
|
2 | 2 |
|
3 |
| -use std::{cmp::Ordering, collections::VecDeque, ops::RangeInclusive}; |
| 3 | +use std::{ |
| 4 | + cmp::Ordering, |
| 5 | + collections::VecDeque, |
| 6 | + ops::{Range, RangeInclusive}, |
| 7 | +}; |
4 | 8 |
|
5 | 9 | use rowan::TextRange;
|
6 | 10 | use rustc_hash::FxHashMap;
|
| 11 | +use stdx::format_to; |
7 | 12 |
|
8 | 13 | use crate::{
|
9 | 14 | syntax_editor::{mapping::MissingMapping, Change, ChangeKind, PositionRepr},
|
@@ -76,11 +81,9 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
|
76 | 81 | || (l.target_range().end() <= r.target_range().start())
|
77 | 82 | });
|
78 | 83 |
|
79 |
| - if stdx::never!( |
80 |
| - !disjoint_replaces_ranges, |
81 |
| - "some replace change ranges intersect: {:?}", |
82 |
| - changes |
83 |
| - ) { |
| 84 | + if !disjoint_replaces_ranges { |
| 85 | + report_intersecting_changes(&changes, get_node_depth, &root); |
| 86 | + |
84 | 87 | return SyntaxEdit {
|
85 | 88 | old_root: root.clone(),
|
86 | 89 | new_root: root,
|
@@ -293,6 +296,78 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
|
293 | 296 | }
|
294 | 297 | }
|
295 | 298 |
|
| 299 | +fn report_intersecting_changes( |
| 300 | + changes: &[Change], |
| 301 | + mut get_node_depth: impl FnMut(rowan::SyntaxNode<crate::RustLanguage>) -> usize, |
| 302 | + root: &rowan::SyntaxNode<crate::RustLanguage>, |
| 303 | +) { |
| 304 | + let intersecting_changes = changes |
| 305 | + .iter() |
| 306 | + .zip(changes.iter().skip(1)) |
| 307 | + .filter(|(l, r)| { |
| 308 | + // We only care about checking for disjoint replace ranges. |
| 309 | + matches!( |
| 310 | + (l.change_kind(), r.change_kind()), |
| 311 | + ( |
| 312 | + ChangeKind::Replace | ChangeKind::ReplaceRange, |
| 313 | + ChangeKind::Replace | ChangeKind::ReplaceRange |
| 314 | + ) |
| 315 | + ) |
| 316 | + }) |
| 317 | + .filter(|(l, r)| { |
| 318 | + get_node_depth(l.target_parent()) == get_node_depth(r.target_parent()) |
| 319 | + && (l.target_range().end() > r.target_range().start()) |
| 320 | + }); |
| 321 | + |
| 322 | + let mut error_msg = String::from("some replace change ranges intersect!\n"); |
| 323 | + |
| 324 | + let parent_str = root.to_string(); |
| 325 | + |
| 326 | + for (l, r) in intersecting_changes { |
| 327 | + let mut highlighted_str = parent_str.clone(); |
| 328 | + let l_range = l.target_range(); |
| 329 | + let r_range = r.target_range(); |
| 330 | + |
| 331 | + let i_range = l_range.intersect(r_range).unwrap(); |
| 332 | + let i_str = format!("\x1b[46m{}", &parent_str[i_range]); |
| 333 | + |
| 334 | + let pre_range: Range<usize> = l_range.start().into()..i_range.start().into(); |
| 335 | + let pre_str = format!("\x1b[44m{}", &parent_str[pre_range]); |
| 336 | + |
| 337 | + let (highlight_range, highlight_str) = if l_range == r_range { |
| 338 | + format_to!(error_msg, "\x1b[46mleft change:\x1b[0m {l:?} {l}\n"); |
| 339 | + format_to!(error_msg, "\x1b[46mequals\x1b[0m\n"); |
| 340 | + format_to!(error_msg, "\x1b[46mright change:\x1b[0m {r:?} {r}\n"); |
| 341 | + let i_highlighted = format!("{i_str}\x1b[0m\x1b[K"); |
| 342 | + let total_range: Range<usize> = i_range.into(); |
| 343 | + (total_range, i_highlighted) |
| 344 | + } else { |
| 345 | + format_to!(error_msg, "\x1b[44mleft change:\x1b[0m {l:?} {l}\n"); |
| 346 | + let range_end = if l_range.contains_range(r_range) { |
| 347 | + format_to!(error_msg, "\x1b[46mcovers\x1b[0m\n"); |
| 348 | + format_to!(error_msg, "\x1b[46mright change:\x1b[0m {r:?} {r}\n"); |
| 349 | + l_range.end() |
| 350 | + } else { |
| 351 | + format_to!(error_msg, "\x1b[46mintersects\x1b[0m\n"); |
| 352 | + format_to!(error_msg, "\x1b[42mright change:\x1b[0m {r:?} {r}\n"); |
| 353 | + r_range.end() |
| 354 | + }; |
| 355 | + |
| 356 | + let post_range: Range<usize> = i_range.end().into()..range_end.into(); |
| 357 | + |
| 358 | + let post_str = format!("\x1b[42m{}", &parent_str[post_range]); |
| 359 | + let result = format!("{pre_str}{i_str}{post_str}\x1b[0m\x1b[K"); |
| 360 | + let total_range: Range<usize> = l_range.start().into()..range_end.into(); |
| 361 | + (total_range, result) |
| 362 | + }; |
| 363 | + highlighted_str.replace_range(highlight_range, &highlight_str); |
| 364 | + |
| 365 | + format_to!(error_msg, "{highlighted_str}\n"); |
| 366 | + } |
| 367 | + |
| 368 | + stdx::always!(false, "{}", error_msg); |
| 369 | +} |
| 370 | + |
296 | 371 | fn to_owning_node(element: &SyntaxElement) -> SyntaxNode {
|
297 | 372 | match element {
|
298 | 373 | SyntaxElement::Node(node) => node.clone(),
|
|
0 commit comments