This commit is contained in:
Dhruv Manilawala 2023-10-27 16:39:17 +05:30
parent 097e703071
commit c086e0a861
No known key found for this signature in database
GPG Key ID: 9601911DE8797432
4 changed files with 109 additions and 26 deletions

View File

@ -156,10 +156,10 @@ fn extract_noqa_line_for(lxr: &[LexResult], locator: &Locator, indexer: &Indexer
// the inner f-strings. // the inner f-strings.
let mut last_fstring_range: TextRange = TextRange::default(); let mut last_fstring_range: TextRange = TextRange::default();
for fstring_range in indexer.fstring_ranges().values() { for fstring_range in indexer.fstring_ranges().values() {
if !locator.contains_line_break(*fstring_range) { if !locator.contains_line_break(fstring_range.range()) {
continue; continue;
} }
if last_fstring_range.contains_range(*fstring_range) { if last_fstring_range.contains_range(fstring_range.range()) {
continue; continue;
} }
let new_range = TextRange::new( let new_range = TextRange::new(

View File

@ -728,23 +728,65 @@ impl<'a> NoqaDirectives<'a> {
} }
} }
pub enum Completeness {
Complete,
Incomplete,
}
pub struct NoqaOffset {
range: TextRange,
completeness: Completeness,
}
impl NoqaOffset {
pub fn new(range: TextRange, completeness: Completeness) -> Self {
Self {
range,
completeness,
}
}
pub fn completeness(&self) -> Completeness {
self.completeness
}
pub const fn is_complete(&self) -> bool {
matches!(self.completeness, Completeness::Complete)
}
}
impl Ranged for NoqaOffset {
fn range(&self) -> TextRange {
self.range
}
}
impl From<TextRange> for NoqaOffset {
fn from(range: TextRange) -> Self {
Self {
range,
completeness: Completeness::Complete,
}
}
}
/// Remaps offsets falling into one of the ranges to instead check for a noqa comment on the /// Remaps offsets falling into one of the ranges to instead check for a noqa comment on the
/// line specified by the offset. /// line specified by the offset.
#[derive(Debug, Default, PartialEq, Eq)] #[derive(Debug, Default, PartialEq, Eq)]
pub struct NoqaMapping { pub struct NoqaMapping {
ranges: Vec<TextRange>, offsets: Vec<NoqaOffset>,
} }
impl NoqaMapping { impl NoqaMapping {
pub(crate) fn with_capacity(capacity: usize) -> Self { pub(crate) fn with_capacity(capacity: usize) -> Self {
Self { Self {
ranges: Vec::with_capacity(capacity), offsets: Vec::with_capacity(capacity),
} }
} }
/// Returns the re-mapped position or `position` if no mapping exists. /// Returns the re-mapped position or `position` if no mapping exists.
pub(crate) fn resolve(&self, offset: TextSize) -> TextSize { pub(crate) fn resolve(&self, offset: TextSize) -> Option<TextSize> {
let index = self.ranges.binary_search_by(|range| { let index = self.offsets.binary_search_by(|range| {
if range.end() < offset { if range.end() < offset {
std::cmp::Ordering::Less std::cmp::Ordering::Less
} else if range.contains(offset) { } else if range.contains(offset) {
@ -755,30 +797,36 @@ impl NoqaMapping {
}); });
if let Ok(index) = index { if let Ok(index) = index {
self.ranges[index].end() let offset = self.offsets[index];
if offset.is_complete() {
Some(offset.end())
} else {
None
}
} else { } else {
offset Some(offset)
} }
} }
pub(crate) fn push_mapping(&mut self, range: TextRange) { pub(crate) fn push_mapping(&mut self, offset: Into<NoqaOffset>) {
if let Some(last_range) = self.ranges.last_mut() { let offset: NoqaOffset = offset.into();
if let Some(last_offset) = self.offsets.last_mut() {
// Strictly sorted insertion // Strictly sorted insertion
if last_range.end() < range.start() { if last_offset.end() < offset.start() {
// OK // OK
} else if range.end() < last_range.start() { } else if offset.end() < last_offset.start() {
// Incoming range is strictly before the last range which violates // Incoming range is strictly before the last range which violates
// the function's contract. // the function's contract.
panic!("Ranges must be inserted in sorted order") panic!("Offsets must be inserted in sorted order")
} else { } else {
// Here, it's guaranteed that `last_range` and `range` overlap // Here, it's guaranteed that `last_range` and `range` overlap
// in some way. We want to merge them into a single range. // in some way. We want to merge them into a single range.
*last_range = last_range.cover(range); *last_offset = last_offset.range().cover(offset.range());
return; return;
} }
} }
self.ranges.push(range); self.offsets.push(offset);
} }
} }

View File

@ -1,7 +1,7 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use ruff_python_parser::Tok; use ruff_python_parser::Tok;
use ruff_text_size::{TextRange, TextSize}; use ruff_text_size::{Ranged, TextRange, TextSize};
/// Stores the ranges of all f-strings in a file sorted by [`TextRange::start`]. /// Stores the ranges of all f-strings in a file sorted by [`TextRange::start`].
/// There can be multiple overlapping ranges for nested f-strings. /// There can be multiple overlapping ranges for nested f-strings.
@ -10,21 +10,21 @@ use ruff_text_size::{TextRange, TextSize};
#[derive(Debug)] #[derive(Debug)]
pub struct FStringRanges { pub struct FStringRanges {
// Mapping from the f-string start location to its range. // Mapping from the f-string start location to its range.
raw: BTreeMap<TextSize, TextRange>, raw: BTreeMap<TextSize, FStringRange>,
} }
impl FStringRanges { impl FStringRanges {
/// Return the [`TextRange`] of the innermost f-string at the given offset. /// Return the [`TextRange`] of the innermost f-string at the given offset.
pub fn innermost(&self, offset: TextSize) -> Option<TextRange> { pub fn innermost(&self, offset: TextSize) -> Option<&FStringRange> {
self.raw self.raw
.range(..=offset) .range(..=offset)
.rev() .rev()
.find(|(_, range)| range.contains(offset)) .find(|(_, range)| range.contains(offset))
.map(|(_, range)| *range) .map(|(_, range)| range)
} }
/// Return the [`TextRange`] of the outermost f-string at the given offset. /// Return the [`TextRange`] of the outermost f-string at the given offset.
pub fn outermost(&self, offset: TextSize) -> Option<TextRange> { pub fn outermost(&self, offset: TextSize) -> Option<&FStringRange> {
// Explanation of the algorithm: // Explanation of the algorithm:
// //
// ```python // ```python
@ -50,7 +50,7 @@ impl FStringRanges {
.skip_while(|(_, range)| !range.contains(offset)) .skip_while(|(_, range)| !range.contains(offset))
.take_while(|(_, range)| range.contains(offset)) .take_while(|(_, range)| range.contains(offset))
.last() .last()
.map(|(_, range)| *range) .map(|(_, range)| range)
} }
/// Returns an iterator over all f-string [`TextRange`] sorted by their /// Returns an iterator over all f-string [`TextRange`] sorted by their
@ -59,7 +59,7 @@ impl FStringRanges {
/// For nested f-strings, the outermost f-string is yielded first, moving /// For nested f-strings, the outermost f-string is yielded first, moving
/// inwards with each iteration. /// inwards with each iteration.
#[inline] #[inline]
pub fn values(&self) -> impl Iterator<Item = &TextRange> + '_ { pub fn values(&self) -> impl Iterator<Item = &FStringRange> + '_ {
self.raw.values() self.raw.values()
} }
@ -73,7 +73,7 @@ impl FStringRanges {
#[derive(Default)] #[derive(Default)]
pub(crate) struct FStringRangesBuilder { pub(crate) struct FStringRangesBuilder {
start_locations: Vec<TextSize>, start_locations: Vec<TextSize>,
raw: BTreeMap<TextSize, TextRange>, raw: BTreeMap<TextSize, FStringRange>,
} }
impl FStringRangesBuilder { impl FStringRangesBuilder {
@ -84,14 +84,49 @@ impl FStringRangesBuilder {
} }
Tok::FStringEnd => { Tok::FStringEnd => {
if let Some(start) = self.start_locations.pop() { if let Some(start) = self.start_locations.pop() {
self.raw.insert(start, TextRange::new(start, range.end())); self.raw.insert(
start,
FStringRange::new(TextRange::new(start, range.end()), true),
);
} }
} }
_ => {} _ => {}
} }
} }
pub(crate) fn finish(self) -> FStringRanges { pub(crate) fn finish(mut self, end_location: TextSize) -> FStringRanges {
while let Some(start) = self.start_locations.pop() {
self.raw.insert(
start,
FStringRange::new(TextRange::new(start, end_location), false),
);
}
FStringRanges { raw: self.raw } FStringRanges { raw: self.raw }
} }
} }
#[derive(Debug)]
pub struct FStringRange {
range: TextRange,
complete: bool,
}
impl FStringRange {
fn new(range: TextRange, complete: bool) -> Self {
Self { range, complete }
}
pub fn range(&self) -> TextRange {
self.range
}
pub fn is_complete(&self) -> bool {
self.complete
}
}
impl Ranged for FStringRange {
fn range(&self) -> TextRange {
self.range
}
}

View File

@ -72,7 +72,7 @@ impl Indexer {
Self { Self {
comment_ranges: comment_ranges_builder.finish(), comment_ranges: comment_ranges_builder.finish(),
continuation_lines, continuation_lines,
fstring_ranges: fstring_ranges_builder.finish(), fstring_ranges: fstring_ranges_builder.finish(locator.text_len()),
} }
} }