|
|
@@ -52,14 +52,55 @@ fn cleaners() -> &'static Cleaners {
|
|
|
paren_star_close: Regex::new(r"\*\)$").unwrap(),
|
|
|
brace_open: Regex::new(r"^\{").unwrap(),
|
|
|
brace_close: Regex::new(r"\}$").unwrap(),
|
|
|
- slashes: Regex::new(r"(?m)^//[/!]?\s?").unwrap(),
|
|
|
- dashes: Regex::new(r"(?m)^--\s?").unwrap(),
|
|
|
- hash: Regex::new(r"(?m)^#\s?").unwrap(),
|
|
|
- percent: Regex::new(r"(?m)^%+\s?").unwrap(),
|
|
|
- star_cont: Regex::new(r"(?m)^\s*\*\s?").unwrap(),
|
|
|
+ slashes: Regex::new(r"\A//[/!]?\s?").unwrap(),
|
|
|
+ dashes: Regex::new(r"\A--\s?").unwrap(),
|
|
|
+ hash: Regex::new(r"\A#\s?").unwrap(),
|
|
|
+ percent: Regex::new(r"\A%+\s?").unwrap(),
|
|
|
+ star_cont: Regex::new(r"\A\s*\*\s?").unwrap(),
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+/// JS multiline `^` anchors after \n, \r, U+2028, U+2029; the regex crate's
|
|
|
+/// `(?m)^` anchors after `\n` only. On CRLF content the JS engine finds a line
|
|
|
+/// start after the `\r`, so a greedy leading `\s*` (the block-continuation
|
|
|
+/// rule) consumes the `\n` and leaves the bare `\r` in the docstring —
|
|
|
+/// byte-parity on CRLF checkouts (every Windows autocrlf clone) depends on
|
|
|
+/// reproducing exactly that.
|
|
|
+fn is_js_line_terminator(ch: char) -> bool {
|
|
|
+ matches!(ch, '\n' | '\r' | '\u{2028}' | '\u{2029}')
|
|
|
+}
|
|
|
+
|
|
|
+/// JS-semantics `str.replace(/^<pat>/gm, "")`: try the \A-anchored `pat` at
|
|
|
+/// position 0 and after every JS line terminator, left to right, resuming
|
|
|
+/// after each match's end — a faithful /g replace. (Remaining known
|
|
|
+/// divergence: JS `\s` includes U+FEFF, Rust's does not; an embedded BOM
|
|
|
+/// inside a comment is accepted as unreachable.)
|
|
|
+fn js_multiline_strip(s: &str, pat: &Regex) -> String {
|
|
|
+ let mut out = String::with_capacity(s.len());
|
|
|
+ let mut last = 0usize;
|
|
|
+ let mut pos = 0usize;
|
|
|
+ while pos <= s.len() {
|
|
|
+ let at_line_start = pos == 0
|
|
|
+ || s[..pos].chars().next_back().is_some_and(is_js_line_terminator);
|
|
|
+ if at_line_start {
|
|
|
+ if let Some(m) = pat.find(&s[pos..]) {
|
|
|
+ if !m.is_empty() {
|
|
|
+ out.push_str(&s[last..pos]);
|
|
|
+ last = pos + m.end();
|
|
|
+ pos = last;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ match s[pos..].chars().next() {
|
|
|
+ Some(c) => pos += c.len_utf8(),
|
|
|
+ None => break,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ out.push_str(&s[last..]);
|
|
|
+ out
|
|
|
+}
|
|
|
+
|
|
|
/// cleanCommentMarkers — strip comment syntax, keep the prose.
|
|
|
pub fn clean_comment_markers(comment: &str) -> String {
|
|
|
let c = cleaners();
|
|
|
@@ -77,11 +118,11 @@ pub fn clean_comment_markers(comment: &str) -> String {
|
|
|
s = c.brace_open.replace(&s, "").into_owned();
|
|
|
s = c.brace_close.replace(&s, "").into_owned();
|
|
|
}
|
|
|
- s = c.slashes.replace_all(&s, "").into_owned();
|
|
|
- s = c.dashes.replace_all(&s, "").into_owned();
|
|
|
- s = c.hash.replace_all(&s, "").into_owned();
|
|
|
- s = c.percent.replace_all(&s, "").into_owned();
|
|
|
- s = c.star_cont.replace_all(&s, "").into_owned();
|
|
|
+ s = js_multiline_strip(&s, &c.slashes);
|
|
|
+ s = js_multiline_strip(&s, &c.dashes);
|
|
|
+ s = js_multiline_strip(&s, &c.hash);
|
|
|
+ s = js_multiline_strip(&s, &c.percent);
|
|
|
+ s = js_multiline_strip(&s, &c.star_cont);
|
|
|
s.trim().to_string()
|
|
|
}
|
|
|
|
|
|
@@ -137,4 +178,20 @@ mod tests {
|
|
|
"Adds things.\n@param a first"
|
|
|
);
|
|
|
}
|
|
|
+
|
|
|
+ /// CRLF parity with the JS reference: multiline `^` matches after `\r`,
|
|
|
+ /// so the block-continuation `\s*` eats the `\n` and the bare `\r`
|
|
|
+ /// survives in the cleaned docstring (pinned against the wasm extractor
|
|
|
+ /// on a CRLF checkout — the Windows autocrlf shape).
|
|
|
+ #[test]
|
|
|
+ fn crlf_matches_js_reference() {
|
|
|
+ assert_eq!(
|
|
|
+ clean_comment_markers("/**\r\n * Class docs.\r\n * Multi-line.\r\n */"),
|
|
|
+ "Class docs.\rMulti-line."
|
|
|
+ );
|
|
|
+ assert_eq!(
|
|
|
+ clean_comment_markers("// a\r\n// b"),
|
|
|
+ "a\r\nb"
|
|
|
+ );
|
|
|
+ }
|
|
|
}
|