Skip to content

Commit

Permalink
Add LineSuffix reserved width (#6830)
Browse files Browse the repository at this point in the history
Thanks for working on this.
  • Loading branch information
cnpryer authored Aug 28, 2023
1 parent 6bc1ba6 commit 039694a
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 28 deletions.
56 changes: 44 additions & 12 deletions crates/ruff_formatter/src/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,46 +435,78 @@ fn debug_assert_no_newlines(text: &str) {
debug_assert!(!text.contains('\r'), "The content '{text}' contains an unsupported '\\r' line terminator character but text must only use line feeds '\\n' as line separator. Use '\\n' instead of '\\r' and '\\r\\n' to insert a line break in strings.");
}

/// Pushes some content to the end of the current line
/// Pushes some content to the end of the current line.
///
/// ## Examples
///
/// ```
/// use ruff_formatter::{format};
/// ```rust
/// use ruff_formatter::format;
/// use ruff_formatter::prelude::*;
///
/// fn main() -> FormatResult<()> {
/// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [
/// text("a"),
/// line_suffix(&text("c")),
/// line_suffix(&text("c"), 0),
/// text("b")
/// ])?;
///
/// assert_eq!(
/// "abc",
/// elements.print()?.as_code()
/// );
/// assert_eq!("abc", elements.print()?.as_code());
/// # Ok(())
/// # }
/// ```
///
/// Provide reserved width for the line suffix to include it during measurement.
/// ```rust
/// use ruff_formatter::{format, format_args, LineWidth, SimpleFormatContext, SimpleFormatOptions};
/// use ruff_formatter::prelude::*;
///
/// # fn main() -> FormatResult<()> {
/// let context = SimpleFormatContext::new(SimpleFormatOptions {
/// line_width: LineWidth::try_from(10).unwrap(),
/// ..SimpleFormatOptions::default()
/// });
///
/// let elements = format!(context, [
/// // Breaks
/// group(&format_args![
/// if_group_breaks(&text("(")),
/// soft_block_indent(&format_args![text("a"), line_suffix(&text(" // a comment"), 13)]),
/// if_group_breaks(&text(")"))
/// ]),
///
/// // Fits
/// group(&format_args![
/// if_group_breaks(&text("(")),
/// soft_block_indent(&format_args![text("a"), line_suffix(&text(" // a comment"), 0)]),
/// if_group_breaks(&text(")"))
/// ]),
/// ])?;
/// # assert_eq!("(\n\ta // a comment\n)a // a comment", elements.print()?.as_code());
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn line_suffix<Content, Context>(inner: &Content) -> LineSuffix<Context>
pub fn line_suffix<Content, Context>(inner: &Content, reserved_width: u32) -> LineSuffix<Context>
where
Content: Format<Context>,
{
LineSuffix {
content: Argument::new(inner),
reserved_width,
}
}

#[derive(Copy, Clone)]
pub struct LineSuffix<'a, Context> {
content: Argument<'a, Context>,
reserved_width: u32,
}

impl<Context> Format<Context> for LineSuffix<'_, Context> {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
f.write_element(FormatElement::Tag(StartLineSuffix));
f.write_element(FormatElement::Tag(StartLineSuffix {
reserved_width: self.reserved_width,
}));
Arguments::from(&self.content).fmt(f)?;
f.write_element(FormatElement::Tag(EndLineSuffix));

Expand All @@ -501,7 +533,7 @@ impl<Context> std::fmt::Debug for LineSuffix<'_, Context> {
/// # fn main() -> FormatResult<()> {
/// let elements = format!(SimpleFormatContext::default(), [
/// text("a"),
/// line_suffix(&text("c")),
/// line_suffix(&text("c"), 0),
/// text("b"),
/// line_suffix_boundary(),
/// text("d")
Expand Down
16 changes: 13 additions & 3 deletions crates/ruff_formatter/src/format_element/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,8 +459,16 @@ impl Format<IrFormatContext<'_>> for &[FormatElement] {
)?;
}

