rustc_ast/util/
comments.rs1use rustc_span::{BytePos, Symbol};
2
3use crate::token::CommentKind;
4
5#[cfg(test)]
6mod tests;
7
8#[derive(Clone, Copy, PartialEq, Debug)]
9pub enum CommentStyle {
10 Isolated,
12 Trailing,
14 Mixed,
16 BlankLine,
18}
19
20#[derive(Clone)]
21pub struct Comment {
22 pub style: CommentStyle,
23 pub lines: Vec<String>,
24 pub pos: BytePos,
25}
26
27#[inline]
31pub fn may_have_doc_links(s: &str) -> bool {
32 s.contains('[')
33}
34
35pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol {
38 fn get_vertical_trim(lines: &[&str]) -> Option<(usize, usize)> {
39 let mut i = 0;
40 let mut j = lines.len();
41 if lines.first().is_some_and(|line| line.chars().all(|c| c == '*')) {
43 i += 1;
44 }
45
46 if j > i && !lines[j - 1].is_empty() && lines[j - 1].chars().all(|c| c == '*') {
48 j -= 1;
49 }
50
51 if i != 0 || j != lines.len() { Some((i, j)) } else { None }
52 }
53
54 fn get_horizontal_trim(lines: &[&str], kind: CommentKind) -> Option<String> {
55 let mut i = usize::MAX;
56 let mut first = true;
57
58 let lines = match kind {
62 CommentKind::Block => {
63 let mut i = lines
65 .first()
66 .map(|l| if l.trim_start().starts_with('*') { 0 } else { 1 })
67 .unwrap_or(0);
68 let mut j = lines.len();
69
70 while i < j && lines[i].trim().is_empty() {
71 i += 1;
72 }
73 while j > i && lines[j - 1].trim().is_empty() {
74 j -= 1;
75 }
76 &lines[i..j]
77 }
78 CommentKind::Line => lines,
79 };
80
81 for line in lines {
82 for (j, c) in line.chars().enumerate() {
83 if j > i || !"* \t".contains(c) {
84 return None;
85 }
86 if c == '*' {
87 if first {
88 i = j;
89 first = false;
90 } else if i != j {
91 return None;
92 }
93 break;
94 }
95 }
96 if i >= line.len() {
97 return None;
98 }
99 }
100 Some(lines.first()?[..i].to_string())
101 }
102
103 let data_s = data.as_str();
104 if data_s.contains('\n') {
105 let mut lines = data_s.lines().collect::<Vec<&str>>();
106 let mut changes = false;
107 let lines = if let Some((i, j)) = get_vertical_trim(&lines) {
108 changes = true;
109 &mut lines[i..j]
111 } else {
112 &mut lines
113 };
114 if let Some(horizontal) = get_horizontal_trim(lines, kind) {
115 changes = true;
116 for line in lines.iter_mut() {
118 if let Some(tmp) = line.strip_prefix(&horizontal) {
119 *line = tmp;
120 if kind == CommentKind::Block
121 && (*line == "*" || line.starts_with("* ") || line.starts_with("**"))
122 {
123 *line = &line[1..];
124 }
125 }
126 }
127 }
128 if changes {
129 return Symbol::intern(&lines.join("\n"));
130 }
131 }
132 data
133}