1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use visitor::FmtVisitor;
13 use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
15 use syntax::{ast, ptr};
16 use syntax::codemap::{Span, Pos};
20 impl<'a> FmtVisitor<'a> {
22 fn rewrite_string_lit(&mut self, s: &str, span: Span, width: usize, offset: usize) -> String {
23 // FIXME I bet this stomps unicode escapes in the source string
25 // Check if there is anything to fix: we always try to fixup multi-line
26 // strings, or if the string is too long for the line.
27 let l_loc = self.codemap.lookup_char_pos(span.lo);
28 let r_loc = self.codemap.lookup_char_pos(span.hi);
29 if l_loc.line == r_loc.line && r_loc.col.to_usize() <= config!(max_width) {
30 return self.snippet(span);
33 // TODO if lo.col > IDEAL - 10, start a new line (need cur indent for that)
35 let s = s.escape_default();
37 let offset = offset + 1;
38 let indent = make_indent(offset);
41 let max_chars = width - 1;
43 let mut cur_start = 0;
44 let mut result = String::new();
47 let mut cur_end = cur_start + max_chars;
49 if cur_end >= s.len() {
50 result.push_str(&s[cur_start..]);
54 // Make sure we're on a char boundary.
55 cur_end = next_char(&s, cur_end);
57 // Push cur_end left until we reach whitespace
58 while !s.char_at(cur_end-1).is_whitespace() {
59 cur_end = prev_char(&s, cur_end);
61 if cur_end - cur_start < MIN_STRING {
62 // We can't break at whitespace, fall back to splitting
63 // anywhere that doesn't break an escape sequence
64 cur_end = next_char(&s, cur_start + max_chars);
65 while s.char_at(cur_end) == '\\' {
66 cur_end = prev_char(&s, cur_end);
70 // Make sure there is no whitespace to the right of the break.
71 while cur_end < s.len() && s.char_at(cur_end).is_whitespace() {
72 cur_end = next_char(&s, cur_end+1);
74 result.push_str(&s[cur_start..cur_end]);
75 result.push_str("\\\n");
76 result.push_str(indent);
85 fn rewrite_call(&mut self,
87 args: &[ptr::P<ast::Expr>],
92 debug!("rewrite_call, width: {}, offset: {}", width, offset);
94 // TODO using byte lens instead of char lens (and probably all over the place too)
95 let callee_str = self.rewrite_expr(callee, width, offset);
96 debug!("rewrite_call, callee_str: `{}`", callee_str);
98 let remaining_width = width - callee_str.len() - 2;
99 let offset = callee_str.len() + 1 + offset;
100 let arg_count = args.len();
102 let args_str = if arg_count > 0 {
103 let args: Vec<_> = args.iter().map(|e| (self.rewrite_expr(e,
105 offset), String::new())).collect();
106 // TODO move this into write_list
107 let tactics = if args.iter().any(|&(ref s, _)| s.contains('\n')) {
110 ListTactic::HorizontalVertical
112 let fmt = ListFormatting {
115 trailing_separator: SeparatorTactic::Never,
117 h_width: remaining_width,
118 v_width: remaining_width,
120 write_list(&args, &fmt)
125 format!("{}({})", callee_str, args_str)
128 fn rewrite_paren(&mut self, subexpr: &ast::Expr, width: usize, offset: usize) -> String {
129 debug!("rewrite_paren, width: {}, offset: {}", width, offset);
130 // 1 is for opening paren
131 let subexpr_str = self.rewrite_expr(subexpr, width-1, offset+1);
132 debug!("rewrite_paren, subexpr_str: `{}`", subexpr_str);
133 let mut lines = subexpr_str.rsplit('\n');
134 let last_line_len = lines.next().unwrap().len();
135 let last_line_offset = match lines.next() {
139 if width + offset - last_line_offset - last_line_len > 0 {
140 format!("({})", subexpr_str)
142 // FIXME That's correct unless we have width < 2. Return an Option for such cases ?
143 format!("({}\n{} )", subexpr_str, make_indent(offset))
148 pub fn rewrite_expr(&mut self, expr: &ast::Expr, width: usize, offset: usize) -> String {
150 ast::Expr_::ExprLit(ref l) => {
152 ast::Lit_::LitStr(ref is, _) => {
153 let result = self.rewrite_string_lit(&is, l.span, width, offset);
154 debug!("string lit: `{}`", result);
160 ast::Expr_::ExprCall(ref callee, ref args) => {
161 return self.rewrite_call(callee, args, width, offset);
163 ast::Expr_::ExprParen(ref subexpr) => {
164 return self.rewrite_paren(subexpr, width, offset);
169 let result = self.snippet(expr.span);