]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | //! The preprocessing we apply to doc comments. |
2 | //! | |
3 | //! structopt works in terms of "paragraphs". Paragraph is a sequence of | |
4 | //! non-empty adjacent lines, delimited by sequences of blank (whitespace only) lines. | |
5 | ||
6 | use crate::attrs::Method; | |
7 | use quote::{format_ident, quote}; | |
8 | use std::iter; | |
9 | ||
10 | pub fn process_doc_comment(lines: Vec<String>, name: &str, preprocess: bool) -> Vec<Method> { | |
11 | // multiline comments (`/** ... */`) may have LFs (`\n`) in them, | |
12 | // we need to split so we could handle the lines correctly | |
13 | // | |
14 | // we also need to remove leading and trailing blank lines | |
15 | let mut lines: Vec<&str> = lines | |
16 | .iter() | |
17 | .skip_while(|s| is_blank(s)) | |
18 | .flat_map(|s| s.split('\n')) | |
19 | .collect(); | |
20 | ||
21 | while let Some(true) = lines.last().map(|s| is_blank(s)) { | |
22 | lines.pop(); | |
23 | } | |
24 | ||
25 | // remove one leading space no matter what | |
26 | for line in lines.iter_mut() { | |
27 | if line.starts_with(' ') { | |
28 | *line = &line[1..]; | |
29 | } | |
30 | } | |
31 | ||
32 | if lines.is_empty() { | |
33 | return vec![]; | |
34 | } | |
35 | ||
36 | let short_name = format_ident!("{}", name); | |
37 | let long_name = format_ident!("long_{}", name); | |
38 | ||
39 | if let Some(first_blank) = lines.iter().position(|s| is_blank(s)) { | |
40 | let (short, long) = if preprocess { | |
41 | let paragraphs = split_paragraphs(&lines); | |
42 | let short = paragraphs[0].clone(); | |
43 | let long = paragraphs.join("\n\n"); | |
44 | (remove_period(short), long) | |
45 | } else { | |
46 | let short = lines[..first_blank].join("\n"); | |
47 | let long = lines.join("\n"); | |
48 | (short, long) | |
49 | }; | |
50 | ||
51 | vec![ | |
52 | Method::new(short_name, quote!(#short)), | |
53 | Method::new(long_name, quote!(#long)), | |
54 | ] | |
55 | } else { | |
56 | let short = if preprocess { | |
57 | let s = merge_lines(&lines); | |
58 | remove_period(s) | |
59 | } else { | |
60 | lines.join("\n") | |
61 | }; | |
62 | ||
63 | vec![Method::new(short_name, quote!(#short))] | |
64 | } | |
65 | } | |
66 | ||
67 | fn split_paragraphs(lines: &[&str]) -> Vec<String> { | |
68 | let mut last_line = 0; | |
69 | iter::from_fn(|| { | |
70 | let slice = &lines[last_line..]; | |
71 | let start = slice.iter().position(|s| !is_blank(s)).unwrap_or(0); | |
72 | ||
73 | let slice = &slice[start..]; | |
74 | let len = slice | |
75 | .iter() | |
76 | .position(|s| is_blank(s)) | |
77 | .unwrap_or_else(|| slice.len()); | |
78 | ||
79 | last_line += start + len; | |
80 | ||
81 | if len != 0 { | |
82 | Some(merge_lines(&slice[..len])) | |
83 | } else { | |
84 | None | |
85 | } | |
86 | }) | |
87 | .collect() | |
88 | } | |
89 | ||
90 | fn remove_period(mut s: String) -> String { | |
91 | if s.ends_with('.') && !s.ends_with("..") { | |
92 | s.pop(); | |
93 | } | |
94 | s | |
95 | } | |
96 | ||
97 | fn is_blank(s: &str) -> bool { | |
98 | s.trim().is_empty() | |
99 | } | |
100 | ||
101 | fn merge_lines(lines: &[&str]) -> String { | |
102 | lines.iter().map(|s| s.trim()).collect::<Vec<_>>().join(" ") | |
103 | } |