fix(formatter/jsdoc): more alignment with original plugin (#27039)

- Handle non-leading JSDoc and also legal
- Keep trailing 2 spaces (for hard break)
- Remove useless spaces when they can hug
This commit is contained in:
leaysgur committed 2026-09-25 07:18:33 +00:00
1 parent 50be18e6ef
commit 8fbddb123e
8 files changed
+79 -19

No files matched your search

@@ -12,6 +12,10 @@ use super::super::markers::{ListMarker, list_marker};
/// Author opt-outs (`\- ` escape, blank line before an intended list) are pinned in
/// `descriptions/043-list-interrupting-continuation`.
pub(super) fn needs_mdast_parsing(text: &str) -> bool {
// Hard line break by 2 trailing spaces
if text.contains(" \n") {
return true;
}
let bytes = text.as_bytes();
let len = bytes.len();
let mut i = 0;
@@ -473,10 +473,13 @@ impl JsdocFormatter<'_, '_> {
}
// Split description into first line and rest (avoids collecting all lines)
let (first_text_line, rest_of_desc) = match desc_raw.split_once('\n') {
Some((first, rest)) => (first.trim(), Some(rest)),
None => (desc_raw.trim(), None),
let (first_line, rest_of_desc) = match desc_raw.split_once('\n') {
Some((first, rest)) => (first, Some(rest)),
None => (desc_raw, None),
};
let first_text_line = first_line.trim();
// Keep a Markdown hard line break, which `trim()` drops
let first_hard_break = rest_of_desc.is_some() && first_line.ends_with(" ");
// If the description starts with a code fence, output the tag line alone
// and treat the entire description as structural content with a blank line separator
@@ -626,6 +629,9 @@ impl JsdocFormatter<'_, '_> {
let mut s = if has_remaining {
let mut s = String::with_capacity(first_text.len() + 1 + remaining_desc.len());
s.push_str(&first_text);
if first_hard_break {
s.push_str(" ");
}
s.push('\n');
s.push_str(&remaining_desc);
s
@@ -1130,8 +1136,11 @@ impl JsdocFormatter<'_, '_> {
}
let fits_on_one_line = prefix_len + str_width(&desc_text) <= self.wrap_width;
if fits_on_one_line || skip_wrapping {
// Fits on one line, or tag skips description formatting (no wrapping).
// Other descriptions go through wrapping, which collapses line breaks and spaces
let is_plain_one_liner =
fits_on_one_line && !desc_text.contains('\n') && !desc_text.contains(" ");
if is_plain_one_liner || skip_wrapping {
// Fits on one line as is, or tag skips description formatting (no wrapping).
// Tags in TAGS_PEV_FORMATE_DESCRIPTION (e.g. @see) and unknown tags
// keep their description on one line regardless of length.
// When desc has embedded newlines (e.g. @module Name\n\nMore text),
+1 -2
View File
@@ -521,8 +521,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatCommentText<'_> {
}
// JSDoc formatting: if enabled, try to format JSDoc comments
let formatted_jsdoc = if comment.is_jsdoc()
&& !comment.is_legal()
let formatted_jsdoc = if is_jsdoc_comment(comment)
&& let Some(jsdoc_options) = &f.options().jsdoc
{
let source: &str = &f.source_text();
@@ -0,0 +1,14 @@
/**
* JSDoc with new line using double space
* It is on another line
*
* @param a first
* Continued
*/
function f(a) {}
/***
* triple
* Next
*/
function g() {}
@@ -0,0 +1,14 @@
/**
* JSDoc with new line using double space\
* It is on another line
*
* @param a First\
* Continued
*/
function f(a) {}
/**
* Triple\
* Next
*/
function g() {}
@@ -0,0 +1,10 @@
/** @license MIT
* some text */
const a = 1;
/** @author MIT
* some text */
const b = 1;
const c = 1; /** trailing doc
* next */
@@ -0,0 +1,7 @@
/** @license MIT some text */
const a = 1;
/** @author MIT Some text */
const b = 1;
const c = 1; /** Trailing doc next */
+15 -12
View File
@@ -105,20 +105,23 @@ impl<'a> JSDocCommentPart<'a> {
result.push('\n');
}
let trimmed = line.trim();
if let Some(rest) = trimmed.strip_prefix('*') {
// Strip `*` as a comment continuation prefix UNLESS it looks like
// markdown emphasis (`*word*`). Emphasis has `*` followed by an
// alphanumeric char; continuation prefixes have `*` followed by
// space, backtick, punctuation, or nothing.
let is_emphasis = rest.starts_with(|c: char| c.is_alphanumeric() || c == '_');
if !is_emphasis {
// Strip at most one leading space after `*` (the conventional ` * ` prefix)
// to preserve any additional indentation (e.g. for indented code blocks)
result.push_str(rest.strip_prefix(' ').unwrap_or(rest));
continue;
let content = match trimmed.strip_prefix('*') {
// Strip `*` as a comment continuation prefix
// UNLESS it looks like markdown emphasis (`*word*`).
// Emphasis has `*` followed by an alphanumeric char;
// continuation prefixes have `*` followed by space, backtick, punctuation, or nothing.
// Strip at most one leading space after `*` (the conventional ` * ` prefix)
// to preserve any additional indentation (e.g. for indented code blocks)
Some(rest) if !rest.starts_with(|c: char| c.is_alphanumeric() || c == '_') => {
rest.strip_prefix(' ').unwrap_or(rest)
}
_ => trimmed,
};
result.push_str(content);
// Keep a Markdown hard line break, as 2 trailing spaces
if line.ends_with(" ") && !content.is_empty() {
result.push_str(" ");
}
result.push_str(trimmed);
}
result
}