Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect padding in doc comments #204

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 76 additions & 2 deletions src/documentation/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,20 +111,40 @@ impl CommentVariant {
}
}

/// Track the style of padding for multiline comments
#[derive(Clone, Debug, Hash, PartialEq)]
pub enum CommentPaddingStyle {
/// " * ", with container for number of spaces
AsteriskSpace {
/// Number of leading spaces
leading_spaces: usize,
},
/// Absence of padding
NoPadding,
}

impl Default for CommentPaddingStyle {
fn default() -> Self {
Self::NoPadding
}
}

/// A literal with meta info where the first and list whitespace may be found.
#[derive(Clone)]
pub struct TrimmedLiteral {
/// Track what kind of comment the literal is
variant: CommentVariant,
/// The span of rendered content, minus pre and post already applied.
span: Span,
/// The style of the padding for comments, if present
padding_style: CommentPaddingStyle,
/// the complete rendered string including post and pre.
rendered: String,
/// Literal prefix length.
pre: usize,
/// Literal postfix length.
post: usize,
/// Length of rendered **minus** `pre` and `post` in UTF-8 characters.
/// Length of rendered **minus** `pre`, `post`, and `padding` in UTF-8 characters.
len_in_chars: usize,
len_in_bytes: usize,
}
Expand All @@ -146,6 +166,9 @@ impl std::cmp::PartialEq for TrimmedLiteral {
if self.span != other.span {
return false;
}
if self.padding_style != other.padding_style {
return false;
}
if self.variant != other.variant {
return false;
}
Expand All @@ -161,6 +184,7 @@ impl std::hash::Hash for TrimmedLiteral {
self.variant.hash(hasher);
self.rendered.hash(hasher);
self.span.hash(hasher);
self.padding_style.hash(hasher);
self.pre.hash(hasher);
self.post.hash(hasher);
self.len_in_bytes.hash(hasher);
Expand Down Expand Up @@ -313,6 +337,24 @@ fn detect_comment_variant(
Ok((variant, span, pre, post))
}

fn detect_and_remove_padding(content: &str) -> (CommentPaddingStyle, usize) {
lazy_static! {
static ref PADDING_STR: Regex =
Regex::new(r##"(?m)^\s\*\s"##).expect("PADDING_STR regex compiles");
};
Comment on lines +341 to +344
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#202 introduced an optimization to avoid the regex entirely and use smid instructions as available, probably worth to hand roll this, but that can be done once you're confident of the structure - this is works and is good for the time being 👍


if let Ok(Some(_)) = PADDING_STR.find(content) {
let padding = CommentPaddingStyle::AsteriskSpace { leading_spaces: 1 };
let clean_content = PADDING_STR.replace_all(content, "");
return (
padding,
content.chars().count() - clean_content.chars().count(),
);
}

(CommentPaddingStyle::NoPadding, content.len())
}

impl TryFrom<(&str, proc_macro2::Literal)> for TrimmedLiteral {
type Error = Error;
fn try_from((content, literal): (&str, proc_macro2::Literal)) -> Result<Self> {
Expand Down Expand Up @@ -354,6 +396,14 @@ impl TryFrom<(&str, proc_macro2::Literal)> for TrimmedLiteral {
log::trace!("extracted from source: >{}< @ {:?}", rendered, span);
let (variant, span, pre, post) = detect_comment_variant(content, &rendered, span)?;

let (padding_style, padding) = match variant {
CommentVariant::SlashStar
| CommentVariant::SlashAsterisk
| CommentVariant::SlashAsteriskEM
| CommentVariant::SlashAsteriskAsterisk => detect_and_remove_padding(content),
_ => (CommentPaddingStyle::NoPadding, 0),
};

let len_in_chars = rendered_len.saturating_sub(post + pre);

if let Some(span_len) = span.one_line_len() {
Expand All @@ -372,6 +422,7 @@ impl TryFrom<(&str, proc_macro2::Literal)> for TrimmedLiteral {
len_in_bytes,
rendered,
span,
padding_style,
pre,
post,
};
Expand Down Expand Up @@ -408,6 +459,7 @@ impl TrimmedLiteral {
Ok(TrimmedLiteral {
variant,
span,
padding_style: CommentPaddingStyle::NoPadding,
rendered: content.to_string(),
pre,
post,
Expand Down Expand Up @@ -471,6 +523,11 @@ impl TrimmedLiteral {
self.span.clone()
}

/// The padding style for this literal.
pub fn padding_style(&self) -> CommentPaddingStyle {
self.padding_style.clone()
}

/// Access the characters via an iterator.
pub fn chars<'a>(&'a self) -> impl Iterator<Item = char> + 'a {
self.as_str().chars()
Expand Down Expand Up @@ -626,6 +683,23 @@ mod tests {
});
}

#[test]
fn padding_style_detect() {
let result = "/**\ndoc\ndoc\n */".to_string();
assert_matches!(
detect_and_remove_padding("/**\n * doc\n * doc\n */"),
(
CommentPaddingStyle::AsteriskSpace { leading_spaces: 1 },
result
)
);
let content = "/**\n doc\n doc\n */".to_string();
assert_matches!(
detect_and_remove_padding(&content),
(CommentPaddingStyle::NoPadding, content)
Comment on lines +697 to +699
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 nice use of assert_matches :)

);
Comment on lines +696 to +700
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer smaller test cases, even if this has a compile time cost to it.

And I think we need a few more to cover the indented variants, as in multiple leading spaces before the asterisk.

}

macro_rules! block_comment_test {
($name:ident, $content:literal) => {
#[test]
Expand Down Expand Up @@ -656,7 +730,7 @@ mod tests {
block_comment_test!(
trimmed_multi_doc,
"/**
mood
* mood
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both variants should work, the * should be optional.

*/"
);
block_comment_test!(
Expand Down