Skip LibCST parsing for standard dedent adjustments (#9769)

## Summary

Often, when fixing, we need to dedent a block of code (e.g., if we
remove an `if` and dedent its body). Today, we use LibCST to parse and
adjust the indentation, which is really expensive -- but this is only
really necessary if the block contains a multiline string, since naively
adjusting the indentation for such a string can change the whitespace
_within_ the string.

This PR uses a simple dedent implementation for cases in which the block
doesn't intersect with a multi-line string (or an f-string, since we
don't support tracking multi-line strings for f-strings right now).

We could improve this even further by using the ranges to guide the
dedent function, such that we don't apply the dedent if the line starts
within a multiline string. But that would also need to take f-strings
into account, which is a little tricky.

## Test Plan

`cargo test`
This commit is contained in:
Charlie Marsh 2024-02-02 10:13:46 -08:00 committed by GitHub
parent 4f7fb566f0
commit c3ca34543f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 193 additions and 19 deletions

View file

@ -14,6 +14,14 @@ pub struct FStringRanges {
}
impl FStringRanges {
/// Returns `true` if the given range intersects with any f-string range.
pub fn intersects(&self, target: TextRange) -> bool {
self.raw
.values()
.take_while(|range| range.start() < target.end())
.any(|range| target.intersect(*range).is_some())
}
/// Return the [`TextRange`] of the innermost f-string at the given offset.
pub fn innermost(&self, offset: TextSize) -> Option<TextRange> {
self.raw

View file

@ -9,7 +9,7 @@ pub struct MultilineRanges {
impl MultilineRanges {
/// Returns `true` if the given range is inside a multiline string.
pub fn intersects(&self, target: TextRange) -> bool {
pub fn contains_range(&self, target: TextRange) -> bool {
self.ranges
.binary_search_by(|range| {
if range.contains_range(target) {
@ -22,6 +22,21 @@ impl MultilineRanges {
})
.is_ok()
}
/// Returns `true` if the given range intersects with any multiline string.
pub fn intersects(&self, target: TextRange) -> bool {
self.ranges
.binary_search_by(|range| {
if target.intersect(*range).is_some() {
std::cmp::Ordering::Equal
} else if range.end() < target.start() {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Greater
}
})
.is_ok()
}
}
#[derive(Default)]