Introduce SourceCodeSlice to reduce the size of FormatElement (#4622)

Introduce `SourceCodeSlice` to reduce the size of `FormatElement`
This commit is contained in:
Micha Reiser 2023-05-24 17:04:52 +02:00 committed by GitHub
parent 6943beee66
commit 86ced3516b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 408 additions and 171 deletions

View file

@ -1,14 +1,13 @@
use crate::format_element::tag::{Condition, Tag}; use crate::format_element::tag::{Condition, Tag};
use crate::prelude::tag::{DedentMode, GroupMode, LabelId}; use crate::prelude::tag::{DedentMode, GroupMode, LabelId};
use crate::prelude::*; use crate::prelude::*;
use crate::{format_element, write, Argument, Arguments, GroupId, TextSize}; use crate::{format_element, write, Argument, Arguments, FormatContext, GroupId, TextSize};
use crate::{Buffer, VecBuffer}; use crate::{Buffer, VecBuffer};
use ruff_text_size::TextRange; use ruff_text_size::TextRange;
use std::cell::Cell; use std::cell::Cell;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::num::NonZeroU8; use std::num::NonZeroU8;
use std::rc::Rc;
use Tag::*; use Tag::*;
/// A line break that only gets printed if the enclosing `Group` doesn't fit on a single line. /// A line break that only gets printed if the enclosing `Group` doesn't fit on a single line.
@ -361,31 +360,65 @@ impl std::fmt::Debug for DynamicText<'_> {
} }
} }
/// Creates a text from a dynamic string and a range of the input source /// Emits a text as it is written in the source document. Optimized to avoid allocations.
pub fn static_text_slice(text: Rc<str>, range: TextRange) -> StaticTextSlice { pub const fn source_text_slice(
debug_assert_no_newlines(&text[range]);
StaticTextSlice { text, range }
}
#[derive(Eq, PartialEq)]
pub struct StaticTextSlice {
text: Rc<str>,
range: TextRange, range: TextRange,
} newlines: ContainsNewlines,
) -> SourceTextSliceBuilder {
impl<Context> Format<Context> for StaticTextSlice { SourceTextSliceBuilder {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> { range,
f.write_element(FormatElement::StaticTextSlice { new_lines: newlines,
text: self.text.clone(),
range: self.range,
})
} }
} }
impl std::fmt::Debug for StaticTextSlice { #[derive(Copy, Clone, Eq, PartialEq, Debug)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { pub enum ContainsNewlines {
std::write!(f, "StaticTextSlice({})", &self.text[self.range]) /// The string contains newline characters
Yes,
/// The string contains no newline characters
No,
/// The string may contain newline characters, search the string to determine if there are any newlines.
Detect,
}
#[derive(Eq, PartialEq, Debug)]
pub struct SourceTextSliceBuilder {
range: TextRange,
new_lines: ContainsNewlines,
}
impl<Context> Format<Context> for SourceTextSliceBuilder
where
Context: FormatContext,
{
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
let source_code = f.context().source_code();
let slice = source_code.slice(self.range);
debug_assert_no_newlines(slice.text(source_code));
let contains_newlines = match self.new_lines {
ContainsNewlines::Yes => {
debug_assert!(
slice.text(source_code).contains('\n'),
"Text contains no new line characters but the caller specified that it does."
);
true
}
ContainsNewlines::No => {
debug_assert!(
!slice.text(source_code).contains('\n'),
"Text contains new line characters but the caller specified that it does not."
);
false
}
ContainsNewlines::Detect => slice.text(source_code).contains('\n'),
};
f.write_element(FormatElement::SourceCodeSlice {
slice,
contains_newlines,
})
} }
} }

View file

