Rewrite match expressions
This commit is contained in:
parent
8dbe2133fb
commit
dc2544712c
174
src/expr.rs
174
src/expr.rs
@ -12,7 +12,7 @@ use rewrite::{Rewrite, RewriteContext};
|
||||
use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic};
|
||||
use string::{StringFormat, rewrite_string};
|
||||
use StructLitStyle;
|
||||
use utils::{span_after, make_indent, extra_offset};
|
||||
use utils::{span_after, make_indent, extra_offset, first_line_width, last_line_width};
|
||||
use visitor::FmtVisitor;
|
||||
use config::BlockIndentStyle;
|
||||
use comment::{FindUncommented, rewrite_comment};
|
||||
@ -99,6 +99,9 @@ impl Rewrite for ast::Expr {
|
||||
width,
|
||||
offset)
|
||||
}
|
||||
ast::Expr_::ExprMatch(ref cond, ref arms, _) => {
|
||||
rewrite_match(context, cond, arms, width, offset)
|
||||
}
|
||||
ast::Expr_::ExprPath(ref qself, ref path) => {
|
||||
rewrite_path(context, qself.as_ref(), path, width, offset)
|
||||
}
|
||||
@ -303,6 +306,175 @@ fn rewrite_if_else(context: &RewriteContext,
|
||||
Some(result)
|
||||
}
|
||||
|
||||
fn rewrite_match(context: &RewriteContext,
|
||||
cond: &ast::Expr,
|
||||
arms: &[ast::Arm],
|
||||
width: usize,
|
||||
offset: usize)
|
||||
-> Option<String> {
|
||||
// TODO comments etc. (I am somewhat surprised we don't need handling for these).
|
||||
|
||||
// `match `cond` {`
|
||||
let cond_str = try_opt!(cond.rewrite(context, width - 8, offset + 6));
|
||||
let mut result = format!("match {} {{", cond_str);
|
||||
|
||||
let block_indent = context.block_indent;
|
||||
let nested_context = context.nested_context();
|
||||
let arm_indent_str = make_indent(nested_context.block_indent);
|
||||
|
||||
for arm in arms {
|
||||
result.push('\n');
|
||||
result.push_str(&arm_indent_str);
|
||||
result.push_str(&&try_opt!(arm.rewrite(&nested_context,
|
||||
context.config.max_width -
|
||||
nested_context.block_indent,
|
||||
nested_context.block_indent)));
|
||||
}
|
||||
|
||||
result.push('\n');
|
||||
result.push_str(&make_indent(block_indent));
|
||||
result.push('}');
|
||||
Some(result)
|
||||
}
|
||||
|
||||
// Match arms.
|
||||
impl Rewrite for ast::Arm {
|
||||
fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option<String> {
|
||||
let &ast::Arm { ref attrs, ref pats, ref guard, ref body } = self;
|
||||
let indent_str = make_indent(offset);
|
||||
|
||||
// TODO attrs
|
||||
|
||||
// Patterns
|
||||
let pat_strs = pats.iter().map(|p| p.rewrite(context,
|
||||
// 5 = ` => {`
|
||||
width - 5,
|
||||
offset + context.config.tab_spaces)).collect::<Vec<_>>();
|
||||
if pat_strs.iter().any(|p| p.is_none()) {
|
||||
return None;
|
||||
}
|
||||
let pat_strs = pat_strs.into_iter().map(|p| p.unwrap()).collect::<Vec<_>>();
|
||||
|
||||
let mut total_width = pat_strs.iter().fold(0, |a, p| a + p.len());
|
||||
// Add ` | `.len().
|
||||
total_width += (pat_strs.len() - 1) * 3;
|
||||
|
||||
let mut vertical = total_width > width - 5 || pat_strs.iter().any(|p| p.contains('\n'));
|
||||
if !vertical {
|
||||
// If the patterns were previously stacked, keep them stacked.
|
||||
// FIXME should be an option.
|
||||
let pat_span = mk_sp(pats[0].span.lo, pats[pats.len() - 1].span.hi);
|
||||
let pat_str = context.codemap.span_to_snippet(pat_span).unwrap();
|
||||
vertical = pat_str.find('\n').is_some();
|
||||
}
|
||||
|
||||
|
||||
let pats_width = if vertical {
|
||||
pat_strs[pat_strs.len() - 1].len()
|
||||
} else {
|
||||
total_width
|
||||
};
|
||||
|
||||
let mut pats_str = String::new();
|
||||
for p in pat_strs {
|
||||
if pats_str.len() > 0 {
|
||||
if vertical {
|
||||
pats_str.push_str(" |\n");
|
||||
pats_str.push_str(&indent_str);
|
||||
} else {
|
||||
pats_str.push_str(" | ");
|
||||
}
|
||||
}
|
||||
pats_str.push_str(&p);
|
||||
}
|
||||
|
||||
// TODO probably want to compute the guard width first, then the rest
|
||||
// TODO also, only subtract the guard width from the last pattern.
|
||||
// If guard.
|
||||
let guard_str = try_opt!(rewrite_guard(context, guard, width, offset, pats_width));
|
||||
|
||||
let pats_str = format!("{}{}", pats_str, guard_str);
|
||||
// Where the next text can start.
|
||||
let mut line_start = last_line_width(&pats_str);
|
||||
if pats_str.find('\n').is_none() {
|
||||
line_start += offset;
|
||||
}
|
||||
|
||||
let comma = if let ast::ExprBlock(_) = body.node {
|
||||
String::new()
|
||||
} else {
|
||||
",".to_owned()
|
||||
};
|
||||
|
||||
// Let's try and get the arm body on the same line as the condition.
|
||||
// 4 = ` => `.len()
|
||||
if context.config.max_width > line_start + comma.len() + 4 {
|
||||
let budget = context.config.max_width - line_start - comma.len() - 4;
|
||||
if let Some(ref body_str) = body.rewrite(context,
|
||||
budget,
|
||||
offset + context.config.tab_spaces) {
|
||||
if first_line_width(body_str) <= budget {
|
||||
return Some(format!("{} => {}{}", pats_str, body_str, comma));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We have to push the body to the next line.
|
||||
if comma.len() > 0 {
|
||||
// We're trying to fit a block in, but it still failed, give up.
|
||||
return None;
|
||||
}
|
||||
|
||||
let body_str = try_opt!(body.rewrite(context,
|
||||
width - context.config.tab_spaces,
|
||||
offset + context.config.tab_spaces));
|
||||
Some(format!("{} =>\n{}{}",
|
||||
pats_str,
|
||||
make_indent(offset + context.config.tab_spaces),
|
||||
body_str))
|
||||
}
|
||||
}
|
||||
|
||||
// The `if ...` guard on a match arm.
|
||||
fn rewrite_guard(context: &RewriteContext,
|
||||
guard: &Option<ptr::P<ast::Expr>>,
|
||||
width: usize,
|
||||
offset: usize,
|
||||
// The amount of space used up on this line for the pattern in
|
||||
// the arm.
|
||||
pattern_width: usize)
|
||||
-> Option<String> {
|
||||
if let &Some(ref guard) = guard {
|
||||
// 4 = ` if `, 5 = ` => {`
|
||||
let overhead = pattern_width + 4 + 5;
|
||||
if overhead < width {
|
||||
let cond_str = guard.rewrite(context,
|
||||
width - overhead,
|
||||
offset + context.config.tab_spaces);
|
||||
if let Some(cond_str) = cond_str {
|
||||
return Some(format!(" if {}", cond_str));
|
||||
}
|
||||
}
|
||||
|
||||
// Not enough space to put the guard after the pattern, try a newline.
|
||||
let overhead = context.config.tab_spaces + 4 + 5;
|
||||
if overhead < width {
|
||||
let cond_str = guard.rewrite(context,
|
||||
width - overhead,
|
||||
offset + context.config.tab_spaces);
|
||||
if let Some(cond_str) = cond_str {
|
||||
return Some(format!("\n{}if {}",
|
||||
make_indent(offset + context.config.tab_spaces),
|
||||
cond_str));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
} else {
|
||||
Some(String::new())
|
||||
}
|
||||
}
|
||||
|
||||
fn rewrite_pat_expr(context: &RewriteContext,
|
||||
pat: Option<&ast::Pat>,
|
||||
expr: &ast::Expr,
|
||||
|
@ -30,3 +30,13 @@ pub struct RewriteContext<'a> {
|
||||
pub config: &'a Config,
|
||||
pub block_indent: usize,
|
||||
}
|
||||
|
||||
impl<'a> RewriteContext<'a> {
|
||||
pub fn nested_context(&self) -> RewriteContext<'a> {
|
||||
RewriteContext {
|
||||
codemap: self.codemap,
|
||||
config: self.config,
|
||||
block_indent: self.block_indent + self.config.tab_spaces,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
20
src/utils.rs
20
src/utils.rs
@ -82,6 +82,25 @@ pub fn format_mutability(mutability: ast::Mutability) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
// The width of the first line in s.
|
||||
#[inline]
|
||||
pub fn first_line_width(s: &str) -> usize {
|
||||
match s.find('\n') {
|
||||
Some(n) => n,
|
||||
None => s.len(),
|
||||
}
|
||||
}
|
||||
|
||||
// The width of the last line in s.
|
||||
#[inline]
|
||||
pub fn last_line_width(s: &str) -> usize {
|
||||
match s.rfind('\n') {
|
||||
Some(n) => s.len() - n,
|
||||
None => s.len(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_skip(meta_item: &MetaItem) -> bool {
|
||||
match meta_item.node {
|
||||
MetaItem_::MetaWord(ref s) => *s == SKIP_ANNOTATION,
|
||||
@ -95,6 +114,7 @@ pub fn contains_skip(attrs: &[Attribute]) -> bool {
|
||||
}
|
||||
|
||||
// Find the end of a TyParam
|
||||
#[inline]
|
||||
pub fn end_typaram(typaram: &ast::TyParam) -> BytePos {
|
||||
typaram.bounds.last().map(|bound| match *bound {
|
||||
ast::RegionTyParamBound(ref lt) => lt.span,
|
||||
|
42
tests/target/match.rs
Normal file
42
tests/target/match.rs
Normal file
@ -0,0 +1,42 @@
|
||||
// Match expressions.
|
||||
|
||||
fn foo() {
|
||||
// A match expression.
|
||||
match x {
|
||||
// Some comment.
|
||||
a => foo(),
|
||||
b if 0 < 42 => foo(),
|
||||
c => { // Another comment.
|
||||
// Comment.
|
||||
an_expression;
|
||||
foo()
|
||||
}
|
||||
// Perhaps this should introduce braces?
|
||||
Foo(ref bar) =>
|
||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
|
||||
Pattern1 | Pattern2 | Pattern3 => false,
|
||||
Paternnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn |
|
||||
Paternnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn => {
|
||||
blah
|
||||
}
|
||||
Patternnnnnnnnnnnnnnnnnnn |
|
||||
Patternnnnnnnnnnnnnnnnnnn |
|
||||
Patternnnnnnnnnnnnnnnnnnn |
|
||||
Patternnnnnnnnnnnnnnnnnnn => meh,
|
||||
|
||||
Patternnnnnnnnnnnnnnnnnnn |
|
||||
Patternnnnnnnnnnnnnnnnnnn if looooooooooooooooooong_guard => meh,
|
||||
|
||||
Patternnnnnnnnnnnnnnnnnnnnnnnnn |
|
||||
Patternnnnnnnnnnnnnnnnnnnnnnnnn
|
||||
if looooooooooooooooooooooooooooooooooooooooong_guard => meh,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let whatever = match something {
|
||||
/// DOC COMMENT!
|
||||
Some(_) => 42,
|
||||
#[an_attribute]
|
||||
None => 0,
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user