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::prelude::tag::{DedentMode, GroupMode, LabelId};
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 ruff_text_size::TextRange;
use std::cell::Cell;
use std::marker::PhantomData;
use std::num::NonZeroU8;
use std::rc::Rc;
use Tag::*;
/// 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
pub fn static_text_slice(text: Rc<str>, range: TextRange) -> StaticTextSlice {
debug_assert_no_newlines(&text[range]);
StaticTextSlice { text, range }
}
#[derive(Eq, PartialEq)]
pub struct StaticTextSlice {
text: Rc<str>,
/// Emits a text as it is written in the source document. Optimized to avoid allocations.
pub const fn source_text_slice(
range: TextRange,
}
impl<Context> Format<Context> for StaticTextSlice {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
f.write_element(FormatElement::StaticTextSlice {
text: self.text.clone(),
range: self.range,
})
newlines: ContainsNewlines,
) -> SourceTextSliceBuilder {
SourceTextSliceBuilder {
range,
new_lines: newlines,
}
}
impl std::fmt::Debug for StaticTextSlice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::write!(f, "StaticTextSlice({})", &self.text[self.range])
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum ContainsNewlines {
/// 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 crate::format_element::tag::{LabelId, Tag};
use crate::source_code::SourceCodeSlice;
use crate::TagKind;
use ruff_text_size::{TextRange, TextSize};
use ruff_text_size::TextSize;
/// Language agnostic IR for formatting source code.
///
@ -39,8 +40,12 @@ pub enum FormatElement {
text: Box<str>,
},
/// Token constructed by slicing a defined range from a static string.
StaticTextSlice { text: Rc<str>, range: TextRange },
/// Text that gets emitted as it is in the source code. Optimized to avoid any allocations.
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
/// line suffixes, potentially by inserting a hard line break.
@ -70,9 +75,14 @@ impl std::fmt::Debug for FormatElement {
FormatElement::DynamicText { text, .. } => {
fmt.debug_tuple("DynamicText").field(text).finish()
}
FormatElement::StaticTextSlice { text, .. } => {
fmt.debug_tuple("Text").field(text).finish()
}
FormatElement::SourceCodeSlice {
slice,
contains_newlines,
} => fmt
.debug_tuple("Text")
.field(slice)
.field(contains_newlines)
.finish(),
FormatElement::LineSuffixBoundary => write!(fmt, "LineSuffixBoundary"),
FormatElement::BestFitting(best_fitting) => {
fmt.debug_tuple("BestFitting").field(&best_fitting).finish()
@ -221,7 +231,7 @@ impl FormatElement {
pub const fn is_text(&self) -> bool {
matches!(
self,
FormatElement::StaticTextSlice { .. }
FormatElement::SourceCodeSlice { .. }
| FormatElement::DynamicText { .. }
| FormatElement::StaticText { .. }
)
@ -240,7 +250,9 @@ impl FormatElements for FormatElement {
FormatElement::Line(line_mode) => matches!(line_mode, LineMode::Hard | LineMode::Empty),
FormatElement::StaticText { 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(),
// 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.
@ -380,20 +392,19 @@ mod sizes {
// be recomputed at a later point in time?
// 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;
assert_eq_size!(TextRange, [u8; 8]);
assert_eq_size!(VerbatimKind, [u8; 8]);
assert_eq_size!(Interned, [u8; 16]);
assert_eq_size!(BestFitting, [u8; 16]);
assert_eq_size!(ruff_text_size::TextRange, [u8; 8]);
assert_eq_size!(crate::prelude::tag::VerbatimKind, [u8; 8]);
assert_eq_size!(crate::prelude::Interned, [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))]
assert_eq_size!(crate::format_element::Tag, [u8; 16]);
#[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::*;
use crate::printer::LineEnding;
use crate::source_code::SourceCode;
use crate::{format, write};
use crate::{
BufferExtensions, Format, FormatContext, FormatElement, FormatOptions, FormatResult, Formatter,
@ -80,7 +81,9 @@ impl Document {
}
FormatElement::StaticText { 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::Line(LineMode::Hard | LineMode::Empty) => true,
_ => false,
@ -99,6 +102,13 @@ impl Document {
let mut interned: FxHashMap<&Interned, bool> = FxHashMap::default();
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 {
@ -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 {
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");
f.write_str(
@ -129,18 +144,33 @@ impl std::fmt::Display for Document {
}
}
#[derive(Clone, Default, Debug)]
struct IrFormatContext {
#[derive(Clone, Debug)]
struct IrFormatContext<'a> {
/// The interned elements that have been printed to this point
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;
fn options(&self) -> &Self::Options {
&IrFormatOptions
}
fn source_code(&self) -> SourceCode {
self.source_code
}
}
#[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<()> {
use Tag::*;
@ -189,7 +219,7 @@ impl Format<IrFormatContext> for &[FormatElement] {
element @ FormatElement::Space
| element @ FormatElement::StaticText { .. }
| element @ FormatElement::DynamicText { .. }
| element @ FormatElement::StaticTextSlice { .. } => {
| element @ FormatElement::SourceCodeSlice { .. } => {
if !in_text {
write!(f, [text("\"")])?;
}
@ -489,7 +519,7 @@ impl Format<IrFormatContext> for &[FormatElement] {
struct ContentArrayStart;
impl Format<IrFormatContext> for ContentArrayStart {
impl Format<IrFormatContext<'_>> for ContentArrayStart {
fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> {
use Tag::*;
@ -505,7 +535,7 @@ impl Format<IrFormatContext> for ContentArrayStart {
struct ContentArrayEnd;
impl Format<IrFormatContext> for ContentArrayEnd {
impl Format<IrFormatContext<'_>> for ContentArrayEnd {
fn fmt(&self, f: &mut Formatter<IrFormatContext>) -> FormatResult<()> {
use Tag::*;
f.write_elements([
@ -615,8 +645,9 @@ impl FormatElements for [FormatElement] {
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::SimpleFormatContext;
use crate::{format, format_args, write};
use crate::{SimpleFormatContext, SourceCode};
use ruff_text_size::{TextRange, TextSize};
#[test]
fn display_elements() {
@ -641,7 +672,51 @@ mod tests {
let document = formatted.into_document();
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#"[
group([
"(",
@ -677,7 +752,7 @@ mod tests {
]);
assert_eq!(
&std::format!("{document}"),
&std::format!("{}", document.display(SourceCode::default())),
r#"[
"[",
group([

View file

@ -33,6 +33,7 @@ pub mod group_id;
pub mod macros;
pub mod prelude;
pub mod printer;
mod source_code;
use crate::formatter::Formatter;
use crate::group_id::UniqueGroupIdBuilder;
@ -47,6 +48,7 @@ pub use buffer::{
VecBuffer,
};
pub use builders::BestFitting;
pub use source_code::{SourceCode, SourceCodeSlice};
pub use crate::diagnostics::{ActualStart, FormatError, InvalidDocumentError, PrintError};
pub use format_element::{normalize_newlines, FormatElement, LINE_TERMINATORS};
@ -202,6 +204,9 @@ pub trait FormatContext {
/// Returns the formatting 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.
@ -219,11 +224,20 @@ pub trait FormatOptions {
#[derive(Debug, Default, Eq, PartialEq)]
pub struct SimpleFormatContext {
options: SimpleFormatOptions,
source_code: String,
}
impl SimpleFormatContext {
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 {
&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 indent_style: IndentStyle,
pub line_width: LineWidth,
@ -302,15 +320,18 @@ where
Context: FormatContext,
{
pub fn print(&self) -> PrintResult<Printed> {
let source_code = self.context.source_code();
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)
}
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 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)
}

View file

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

View file

@ -10,7 +10,7 @@ pub struct FormatAlias<'a> {
item: &'a Alias,
}
impl AsFormat<ASTFormatContext> for Alias {
impl AsFormat<ASTFormatContext<'_>> for Alias {
type Format<'a> = FormatAlias<'a>;
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<()> {
let alias = self.item;

View file

@ -10,7 +10,7 @@ pub struct FormatArg<'a> {
item: &'a Arg,
}
impl AsFormat<ASTFormatContext> for Arg {
impl AsFormat<ASTFormatContext<'_>> for Arg {
type Format<'a> = FormatArg<'a>;
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<()> {
let arg = self.item;

View file

@ -9,7 +9,7 @@ pub struct FormatArguments<'a> {
item: &'a Arguments,
}
impl AsFormat<ASTFormatContext> for Arguments {
impl AsFormat<ASTFormatContext<'_>> for Arguments {
type Format<'a> = FormatArguments<'a>;
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<()> {
let args = self.item;

View file

@ -10,7 +10,7 @@ pub struct FormatBoolOp<'a> {
item: &'a BoolOp,
}
impl AsFormat<ASTFormatContext> for BoolOp {
impl AsFormat<ASTFormatContext<'_>> for BoolOp {
type Format<'a> = FormatBoolOp<'a>;
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<()> {
let bool_op = self.item;
write!(f, [leading_comments(bool_op)])?;

View file

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

View file

@ -10,7 +10,7 @@ pub struct FormatCmpOp<'a> {
item: &'a CmpOp,
}
impl AsFormat<ASTFormatContext> for CmpOp {
impl AsFormat<ASTFormatContext<'_>> for CmpOp {
type Format<'a> = FormatCmpOp<'a>;
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<()> {
let cmp_op = self.item;
write!(f, [leading_comments(cmp_op)])?;

View file

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

View file

@ -9,7 +9,7 @@ pub struct FormatComprehension<'a> {
item: &'a Comprehension,
}
impl AsFormat<ASTFormatContext> for Comprehension {
impl AsFormat<ASTFormatContext<'_>> for Comprehension {
type Format<'a> = FormatComprehension<'a>;
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<()> {
let comprehension = self.item;

View file

@ -11,7 +11,7 @@ pub struct FormatExcepthandler<'a> {
item: &'a Excepthandler,
}
impl AsFormat<ASTFormatContext> for Excepthandler {
impl AsFormat<ASTFormatContext<'_>> for Excepthandler {
type Format<'a> = FormatExcepthandler<'a>;
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<()> {
let excepthandler = self.item;
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<()> {
write!(f, [literal(expr.range())])?;
write!(f, [literal(expr.range(), ContainsNewlines::No)])?;
write!(f, [end_of_line_comments(expr)])?;
Ok(())
}
@ -57,7 +57,7 @@ fn format_subscript(
if let TriviaKind::OwnLineComment(range) = trivia.kind {
write!(f, [expand_parent()])?;
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,
_values: &[Expr],
) -> FormatResult<()> {
write!(f, [literal(expr.range())])?;
write!(f, [literal(expr.range(), ContainsNewlines::Detect)])?;
write!(f, [end_of_line_comments(expr)])?;
Ok(())
}
@ -800,7 +800,7 @@ fn format_if_exp(
Ok(())
}
impl Format<ASTFormatContext> for FormatExpr<'_> {
impl Format<ASTFormatContext<'_>> for FormatExpr<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
if self.item.parentheses.is_always() {
write!(f, [text("(")])?;
@ -871,7 +871,7 @@ impl Format<ASTFormatContext> for FormatExpr<'_> {
if trivia.relationship.is_trailing() {
if let TriviaKind::OwnLineComment(range) = trivia.kind {
write!(f, [expand_parent()])?;
write!(f, [literal(range)])?;
write!(f, [literal(range, ContainsNewlines::No)])?;
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>;
fn format(&self) -> Self::Format<'_> {

View file

@ -10,7 +10,7 @@ pub struct FormatKeyword<'a> {
item: &'a Keyword,
}
impl AsFormat<ASTFormatContext> for Keyword {
impl AsFormat<ASTFormatContext<'_>> for Keyword {
type Format<'a> = FormatKeyword<'a>;
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<()> {
let keyword = self.item;

View file

@ -11,7 +11,7 @@ pub struct FormatMatchCase<'a> {
item: &'a MatchCase,
}
impl AsFormat<ASTFormatContext> for MatchCase {
impl AsFormat<ASTFormatContext<'_>> for MatchCase {
type Format<'a> = FormatMatchCase<'a>;
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<()> {
let MatchCase {
pattern,

View file

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

View file

@ -10,7 +10,7 @@ pub struct FormatOperator<'a> {
item: &'a Operator,
}
impl AsFormat<ASTFormatContext> for Operator {
impl AsFormat<ASTFormatContext<'_>> for Operator {
type Format<'a> = FormatOperator<'a>;
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<()> {
let operator = self.item;
write!(f, [leading_comments(operator)])?;

View file

@ -11,7 +11,7 @@ pub struct FormatPattern<'a> {
item: &'a Pattern,
}
impl AsFormat<ASTFormatContext> for Pattern {
impl AsFormat<ASTFormatContext<'_>> for Pattern {
type Format<'a> = FormatPattern<'a>;
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<()> {
let pattern = self.item;

View file

@ -756,7 +756,7 @@ pub struct FormatStmt<'a> {
item: &'a Stmt,
}
impl Format<ASTFormatContext> for FormatStmt<'_> {
impl Format<ASTFormatContext<'_>> for FormatStmt<'_> {
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
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>;
fn format(&self) -> Self::Format<'_> {

View file

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

View file

@ -9,7 +9,7 @@ pub struct FormatUnaryOp<'a> {
item: &'a UnaryOp,
}
impl AsFormat<ASTFormatContext> for UnaryOp {
impl AsFormat<ASTFormatContext<'_>> for UnaryOp {
type Format<'a> = FormatUnaryOp<'a>;
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<()> {
let unary_op = self.item;
write!(

View file

@ -9,7 +9,7 @@ pub struct FormatWithitem<'a> {
item: &'a Withitem,
}
impl AsFormat<ASTFormatContext> for Withitem {
impl AsFormat<ASTFormatContext<'_>> for Withitem {
type Format<'a> = FormatWithitem<'a>;
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<()> {
let withitem = self.item;