StartLineSuffix => {
write!(f, [text("line_suffix(")])?;
StartLineSuffix { reserved_width } => {
write!(
f,
[
text("line_suffix("),
dynamic_text(&std::format!("{reserved_width:?}"), None),
text(","),
space(),
]
)?;
}

StartVerbatim(_) => {
Expand Down Expand Up @@ -672,7 +680,9 @@ impl FormatElements for [FormatElement] {
match element {
// Line suffix
// Ignore if any of its content breaks
FormatElement::Tag(Tag::StartLineSuffix | Tag::StartFitsExpanded(_)) => {
FormatElement::Tag(
Tag::StartLineSuffix { reserved_width: _ } | Tag::StartFitsExpanded(_),
) => {
ignore_depth += 1;
}
FormatElement::Tag(Tag::EndLineSuffix | Tag::EndFitsExpanded) => {
Expand Down
11 changes: 7 additions & 4 deletions crates/ruff_formatter/src/format_element/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ pub enum Tag {
StartEntry,
EndEntry,

/// Delay the printing of its content until the next line break
StartLineSuffix,
/// Delay the printing of its content until the next line break. Using reserved width will include
/// the associated line suffix during measurement.
StartLineSuffix {
reserved_width: u32,
},
EndLineSuffix,

/// A token that tracks tokens/nodes that are printed as verbatim.
Expand Down Expand Up @@ -96,7 +99,7 @@ impl Tag {
| Tag::StartIndentIfGroupBreaks(_)
| Tag::StartFill
| Tag::StartEntry
| Tag::StartLineSuffix
| Tag::StartLineSuffix { reserved_width: _ }
| Tag::StartVerbatim(_)
| Tag::StartLabelled(_)
| Tag::StartFitsExpanded(_)
Expand All @@ -122,7 +125,7 @@ impl Tag {
StartIndentIfGroupBreaks(_) | EndIndentIfGroupBreaks => TagKind::IndentIfGroupBreaks,
StartFill | EndFill => TagKind::Fill,
StartEntry | EndEntry => TagKind::Entry,
StartLineSuffix | EndLineSuffix => TagKind::LineSuffix,
StartLineSuffix { reserved_width: _ } | EndLineSuffix => TagKind::LineSuffix,
StartVerbatim(_) | EndVerbatim => TagKind::Verbatim,
StartLabelled(_) | EndLabelled => TagKind::Labelled,
StartFitsExpanded { .. } | EndFitsExpanded => TagKind::FitsExpanded,
Expand Down
41 changes: 38 additions & 3 deletions crates/ruff_formatter/src/printer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ impl<'a> Printer<'a> {
stack.push(TagKind::IndentIfGroupBreaks, args);
}

FormatElement::Tag(StartLineSuffix) => {
FormatElement::Tag(StartLineSuffix { reserved_width }) => {
self.state.line_width += reserved_width;
self.state
.line_suffixes
.extend(args, queue.iter_content(TagKind::LineSuffix));
Expand Down Expand Up @@ -1191,7 +1192,11 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
}
}

FormatElement::Tag(StartLineSuffix) => {
FormatElement::Tag(StartLineSuffix { reserved_width }) => {
self.state.line_width += reserved_width;
if self.state.line_width > self.options().print_width.into() {
return Ok(Fits::No);
}
self.queue.skip_content(TagKind::LineSuffix);
self.state.has_line_suffix = true;
}
Expand Down Expand Up @@ -1727,12 +1732,42 @@ two lines`,
text("]")
]),
text(";"),
line_suffix(&format_args![space(), text("// trailing")])
line_suffix(&format_args![space(), text("// trailing")], 0)
]);

assert_eq!(printed.as_code(), "[1, 2, 3]; // trailing");
}

#[test]
fn line_suffix_with_reserved_width() {
let printed = format(&format_args![
group(&format_args![
text("["),
soft_block_indent(&format_with(|f| {
f.fill()
.entry(
&soft_line_break_or_space(),
&format_args!(text("1"), text(",")),
)
.entry(
&soft_line_break_or_space(),
&format_args!(text("2"), text(",")),
)
.entry(
&soft_line_break_or_space(),
&format_args!(text("3"), if_group_breaks(&text(","))),
)
.finish()
})),
text("]")
]),
text(";"),
line_suffix(&format_args![space(), text("// Using reserved width causes this content to not fit even though it's a line suffix element")], 93)
]);

assert_eq!(printed.as_code(), "[\n 1, 2, 3\n]; // Using reserved width causes this content to not fit even though it's a line suffix element");
}

#[test]
fn conditional_with_group_id_in_fits() {
let content = format_with(|f| {
Expand Down
15 changes: 9 additions & 6 deletions crates/ruff_python_formatter/src/comments/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,18 +150,21 @@ impl Format<PyFormatContext<'_>> for FormatTrailingComments<'_> {
write!(
f,
[
line_suffix(&format_args![
empty_lines(lines_before_comment),
format_comment(trailing)
]),
line_suffix(
&format_args![
empty_lines(lines_before_comment),
format_comment(trailing)
],
0
),
expand_parent()
]
)?;
} else {
write!(
f,
[
line_suffix(&format_args![space(), space(), format_comment(trailing)]),
line_suffix(&format_args![space(), space(), format_comment(trailing)], 0),
expand_parent()
]
)?;
Expand Down Expand Up @@ -266,7 +269,7 @@ impl Format<PyFormatContext<'_>> for FormatDanglingOpenParenthesisComments<'_> {
write!(
f,
[
line_suffix(&format_args!(space(), space(), format_comment(comment))),
line_suffix(&format_args!(space(), space(), format_comment(comment)), 0),
expand_parent()
]
)?;
Expand Down

0 comments on commit 039694a

Please sign in to comment.