1 use crate::token
::CommentKind
;
2 use rustc_span
::source_map
::SourceMap
;
3 use rustc_span
::{BytePos, CharPos, FileName, Pos, Symbol}
;
8 #[derive(Clone, Copy, PartialEq, Debug)]
9 pub enum CommentStyle
{
10 /// No code on either side of each line of the comment
12 /// Code exists to the left of the comment
14 /// Code before /* foo */ and after the comment
16 /// Just a manual blank line "\n\n", for layout
22 pub style
: CommentStyle
,
23 pub lines
: Vec
<String
>,
27 /// A fast conservative estimate on whether the string can contain documentation links.
28 /// A pair of square brackets `[]` must exist in the string, but we only search for the
29 /// opening bracket because brackets always go in pairs in practice.
31 pub fn may_have_doc_links(s
: &str) -> bool
{
35 /// Makes a doc string more presentable to users.
36 /// Used by rustdoc and perhaps other tools, but not by rustc.
37 pub fn beautify_doc_string(data
: Symbol
, kind
: CommentKind
) -> Symbol
{
38 fn get_vertical_trim(lines
: &[&str]) -> Option
<(usize, usize)> {
40 let mut j
= lines
.len();
41 // first line of all-stars should be omitted
42 if !lines
.is_empty() && lines
[0].chars().all(|c
| c
== '
*'
) {
46 // like the first, a last line of all stars should be omitted
47 if j
> i
&& !lines
[j
- 1].is_empty() && lines
[j
- 1].chars().all(|c
| c
== '
*'
) {
51 if i
!= 0 || j
!= lines
.len() { Some((i, j)) }
else { None }
54 fn get_horizontal_trim
<'a
>(lines
: &'a
[&str], kind
: CommentKind
) -> Option
<String
> {
55 let mut i
= usize::MAX
;
58 // In case we have doc comments like `/**` or `/*!`, we want to remove stars if they are
59 // present. However, we first need to strip the empty lines so they don't get in the middle
60 // when we try to compute the "horizontal trim".
61 let lines
= if kind
== CommentKind
::Block
{
62 // Whatever happens, we skip the first line.
65 .map(|l
| if l
.trim_start().starts_with('
*'
) { 0 }
else { 1 }
)
67 let mut j
= lines
.len();
69 while i
< j
&& lines
[i
].trim().is_empty() {
72 while j
> i
&& lines
[j
- 1].trim().is_empty() {
81 for (j
, c
) in line
.chars().enumerate() {
82 if j
> i
|| !"* \t".contains(c
) {
99 if lines
.is_empty() { None }
else { Some(lines[0][..i].into()) }
102 let data_s
= data
.as_str();
103 if data_s
.contains('
\n'
) {
104 let mut lines
= data_s
.lines().collect
::<Vec
<&str>>();
105 let mut changes
= false;
106 let lines
= if let Some((i
, j
)) = get_vertical_trim(&lines
) {
108 // remove whitespace-only lines from the start/end of lines
113 if let Some(horizontal
) = get_horizontal_trim(lines
, kind
) {
115 // remove a "[ \t]*\*" block from each line, if possible
116 for line
in lines
.iter_mut() {
117 if let Some(tmp
) = line
.strip_prefix(&horizontal
) {
119 if kind
== CommentKind
::Block
120 && (*line
== "*" || line
.starts_with("* ") || line
.starts_with("**"))
128 return Symbol
::intern(&lines
.join("\n"));
134 /// Returns `None` if the first `col` chars of `s` contain a non-whitespace char.
135 /// Otherwise returns `Some(k)` where `k` is first char offset after that leading
136 /// whitespace. Note that `k` may be outside bounds of `s`.
137 fn all_whitespace(s
: &str, col
: CharPos
) -> Option
<usize> {
139 for (i
, ch
) in s
.char_indices().take(col
.to_usize()) {
140 if !ch
.is_whitespace() {
143 idx
= i
+ ch
.len_utf8();
148 fn trim_whitespace_prefix(s
: &str, col
: CharPos
) -> &str {
150 match all_whitespace(s
, col
) {
162 fn split_block_comment_into_lines(text
: &str, col
: CharPos
) -> Vec
<String
> {
163 let mut res
: Vec
<String
> = vec
![];
164 let mut lines
= text
.lines();
165 // just push the first line
166 res
.extend(lines
.next().map(|it
| it
.to_string()));
167 // for other lines, strip common whitespace prefix
169 res
.push(trim_whitespace_prefix(line
, col
).to_string())
174 // it appears this function is called only from pprust... that's
175 // probably not a good thing.
176 pub fn gather_comments(sm
: &SourceMap
, path
: FileName
, src
: String
) -> Vec
<Comment
> {
177 let sm
= SourceMap
::new(sm
.path_mapping().clone());
178 let source_file
= sm
.new_source_file(path
, src
);
179 let text
= (*source_file
.src
.as_ref().unwrap()).clone();
181 let text
: &str = text
.as_str();
182 let start_bpos
= source_file
.start_pos
;
184 let mut comments
: Vec
<Comment
> = Vec
::new();
185 let mut code_to_the_left
= false;
187 if let Some(shebang_len
) = rustc_lexer
::strip_shebang(text
) {
188 comments
.push(Comment
{
189 style
: CommentStyle
::Isolated
,
190 lines
: vec
![text
[..shebang_len
].to_string()],
196 for token
in rustc_lexer
::tokenize(&text
[pos
..]) {
197 let token_text
= &text
[pos
..pos
+ token
.len
as usize];
199 rustc_lexer
::TokenKind
::Whitespace
=> {
200 if let Some(mut idx
) = token_text
.find('
\n'
) {
201 code_to_the_left
= false;
202 while let Some(next_newline
) = &token_text
[idx
+ 1..].find('
\n'
) {
203 idx
+= 1 + next_newline
;
204 comments
.push(Comment
{
205 style
: CommentStyle
::BlankLine
,
207 pos
: start_bpos
+ BytePos((pos
+ idx
) as u32),
212 rustc_lexer
::TokenKind
::BlockComment { doc_style, .. }
=> {
213 if doc_style
.is_none() {
214 let code_to_the_right
= !matches
!(
215 text
[pos
+ token
.len
as usize..].chars().next(),
218 let style
= match (code_to_the_left
, code_to_the_right
) {
219 (_
, true) => CommentStyle
::Mixed
,
220 (false, false) => CommentStyle
::Isolated
,
221 (true, false) => CommentStyle
::Trailing
,
224 // Count the number of chars since the start of the line by rescanning.
225 let pos_in_file
= start_bpos
+ BytePos(pos
as u32);
226 let line_begin_in_file
= source_file
.line_begin_pos(pos_in_file
);
227 let line_begin_pos
= (line_begin_in_file
- start_bpos
).to_usize();
228 let col
= CharPos(text
[line_begin_pos
..pos
].chars().count());
230 let lines
= split_block_comment_into_lines(token_text
, col
);
231 comments
.push(Comment { style, lines, pos: pos_in_file }
)
234 rustc_lexer
::TokenKind
::LineComment { doc_style }
=> {
235 if doc_style
.is_none() {
236 comments
.push(Comment
{
237 style
: if code_to_the_left
{
238 CommentStyle
::Trailing
240 CommentStyle
::Isolated
242 lines
: vec
![token_text
.to_string()],
243 pos
: start_bpos
+ BytePos(pos
as u32),
248 code_to_the_left
= true;
251 pos
+= token
.len
as usize;