1 use rustc_span
::source_map
::SourceMap
;
2 use rustc_span
::{BytePos, CharPos, FileName, Pos, Symbol}
;
7 #[derive(Clone, Copy, PartialEq, Debug)]
8 pub enum CommentStyle
{
9 /// No code on either side of each line of the comment
11 /// Code exists to the left of the comment
13 /// Code before /* foo */ and after the comment
15 /// Just a manual blank line "\n\n", for layout
21 pub style
: CommentStyle
,
22 pub lines
: Vec
<String
>,
26 /// Makes a doc string more presentable to users.
27 /// Used by rustdoc and perhaps other tools, but not by rustc.
28 pub fn beautify_doc_string(data
: Symbol
) -> Symbol
{
29 fn get_vertical_trim(lines
: &[&str]) -> Option
<(usize, usize)> {
31 let mut j
= lines
.len();
32 // first line of all-stars should be omitted
33 if !lines
.is_empty() && lines
[0].chars().all(|c
| c
== '
*'
) {
37 while i
< j
&& lines
[i
].trim().is_empty() {
40 // like the first, a last line of all stars should be omitted
41 if j
> i
&& lines
[j
- 1].chars().skip(1).all(|c
| c
== '
*'
) {
45 while j
> i
&& lines
[j
- 1].trim().is_empty() {
49 if i
!= 0 || j
!= lines
.len() { Some((i, j)) }
else { None }
52 fn get_horizontal_trim(lines
: &[&str]) -> Option
<usize> {
53 let mut i
= usize::MAX
;
57 for (j
, c
) in line
.chars().enumerate() {
58 if j
> i
|| !"* \t".contains(c
) {
78 let data_s
= data
.as_str();
79 if data_s
.contains('
\n'
) {
80 let mut lines
= data_s
.lines().collect
::<Vec
<&str>>();
81 let mut changes
= false;
82 let lines
= if let Some((i
, j
)) = get_vertical_trim(&lines
) {
84 // remove whitespace-only lines from the start/end of lines
89 if let Some(horizontal
) = get_horizontal_trim(&lines
) {
91 // remove a "[ \t]*\*" block from each line, if possible
92 for line
in lines
.iter_mut() {
93 *line
= &line
[horizontal
+ 1..];
97 return Symbol
::intern(&lines
.join("\n"));
103 /// Returns `None` if the first `col` chars of `s` contain a non-whitespace char.
104 /// Otherwise returns `Some(k)` where `k` is first char offset after that leading
105 /// whitespace. Note that `k` may be outside bounds of `s`.
106 fn all_whitespace(s
: &str, col
: CharPos
) -> Option
<usize> {
108 for (i
, ch
) in s
.char_indices().take(col
.to_usize()) {
109 if !ch
.is_whitespace() {
112 idx
= i
+ ch
.len_utf8();
117 fn trim_whitespace_prefix(s
: &str, col
: CharPos
) -> &str {
119 match all_whitespace(&s
, col
) {
131 fn split_block_comment_into_lines(text
: &str, col
: CharPos
) -> Vec
<String
> {
132 let mut res
: Vec
<String
> = vec
![];
133 let mut lines
= text
.lines();
134 // just push the first line
135 res
.extend(lines
.next().map(|it
| it
.to_string()));
136 // for other lines, strip common whitespace prefix
138 res
.push(trim_whitespace_prefix(line
, col
).to_string())
143 // it appears this function is called only from pprust... that's
144 // probably not a good thing.
145 pub fn gather_comments(sm
: &SourceMap
, path
: FileName
, src
: String
) -> Vec
<Comment
> {
146 let sm
= SourceMap
::new(sm
.path_mapping().clone());
147 let source_file
= sm
.new_source_file(path
, src
);
148 let text
= (*source_file
.src
.as_ref().unwrap()).clone();
150 let text
: &str = text
.as_str();
151 let start_bpos
= source_file
.start_pos
;
153 let mut comments
: Vec
<Comment
> = Vec
::new();
154 let mut code_to_the_left
= false;
156 if let Some(shebang_len
) = rustc_lexer
::strip_shebang(text
) {
157 comments
.push(Comment
{
158 style
: CommentStyle
::Isolated
,
159 lines
: vec
![text
[..shebang_len
].to_string()],
165 for token
in rustc_lexer
::tokenize(&text
[pos
..]) {
166 let token_text
= &text
[pos
..pos
+ token
.len
];
168 rustc_lexer
::TokenKind
::Whitespace
=> {
169 if let Some(mut idx
) = token_text
.find('
\n'
) {
170 code_to_the_left
= false;
171 while let Some(next_newline
) = &token_text
[idx
+ 1..].find('
\n'
) {
172 idx
= idx
+ 1 + next_newline
;
173 comments
.push(Comment
{
174 style
: CommentStyle
::BlankLine
,
176 pos
: start_bpos
+ BytePos((pos
+ idx
) as u32),
181 rustc_lexer
::TokenKind
::BlockComment { doc_style, .. }
=> {
182 if doc_style
.is_none() {
183 let code_to_the_right
=
184 !matches
!(text
[pos
+ token
.len
..].chars().next(), Some('
\r'
| '
\n'
));
185 let style
= match (code_to_the_left
, code_to_the_right
) {
186 (_
, true) => CommentStyle
::Mixed
,
187 (false, false) => CommentStyle
::Isolated
,
188 (true, false) => CommentStyle
::Trailing
,
191 // Count the number of chars since the start of the line by rescanning.
192 let pos_in_file
= start_bpos
+ BytePos(pos
as u32);
193 let line_begin_in_file
= source_file
.line_begin_pos(pos_in_file
);
194 let line_begin_pos
= (line_begin_in_file
- start_bpos
).to_usize();
195 let col
= CharPos(text
[line_begin_pos
..pos
].chars().count());
197 let lines
= split_block_comment_into_lines(token_text
, col
);
198 comments
.push(Comment { style, lines, pos: pos_in_file }
)
201 rustc_lexer
::TokenKind
::LineComment { doc_style }
=> {
202 if doc_style
.is_none() {
203 comments
.push(Comment
{
204 style
: if code_to_the_left
{
205 CommentStyle
::Trailing
207 CommentStyle
::Isolated
209 lines
: vec
![token_text
.to_string()],
210 pos
: start_bpos
+ BytePos(pos
as u32),
215 code_to_the_left
= true;