|
| 1 | +use proc_macro2::{Delimiter, Spacing, TokenStream, TokenTree}; |
| 2 | +use syn::Lit; |
| 3 | + |
| 4 | +/// Write tokens same way as `TokenStream::to_string` would do, but with normalization of doc |
| 5 | +/// attributes into `///`. |
| 6 | +pub fn write_tokens_normalized( |
| 7 | + f: &mut std::fmt::Formatter, |
| 8 | + tokens: TokenStream, |
| 9 | +) -> std::fmt::Result { |
| 10 | + let mut tokens = tokens.into_iter().peekable(); |
| 11 | + let mut joint = false; |
| 12 | + let mut first = true; |
| 13 | + while let Some(tt) = tokens.next() { |
| 14 | + if !first && !joint { |
| 15 | + write!(f, " ")?; |
| 16 | + } |
| 17 | + first = false; |
| 18 | + joint = false; |
| 19 | + |
| 20 | + if let Some(comment) = tokens |
| 21 | + .peek() |
| 22 | + .and_then(|lookahead| as_doc_comment(&tt, lookahead)) |
| 23 | + { |
| 24 | + let _ignore = tokens.next(); |
| 25 | + writeln!(f, "///{}", comment)?; |
| 26 | + continue; |
| 27 | + } |
| 28 | + match tt { |
| 29 | + TokenTree::Group(ref tt) => { |
| 30 | + let (start, end) = match tt.delimiter() { |
| 31 | + Delimiter::Parenthesis => ("(", ")"), |
| 32 | + Delimiter::Brace => ("{", "}"), |
| 33 | + Delimiter::Bracket => ("[", "]"), |
| 34 | + Delimiter::None => ("", ""), |
| 35 | + }; |
| 36 | + if tt.stream().into_iter().next().is_none() { |
| 37 | + write!(f, "{} {}", start, end)? |
| 38 | + } else { |
| 39 | + write!(f, "{} ", start)?; |
| 40 | + write_tokens_normalized(f, tt.stream())?; |
| 41 | + write!(f, " {}", end)? |
| 42 | + } |
| 43 | + } |
| 44 | + TokenTree::Ident(ref tt) => write!(f, "{}", tt)?, |
| 45 | + TokenTree::Punct(ref tt) => { |
| 46 | + write!(f, "{}", tt.as_char())?; |
| 47 | + match tt.spacing() { |
| 48 | + Spacing::Alone => {} |
| 49 | + Spacing::Joint => joint = true, |
| 50 | + } |
| 51 | + } |
| 52 | + TokenTree::Literal(ref tt) => write!(f, "{}", tt)?, |
| 53 | + } |
| 54 | + } |
| 55 | + Ok(()) |
| 56 | +} |
| 57 | + |
| 58 | +fn as_doc_comment(first: &TokenTree, second: &TokenTree) -> Option<String> { |
| 59 | + match (first, second) { |
| 60 | + (TokenTree::Punct(first), TokenTree::Group(group)) |
| 61 | + if first.as_char() == '#' && group.delimiter() == Delimiter::Bracket => |
| 62 | + { |
| 63 | + let mut it = group.stream().into_iter(); |
| 64 | + match (it.next(), it.next(), it.next()) { |
| 65 | + ( |
| 66 | + Some(TokenTree::Ident(ident)), |
| 67 | + Some(TokenTree::Punct(punct)), |
| 68 | + Some(TokenTree::Literal(lit)), |
| 69 | + ) => { |
| 70 | + if ident == "doc" && punct.as_char() == '=' { |
| 71 | + if let Lit::Str(lit) = Lit::new(lit) { |
| 72 | + return Some(lit.value()); |
| 73 | + } |
| 74 | + } |
| 75 | + } |
| 76 | + _ => {} |
| 77 | + } |
| 78 | + } |
| 79 | + _ => {} |
| 80 | + } |
| 81 | + None |
| 82 | +} |
0 commit comments