mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 22:01:47 +00:00
Range formatting: Fix invalid syntax after parenthesizing expression (#9751)
This commit is contained in:
parent
50bfbcf568
commit
4f7fb566f0
24 changed files with 351 additions and 212 deletions
|
@ -41,7 +41,7 @@ use std::marker::PhantomData;
|
|||
use std::num::{NonZeroU16, NonZeroU8, TryFromIntError};
|
||||
|
||||
use crate::format_element::document::Document;
|
||||
use crate::printer::{Printer, PrinterOptions, SourceMapGeneration};
|
||||
use crate::printer::{Printer, PrinterOptions};
|
||||
pub use arguments::{Argument, Arguments};
|
||||
pub use buffer::{
|
||||
Buffer, BufferExtensions, BufferSnapshot, Inspect, RemoveSoftLinesBuffer, VecBuffer,
|
||||
|
@ -269,7 +269,6 @@ impl FormatOptions for SimpleFormatOptions {
|
|||
line_width: self.line_width,
|
||||
indent_style: self.indent_style,
|
||||
indent_width: self.indent_width,
|
||||
source_map_generation: SourceMapGeneration::Enabled,
|
||||
..PrinterOptions::default()
|
||||
}
|
||||
}
|
||||
|
@ -433,28 +432,40 @@ impl Printed {
|
|||
std::mem::take(&mut self.verbatim_ranges)
|
||||
}
|
||||
|
||||
/// Slices the formatted code to the sub-slices that covers the passed `source_range`.
|
||||
/// Slices the formatted code to the sub-slices that covers the passed `source_range` in `source`.
|
||||
///
|
||||
/// The implementation uses the source map generated during formatting to find the closest range
|
||||
/// in the formatted document that covers `source_range` or more. The returned slice
|
||||
/// matches the `source_range` exactly (except indent, see below) if the formatter emits [`FormatElement::SourcePosition`] for
|
||||
/// the range's offsets.
|
||||
///
|
||||
/// ## Indentation
|
||||
/// The indentation before `source_range.start` is replaced with the indentation returned by the formatter
|
||||
/// to fix up incorrectly intended code.
|
||||
///
|
||||
/// Returns the entire document if the source map is empty.
|
||||
///
|
||||
/// # Panics
|
||||
/// If `source_range` points to offsets that are not in the bounds of `source`.
|
||||
#[must_use]
|
||||
pub fn slice_range(self, source_range: TextRange) -> PrintedRange {
|
||||
pub fn slice_range(self, source_range: TextRange, source: &str) -> PrintedRange {
|
||||
let mut start_marker: Option<SourceMarker> = None;
|
||||
let mut end_marker: Option<SourceMarker> = None;
|
||||
|
||||
// Note: The printer can generate multiple source map entries for the same source position.
|
||||
// For example if you have:
|
||||
// * token("a + b")
|
||||
// * `source_position(276)`
|
||||
// * `token("def")`
|
||||
// * `token("foo")`
|
||||
// * `source_position(284)`
|
||||
// The printer uses the source position 276 for both the tokens `def` and `foo` because that's the only position it knows of.
|
||||
// * `token(")")`
|
||||
// * `source_position(276)`
|
||||
// * `hard_line_break`
|
||||
// The printer uses the source position 276 for both the tokens `)` and the `\n` because
|
||||
// there were multiple `source_position` entries in the IR with the same offset.
|
||||
// This can happen if multiple nodes start or end at the same position. A common example
|
||||
// for this are expressions and expression statement that always end at the same offset.
|
||||
//
|
||||
// Warning: Source markers are often emitted sorted by their source position but it's not guaranteed.
|
||||
// Warning: Source markers are often emitted sorted by their source position but it's not guaranteed
|
||||
// and depends on the emitted `IR`.
|
||||
// They are only guaranteed to be sorted in increasing order by their destination position.
|
||||
for marker in self.sourcemap {
|
||||
// Take the closest start marker, but skip over start_markers that have the same start.
|
||||
|
@ -471,17 +482,44 @@ impl Printed {
|
|||
}
|
||||
}
|
||||
|
||||
let start = start_marker.map(|marker| marker.dest).unwrap_or_default();
|
||||
let end = end_marker.map_or_else(|| self.code.text_len(), |marker| marker.dest);
|
||||
let code_range = TextRange::new(start, end);
|
||||
let (source_start, formatted_start) = start_marker
|
||||
.map(|marker| (marker.source, marker.dest))
|
||||
.unwrap_or_default();
|
||||
|
||||
let (source_end, formatted_end) = end_marker
|
||||
.map_or((source.text_len(), self.code.text_len()), |marker| {
|
||||
(marker.source, marker.dest)
|
||||
});
|
||||
|
||||
let source_range = TextRange::new(source_start, source_end);
|
||||
let formatted_range = TextRange::new(formatted_start, formatted_end);
|
||||
|
||||
// Extend both ranges to include the indentation
|
||||
let source_range = extend_range_to_include_indent(source_range, source);
|
||||
let formatted_range = extend_range_to_include_indent(formatted_range, &self.code);
|
||||
|
||||
PrintedRange {
|
||||
code: self.code[code_range].to_string(),
|
||||
code: self.code[formatted_range].to_string(),
|
||||
source_range,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extends `range` backwards (by reducing `range.start`) to include any directly preceding whitespace (`\t` or ` `).
|
||||
///
|
||||
/// # Panics
|
||||
/// If `range.start` is out of `source`'s bounds.
|
||||
fn extend_range_to_include_indent(range: TextRange, source: &str) -> TextRange {
|
||||
let whitespace_len: TextSize = source[..usize::from(range.start())]
|
||||
.chars()
|
||||
.rev()
|
||||
.take_while(|c| matches!(c, ' ' | '\t'))
|
||||
.map(TextLen::text_len)
|
||||
.sum();
|
||||
|
||||
TextRange::new(range.start() - whitespace_len, range.end())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
|
@ -537,7 +575,7 @@ pub type FormatResult<F> = Result<F, FormatError>;
|
|||
/// impl Format<SimpleFormatContext> for Paragraph {
|
||||
/// fn fmt(&self, f: &mut Formatter<SimpleFormatContext>) -> FormatResult<()> {
|
||||
/// write!(f, [
|
||||
/// text(&self.0, None),
|
||||
/// text(&self.0),
|
||||
/// hard_line_break(),
|
||||
/// ])
|
||||
/// }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue