• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::algorithm::Printer;
2 use crate::fixup::FixupContext;
3 use crate::path::PathKind;
4 use crate::INDENT;
5 use proc_macro2::{Delimiter, Group, TokenStream, TokenTree};
6 use syn::{AttrStyle, Attribute, Expr, Lit, MacroDelimiter, Meta, MetaList, MetaNameValue};
7 
8 impl Printer {
outer_attrs(&mut self, attrs: &[Attribute])9     pub fn outer_attrs(&mut self, attrs: &[Attribute]) {
10         for attr in attrs {
11             if let AttrStyle::Outer = attr.style {
12                 self.attr(attr);
13             }
14         }
15     }
16 
inner_attrs(&mut self, attrs: &[Attribute])17     pub fn inner_attrs(&mut self, attrs: &[Attribute]) {
18         for attr in attrs {
19             if let AttrStyle::Inner(_) = attr.style {
20                 self.attr(attr);
21             }
22         }
23     }
24 
attr(&mut self, attr: &Attribute)25     fn attr(&mut self, attr: &Attribute) {
26         if let Some(mut doc) = value_of_attribute("doc", attr) {
27             if !doc.contains('\n')
28                 && match attr.style {
29                     AttrStyle::Outer => !doc.starts_with('/'),
30                     AttrStyle::Inner(_) => true,
31                 }
32             {
33                 trim_trailing_spaces(&mut doc);
34                 self.word(match attr.style {
35                     AttrStyle::Outer => "///",
36                     AttrStyle::Inner(_) => "//!",
37                 });
38                 self.word(doc);
39                 self.hardbreak();
40                 return;
41             } else if can_be_block_comment(&doc)
42                 && match attr.style {
43                     AttrStyle::Outer => !doc.starts_with(&['*', '/'][..]),
44                     AttrStyle::Inner(_) => true,
45                 }
46             {
47                 trim_interior_trailing_spaces(&mut doc);
48                 self.word(match attr.style {
49                     AttrStyle::Outer => "/**",
50                     AttrStyle::Inner(_) => "/*!",
51                 });
52                 self.word(doc);
53                 self.word("*/");
54                 self.hardbreak();
55                 return;
56             }
57         } else if let Some(mut comment) = value_of_attribute("comment", attr) {
58             if !comment.contains('\n') {
59                 trim_trailing_spaces(&mut comment);
60                 self.word("//");
61                 self.word(comment);
62                 self.hardbreak();
63                 return;
64             } else if can_be_block_comment(&comment) && !comment.starts_with(&['*', '!'][..]) {
65                 trim_interior_trailing_spaces(&mut comment);
66                 self.word("/*");
67                 self.word(comment);
68                 self.word("*/");
69                 self.hardbreak();
70                 return;
71             }
72         }
73 
74         self.word(match attr.style {
75             AttrStyle::Outer => "#",
76             AttrStyle::Inner(_) => "#!",
77         });
78         self.word("[");
79         self.meta(&attr.meta);
80         self.word("]");
81         self.space();
82     }
83 
meta(&mut self, meta: &Meta)84     fn meta(&mut self, meta: &Meta) {
85         match meta {
86             Meta::Path(path) => self.path(path, PathKind::Simple),
87             Meta::List(meta) => self.meta_list(meta),
88             Meta::NameValue(meta) => self.meta_name_value(meta),
89         }
90     }
91 
meta_list(&mut self, meta: &MetaList)92     fn meta_list(&mut self, meta: &MetaList) {
93         self.path(&meta.path, PathKind::Simple);
94         let delimiter = match meta.delimiter {
95             MacroDelimiter::Paren(_) => Delimiter::Parenthesis,
96             MacroDelimiter::Brace(_) => Delimiter::Brace,
97             MacroDelimiter::Bracket(_) => Delimiter::Bracket,
98         };
99         let group = Group::new(delimiter, meta.tokens.clone());
100         self.attr_tokens(TokenStream::from(TokenTree::Group(group)));
101     }
102 
meta_name_value(&mut self, meta: &MetaNameValue)103     fn meta_name_value(&mut self, meta: &MetaNameValue) {
104         self.path(&meta.path, PathKind::Simple);
105         self.word(" = ");
106         self.expr(&meta.value, FixupContext::NONE);
107     }
108 
attr_tokens(&mut self, tokens: TokenStream)109     fn attr_tokens(&mut self, tokens: TokenStream) {
110         let mut stack = Vec::new();
111         stack.push((tokens.into_iter().peekable(), Delimiter::None));
112         let mut space = Self::nbsp as fn(&mut Self);
113 
114         #[derive(PartialEq)]
115         enum State {
116             Word,
117             Punct,
118             TrailingComma,
119         }
120 
121         use State::*;
122         let mut state = Word;
123 
124         while let Some((tokens, delimiter)) = stack.last_mut() {
125             match tokens.next() {
126                 Some(TokenTree::Ident(ident)) => {
127                     if let Word = state {
128                         space(self);
129                     }
130                     self.ident(&ident);
131                     state = Word;
132                 }
133                 Some(TokenTree::Punct(punct)) => {
134                     let ch = punct.as_char();
135                     if let (Word, '=') = (state, ch) {
136                         self.nbsp();
137                     }
138                     if ch == ',' && tokens.peek().is_none() {
139                         self.trailing_comma(true);
140                         state = TrailingComma;
141                     } else {
142                         self.token_punct(ch);
143                         if ch == '=' {
144                             self.nbsp();
145                         } else if ch == ',' {
146                             space(self);
147                         }
148                         state = Punct;
149                     }
150                 }
151                 Some(TokenTree::Literal(literal)) => {
152                     if let Word = state {
153                         space(self);
154                     }
155                     self.token_literal(&literal);
156                     state = Word;
157                 }
158                 Some(TokenTree::Group(group)) => {
159                     let delimiter = group.delimiter();
160                     let stream = group.stream();
161                     match delimiter {
162                         Delimiter::Parenthesis => {
163                             self.word("(");
164                             self.cbox(INDENT);
165                             self.zerobreak();
166                             state = Punct;
167                         }
168                         Delimiter::Brace => {
169                             self.word("{");
170                             state = Punct;
171                         }
172                         Delimiter::Bracket => {
173                             self.word("[");
174                             state = Punct;
175                         }
176                         Delimiter::None => {}
177                     }
178                     stack.push((stream.into_iter().peekable(), delimiter));
179                     space = Self::space;
180                 }
181                 None => {
182                     match delimiter {
183                         Delimiter::Parenthesis => {
184                             if state != TrailingComma {
185                                 self.zerobreak();
186                             }
187                             self.offset(-INDENT);
188                             self.end();
189                             self.word(")");
190                             state = Punct;
191                         }
192                         Delimiter::Brace => {
193                             self.word("}");
194                             state = Punct;
195                         }
196                         Delimiter::Bracket => {
197                             self.word("]");
198                             state = Punct;
199                         }
200                         Delimiter::None => {}
201                     }
202                     stack.pop();
203                     if stack.is_empty() {
204                         space = Self::nbsp;
205                     }
206                 }
207             }
208         }
209     }
210 }
211 
value_of_attribute(requested: &str, attr: &Attribute) -> Option<String>212 fn value_of_attribute(requested: &str, attr: &Attribute) -> Option<String> {
213     let value = match &attr.meta {
214         Meta::NameValue(meta) if meta.path.is_ident(requested) => &meta.value,
215         _ => return None,
216     };
217     let lit = match value {
218         Expr::Lit(expr) if expr.attrs.is_empty() => &expr.lit,
219         _ => return None,
220     };
221     match lit {
222         Lit::Str(string) => Some(string.value()),
223         _ => None,
224     }
225 }
226 
has_outer(attrs: &[Attribute]) -> bool227 pub fn has_outer(attrs: &[Attribute]) -> bool {
228     for attr in attrs {
229         if let AttrStyle::Outer = attr.style {
230             return true;
231         }
232     }
233     false
234 }
235 
has_inner(attrs: &[Attribute]) -> bool236 pub fn has_inner(attrs: &[Attribute]) -> bool {
237     for attr in attrs {
238         if let AttrStyle::Inner(_) = attr.style {
239             return true;
240         }
241     }
242     false
243 }
244 
trim_trailing_spaces(doc: &mut String)245 fn trim_trailing_spaces(doc: &mut String) {
246     doc.truncate(doc.trim_end_matches(' ').len());
247 }
248 
trim_interior_trailing_spaces(doc: &mut String)249 fn trim_interior_trailing_spaces(doc: &mut String) {
250     if !doc.contains(" \n") {
251         return;
252     }
253     let mut trimmed = String::with_capacity(doc.len());
254     let mut lines = doc.split('\n').peekable();
255     while let Some(line) = lines.next() {
256         if lines.peek().is_some() {
257             trimmed.push_str(line.trim_end_matches(' '));
258             trimmed.push('\n');
259         } else {
260             trimmed.push_str(line);
261         }
262     }
263     *doc = trimmed;
264 }
265 
can_be_block_comment(value: &str) -> bool266 fn can_be_block_comment(value: &str) -> bool {
267     let mut depth = 0usize;
268     let bytes = value.as_bytes();
269     let mut i = 0usize;
270     let upper = bytes.len() - 1;
271 
272     while i < upper {
273         if bytes[i] == b'/' && bytes[i + 1] == b'*' {
274             depth += 1;
275             i += 2;
276         } else if bytes[i] == b'*' && bytes[i + 1] == b'/' {
277             if depth == 0 {
278                 return false;
279             }
280             depth -= 1;
281             i += 2;
282         } else {
283             i += 1;
284         }
285     }
286 
287     depth == 0
288 }
289