mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-29 02:52:11 +00:00
internal: Improve reporting of intersecting changes
This commit is contained in:
parent
8925544998
commit
b4f2d62952
3 changed files with 142 additions and 8 deletions
|
|
@ -5,6 +5,7 @@
|
|||
//! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs
|
||||
|
||||
use std::{
|
||||
fmt,
|
||||
num::NonZeroU32,
|
||||
ops::RangeInclusive,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
|
|
@ -282,6 +283,64 @@ enum ChangeKind {
|
|||
Replace,
|
||||
}
|
||||
|
||||
impl fmt::Display for Change {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Change::Insert(position, node_or_token) => {
|
||||
let parent = position.parent();
|
||||
let mut parent_str = parent.to_string();
|
||||
let target_range = self.target_range().start() - parent.text_range().start();
|
||||
|
||||
parent_str.insert_str(
|
||||
target_range.into(),
|
||||
&format!("\x1b[42m{node_or_token}\x1b[0m\x1b[K"),
|
||||
);
|
||||
f.write_str(&parent_str)
|
||||
}
|
||||
Change::InsertAll(position, vec) => {
|
||||
let parent = position.parent();
|
||||
let mut parent_str = parent.to_string();
|
||||
let target_range = self.target_range().start() - parent.text_range().start();
|
||||
let insertion: String = vec.iter().map(|it| it.to_string()).collect();
|
||||
|
||||
parent_str
|
||||
.insert_str(target_range.into(), &format!("\x1b[42m{insertion}\x1b[0m\x1b[K"));
|
||||
f.write_str(&parent_str)
|
||||
}
|
||||
Change::Replace(old, new) => {
|
||||
if let Some(new) = new {
|
||||
write!(f, "\x1b[41m{old}\x1b[42m{new}\x1b[0m\x1b[K")
|
||||
} else {
|
||||
write!(f, "\x1b[41m{old}\x1b[0m\x1b[K")
|
||||
}
|
||||
}
|
||||
Change::ReplaceWithMany(old, vec) => {
|
||||
let new: String = vec.iter().map(|it| it.to_string()).collect();
|
||||
write!(f, "\x1b[41m{old}\x1b[42m{new}\x1b[0m\x1b[K")
|
||||
}
|
||||
Change::ReplaceAll(range, vec) => {
|
||||
let parent = range.start().parent().unwrap();
|
||||
let parent_str = parent.to_string();
|
||||
let pre_range =
|
||||
TextRange::new(parent.text_range().start(), range.start().text_range().start());
|
||||
let old_range = TextRange::new(
|
||||
range.start().text_range().start(),
|
||||
range.end().text_range().end(),
|
||||
);
|
||||
let post_range =
|
||||
TextRange::new(range.end().text_range().end(), parent.text_range().end());
|
||||
|
||||
let pre_str = &parent_str[pre_range - parent.text_range().start()];
|
||||
let old_str = &parent_str[old_range - parent.text_range().start()];
|
||||
let post_str = &parent_str[post_range - parent.text_range().start()];
|
||||
let new: String = vec.iter().map(|it| it.to_string()).collect();
|
||||
|
||||
write!(f, "{pre_str}\x1b[41m{old_str}\x1b[42m{new}\x1b[0m\x1b[K{post_str}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility trait to allow calling syntax editor functions with references or owned
|
||||
/// nodes. Do not use outside of this module.
|
||||
pub trait Element {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
//! Implementation of applying changes to a syntax tree.
|
||||
|
||||
use std::{cmp::Ordering, collections::VecDeque, ops::RangeInclusive};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::VecDeque,
|
||||
ops::{Range, RangeInclusive},
|
||||
};
|
||||
|
||||
use rowan::TextRange;
|
||||
use rustc_hash::FxHashMap;
|
||||
use stdx::format_to;
|
||||
|
||||
use crate::{
|
||||
syntax_editor::{mapping::MissingMapping, Change, ChangeKind, PositionRepr},
|
||||
|
|
@ -76,11 +81,9 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
|
|||
|| (l.target_range().end() <= r.target_range().start())
|
||||
});
|
||||
|
||||
if stdx::never!(
|
||||
!disjoint_replaces_ranges,
|
||||
"some replace change ranges intersect: {:?}",
|
||||
changes
|
||||
) {
|
||||
if !disjoint_replaces_ranges {
|
||||
report_intersecting_changes(&changes, get_node_depth, &root);
|
||||
|
||||
return SyntaxEdit {
|
||||
old_root: root.clone(),
|
||||
new_root: root,
|
||||
|
|
@ -293,6 +296,78 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
|
|||
}
|
||||
}
|
||||
|
||||
fn report_intersecting_changes(
|
||||
changes: &[Change],
|
||||
mut get_node_depth: impl FnMut(rowan::SyntaxNode<crate::RustLanguage>) -> usize,
|
||||
root: &rowan::SyntaxNode<crate::RustLanguage>,
|
||||
) {
|
||||
let intersecting_changes = changes
|
||||
.iter()
|
||||
.zip(changes.iter().skip(1))
|
||||
.filter(|(l, r)| {
|
||||
// We only care about checking for disjoint replace ranges.
|
||||
matches!(
|
||||
(l.change_kind(), r.change_kind()),
|
||||
(
|
||||
ChangeKind::Replace | ChangeKind::ReplaceRange,
|
||||
ChangeKind::Replace | ChangeKind::ReplaceRange
|
||||
)
|
||||
)
|
||||
})
|
||||
.filter(|(l, r)| {
|
||||
get_node_depth(l.target_parent()) == get_node_depth(r.target_parent())
|
||||
&& (l.target_range().end() > r.target_range().start())
|
||||
});
|
||||
|
||||
let mut error_msg = String::from("some replace change ranges intersect!\n");
|
||||
|
||||
let parent_str = root.to_string();
|
||||
|
||||
for (l, r) in intersecting_changes {
|
||||
let mut highlighted_str = parent_str.clone();
|
||||
let l_range = l.target_range();
|
||||
let r_range = r.target_range();
|
||||
|
||||
let i_range = l_range.intersect(r_range).unwrap();
|
||||
let i_str = format!("\x1b[46m{}", &parent_str[i_range]);
|
||||
|
||||
let pre_range: Range<usize> = l_range.start().into()..i_range.start().into();
|
||||
let pre_str = format!("\x1b[44m{}", &parent_str[pre_range]);
|
||||
|
||||
let (highlight_range, highlight_str) = if l_range == r_range {
|
||||
format_to!(error_msg, "\x1b[46mleft change:\x1b[0m {l:?} {l}\n");
|
||||
format_to!(error_msg, "\x1b[46mequals\x1b[0m\n");
|
||||
format_to!(error_msg, "\x1b[46mright change:\x1b[0m {r:?} {r}\n");
|
||||
let i_highlighted = format!("{i_str}\x1b[0m\x1b[K");
|
||||
let total_range: Range<usize> = i_range.into();
|
||||
(total_range, i_highlighted)
|
||||
} else {
|
||||
format_to!(error_msg, "\x1b[44mleft change:\x1b[0m {l:?} {l}\n");
|
||||
let range_end = if l_range.contains_range(r_range) {
|
||||
format_to!(error_msg, "\x1b[46mcovers\x1b[0m\n");
|
||||
format_to!(error_msg, "\x1b[46mright change:\x1b[0m {r:?} {r}\n");
|
||||
l_range.end()
|
||||
} else {
|
||||
format_to!(error_msg, "\x1b[46mintersects\x1b[0m\n");
|
||||
format_to!(error_msg, "\x1b[42mright change:\x1b[0m {r:?} {r}\n");
|
||||
r_range.end()
|
||||
};
|
||||
|
||||
let post_range: Range<usize> = i_range.end().into()..range_end.into();
|
||||
|
||||
let post_str = format!("\x1b[42m{}", &parent_str[post_range]);
|
||||
let result = format!("{pre_str}{i_str}{post_str}\x1b[0m\x1b[K");
|
||||
let total_range: Range<usize> = l_range.start().into()..range_end.into();
|
||||
(total_range, result)
|
||||
};
|
||||
highlighted_str.replace_range(highlight_range, &highlight_str);
|
||||
|
||||
format_to!(error_msg, "{highlighted_str}\n");
|
||||
}
|
||||
|
||||
stdx::always!(false, "{}", error_msg);
|
||||
}
|
||||
|
||||
fn to_owning_node(element: &SyntaxElement) -> SyntaxNode {
|
||||
match element {
|
||||
SyntaxElement::Node(node) => node.clone(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue