]> git.lizzy.rs Git - rust.git/blob - src/comment.rs
Run rustfmt on the code
[rust.git] / src / comment.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 comments.
12
13 use string::{StringFormat, rewrite_string};
14 use utils::make_indent;
15
16 pub fn rewrite_comment(orig: &str, block_style: bool, width: usize, offset: usize) -> String {
17     let s = orig.trim();
18
19     // Edge case: block comments. Let's not trim their lines (for now).
20     let opener = if block_style { "/* " } else { "// " };
21     let closer = if block_style { " */" } else { "" };
22     let line_start = if block_style { " * " } else { "// " };
23
24     let max_chars = width.checked_sub(closer.len()).unwrap_or(1)
25                          .checked_sub(opener.len()).unwrap_or(1);
26
27     let fmt = StringFormat { opener: "",
28                              closer: "",
29                              line_start: line_start,
30                              line_end: "",
31                              width: max_chars,
32                              offset: offset + opener.len() - line_start.len(),
33                              trim_end: true, };
34
35     let indent_str = make_indent(offset);
36     let line_breaks = s.chars().filter(|&c| c == '\n').count();
37
38     let (_, mut s) = s.lines().enumerate()
39         .map(|(i, mut line)| {
40             line = line.trim();
41
42             // Drop old closer.
43             if i == line_breaks && line.ends_with("*/") && !line.starts_with("//") {
44                 line = &line[..(line.len() - 2)];
45             }
46
47             line.trim_right()
48         })
49         .map(left_trim_comment_line)
50         .map(|line| {
51             if line_breaks == 0 {
52                 line.trim_left()
53             } else {
54                 line
55             }
56         })
57         .fold((true, opener.to_owned()), |(first, mut acc), line| {
58             if !first {
59                 acc.push('\n');
60                 acc.push_str(&indent_str);
61                 acc.push_str(line_start);
62             }
63
64             if line.len() > max_chars {
65                 acc.push_str(&rewrite_string(line, &fmt));
66             } else {
67                 acc.push_str(line);
68             }
69
70             (false, acc)
71         });
72
73     s.push_str(closer);
74
75     s
76 }
77
78 fn left_trim_comment_line<'a>(line: &'a str) -> &'a str {
79     if line.starts_with("/* ") || line.starts_with("// ") {
80         &line[3..]
81     } else if line.starts_with("/*") || line.starts_with("* ") || line.starts_with("//") {
82         &line[2..]
83     } else if line.starts_with("*") {
84         &line[1..]
85     } else {
86         line
87     }
88 }
89
90 #[test]
91 fn format_comments() {
92     assert_eq!("/* test */", rewrite_comment(" //test", true, 100, 100));
93     assert_eq!("// comment\n// on a", rewrite_comment("// comment on a", false, 10, 0));
94
95     assert_eq!("//  A multi line comment\n            // between args.",
96                rewrite_comment("//  A multi line comment\n             // between args.",
97                                false,
98                                60,
99                                12));
100
101     let input = "// comment";
102     let expected_output = "/* com\n                                                                      \
103                            * men\n                                                                      \
104                            * t */";
105     assert_eq!(expected_output, rewrite_comment(input, true, 9, 69));
106
107     assert_eq!("/* trimmed */", rewrite_comment("/*   trimmed    */", true, 100, 100));
108 }
109
110
111 pub trait FindUncommented {
112     fn find_uncommented(&self, pat: &str) -> Option<usize>;
113 }
114
115 impl FindUncommented for str {
116     fn find_uncommented(&self, pat: &str) -> Option<usize> {
117         let mut needle_iter = pat.chars();
118         let mut possible_comment = false;
119
120         for (i, b) in self.char_indices() {
121             match needle_iter.next() {
122                 Some(c) => {
123                     if b != c {
124                         needle_iter = pat.chars();
125                     }
126                 },
127                 None => return Some(i - pat.len())
128             }
129
130             if possible_comment && (b == '/' || b == '*') {
131                 return find_comment_end(&self[(i-1)..])
132                     .and_then(|end| {
133                         self[(end + i - 1)..].find_uncommented(pat)
134                                              .map(|idx| idx + end + i - 1)
135                     });
136             }
137
138             possible_comment = b == '/';
139         }
140
141         // Handle case where the pattern is a suffix of the search string
142         match needle_iter.next() {
143             Some(_) => None,
144             None => Some(self.len() - pat.len())
145         }
146     }
147 }
148
149 #[test]
150 fn test_find_uncommented() {
151     fn check(haystack: &str, needle: &str, expected: Option<usize>) {
152         println!("haystack {:?}, needle: {:?}", haystack, needle);
153         assert_eq!(expected, haystack.find_uncommented(needle));
154     }
155
156     check("/*/ */test", "test", Some(6));
157     check("//test\ntest", "test", Some(7));
158     check("/* comment only */", "whatever", None);
159     check("/* comment */ some text /* more commentary */ result", "result", Some(46));
160     check("sup // sup", "p", Some(2));
161     check("sup", "x", None);
162     check("π? /**/ π is nice!", "π is nice", Some(9));
163     check("/*sup yo? \n sup*/ sup", "p", Some(20));
164     check("hel/*lohello*/lo", "hello", None);
165     check("acb", "ab", None);
166     check(",/*A*/ ", ",", Some(0));
167 }
168
169 // Returns the first byte position after the first comment. The given string
170 // is expected to be prefixed by a comment, including delimiters.
171 // Good: "/* /* inner */ outer */ code();"
172 // Bad:  "code(); // hello\n world!"
173 pub fn find_comment_end(s: &str) -> Option<usize> {
174     if s.starts_with("//") {
175         s.find('\n').map(|idx| idx + 1)
176     } else {
177         // Block comment
178         let mut levels = 0;
179         let mut prev_char = 'a';
180
181         for (i, mut c) in s.char_indices() {
182             if c == '*' && prev_char == '/' {
183                 levels += 1;
184                 c = 'a'; // Invalidate prev_char
185             } else if c == '/' && prev_char == '*' {
186                 levels -= 1;
187
188                 if levels == 0 {
189                     return Some(i + 1);
190                 }
191                 c = 'a';
192             }
193
194             prev_char = c;
195         }
196
197         None
198     }
199 }
200
201 #[test]
202 fn comment_end() {
203     assert_eq!(Some(6), find_comment_end("// hi\n"));
204     assert_eq!(Some(9), find_comment_end("/* sup */ "));
205     assert_eq!(Some(9), find_comment_end("/*/**/ */ "));
206     assert_eq!(Some(6), find_comment_end("/*/ */ weird!"));
207     assert_eq!(None, find_comment_end("/* hi /* test */"));
208     assert_eq!(None, find_comment_end("// hi /* test */"));
209     assert_eq!(Some(9), find_comment_end("// hi /*\n."));
210 }