Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions src/chains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,8 +566,13 @@ impl Rewrite for Chain {

formatter.format_root(&self.parent, context, shape)?;
if let Some(result) = formatter.pure_root() {
return wrap_str(result, context.config.max_width(), shape)
.max_width_error(shape.width, self.parent.span);
return wrap_str(
context.config.style_edition(),
result,
context.config.max_width(),
shape,
)
.max_width_error(shape.width, self.parent.span);
}

let first = self.children.first().unwrap_or(&self.parent);
Expand All @@ -582,7 +587,13 @@ impl Rewrite for Chain {
formatter.format_last_child(context, shape, child_shape)?;

let result = formatter.join_rewrites(context, child_shape)?;
wrap_str(result, context.config.max_width(), shape).max_width_error(shape.width, full_span)
wrap_str(
context.config.style_edition(),
result,
context.config.max_width(),
shape,
)
.max_width_error(shape.width, full_span)
}
}

Expand Down Expand Up @@ -973,7 +984,12 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
.visual_indent(self.offset)
.sub_width(self.offset, item.span)?;
let rewrite = item.rewrite_result(context, child_shape)?;
if filtered_str_fits(&rewrite, context.config.max_width(), shape) {
if filtered_str_fits(
context.config.style_edition(),
&rewrite,
context.config.max_width(),
shape,
) {
root_rewrite.push_str(&rewrite);
} else {
// We couldn't fit in at the visual indent, try the last
Expand Down
22 changes: 19 additions & 3 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ pub(crate) fn format_expr(
ast::ExprKind::MacCall(ref mac) => {
rewrite_macro(mac, context, shape, MacroPosition::Expression).or_else(|_| {
wrap_str(
context.config.style_edition(),
context.snippet(expr.span).to_owned(),
context.config.max_width(),
shape,
Expand Down Expand Up @@ -1340,6 +1341,7 @@ pub(crate) fn rewrite_literal(
token::LitKind::Integer => rewrite_int_lit(context, token_lit, span, shape),
token::LitKind::Float => rewrite_float_lit(context, token_lit, span, shape),
_ => wrap_str(
context.config.style_edition(),
context.snippet(span).to_owned(),
context.config.max_width(),
shape,
Expand All @@ -1360,8 +1362,13 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) ->
{
return Ok(string_lit.to_owned());
} else {
return wrap_str(string_lit.to_owned(), context.config.max_width(), shape)
.max_width_error(shape.width, span);
return wrap_str(
context.config.style_edition(),
string_lit.to_owned(),
context.config.max_width(),
shape,
)
.max_width_error(shape.width, span);
}
}

Expand Down Expand Up @@ -1396,6 +1403,7 @@ fn rewrite_int_lit(
};
if let Some(hex_lit) = hex_lit {
return wrap_str(
context.config.style_edition(),
format!(
"0x{}{}",
hex_lit,
Expand All @@ -1409,6 +1417,7 @@ fn rewrite_int_lit(
}

wrap_str(
context.config.style_edition(),
context.snippet(span).to_owned(),
context.config.max_width(),
shape,
Expand All @@ -1427,6 +1436,7 @@ fn rewrite_float_lit(
FloatLiteralTrailingZero::Preserve
) {
return wrap_str(
context.config.style_edition(),
context.snippet(span).to_owned(),
context.config.max_width(),
shape,
Expand Down Expand Up @@ -1468,6 +1478,7 @@ fn rewrite_float_lit(
""
};
wrap_str(
context.config.style_edition(),
format!(
"{}{}{}{}{}",
integer_part,
Expand Down Expand Up @@ -2321,7 +2332,12 @@ fn choose_rhs<R: Rewrite>(

match (orig_rhs, new_rhs) {
(Ok(ref orig_rhs), Ok(ref new_rhs))
if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) =>
if !filtered_str_fits(
context.config.style_edition(),
&new_rhs,
context.config.max_width(),
new_shape,
) =>
{
Ok(format!("{before_space_str}{orig_rhs}"))
}
Expand Down
7 changes: 6 additions & 1 deletion src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,12 @@ impl MacroBranch {
}
};

if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) {
if !filtered_str_fits(
config.style_edition(),
&new_body_snippet.snippet,
config.max_width(),
shape,
) {
return Err(RewriteError::ExceedsMaxWidth {
configured_width: shape.width,
span: self.span,
Expand Down
7 changes: 6 additions & 1 deletion src/pairs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,12 @@ fn rewrite_pairs_one_line<T: Rewrite>(
return None;
}

wrap_str(result, context.config.max_width(), shape)
wrap_str(
context.config.style_edition(),
result,
context.config.max_width(),
shape,
)
}

fn rewrite_pairs_multiline<T: Rewrite>(
Expand Down
7 changes: 6 additions & 1 deletion src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,12 @@ pub(crate) fn rewrite_string<'a>(
}

result.push_str(fmt.closer);
wrap_str(result, fmt.config.max_width(), fmt.shape)
wrap_str(
fmt.config.style_edition(),
result,
fmt.config.max_width(),
fmt.shape,
)
}

/// Returns the index to the end of the URL if the split at index of the given string includes a
Expand Down
93 changes: 78 additions & 15 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,36 +400,99 @@ macro_rules! skip_out_of_file_lines_range_visitor {

// Wraps String in an Option. Returns Some when the string adheres to the
// Rewrite constraints defined for the Rewrite trait and None otherwise.
pub(crate) fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option<String> {
if filtered_str_fits(&s, max_width, shape) {
pub(crate) fn wrap_str(
style_edition: StyleEdition,
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't sure if it was better to pass in StyleEdition or Context<'_> here. Since max_width was already present I opted for StyleEdition

s: String,
max_width: usize,
shape: Shape,
) -> Option<String> {
if filtered_str_fits(style_edition, &s, max_width, shape) {
Some(s)
} else {
None
}
}

pub(crate) fn filtered_str_fits(snippet: &str, max_width: usize, shape: Shape) -> bool {
pub(crate) fn filtered_str_fits(
style_edition: StyleEdition,
snippet: &str,
max_width: usize,
shape: Shape,
) -> bool {
use crate::comment::{FullCodeCharKind, LineClasses};

let snippet = &filter_normal_code(snippet);
if !snippet.is_empty() {
// First line must fits with `shape.width`.
if first_line_width(snippet) > shape.width {
return false;
}
let line_classes = {
if style_edition >= StyleEdition::Edition2027 {
// Collect line classifications to check for string content.
let line_classes: Vec<_> = LineClasses::new(snippet).collect();

// First line must fit with `shape.width`, unless it's a multi-line string
// literal that starts on this line - string content cannot be shortened.
let first_line_width = first_line_width(snippet);
if first_line_width > shape.width {
// Only allow the exception for multi-line strings (StartString indicates
// the string continues on subsequent lines).
let is_multiline = line_classes.len() > 1;
let first_is_multiline_string = is_multiline
&& line_classes
.first()
.is_some_and(|(kind, _)| *kind == FullCodeCharKind::StartString);
if !first_is_multiline_string {
return false;
}
}
line_classes
} else {
// First line must fits with `shape.width`.
if first_line_width(snippet) > shape.width {
return false;
}
vec![]
}
};

// If the snippet does not include newline, we are done.
if is_single_line(snippet) {
return true;
}

// The other lines must fit within the maximum width.
if snippet
.lines()
.skip(1)
.any(|line| unicode_str_width(line) > max_width)
{
return false;
if style_edition < StyleEdition::Edition2027 {
if snippet
.lines()
.skip(1)
.any(|line| unicode_str_width(line) > max_width)
{
return false;
}
}

// Exception: lines that are inside or end a multi-line string literal
// may exceed max_width since string content cannot be reformatted.
let mut last_line_is_string = false;
for (i, (kind, line)) in line_classes.iter().enumerate() {
if i == 0 {
continue; // First line already checked above
}
// Track if the last line is string content
last_line_is_string =
*kind == FullCodeCharKind::InString || *kind == FullCodeCharKind::EndString;

if unicode_str_width(line) > max_width {
// Allow lines that are string continuations (InString) or
// end a string (EndString) to exceed max_width.
if !last_line_is_string {
return false;
}
}
}

// A special check for the last line, since the caller may
// place trailing characters on this line.
if last_line_width(snippet) > shape.used_width() + shape.width {
// place trailing characters on this line. Skip this check if the
// last line is string content (which cannot be reformatted).
if !last_line_is_string && last_line_width(snippet) > shape.used_width() + shape.width {
return false;
}
}
Expand Down
39 changes: 39 additions & 0 deletions tests/source/issue-6769.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// rustfmt-style_edition: 2027

fn test() {
let foo = [
(b"Welcome to Ringboard!" as &[u8], &em),
(
b"Ringboard is a fast, efficient, and composable clipboard manager for Linux."
, &em,
),
(
b"It supports both Wayland and X11 along with multiple clients to manage your \
clipboard history."
, &em,
),
(
b"Clients include a standard GUI, an interactive TUI, and a CLI for all your scripting \
needs."
, &em,
),
(
"Ringboard can copy arbitrary bytes—that includes images!".as_bytes(),
&em,
),
(
b"Plaintext and RegEx search are available for fast entry retrieval.",
&em,
),
(b"Enjoy this image from our AI overlords:", &em),
(
include_bytes!("../logo.jpeg") as &[u8],
&MimeType::from("image/jpeg").unwrap(),
),
(
b"Finally, it's worth mentioning that Ringboard is extremely efficient, performant, \
and scalable."
, &em,
),
];
}
28 changes: 28 additions & 0 deletions tests/source/issue-6778.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// rustfmt-style_edition: 2027
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified that the tests breaks without this (git revert d46365d25325954cbc1c1475d840657f519c6818 && cargo test)


#![allow(dead_code)]

struct Parameter {
required: bool,
description: &'static str,
}

pub struct Test;

impl Test {
fn parameters(&self) -> &'static [Parameter] {
&[
Parameter {
required: true,
description: "Foo",
},
Parameter {
required: false,
description: "Bar
This string is exactly 100 chars long. Delete one character to make it 99 chars long and it'll work!",
},
]
}
}

fn main() {}
39 changes: 39 additions & 0 deletions tests/target/issue-6769.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// rustfmt-style_edition: 2027

fn test() {
let foo = [
(b"Welcome to Ringboard!" as &[u8], &em),
(
b"Ringboard is a fast, efficient, and composable clipboard manager for Linux.",
&em,
),
(
b"It supports both Wayland and X11 along with multiple clients to manage your \
clipboard history.",
&em,
),
(
b"Clients include a standard GUI, an interactive TUI, and a CLI for all your scripting \
needs.",
&em,
),
(
"Ringboard can copy arbitrary bytes—that includes images!".as_bytes(),
&em,
),
(
b"Plaintext and RegEx search are available for fast entry retrieval.",
&em,
),
(b"Enjoy this image from our AI overlords:", &em),
(
include_bytes!("../logo.jpeg") as &[u8],
&MimeType::from("image/jpeg").unwrap(),
),
(
b"Finally, it's worth mentioning that Ringboard is extremely efficient, performant, \
and scalable.",
&em,
),
];
}
Loading
Loading