diff options
Diffstat (limited to 'vendor/clap_derive/src/utils/doc_comments.rs')
-rw-r--r-- | vendor/clap_derive/src/utils/doc_comments.rs | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/vendor/clap_derive/src/utils/doc_comments.rs b/vendor/clap_derive/src/utils/doc_comments.rs new file mode 100644 index 0000000..63c6ad1 --- /dev/null +++ b/vendor/clap_derive/src/utils/doc_comments.rs @@ -0,0 +1,126 @@ +//! The preprocessing we apply to doc comments. +//! +//! #[derive(Parser)] works in terms of "paragraphs". Paragraph is a sequence of +//! non-empty adjacent lines, delimited by sequences of blank (whitespace only) lines. + +use std::iter; + +pub fn extract_doc_comment(attrs: &[syn::Attribute]) -> Vec<String> { + // multiline comments (`/** ... */`) may have LFs (`\n`) in them, + // we need to split so we could handle the lines correctly + // + // we also need to remove leading and trailing blank lines + let mut lines: Vec<_> = attrs + .iter() + .filter(|attr| attr.path().is_ident("doc")) + .filter_map(|attr| { + // non #[doc = "..."] attributes are not our concern + // we leave them for rustc to handle + match &attr.meta { + syn::Meta::NameValue(syn::MetaNameValue { + value: + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(s), + .. + }), + .. + }) => Some(s.value()), + _ => None, + } + }) + .skip_while(|s| is_blank(s)) + .flat_map(|s| { + let lines = s + .split('\n') + .map(|s| { + // remove one leading space no matter what + let s = s.strip_prefix(' ').unwrap_or(s); + s.to_owned() + }) + .collect::<Vec<_>>(); + lines + }) + .collect(); + + while let Some(true) = lines.last().map(|s| is_blank(s)) { + lines.pop(); + } + + lines +} + +pub fn format_doc_comment( + lines: &[String], + preprocess: bool, + force_long: bool, +) -> (Option<String>, Option<String>) { + if let Some(first_blank) = lines.iter().position(|s| is_blank(s)) { + let (short, long) = if preprocess { + let paragraphs = split_paragraphs(lines); + let short = paragraphs[0].clone(); + let long = paragraphs.join("\n\n"); + (remove_period(short), long) + } else { + let short = lines[..first_blank].join("\n"); + let long = lines.join("\n"); + (short, long) + }; + + (Some(short), Some(long)) + } else { + let (short, long) = if preprocess { + let short = merge_lines(lines); + let long = force_long.then(|| short.clone()); + let short = remove_period(short); + (short, long) + } else { + let short = lines.join("\n"); + let long = force_long.then(|| short.clone()); + (short, long) + }; + + (Some(short), long) + } +} + +fn split_paragraphs(lines: &[String]) -> Vec<String> { + let mut last_line = 0; + iter::from_fn(|| { + let slice = &lines[last_line..]; + let start = slice.iter().position(|s| !is_blank(s)).unwrap_or(0); + + let slice = &slice[start..]; + let len = slice + .iter() + .position(|s| is_blank(s)) + .unwrap_or(slice.len()); + + last_line += start + len; + + if len != 0 { + Some(merge_lines(&slice[..len])) + } else { + None + } + }) + .collect() +} + +fn remove_period(mut s: String) -> String { + if s.ends_with('.') && !s.ends_with("..") { + s.pop(); + } + s +} + +fn is_blank(s: &str) -> bool { + s.trim().is_empty() +} + +fn merge_lines(lines: impl IntoIterator<Item = impl AsRef<str>>) -> String { + lines + .into_iter() + .map(|s| s.as_ref().trim().to_owned()) + .collect::<Vec<_>>() + .join(" ") +} |