-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix replacement edit range computation #12171
Conversation
.zip(modified_line_starts.iter().copied()) | ||
.skip(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skipping the first line start i.e., change (2)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it guaranteed that source_line_starts[0] == TextSize::new(0)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes:
ruff/crates/ruff_source_file/src/line_index.rs
Lines 28 to 32 in 8e6f722
impl LineIndex { | |
/// Builds the [`LineIndex`] from the source text of a file. | |
pub fn from_source_text(text: &str) -> Self { | |
let mut line_starts: Vec<TextSize> = Vec::with_capacity(text.len() / 88); | |
line_starts.push(TextSize::default()); |
for (old_line_start, new_line_start) in line_iter.by_ref() { | ||
if old_line_start <= source_start | ||
|| new_line_start <= replaced_start | ||
|| source[TextRange::new(old_line_start, source_end)] | ||
!= modified[TextRange::new(new_line_start, replaced_end)] | ||
for (source_line_start, modified_line_start) in source_line_starts | ||
.iter() | ||
.rev() | ||
.copied() | ||
.zip(modified_line_starts.iter().rev().copied()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The zip
and rev
bug u.e., change (1)
for (old_line_start, new_line_start) in line_iter.by_ref() { | ||
if old_line_start <= source_start | ||
|| new_line_start <= replaced_start | ||
|| source[TextRange::new(old_line_start, source_end)] | ||
!= modified[TextRange::new(new_line_start, replaced_end)] | ||
for (source_line_start, modified_line_start) in source_line_starts | ||
.iter() | ||
.rev() | ||
.copied() | ||
.zip(modified_line_starts.iter().rev().copied()) | ||
{ | ||
if source_line_start < source_start | ||
|| modified_line_start < modified_start |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switching from <=
to <
i.e., change (3)
.zip(modified_line_starts.iter().copied()) | ||
.skip(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it guaranteed that source_line_starts[0] == TextSize::new(0)
?
|
Summary
This PR fixes various bugs for computing the replacement range between the original and modified source for the language server.
zip
on the reversed iterator. The bug was that it was reversing the already zipped iterator. The problem here is that the length of both slices aren't going to be the same unless the source wasn't modified at all. Refer to the Rust playground where you can see this in action.<
instead of<=
.fixes: #12128
Test Plan
Add test cases where the text is being inserted, deleted, and replaced between the original and new source code, validate the replacement ranges.