mirror of https://github.com/astral-sh/ruff
WIP
This commit is contained in:
parent
097e703071
commit
c086e0a861
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue