]> git.lizzy.rs Git - rust.git/blob - src/string.rs
Merge pull request #113 from marcusklaas/import-comments
[rust.git] / src / string.rs
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.
4 //
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.
10
11 // Format string literals.
12
13 use utils::{make_indent, next_char, prev_char, round_up_to_power_of_two};
14
15 use MIN_STRING;
16
17 pub struct StringFormat<'a> {
18     pub opener: &'a str,
19     pub closer: &'a str,
20     pub line_start: &'a str,
21     pub line_end: &'a str,
22     pub width: usize,
23     pub offset: usize,
24     pub trim_end: bool,
25 }
26
27 // TODO: simplify this!
28 pub fn rewrite_string<'a>(s: &str, fmt: &StringFormat<'a>) -> String {
29     // FIXME I bet this stomps unicode escapes in the source string
30     // TODO if lo.col > IDEAL - 10, start a new line (need cur indent for that)
31
32     let indent = make_indent(fmt.offset);
33     let indent = &indent;
34
35     let mut cur_start = 0;
36     let mut result = String::with_capacity(round_up_to_power_of_two(s.len()));
37     result.push_str(fmt.opener);
38
39     let ender_length = fmt.line_end.len();
40     let max_chars = fmt.width.checked_sub(fmt.opener.len()).unwrap_or(0)
41                              .checked_sub(ender_length).unwrap_or(1);
42
43     loop {
44         let mut cur_end = cur_start + max_chars;
45
46         if cur_end >= s.len() {
47             result.push_str(&s[cur_start..]);
48             break;
49         }
50
51         // Make sure we're on a char boundary.
52         cur_end = next_char(&s, cur_end);
53
54         // Push cur_end left until we reach whitespace.
55         while !s.char_at(cur_end - 1).is_whitespace() {
56             cur_end = prev_char(&s, cur_end);
57
58             if cur_end - cur_start < MIN_STRING {
59                 // We can't break at whitespace, fall back to splitting
60                 // anywhere that doesn't break an escape sequence.
61                 cur_end = next_char(&s, cur_start + max_chars);
62                 while s.char_at(prev_char(&s, cur_end)) == '\\' {
63                     cur_end = prev_char(&s, cur_end);
64                 }
65                 break;
66             }
67         }
68         // Make sure there is no whitespace to the right of the break.
69         while cur_end < s.len() && s.char_at(cur_end).is_whitespace() {
70             cur_end = next_char(&s, cur_end + 1);
71         }
72
73         let line: &str = if fmt.trim_end {
74             &s[cur_start..cur_end].trim_right_matches(char::is_whitespace)
75         } else {
76             &s[cur_start..cur_end]
77         };
78
79         result.push_str(line);
80         result.push_str(fmt.line_end);
81         result.push('\n');
82         result.push_str(indent);
83         result.push_str(fmt.line_start);
84
85         cur_start = cur_end;
86     }
87     result.push_str(fmt.closer);
88
89     result
90 }