Skip to content

Commit 15f1d76

Browse files
feat: support wrapping call chains with long elements
1 parent d35ebf7 commit 15f1d76

File tree

6 files changed

+113
-12
lines changed

6 files changed

+113
-12
lines changed

Configurations.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,34 @@ To enable unstable options, set `unstable_features = true` in `rustfmt.toml` or
1717

1818
Below you find a detailed visual guide on all the supported configuration options of rustfmt:
1919

20+
## `allow_chain_call_overflow`
21+
22+
Wrap elements of a call chain even if one or more lines exceed the `max_width`
23+
24+
- **Default value**: `false`
25+
- **Possible values**: `true`, `false`
26+
- **Stable**: No (tracking issue: ...)
27+
28+
#### `false` (default):
29+
30+
```rust
31+
fn example() {
32+
foo("This text is under the max_width limit, and shouldn't cause any problems on its own.").long("But this line is extra long, and doesn't fit within 100 max_width. 1234567890123456789").baz().collect().unwrap();
33+
}
34+
```
35+
36+
#### `true`:
37+
38+
```rust
39+
fn example() {
40+
foo("This text is under the max_width limit, and shouldn't cause any problems on its own.")
41+
.long("But this line is extra long, and doesn't fit within 100 max_width. 1234567890123456789")
42+
.baz()
43+
.collect()
44+
.unwrap();
45+
}
46+
```
47+
2048

2149
## `binop_separator`
2250

src/chains.rs

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -413,12 +413,8 @@ impl Chain {
413413
_ => expr.clone(),
414414
}
415415
}
416-
}
417-
418-
impl Rewrite for Chain {
419-
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
420-
debug!("rewrite chain {:?} {:?}", self, shape);
421416

417+
fn format(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
422418
let mut formatter = match context.config.indent_style() {
423419
IndentStyle::Block => {
424420
Box::new(ChainFormatterBlock::new(self)) as Box<dyn ChainFormatter>
@@ -439,8 +435,39 @@ impl Rewrite for Chain {
439435
formatter.format_children(context, child_shape)?;
440436
formatter.format_last_child(context, shape, child_shape)?;
441437

442-
let result = formatter.join_rewrites(context, child_shape)?;
443-
wrap_str(result, context.config.max_width(), shape)
438+
formatter.join_rewrites(context, child_shape)
439+
}
440+
}
441+
442+
impl Rewrite for Chain {
443+
fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
444+
debug!("rewrite chain {:?} {:?}", self, shape);
445+
446+
let result = self.format(context, shape)?;
447+
if context.config.allow_chain_call_overflow() {
448+
match context.config.indent_style() {
449+
IndentStyle::Block => Some(result),
450+
IndentStyle::Visual => match wrap_str(result, context.config.max_width(), shape) {
451+
Some(r) => Some(r),
452+
None => {
453+
let new_shape = Shape::indented(
454+
shape.indent.block_indent(context.config),
455+
context.config,
456+
);
457+
if let Some(result) = self.format(context, new_shape) {
458+
let prefix = shape
459+
.indent
460+
.block_indent(context.config)
461+
.to_string_with_newline(context.config);
462+
return Some(format!("{}{}", prefix, result));
463+
}
464+
None
465+
}
466+
},
467+
}
468+
} else {
469+
wrap_str(result, context.config.max_width(), shape)
470+
}
444471
}
445472
}
446473

@@ -693,7 +720,13 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
693720
context: &RewriteContext<'_>,
694721
shape: Shape,
695722
) -> Option<()> {
696-
let mut root_rewrite: String = parent.rewrite(context, shape)?;
723+
let mut root_rewrite = if context.config.allow_chain_call_overflow() {
724+
parent
725+
.rewrite(context, shape)
726+
.unwrap_or_else(|| context.snippet(parent.span).to_owned())
727+
} else {
728+
parent.rewrite(context, shape)?
729+
};
697730

698731
let mut root_ends_with_block = parent.kind.is_block_like(context, &root_rewrite);
699732
let tab_width = context.config.tab_spaces().saturating_sub(shape.offset);
@@ -733,8 +766,14 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
733766
}
734767

735768
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
769+
let allow_chain_call_overflow = context.config.allow_chain_call_overflow();
736770
for item in &self.shared.children[..self.shared.children.len() - 1] {
737-
let rewrite = item.rewrite(context, child_shape)?;
771+
let rewrite = if allow_chain_call_overflow {
772+
item.rewrite(context, child_shape)
773+
.unwrap_or_else(|| context.snippet(item.span).to_owned())
774+
} else {
775+
item.rewrite(context, child_shape)?
776+
};
738777
self.shared.rewrites.push(rewrite);
739778
}
740779
Some(())
@@ -827,8 +866,14 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
827866
}
828867