@ -7,8 +7,9 @@ use std::ops::Deref;
use std::rc::Rc; use std::rc::Rc;
use crate::format_element::tag::{LabelId, Tag}; use crate::format_element::tag::{LabelId, Tag};
use crate::source_code::SourceCodeSlice;
use crate::TagKind; use crate::TagKind;
use ruff_text_size::{TextRange, TextSize}; use ruff_text_size::TextSize;
/// Language agnostic IR for formatting source code. /// Language agnostic IR for formatting source code.
/// ///
@ -39,8 +40,12 @@ pub enum FormatElement {
text: Box<str>, text: Box<str>,
}, },
/// Token constructed by slicing a defined range from a static string. /// Text that gets emitted as it is in the source code. Optimized to avoid any allocations.
StaticTextSlice { text: Rc<str>, range: TextRange }, SourceCodeSlice {
slice: SourceCodeSlice,
/// Whether the string contains any new line characters
contains_newlines: bool,
},
/// Prevents that line suffixes move past this boundary. Forces the printer to print any pending /// Prevents that line suffixes move past this boundary. Forces the printer to print any pending
/// line suffixes, potentially by inserting a hard line break. /// line suffixes, potentially by inserting a hard line break.
@ -70,9 +75,14 @@ impl std::fmt::Debug for FormatElement {
FormatElement::DynamicText { text, .. } => { FormatElement::DynamicText { text, .. } => {
fmt.debug_tuple("DynamicText").field(text).finish() fmt.debug_tuple("DynamicText").field(text).finish()
} }
FormatElement::StaticTextSlice { text, .. } => { FormatElement::SourceCodeSlice {
fmt.debug_tuple("Text").field(text).finish() slice,
} contains_newlines,
} => fmt
.debug_tuple("Text")
.field(slice)
.field(contains_newlines)
.finish(),
FormatElement::LineSuffixBoundary => write!(fmt, "LineSuffixBoundary"), FormatElement::LineSuffixBoundary => write!(fmt, "LineSuffixBoundary"),
FormatElement::BestFitting(best_fitting) => { FormatElement::BestFitting(best_fitting) => {
fmt.debug_tuple("BestFitting").field(&best_fitting).finish() fmt.debug_tuple("BestFitting").field(&best_fitting).finish()
@ -221,7 +231,7 @@ impl FormatElement {
pub const fn is_text(&self) -> bool { pub const fn is_text(&self) -> bool {
matches!( matches!(
self, self,
FormatElement::StaticTextSlice { .. } FormatElement::SourceCodeSlice { .. }
| FormatElement::DynamicText { .. } | FormatElement::DynamicText { .. }
| FormatElement::StaticText { .. } | FormatElement::StaticText { .. }
) )
@ -240,7 +250,9 @@ impl FormatElements for FormatElement {
FormatElement::Line(line_mode) => matches!(line_mode, LineMode::Hard | LineMode::Empty), FormatElement::Line(line_mode) => matches!(line_mode, LineMode::Hard | LineMode::Empty),
FormatElement::StaticText { text } => text.contains('\n'), FormatElement::StaticText { text } => text.contains('\n'),
FormatElement::DynamicText { text, .. } => text.contains('\n'), FormatElement::DynamicText { text, .. } => text.contains('\n'),
FormatElement::StaticTextSlice { text, range } => text[*range].contains('\n'), FormatElement::SourceCodeSlice {
contains_newlines, ..
} => *contains_newlines,
FormatElement::Interned(interned) => interned.will_break(), FormatElement::Interned(interned) => interned.will_break(),
// Traverse into the most flat version because the content is guaranteed to expand when even // Traverse into the most flat version because the content is guaranteed to expand when even
// the most flat version contains some content that forces a break. // the most flat version contains some content that forces a break.
@ -380,20 +392,19 @@ mod sizes {
// be recomputed at a later point in time? // be recomputed at a later point in time?
// You reduced the size of a format element? Excellent work! // You reduced the size of a format element? Excellent work!
use crate::format_element::BestFitting;
use crate::prelude::tag::VerbatimKind;
use crate::prelude::Interned;
use ruff_text_size::TextRange;
use static_assertions::assert_eq_size; use static_assertions::assert_eq_size;
assert_eq_size!(TextRange, [u8; 8]); assert_eq_size!(ruff_text_size::TextRange, [u8; 8]);
assert_eq_size!(VerbatimKind, [u8; 8]); assert_eq_size!(crate::prelude::tag::VerbatimKind, [u8; 8]);
assert_eq_size!(Interned, [u8; 16]); assert_eq_size!(crate::prelude::Interned, [u8; 16]);
assert_eq_size!(BestFitting, [u8; 16]); assert_eq_size!(crate::format_element::BestFitting, [u8; 16]);
#[cfg(not(debug_assertions))]
assert_eq_size!(crate::SourceCodeSlice, [u8; 8]);
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
assert_eq_size!(crate::format_element::Tag, [u8; 16]); assert_eq_size!(crate::format_element::Tag, [u8; 16]);
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
assert_eq_size!(crate::FormatElement, [u8; 32]); assert_eq_size!(crate::FormatElement, [u8; 24]);
} }

View file

@ -3,6 +3,7 @@ use crate::format_element::tag::DedentMode;
use crate::prelude::tag::GroupMode; use crate::prelude::tag::GroupMode;
use crate::prelude::*; use crate::prelude::*;
use crate::printer::LineEnding; use crate::printer::LineEnding;
use crate::source_code::SourceCode;
use crate::{format, write}; use crate::{format, write};
use crate::{ use crate::{
BufferExtensions, Format, FormatContext, FormatElement, FormatOptions, FormatResult, Formatter, BufferExtensions, Format, FormatContext, FormatElement, FormatOptions, FormatResult, Formatter,
@ -80,7 +81,9 @@ impl Document {
} }
FormatElement::StaticText { text } => text.contains('\n'), FormatElement::StaticText { text } => text.contains('\n'),
FormatElement::DynamicText { text, .. } => text.contains('\n'), FormatElement::DynamicText { text, .. } => text.contains('\n'),
FormatElement::StaticTextSlice { text, range } => text[*range].contains('\n'), FormatElement::SourceCodeSlice {
contains_newlines, ..
} => *contains_newlines,
FormatElement::ExpandParent FormatElement::ExpandParent
| FormatElement::Line(LineMode::Hard | LineMode::Empty) => true, | FormatElement::Line(LineMode::Hard | LineMode::Empty) => true,
_ => false, _ => false,
@ -99,6 +102,13 @@ impl Document {
let mut interned: FxHashMap<&Interned, bool> = FxHashMap::default(); let mut interned: FxHashMap<&Interned, bool> = FxHashMap::default();
propagate_expands(self, &mut enclosing, &mut interned); propagate_expands(self, &mut enclosing, &mut interned);
} }
pub fn display<'a>(&'a self, source_code: SourceCode<'a>) -> DisplayDocument {
DisplayDocument {
elements: self.elements.as_slice(),
source_code,
}
}
} }
impl From<Vec<FormatElement>> for Document { impl From<Vec<FormatElement>> for Document {
@ -115,9 +125,14 @@ impl Deref for Document {
} }
} }
impl std::fmt::Display for Document { pub struct DisplayDocument<'a> {
elements: &'a [FormatElement],
source_code: SourceCode<'a>,
}
impl std::fmt::Display for DisplayDocument<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let formatted = format!(IrFormatContext::default(), [self.elements.as_slice()]) let formatted = format!(IrFormatContext::new(self.source_code), [self.elements])
.expect("Formatting not to throw any FormatErrors"); .expect("Formatting not to throw any FormatErrors");
f.write_str( f.write_str(
@ -129,18 +144,33 @@ impl std::fmt::Display for Document {
} }
} }
#[derive(Clone, Default, Debug)] #[derive(Clone, Debug)]
struct IrFormatContext { struct IrFormatContext<'a> {
/// The interned elements that have been printed to this point /// The interned elements that have been printed to this point
printed_interned_elements: HashMap<Interned, usize>, printed_interned_elements: HashMap<Interned, usize>,
source_code: SourceCode<'a>,
} }
impl FormatContext for IrFormatContext { impl<'a> IrFormatContext<'a> {
fn new(source_code: SourceCode<'a>) -> Self {
Self {
source_code,
printed_interned_elements: HashMap::new(),
}
}
}
impl FormatContext for IrFormatContext<'_> {
type Options = IrFormatOptions; type Options = IrFormatOptions;
fn options(&self) -> &Self::Options { fn options(&self) -> &Self::Options {
&IrFormatOptions &IrFormatOptions
} }
fn source_code(&self) -> SourceCode {
self.source_code
}
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
@ -165,7 +195,7 @@ impl FormatOptions for IrFormatOptions {
} }
} }
impl Format<IrFormatContext> for &[FormatElement] { impl Format<IrFormatContext<'_>> for &[FormatElement] {
fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> {
use Tag::*; use Tag::*;
@ -189,7 +219,7 @@ impl Format<IrFormatContext> for &[FormatElement] {
element @ FormatElement::Space element @ FormatElement::Space
| element @ FormatElement::StaticText { .. } | element @ FormatElement::StaticText { .. }
| element @ FormatElement::DynamicText { .. } | element @ FormatElement::DynamicText { .. }
| element @ FormatElement::StaticTextSlice { .. } => { | element @ FormatElement::SourceCodeSlice { .. } => {
if !in_text { if !in_text {
write!(f, [text("\"")])?; write!(f, [text("\"")])?;
} }
@ -489,7 +519,7 @@ impl Format<IrFormatContext> for &[FormatElement] {
struct ContentArrayStart; struct ContentArrayStart;
impl Format<IrFormatContext> for ContentArrayStart { impl Format<IrFormatContext<'_>> for ContentArrayStart {
fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> {
use Tag::*; use Tag::*;
@ -505,7 +535,7 @@ impl Format<IrFormatContext> for ContentArrayStart {
struct ContentArrayEnd; struct ContentArrayEnd;
impl Format<IrFormatContext> for ContentArrayEnd { impl Format<IrFormatContext<'_>> for ContentArrayEnd {
fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> {
use Tag::*; use Tag::*;
f.write_elements([ f.write_elements([
@ -615,8 +645,9 @@ impl FormatElements for [FormatElement] {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::prelude::*; use crate::prelude::*;
use crate::SimpleFormatContext;
use crate::{format, format_args, write}; use crate::{format, format_args, write};
use crate::{SimpleFormatContext, SourceCode};
use ruff_text_size::{TextRange, TextSize};
#[test] #[test]
fn display_elements() { fn display_elements() {
@ -641,7 +672,51 @@ mod tests {
let document = formatted.into_document(); let document = formatted.into_document();
assert_eq!( assert_eq!(
&std::format!("{document}"), &std::format!("{}", document.display(SourceCode::default())),
r#"[
group([
"(",
indent([
soft_line_break,
"Some longer content That should ultimately break"
]),
soft_line_break
])
]"#
);
}
#[test]
fn display_elements_with_source_text_slice() {
let source_code = "Some longer content\nThat should ultimately break";
let formatted = format!(
SimpleFormatContext::default().with_source_code(source_code),
[format_with(|f| {
write!(
f,
[group(&format_args![
text("("),
soft_block_indent(&format_args![
source_text_slice(
TextRange::at(TextSize::new(0), TextSize::new(19)),
ContainsNewlines::No
),
space(),
source_text_slice(
TextRange::at(TextSize::new(20), TextSize::new(28)),
ContainsNewlines::No
),
])
])]
)
})]
)
.unwrap();
let document = formatted.into_document();
assert_eq!(
&std::format!("{}", document.display(SourceCode::new(source_code))),
r#"[ r#"[
group([ group([
"(", "(",
@ -677,7 +752,7 @@ mod tests {
]); ]);
assert_eq!( assert_eq!(
&std::format!("{document}"), &std::format!("{}", document.display(SourceCode::default())),
r#"[ r#"[
"[", "[",
group([ group([

View file

@ -33,6 +33,7 @@ pub mod group_id;
pub mod macros; pub mod macros;
pub mod prelude; pub mod prelude;
pub mod printer; pub mod printer;
mod source_code;
use crate::formatter::Formatter; use crate::formatter::Formatter;
use crate::group_id::UniqueGroupIdBuilder; use crate::group_id::UniqueGroupIdBuilder;
@ -47,6 +48,7 @@ pub use buffer::{
VecBuffer, VecBuffer,
}; };
pub use builders::BestFitting; pub use builders::BestFitting;
pub use source_code::{SourceCode, SourceCodeSlice};
pub use crate::diagnostics::{ActualStart, FormatError, InvalidDocumentError, PrintError}; pub use crate::diagnostics::{ActualStart, FormatError, InvalidDocumentError, PrintError};
pub use format_element::{normalize_newlines, FormatElement, LINE_TERMINATORS}; pub use format_element::{normalize_newlines, FormatElement, LINE_TERMINATORS};
@ -202,6 +204,9 @@ pub trait FormatContext {
/// Returns the formatting options /// Returns the formatting options
fn options(&self) -> &Self::Options; fn options(&self) -> &Self::Options;
/// Returns the source code from the document that gets formatted.
fn source_code(&self) -> SourceCode;
} }
/// Options customizing how the source code should be formatted. /// Options customizing how the source code should be formatted.
@ -219,11 +224,20 @@ pub trait FormatOptions {
#[derive(Debug, Default, Eq, PartialEq)] #[derive(Debug, Default, Eq, PartialEq)]
pub struct SimpleFormatContext { pub struct SimpleFormatContext {
options: SimpleFormatOptions, options: SimpleFormatOptions,
source_code: String,
} }
impl SimpleFormatContext { impl SimpleFormatContext {
pub fn new(options: SimpleFormatOptions) -> Self { pub fn new(options: SimpleFormatOptions) -> Self {
Self { options } Self {
options,
source_code: String::new(),
}
}
pub fn with_source_code(mut self, code: &str) -> Self {
self.source_code = String::from(code);
self
} }
} }
@ -233,9 +247,13 @@ impl FormatContext for SimpleFormatContext {
fn options(&self) -> &Self::Options { fn options(&self) -> &Self::Options {
&self.options &self.options
} }
fn source_code(&self) -> SourceCode {
SourceCode::new(&self.source_code)
}
} }
#[derive(Debug, Default, Eq, PartialEq)] #[derive(Debug, Default, Eq, PartialEq, Clone)]
pub struct SimpleFormatOptions { pub struct SimpleFormatOptions {
pub indent_style: IndentStyle, pub indent_style: IndentStyle,
pub line_width: LineWidth, pub line_width: LineWidth,
@ -302,15 +320,18 @@ where
Context: FormatContext, Context: FormatContext,
{ {
pub fn print(&self) -> PrintResult<Printed> { pub fn print(&self) -> PrintResult<Printed> {
let source_code = self.context.source_code();
let print_options = self.context.options().as_print_options(); let print_options = self.context.options().as_print_options();
let printed = Printer::new(print_options).print(&self.document)?; let printed = Printer::new(source_code, print_options).print(&self.document)?;
Ok(printed) Ok(printed)
} }
pub fn print_with_indent(&self, indent: u16) -> PrintResult<Printed> { pub fn print_with_indent(&self, indent: u16) -> PrintResult<Printed> {
let source_code = self.context.source_code();
let print_options = self.context.options().as_print_options(); let print_options = self.context.options().as_print_options();
let printed = Printer::new(print_options).print_with_indent(&self.document, indent)?; let printed =
Printer::new(source_code, print_options).print_with_indent(&self.document, indent)?;
Ok(printed) Ok(printed)
} }

View file

@ -23,6 +23,7 @@ use crate::printer::line_suffixes::{LineSuffixEntry, LineSuffixes};
use crate::printer::queue::{ use crate::printer::queue::{
AllPredicate, FitsEndPredicate, FitsQueue, PrintQueue, Queue, SingleEntryPredicate, AllPredicate, FitsEndPredicate, FitsQueue, PrintQueue, Queue, SingleEntryPredicate,
}; };
use crate::source_code::SourceCode;
use drop_bomb::DebugDropBomb; use drop_bomb::DebugDropBomb;
use ruff_text_size::{TextLen, TextSize}; use ruff_text_size::{TextLen, TextSize};
use std::num::NonZeroU8; use std::num::NonZeroU8;
@ -32,12 +33,14 @@ use unicode_width::UnicodeWidthChar;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Printer<'a> { pub struct Printer<'a> {
options: PrinterOptions, options: PrinterOptions,
source_code: SourceCode<'a>,
state: PrinterState<'a>, state: PrinterState<'a>,
} }
impl<'a> Printer<'a> { impl<'a> Printer<'a> {
pub fn new(options: PrinterOptions) -> Self { pub fn new(source_code: SourceCode<'a>, options: PrinterOptions) -> Self {
Self { Self {
source_code,
options, options,
state: PrinterState::default(), state: PrinterState::default(),
} }
@ -96,7 +99,10 @@ impl<'a> Printer<'a> {
FormatElement::StaticText { text } => self.print_text(text, None), FormatElement::StaticText { text } => self.print_text(text, None),
FormatElement::DynamicText { text } => self.print_text(text, None), FormatElement::DynamicText { text } => self.print_text(text, None),
FormatElement::StaticTextSlice { text, range } => self.print_text(&text[*range], None), FormatElement::SourceCodeSlice { slice, .. } => {
let text = slice.text(self.source_code);
self.print_text(text, Some(slice.range()))
}
FormatElement::Line(line_mode) => { FormatElement::Line(line_mode) => {
if args.mode().is_flat() if args.mode().is_flat()
&& matches!(line_mode, LineMode::Soft | LineMode::SoftOrSpace) && matches!(line_mode, LineMode::Soft | LineMode::SoftOrSpace)
@ -994,8 +1000,9 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
FormatElement::StaticText { text } => return Ok(self.fits_text(text)), FormatElement::StaticText { text } => return Ok(self.fits_text(text)),
FormatElement::DynamicText { text, .. } => return Ok(self.fits_text(text)), FormatElement::DynamicText { text, .. } => return Ok(self.fits_text(text)),
FormatElement::StaticTextSlice { text, range } => { FormatElement::SourceCodeSlice { slice, .. } => {
return Ok(self.fits_text(&text[*range])) let text = slice.text(self.printer.source_code);
return Ok(self.fits_text(text));
} }
FormatElement::LineSuffixBoundary => { FormatElement::LineSuffixBoundary => {
if self.state.has_line_suffix { if self.state.has_line_suffix {
@ -1256,6 +1263,7 @@ struct FitsState {
mod tests { mod tests {
use crate::prelude::*; use crate::prelude::*;
use crate::printer::{LineEnding, PrintWidth, Printer, PrinterOptions}; use crate::printer::{LineEnding, PrintWidth, Printer, PrinterOptions};
use crate::source_code::SourceCode;
use crate::{format_args, write, Document, FormatState, IndentStyle, Printed, VecBuffer}; use crate::{format_args, write, Document, FormatState, IndentStyle, Printed, VecBuffer};
fn format(root: &dyn Format<SimpleFormatContext>) -> Printed { fn format(root: &dyn Format<SimpleFormatContext>) -> Printed {
@ -1274,7 +1282,7 @@ mod tests {
) -> Printed { ) -> Printed {
let formatted = crate::format!(SimpleFormatContext::default(), [root]).unwrap(); let formatted = crate::format!(SimpleFormatContext::default(), [root]).unwrap();
Printer::new(options) Printer::new(SourceCode::default(), options)
.print(formatted.document()) .print(formatted.document())
.expect("Document to be valid") .expect("Document to be valid")
} }
@ -1510,9 +1518,12 @@ two lines`,
let document = Document::from(buffer.into_vec()); let document = Document::from(buffer.into_vec());
let printed = Printer::new(PrinterOptions::default().with_print_width(PrintWidth::new(10))) let printed = Printer::new(
.print(&document) SourceCode::default(),
.unwrap(); PrinterOptions::default().with_print_width(PrintWidth::new(10)),
)
.print(&document)
.unwrap();
assert_eq!( assert_eq!(
printed.as_code(), printed.as_code(),

View file

@ -0,0 +1,81 @@
use ruff_text_size::TextRange;
use std::fmt::{Debug, Formatter};
/// The source code of a document that gets formatted
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default)]
pub struct SourceCode<'a> {
text: &'a str,
}
impl<'a> SourceCode<'a> {
pub fn new(text: &'a str) -> Self {
Self { text }
}
pub fn slice(self, range: TextRange) -> SourceCodeSlice {
assert!(
usize::from(range.end()) <= self.text.len(),
"Range end {:?} out of bounds {}.",
range.end(),
self.text.len()
);
assert!(
self.text.is_char_boundary(usize::from(range.start())),
"The range start position {:?} is not a char boundary.",
range.start()
);
assert!(
self.text.is_char_boundary(usize::from(range.end())),
"The range end position {:?} is not a char boundary.",
range.end()
);
SourceCodeSlice {
range,
#[cfg(debug_assertions)]
text: String::from(&self.text[range]).into_boxed_str(),
}
}
}
impl Debug for SourceCode<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("SourceCode").field(&self.text).finish()
}
}
/// A slice into the source text of a document.
///
/// It only stores the range in production builds for a more compact representation, but it
/// keeps the original text in debug builds for better developer experience.
#[derive(Clone, Eq, PartialEq)]
pub struct SourceCodeSlice {
range: TextRange,
#[cfg(debug_assertions)]
text: Box<str>,
}
impl SourceCodeSlice {
/// Returns the slice's text.
pub fn text<'a>(&self, code: SourceCode<'a>) -> &'a str {
assert!(usize::from(self.range.end()) <= code.text.len(), "The range of this slice is out of bounds. Did you provide the correct source code for this slice?");
&code.text[self.range]
}
pub fn range(&self) -> TextRange {
self.range
}
}
impl Debug for SourceCodeSlice {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut tuple = f.debug_tuple("SourceCodeSlice");
#[cfg(debug_assertions)]
tuple.field(&self.text);
tuple.field(&self.range).finish()
}
}

View file

@ -1,36 +1,34 @@
use std::rc::Rc; use ruff_formatter::{FormatContext, SimpleFormatOptions, SourceCode};
use ruff_formatter::{FormatContext, SimpleFormatOptions};
use ruff_python_ast::source_code::Locator; use ruff_python_ast::source_code::Locator;
pub struct ASTFormatContext { #[derive(Clone, Debug)]
pub struct ASTFormatContext<'source> {
options: SimpleFormatOptions, options: SimpleFormatOptions,
contents: Rc<str>, contents: &'source str,
} }
impl ASTFormatContext { impl<'source> ASTFormatContext<'source> {
pub fn new(options: SimpleFormatOptions, contents: &str) -> Self { pub fn new(options: SimpleFormatOptions, contents: &'source str) -> Self {
Self { Self { options, contents }
options, }
contents: Rc::from(contents),
} pub fn contents(&self) -> &'source str {
self.contents
}
pub fn locator(&self) -> Locator<'source> {
Locator::new(self.contents)
} }
} }
impl FormatContext for ASTFormatContext { impl FormatContext for ASTFormatContext<'_> {
type Options = SimpleFormatOptions; type Options = SimpleFormatOptions;
fn options(&self) -> &Self::Options { fn options(&self) -> &Self::Options {
&self.options &self.options
} }
}
impl ASTFormatContext { fn source_code(&self) -> SourceCode {
pub fn contents(&self) -> Rc<str> { SourceCode::new(self.contents)
self.contents.clone()
}
pub fn locator(&self) -> Locator {
Locator::new(&self.contents)
} }
} }

View file

@ -10,7 +10,7 @@ pub struct FormatAlias<'a> {
item: &'a Alias, item: &'a Alias,
} }
impl AsFormat<ASTFormatContext> for Alias { impl AsFormat<ASTFormatContext<'_>> for Alias {
type Format<'a> = FormatAlias<'a>; type Format<'a> = FormatAlias<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -18,7 +18,7 @@ impl AsFormat<ASTFormatContext> for Alias {
} }
} }
impl Format<ASTFormatContext> for FormatAlias<'_> { impl Format<ASTFormatContext<'_>> for FormatAlias<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let alias = self.item; let alias = self.item;

View file

@ -10,7 +10,7 @@ pub struct FormatArg<'a> {
item: &'a Arg, item: &'a Arg,
} }
impl AsFormat<ASTFormatContext> for Arg { impl AsFormat<ASTFormatContext<'_>> for Arg {
type Format<'a> = FormatArg<'a>; type Format<'a> = FormatArg<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -18,7 +18,7 @@ impl AsFormat<ASTFormatContext> for Arg {
} }
} }
impl Format<ASTFormatContext> for FormatArg<'_> { impl Format<ASTFormatContext<'_>> for FormatArg<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let arg = self.item; let arg = self.item;

View file

@ -9,7 +9,7 @@ pub struct FormatArguments<'a> {
item: &'a Arguments, item: &'a Arguments,
} }
impl AsFormat<ASTFormatContext> for Arguments { impl AsFormat<ASTFormatContext<'_>> for Arguments {
type Format<'a> = FormatArguments<'a>; type Format<'a> = FormatArguments<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -17,7 +17,7 @@ impl AsFormat<ASTFormatContext> for Arguments {
} }
} }
impl Format<ASTFormatContext> for FormatArguments<'_> { impl Format<ASTFormatContext<'_>> for FormatArguments<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let args = self.item; let args = self.item;

View file

@ -10,7 +10,7 @@ pub struct FormatBoolOp<'a> {
item: &'a BoolOp, item: &'a BoolOp,
} }
impl AsFormat<ASTFormatContext> for BoolOp { impl AsFormat<ASTFormatContext<'_>> for BoolOp {
type Format<'a> = FormatBoolOp<'a>; type Format<'a> = FormatBoolOp<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -18,7 +18,7 @@ impl AsFormat<ASTFormatContext> for BoolOp {
} }
} }
impl Format<ASTFormatContext> for FormatBoolOp<'_> { impl Format<ASTFormatContext<'_>> for FormatBoolOp<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let bool_op = self.item; let bool_op = self.item;
write!(f, [leading_comments(bool_op)])?; write!(f, [leading_comments(bool_op)])?;

View file

@ -12,7 +12,7 @@ pub(crate) struct Block<'a> {
body: &'a Body, body: &'a Body,
} }
impl Format<ASTFormatContext> for Block<'_> { impl Format<ASTFormatContext<'_>> for Block<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
for (i, stmt) in self.body.iter().enumerate() { for (i, stmt) in self.body.iter().enumerate() {
if i > 0 { if i > 0 {
@ -28,7 +28,7 @@ impl Format<ASTFormatContext> for Block<'_> {
write!(f, [empty_line()])?; write!(f, [empty_line()])?;
} }
TriviaKind::OwnLineComment(range) => { TriviaKind::OwnLineComment(range) => {
write!(f, [literal(range), hard_line_break()])?; write!(f, [literal(range, ContainsNewlines::No), hard_line_break()])?;
} }
_ => {} _ => {}
} }
@ -49,7 +49,7 @@ pub(crate) struct Statements<'a> {
suite: &'a [Stmt], suite: &'a [Stmt],
} }
impl Format<ASTFormatContext> for Statements<'_> { impl Format<ASTFormatContext<'_>> for Statements<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
for (i, stmt) in self.suite.iter().enumerate() { for (i, stmt) in self.suite.iter().enumerate() {
if i > 0 { if i > 0 {
@ -70,20 +70,18 @@ pub(crate) struct Literal {
range: TextRange, range: TextRange,
} }
impl Format<ASTFormatContext> for Literal { impl Format<ASTFormatContext<'_>> for Literal {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let text = f.context().contents(); source_text_slice(self.range, ContainsNewlines::Detect).fmt(f)
f.write_element(FormatElement::StaticTextSlice {
text,
range: self.range,
})
} }
} }
#[inline] #[inline]
pub(crate) const fn literal(range: TextRange) -> Literal { pub(crate) const fn literal(
Literal { range } range: TextRange,
newlines: ContainsNewlines,
) -> SourceTextSliceBuilder {
source_text_slice(range, newlines)
} }
pub(crate) const fn join_names(names: &[String]) -> JoinNames { pub(crate) const fn join_names(names: &[String]) -> JoinNames {

View file

@ -10,7 +10,7 @@ pub struct FormatCmpOp<'a> {
item: &'a CmpOp, item: &'a CmpOp,
} }
impl AsFormat<ASTFormatContext> for CmpOp { impl AsFormat<ASTFormatContext<'_>> for CmpOp {
type Format<'a> = FormatCmpOp<'a>; type Format<'a> = FormatCmpOp<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -18,7 +18,7 @@ impl AsFormat<ASTFormatContext> for CmpOp {
} }
} }
impl Format<ASTFormatContext> for FormatCmpOp<'_> { impl Format<ASTFormatContext<'_>> for FormatCmpOp<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let cmp_op = self.item; let cmp_op = self.item;
write!(f, [leading_comments(cmp_op)])?; write!(f, [leading_comments(cmp_op)])?;

View file

@ -11,7 +11,7 @@ pub(crate) struct LeadingComments<'a, T> {
item: &'a Attributed<T>, item: &'a Attributed<T>,
} }
impl<T> Format<ASTFormatContext> for LeadingComments<'_, T> { impl<T> Format<ASTFormatContext<'_>> for LeadingComments<'_, T> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
for trivia in &self.item.trivia { for trivia in &self.item.trivia {
if trivia.relationship.is_leading() { if trivia.relationship.is_leading() {
@ -20,7 +20,7 @@ impl<T> Format<ASTFormatContext> for LeadingComments<'_, T> {
write!(f, [empty_line()])?; write!(f, [empty_line()])?;
} }
TriviaKind::OwnLineComment(range) => { TriviaKind::OwnLineComment(range) => {
write!(f, [literal(range), hard_line_break()])?; write!(f, [literal(range, ContainsNewlines::No), hard_line_break()])?;
} }
_ => {} _ => {}
} }
@ -40,7 +40,7 @@ pub(crate) struct TrailingComments<'a, T> {
item: &'a Attributed<T>, item: &'a Attributed<T>,
} }
impl<T> Format<ASTFormatContext> for TrailingComments<'_, T> { impl<T> Format<ASTFormatContext<'_>> for TrailingComments<'_, T> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
for trivia in &self.item.trivia { for trivia in &self.item.trivia {
if trivia.relationship.is_trailing() { if trivia.relationship.is_trailing() {
@ -49,7 +49,7 @@ impl<T> Format<ASTFormatContext> for TrailingComments<'_, T> {
write!(f, [empty_line()])?; write!(f, [empty_line()])?;
} }
TriviaKind::OwnLineComment(range) => { TriviaKind::OwnLineComment(range) => {
write!(f, [literal(range), hard_line_break()])?; write!(f, [literal(range, ContainsNewlines::No), hard_line_break()])?;
} }
_ => {} _ => {}
} }
@ -69,7 +69,7 @@ pub(crate) struct EndOfLineComments<'a, T> {
item: &'a Attributed<T>, item: &'a Attributed<T>,
} }
impl<T> Format<ASTFormatContext> for EndOfLineComments<'_, T> { impl<T> Format<ASTFormatContext<'_>> for EndOfLineComments<'_, T> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let mut first = true; let mut first = true;
for range in self for range in self
@ -81,7 +81,7 @@ impl<T> Format<ASTFormatContext> for EndOfLineComments<'_, T> {
if std::mem::take(&mut first) { if std::mem::take(&mut first) {
write!(f, [line_suffix(&text(" "))])?; write!(f, [line_suffix(&text(" "))])?;
} }
write!(f, [line_suffix(&literal(range))])?; write!(f, [line_suffix(&literal(range, ContainsNewlines::No))])?;
} }
Ok(()) Ok(())
} }
@ -97,13 +97,13 @@ pub(crate) struct DanglingComments<'a, T> {
item: &'a Attributed<T>, item: &'a Attributed<T>,
} }
impl<T> Format<ASTFormatContext> for DanglingComments<'_, T> { impl<T> Format<ASTFormatContext<'_>> for DanglingComments<'_, T> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
for trivia in &self.item.trivia { for trivia in &self.item.trivia {
if trivia.relationship.is_dangling() { if trivia.relationship.is_dangling() {
if let TriviaKind::OwnLineComment(range) = trivia.kind { if let TriviaKind::OwnLineComment(range) = trivia.kind {
write!(f, [hard_line_break()])?; write!(f, [hard_line_break()])?;
write!(f, [literal(range)])?; write!(f, [literal(range, ContainsNewlines::No)])?;
write!(f, [hard_line_break()])?; write!(f, [hard_line_break()])?;
} }
} }

View file

@ -9,7 +9,7 @@ pub struct FormatComprehension<'a> {
item: &'a Comprehension, item: &'a Comprehension,
} }
impl AsFormat<ASTFormatContext> for Comprehension { impl AsFormat<ASTFormatContext<'_>> for Comprehension {
type Format<'a> = FormatComprehension<'a>; type Format<'a> = FormatComprehension<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -17,7 +17,7 @@ impl AsFormat<ASTFormatContext> for Comprehension {
} }
} }
impl Format<ASTFormatContext> for FormatComprehension<'_> { impl Format<ASTFormatContext<'_>> for FormatComprehension<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let comprehension = self.item; let comprehension = self.item;

View file

@ -11,7 +11,7 @@ pub struct FormatExcepthandler<'a> {
item: &'a Excepthandler, item: &'a Excepthandler,
} }
impl AsFormat<ASTFormatContext> for Excepthandler { impl AsFormat<ASTFormatContext<'_>> for Excepthandler {
type Format<'a> = FormatExcepthandler<'a>; type Format<'a> = FormatExcepthandler<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -19,7 +19,7 @@ impl AsFormat<ASTFormatContext> for Excepthandler {
} }
} }
impl Format<ASTFormatContext> for FormatExcepthandler<'_> { impl Format<ASTFormatContext<'_>> for FormatExcepthandler<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let excepthandler = self.item; let excepthandler = self.item;
let ExcepthandlerKind::ExceptHandler { type_, name, body } = &excepthandler.node; let ExcepthandlerKind::ExceptHandler { type_, name, body } = &excepthandler.node;

View file

@ -33,7 +33,7 @@ fn format_starred(
} }
fn format_name(f: &mut Formatter<ASTFormatContext>, expr: &Expr, _id: &str) -> FormatResult<()> { fn format_name(f: &mut Formatter<ASTFormatContext>, expr: &Expr, _id: &str) -> FormatResult<()> {
write!(f, [literal(expr.range())])?; write!(f, [literal(expr.range(), ContainsNewlines::No)])?;
write!(f, [end_of_line_comments(expr)])?; write!(f, [end_of_line_comments(expr)])?;
Ok(()) Ok(())
} }
@ -57,7 +57,7 @@ fn format_subscript(
if let TriviaKind::OwnLineComment(range) = trivia.kind { if let TriviaKind::OwnLineComment(range) = trivia.kind {
write!(f, [expand_parent()])?; write!(f, [expand_parent()])?;
write!(f, [hard_line_break()])?; write!(f, [hard_line_break()])?;
write!(f, [literal(range)])?; write!(f, [literal(range, ContainsNewlines::No)])?;
} }
} }
} }
@ -573,7 +573,7 @@ fn format_joined_str(
expr: &Expr, expr: &Expr,
_values: &[Expr], _values: &[Expr],
) -> FormatResult<()> { ) -> FormatResult<()> {
write!(f, [literal(expr.range())])?; write!(f, [literal(expr.range(), ContainsNewlines::Detect)])?;
write!(f, [end_of_line_comments(expr)])?; write!(f, [end_of_line_comments(expr)])?;
Ok(()) Ok(())
} }
@ -800,7 +800,7 @@ fn format_if_exp(
Ok(()) Ok(())
} }
impl Format<ASTFormatContext> for FormatExpr<'_> { impl Format<ASTFormatContext<'_>> for FormatExpr<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
if self.item.parentheses.is_always() { if self.item.parentheses.is_always() {
write!(f, [text("(")])?; write!(f, [text("(")])?;
@ -871,7 +871,7 @@ impl Format<ASTFormatContext> for FormatExpr<'_> {
if trivia.relationship.is_trailing() { if trivia.relationship.is_trailing() {
if let TriviaKind::OwnLineComment(range) = trivia.kind { if let TriviaKind::OwnLineComment(range) = trivia.kind {
write!(f, [expand_parent()])?; write!(f, [expand_parent()])?;
write!(f, [literal(range)])?; write!(f, [literal(range, ContainsNewlines::No)])?;
write!(f, [hard_line_break()])?; write!(f, [hard_line_break()])?;
} }
} }
@ -885,7 +885,7 @@ impl Format<ASTFormatContext> for FormatExpr<'_> {
} }
} }
impl AsFormat<ASTFormatContext> for Expr { impl AsFormat<ASTFormatContext<'_>> for Expr {
type Format<'a> = FormatExpr<'a>; type Format<'a> = FormatExpr<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {

View file

@ -10,7 +10,7 @@ pub struct FormatKeyword<'a> {
item: &'a Keyword, item: &'a Keyword,
} }
impl AsFormat<ASTFormatContext> for Keyword { impl AsFormat<ASTFormatContext<'_>> for Keyword {
type Format<'a> = FormatKeyword<'a>; type Format<'a> = FormatKeyword<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -18,7 +18,7 @@ impl AsFormat<ASTFormatContext> for Keyword {
} }
} }
impl Format<ASTFormatContext> for FormatKeyword<'_> { impl Format<ASTFormatContext<'_>> for FormatKeyword<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let keyword = self.item; let keyword = self.item;

View file

@ -11,7 +11,7 @@ pub struct FormatMatchCase<'a> {
item: &'a MatchCase, item: &'a MatchCase,
} }
impl AsFormat<ASTFormatContext> for MatchCase { impl AsFormat<ASTFormatContext<'_>> for MatchCase {
type Format<'a> = FormatMatchCase<'a>; type Format<'a> = FormatMatchCase<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -19,7 +19,7 @@ impl AsFormat<ASTFormatContext> for MatchCase {
} }
} }
impl Format<ASTFormatContext> for FormatMatchCase<'_> { impl Format<ASTFormatContext<'_>> for FormatMatchCase<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let MatchCase { let MatchCase {
pattern, pattern,

View file

@ -12,7 +12,7 @@ struct FloatAtom {
range: TextRange, range: TextRange,
} }
impl Format<ASTFormatContext> for FloatAtom { impl Format<ASTFormatContext<'_>> for FloatAtom {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let contents = f.context().contents(); let contents = f.context().contents();
@ -26,12 +26,15 @@ impl Format<ASTFormatContext> for FloatAtom {
} else { } else {
write!( write!(
f, f,
[literal(TextRange::new( [literal(
self.range.start(), TextRange::new(
self.range self.range.start(),
.start() self.range
.add(TextSize::try_from(dot_index).unwrap()) .start()
))] .add(TextSize::try_from(dot_index).unwrap())
),
ContainsNewlines::No
)]
)?; )?;
} }
@ -42,16 +45,19 @@ impl Format<ASTFormatContext> for FloatAtom {
} else { } else {
write!( write!(
f, f,
[literal(TextRange::new( [literal(
self.range TextRange::new(
.start() self.range
.add(TextSize::try_from(dot_index + 1).unwrap()), .start()
self.range.end() .add(TextSize::try_from(dot_index + 1).unwrap()),
))] self.range.end()
),
ContainsNewlines::No
)]
)?; )?;
} }
} else { } else {
write!(f, [literal(self.range)])?; write!(f, [literal(self.range, ContainsNewlines::No)])?;
} }
Ok(()) Ok(())
@ -68,7 +74,7 @@ pub(crate) struct FloatLiteral {
range: TextRange, range: TextRange,
} }
impl Format<ASTFormatContext> for FloatLiteral { impl Format<ASTFormatContext<'_>> for FloatLiteral {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let contents = f.context().contents(); let contents = f.context().contents();
@ -93,12 +99,15 @@ impl Format<ASTFormatContext> for FloatLiteral {
let plus = content[exponent_index + 1..].starts_with('+'); let plus = content[exponent_index + 1..].starts_with('+');
write!( write!(
f, f,
[literal(TextRange::new( [literal(
self.range TextRange::new(
.start() self.range.start().add(
.add(TextSize::try_from(exponent_index + 1 + usize::from(plus)).unwrap()), TextSize::try_from(exponent_index + 1 + usize::from(plus)).unwrap()
self.range.end() ),
))] self.range.end()
),
ContainsNewlines::No
)]
)?; )?;
} else { } else {
write!(f, [float_atom(self.range)])?; write!(f, [float_atom(self.range)])?;
@ -118,7 +127,7 @@ pub(crate) struct IntLiteral {
range: TextRange, range: TextRange,
} }
impl Format<ASTFormatContext> for IntLiteral { impl Format<ASTFormatContext<'_>> for IntLiteral {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let contents = f.context().contents(); let contents = f.context().contents();
@ -142,14 +151,14 @@ impl Format<ASTFormatContext> for IntLiteral {
)?; )?;
} else { } else {
// Use the existing source. // Use the existing source.
write!(f, [literal(self.range)])?; write!(f, [literal(self.range, ContainsNewlines::No)])?;
} }
return Ok(()); return Ok(());
} }
} }
write!(f, [literal(self.range)])?; write!(f, [literal(self.range, ContainsNewlines::No)])?;
Ok(()) Ok(())
} }
@ -165,20 +174,20 @@ pub(crate) struct ComplexLiteral {
range: TextRange, range: TextRange,
} }
impl Format<ASTFormatContext> for ComplexLiteral { impl Format<ASTFormatContext<'_>> for ComplexLiteral {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let contents = f.context().contents(); let contents = f.context().contents();
let content = &contents[self.range]; let content = &contents[self.range];
if content.ends_with('j') { if content.ends_with('j') {
write!(f, [literal(self.range)])?; write!(f, [literal(self.range, ContainsNewlines::No)])?;
} else if content.ends_with('J') { } else if content.ends_with('J') {
write!( write!(
f, f,
[literal(TextRange::new( [literal(
self.range.start(), TextRange::new(self.range.start(), self.range.end().sub(TextSize::from(1))),
self.range.end().sub(TextSize::from(1)) ContainsNewlines::No
))] )]
)?; )?;
write!(f, [text("j")])?; write!(f, [text("j")])?;
} else { } else {

View file

@ -10,7 +10,7 @@ pub struct FormatOperator<'a> {
item: &'a Operator, item: &'a Operator,
} }
impl AsFormat<ASTFormatContext> for Operator { impl AsFormat<ASTFormatContext<'_>> for Operator {
type Format<'a> = FormatOperator<'a>; type Format<'a> = FormatOperator<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -18,7 +18,7 @@ impl AsFormat<ASTFormatContext> for Operator {
} }
} }
impl Format<ASTFormatContext> for FormatOperator<'_> { impl Format<ASTFormatContext<'_>> for FormatOperator<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let operator = self.item; let operator = self.item;
write!(f, [leading_comments(operator)])?; write!(f, [leading_comments(operator)])?;

View file

@ -11,7 +11,7 @@ pub struct FormatPattern<'a> {
item: &'a Pattern, item: &'a Pattern,
} }
impl AsFormat<ASTFormatContext> for Pattern { impl AsFormat<ASTFormatContext<'_>> for Pattern {
type Format<'a> = FormatPattern<'a>; type Format<'a> = FormatPattern<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -19,7 +19,7 @@ impl AsFormat<ASTFormatContext> for Pattern {
} }
} }
impl Format<ASTFormatContext> for FormatPattern<'_> { impl Format<ASTFormatContext<'_>> for FormatPattern<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let pattern = self.item; let pattern = self.item;

View file

@ -756,7 +756,7 @@ pub struct FormatStmt<'a> {
item: &'a Stmt, item: &'a Stmt,
} }
impl Format<ASTFormatContext> for FormatStmt<'_> { impl Format<ASTFormatContext<'_>> for FormatStmt<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
write!(f, [leading_comments(self.item)])?; write!(f, [leading_comments(self.item)])?;
@ -939,7 +939,7 @@ impl Format<ASTFormatContext> for FormatStmt<'_> {
} }
} }
impl AsFormat<ASTFormatContext> for Stmt { impl AsFormat<ASTFormatContext<'_>> for Stmt {
type Format<'a> = FormatStmt<'a>; type Format<'a> = FormatStmt<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {

View file

@ -13,7 +13,7 @@ pub(crate) struct StringLiteralPart {
range: TextRange, range: TextRange,
} }
impl Format<ASTFormatContext> for StringLiteralPart { impl Format<ASTFormatContext<'_>> for StringLiteralPart {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let contents = f.context().contents(); let contents = f.context().contents();
@ -115,7 +115,7 @@ pub(crate) struct StringLiteral<'a> {
expr: &'a Expr, expr: &'a Expr,
} }
impl Format<ASTFormatContext> for StringLiteral<'_> { impl Format<ASTFormatContext<'_>> for StringLiteral<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let expr = self.expr; let expr = self.expr;

View file

@ -9,7 +9,7 @@ pub struct FormatUnaryOp<'a> {
item: &'a UnaryOp, item: &'a UnaryOp,
} }
impl AsFormat<ASTFormatContext> for UnaryOp { impl AsFormat<ASTFormatContext<'_>> for UnaryOp {
type Format<'a> = FormatUnaryOp<'a>; type Format<'a> = FormatUnaryOp<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -17,7 +17,7 @@ impl AsFormat<ASTFormatContext> for UnaryOp {
} }
} }
impl Format<ASTFormatContext> for FormatUnaryOp<'_> { impl Format<ASTFormatContext<'_>> for FormatUnaryOp<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let unary_op = self.item; let unary_op = self.item;
write!( write!(

View file

@ -9,7 +9,7 @@ pub struct FormatWithitem<'a> {
item: &'a Withitem, item: &'a Withitem,
} }
impl AsFormat<ASTFormatContext> for Withitem { impl AsFormat<ASTFormatContext<'_>> for Withitem {
type Format<'a> = FormatWithitem<'a>; type Format<'a> = FormatWithitem<'a>;
fn format(&self) -> Self::Format<'_> { fn format(&self) -> Self::Format<'_> {
@ -17,7 +17,7 @@ impl AsFormat<ASTFormatContext> for Withitem {
} }
} }
impl Format<ASTFormatContext> for FormatWithitem<'_> { impl Format<ASTFormatContext<'_>> for FormatWithitem<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> { fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
let withitem = self.item; let withitem = self.item;