Skip to content

Commit 9aaa21c

Browse files
committed
chains: fix visual indent chain layout
1 parent 71825d9 commit 9aaa21c

File tree

2 files changed

+85
-61
lines changed

2 files changed

+85
-61
lines changed

src/chains.rs

Lines changed: 79 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -275,12 +275,12 @@ impl Rewrite for Chain {
275275
};
276276

277277
formatter.format_root(&self.parent, context, shape)?;
278-
if let result @ Some(_) = formatter.pure_root() {
279-
return result;
278+
if let Some(result) = formatter.pure_root() {
279+
return wrap_str(result, context.config.max_width(), shape);
280280
}
281281

282282
// Decide how to layout the rest of the chain.
283-
let child_shape = formatter.child_shape(context, shape);
283+
let child_shape = formatter.child_shape(context, shape)?;
284284

285285
formatter.format_children(context, child_shape)?;
286286
formatter.format_last_child(context, shape, child_shape)?;
@@ -309,7 +309,7 @@ trait ChainFormatter {
309309
context: &RewriteContext,
310310
shape: Shape,
311311
) -> Option<()>;
312-
fn child_shape(&self, context: &RewriteContext, shape: Shape) -> Shape;
312+
fn child_shape(&self, context: &RewriteContext, shape: Shape) -> Option<Shape>;
313313
fn format_children(&mut self, context: &RewriteContext, child_shape: Shape) -> Option<()>;
314314
fn format_last_child(
315315
&mut self,
@@ -414,8 +414,10 @@ impl<'a> ChainFormatterShared<'a> {
414414

415415
let all_in_one_line =
416416
self.rewrites.iter().all(|s| !s.contains('\n')) && one_line_budget > 0;
417-
let last_shape = if all_in_one_line || extendable {
417+
let last_shape = if all_in_one_line {
418418
shape.sub_width(last.tries)?
419+
} else if extendable {
420+
child_shape.sub_width(last.tries)?
419421
} else {
420422
child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)?
421423
};
@@ -481,7 +483,7 @@ impl<'a> ChainFormatterShared<'a> {
481483
if *context.force_one_line_chain.borrow() {
482484
return None;
483485
}
484-
child_shape.indent.to_string_with_newline(context.config)
486+
child_shape.to_string_with_newline(context.config)
485487
};
486488

487489
let mut rewrite_iter = self.rewrites.iter();
@@ -512,37 +514,6 @@ impl<'a> ChainFormatterBlock<'a> {
512514
is_block_like: Vec::with_capacity(chain.children.len() + 1),
513515
}
514516
}
515-
516-
// States whether an expression's last line exclusively consists of closing
517-
// parens, braces, and brackets in its idiomatic formatting.
518-
fn is_block_expr(context: &RewriteContext, expr: &ast::Expr, repr: &str) -> bool {
519-
match expr.node {
520-
ast::ExprKind::Mac(..)
521-
| ast::ExprKind::Call(..)
522-
| ast::ExprKind::MethodCall(..)
523-
| ast::ExprKind::Struct(..)
524-
| ast::ExprKind::While(..)
525-
| ast::ExprKind::WhileLet(..)
526-
| ast::ExprKind::If(..)
527-
| ast::ExprKind::IfLet(..)
528-
| ast::ExprKind::Block(..)
529-
| ast::ExprKind::Loop(..)
530-
| ast::ExprKind::ForLoop(..)
531-
| ast::ExprKind::Match(..) => repr.contains('\n'),
532-
ast::ExprKind::Paren(ref expr)
533-
| ast::ExprKind::Binary(_, _, ref expr)
534-
| ast::ExprKind::Index(_, ref expr)
535-
| ast::ExprKind::Unary(_, ref expr)
536-
| ast::ExprKind::Closure(_, _, _, _, ref expr, _)
537-
| ast::ExprKind::Try(ref expr)
538-
| ast::ExprKind::Yield(Some(ref expr)) => Self::is_block_expr(context, expr, repr),
539-
// This can only be a string lit
540-
ast::ExprKind::Lit(_) => {
541-
repr.contains('\n') && trimmed_last_line_width(repr) <= context.config.tab_spaces()
542-
}
543-
_ => false,
544-
}
545-
}
546517
}
547518

548519
impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
@@ -554,7 +525,7 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
554525
) -> Option<()> {
555526
let mut root_rewrite: String = parent.rewrite(context, shape)?;
556527

557-
let mut root_ends_with_block = Self::is_block_expr(context, &parent.expr, &root_rewrite);
528+
let mut root_ends_with_block = is_block_expr(context, &parent.expr, &root_rewrite);
558529
let tab_width = context.config.tab_spaces().saturating_sub(shape.offset);
559530

560531
while root_rewrite.len() <= tab_width && !root_rewrite.contains('\n') {
@@ -565,7 +536,7 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
565536
None => break,
566537
}
567538

568-
root_ends_with_block = Self::is_block_expr(context, &item.expr, &root_rewrite);
539+
root_ends_with_block = is_block_expr(context, &item.expr, &root_rewrite);
569540

570541
self.shared.children = &self.shared.children[..self.shared.children.len() - 1];
571542
if self.shared.children.is_empty() {
@@ -577,19 +548,21 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
577548
Some(())
578549
}
579550

580-
fn child_shape(&self, context: &RewriteContext, shape: Shape) -> Shape {
581-
if self.is_block_like[0] {
582-
shape
583-
} else {
584-
shape.block_indent(context.config.tab_spaces())
585-
}.with_max_width(context.config)
551+
fn child_shape(&self, context: &RewriteContext, shape: Shape) -> Option<Shape> {
552+
Some(
553+
if self.is_block_like[0] {
554+
shape.block_indent(0)
555+
} else {
556+
shape.block_indent(context.config.tab_spaces())
557+
}.with_max_width(context.config),
558+
)
586559
}
587560

588561
fn format_children(&mut self, context: &RewriteContext, child_shape: Shape) -> Option<()> {
589562
for item in self.shared.children[1..].iter().rev() {
590563
let rewrite = item.rewrite_postfix(context, child_shape)?;
591564
self.is_block_like
592-
.push(Self::is_block_expr(context, &item.expr, &rewrite));
565+
.push(is_block_expr(context, &item.expr, &rewrite));
593566
self.shared.rewrites.push(rewrite);
594567
}
595568
Some(())
@@ -618,12 +591,15 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> {
618591
// Format a chain using visual indent.
619592
struct ChainFormatterVisual<'a> {
620593
shared: ChainFormatterShared<'a>,
594+
// The extra offset from the chain's shape to the position of the `.`
595+
offset: usize,
621596
}
622597

623598
impl<'a> ChainFormatterVisual<'a> {
624599
fn new(chain: &'a Chain) -> ChainFormatterVisual<'a> {
625600
ChainFormatterVisual {
626601
shared: ChainFormatterShared::new(chain),
602+
offset: 0,
627603
}
628604
}
629605
}
@@ -635,23 +611,31 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
635611
context: &RewriteContext,
636612
shape: Shape,
637613
) -> Option<()> {
638-
// Determines if we can continue formatting a given expression on the same line.
639-
fn is_continuable(expr: &ast::Expr) -> bool {
640-
match expr.node {
641-
ast::ExprKind::Path(..) => true,
642-
_ => false,
643-
}
644-
}
645-
646614
let parent_shape = shape.visual_indent(0);
647615
let mut root_rewrite = parent.rewrite(context, parent_shape)?;
616+
let multiline = root_rewrite.contains('\n');
617+
self.offset = if multiline {
618+
last_line_width(&root_rewrite).saturating_sub(shape.used_width())
619+
} else {
620+
trimmed_last_line_width(&root_rewrite)
621+
};
648622

649-
if !root_rewrite.contains('\n') && is_continuable(&parent.expr) {
623+
if !multiline || is_block_expr(context, &parent.expr, &root_rewrite) {
650624
let item = &self.shared.children[self.shared.children.len() - 1];
651-
let overhead = last_line_width(&root_rewrite);
652-
let shape = parent_shape.offset_left(overhead)?;
653-
let rewrite = item.rewrite_postfix(context, shape)?;
654-
root_rewrite.push_str(&rewrite);
625+
let child_shape = parent_shape
626+
.visual_indent(self.offset)
627+
.sub_width(self.offset)?;
628+
let rewrite = item.rewrite_postfix(context, child_shape)?;
629+
match wrap_str(rewrite, context.config.max_width(), shape) {
630+
Some(rewrite) => root_rewrite.push_str(&rewrite),
631+
None => {
632+
// We couldn't fit in at the visual indent, try the last
633+
// indent.
634+
let rewrite = item.rewrite_postfix(context, parent_shape)?;
635+
root_rewrite.push_str(&rewrite);
636+
self.offset = 0;
637+
}
638+
}
655639

656640
self.shared.children = &self.shared.children[..self.shared.children.len() - 1];
657641
}
@@ -660,8 +644,11 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
660644
Some(())
661645
}
662646

663-
fn child_shape(&self, context: &RewriteContext, shape: Shape) -> Shape {
664-
shape.visual_indent(0).with_max_width(context.config)
647+
fn child_shape(&self, context: &RewriteContext, shape: Shape) -> Option<Shape> {
648+
shape
649+
.with_max_width(context.config)
650+
.offset_left(self.offset)
651+
.map(|s| s.visual_indent(0))
665652
}
666653

667654
fn format_children(&mut self, context: &RewriteContext, child_shape: Shape) -> Option<()> {
@@ -691,3 +678,34 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> {
691678
self.shared.pure_root()
692679
}
693680
}
681+
682+
// States whether an expression's last line exclusively consists of closing
683+
// parens, braces, and brackets in its idiomatic formatting.
684+
fn is_block_expr(context: &RewriteContext, expr: &ast::Expr, repr: &str) -> bool {
685+
match expr.node {
686+
ast::ExprKind::Mac(..)
687+
| ast::ExprKind::Call(..)
688+
| ast::ExprKind::MethodCall(..)
689+
| ast::ExprKind::Struct(..)
690+
| ast::ExprKind::While(..)
691+
| ast::ExprKind::WhileLet(..)
692+
| ast::ExprKind::If(..)
693+
| ast::ExprKind::IfLet(..)
694+
| ast::ExprKind::Block(..)
695+
| ast::ExprKind::Loop(..)
696+
| ast::ExprKind::ForLoop(..)
697+
| ast::ExprKind::Match(..) => repr.contains('\n'),
698+
ast::ExprKind::Paren(ref expr)
699+
| ast::ExprKind::Binary(_, _, ref expr)
700+
| ast::ExprKind::Index(_, ref expr)
701+
| ast::ExprKind::Unary(_, ref expr)
702+
| ast::ExprKind::Closure(_, _, _, _, ref expr, _)
703+
| ast::ExprKind::Try(ref expr)
704+
| ast::ExprKind::Yield(Some(ref expr)) => is_block_expr(context, expr, repr),
705+
// This can only be a string lit
706+
ast::ExprKind::Lit(_) => {
707+
repr.contains('\n') && trimmed_last_line_width(repr) <= context.config.tab_spaces()
708+
}
709+
_ => false,
710+
}
711+
}

src/shape.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ impl Shape {
274274
);
275275
Shape { width, ..*self }
276276
}
277+
278+
pub fn to_string_with_newline(&self, config: &Config) -> Cow<'static, str> {
279+
let mut offset_indent = self.indent;
280+
offset_indent.alignment = self.offset;
281+
offset_indent.to_string_inner(config, 0)
282+
}
277283
}
278284

279285
#[cfg(test)]

0 commit comments

Comments
 (0)