829868
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
869+
let allow_chain_call_overflow = context.config.allow_chain_call_overflow();
830870
for item in &self.shared.children[..self.shared.children.len() - 1] {
831-
let rewrite = item.rewrite(context, child_shape)?;
871+
let rewrite = if allow_chain_call_overflow {
872+
item.rewrite(context, child_shape)
873+
.unwrap_or_else(|| context.snippet(item.span).to_owned())
874+
} else {
875+
item.rewrite(context, child_shape)?
876+
};
832877
self.shared.rewrites.push(rewrite);
833878
}
834879
Some(())

src/config/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ create_config! {
112112
inline_attribute_width: usize, 0, false,
113113
"Write an item and its attribute on the same line \
114114
if their combined width is below a threshold";
115+
allow_chain_call_overflow: bool, false, false,
116+
"Format chains even if it includes a chain call which exceeds the max width";
115117

116118
// Options that can change the source code beyond whitespace/blocks (somewhat linty things)
117119
merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one";
@@ -532,6 +534,7 @@ blank_lines_lower_bound = 0
532534
edition = "2015"
533535
version = "One"
534536
inline_attribute_width = 0
537+
allow_chain_call_overflow = false
535538
merge_derives = true
536539
use_try_shorthand = false
537540
use_field_init_shorthand = false

src/expr.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,7 +1957,11 @@ fn choose_rhs<R: Rewrite>(
19571957
if wrap_str(new_rhs.clone(), context.config.max_width(), new_shape)
19581958
.is_none() =>
19591959
{
1960-
Some(format!("{}{}", before_space_str, orig_rhs))
1960+
let prefix = if orig_rhs.starts_with('\n') || has_rhs_comment {
1961+
""
1962+
} else {
1963+
" "
1964+
};
19611965
}
19621966
(Some(ref orig_rhs), Some(ref new_rhs))
19631967
if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) =>
@@ -1971,7 +1975,14 @@ fn choose_rhs<R: Rewrite>(
19711975
.map(|s| format!("{}{}", before_space_str, s))
19721976
}
19731977
(None, None) => None,
1974-
(Some(orig_rhs), _) => Some(format!("{}{}", before_space_str, orig_rhs)),
1978+
(Some(orig_rhs), _) => {
1979+
let prefix = if orig_rhs.starts_with('\n') || has_rhs_comment {
1980+
""
1981+
} else {
1982+
" "
1983+
};
1984+
Some(format!("{}{}", prefix, orig_rhs))
1985+
}
19751986
}
19761987
}
19771988
}

tests/source/chains_long_calls.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// rustfmt-allow_chain_call_overflow: true
2+
// https://github.com/rust-lang/rustfmt/issues/3863
3+
fn f() {
4+
foo("This text is under the max_width limit, and shouldn't cause any problems on its own.").long("But this line is extra long, and doesn't fit within 100 max_width. 1234567890123456789 aBcDeFgHiJ").baz().collect().unwrap();
5+
}

tests/target/chains_long_calls.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// rustfmt-allow_chain_call_overflow: true
2+
// https://github.com/rust-lang/rustfmt/issues/3863
3+
fn f() {
4+
foo("This text is under the max_width limit, and shouldn't cause any problems on its own.")
5+
.long("But this line is extra long, and doesn't fit within 100 max_width. 1234567890123456789 aBcDeFgHiJ")
6+
.baz()
7+
.collect()
8+
.unwrap();
9+
}

0 commit comments

Comments
 (0)