docs(oxfmt,formatter_htmlish,formatter): clean up behavior comments (#28384)

We do not need to note about Prettier internals.
This commit is contained in:
Yuji Sugiura authored and GitHub committed 2026-10-30 08:45:37 +00:00
1 parent a5b0c0a054
commit 01ed68ac81
21 files changed
+40 -42

No files matched your search

+1 -2
View File
@@ -342,8 +342,7 @@ pub fn build_dispatcher(
dispatch_config.toml_options(),
),
NativeLanguage::Html(variant) => {
// Every part is embedded in its parent (Prettier's `parentParser`),
// but a JSDoc code block, formatted on its own
// Every part is embedded in its parent, but a JSDoc code block, formatted on its own
let embedded_in =
if let Some(c) = request.parent_context_as::<HtmlInJsTemplate>() {
Some(HtmlishEmbeddedIn {
+1 -1
View File
@@ -17,7 +17,7 @@ use oxc_formatter_core::{
use super::dispatcher::ResolvedDispatchConfig;
/// Parent→child context for a JSDoc fenced code block:
/// a part formatted on its own, as Prettier formats it (`prettier.format`, no `parentParser`),
/// a part formatted on its own, as a file is,
/// where every other dispatched part is embedded in its parent.
pub struct XxxInJsdocFence;
@@ -284,9 +284,8 @@ describe("Embedded languages", () => {
`);
});
// (css-in-html)-in-js: For now, HTML is handled by Prettier, so CSS is also handled by Prettier too.
// The CSS `Doc` carries a trailing space after `prop: ` that only materializes when the value fits flat;
// when the value breaks, it must not leak (the Doc→IR conversion maps it to a pending space, dropped at the break).
// (css-in-html)-in-js: the space after `prop:` belongs to a value that fits on the line,
// so a value that breaks must not leave it at the end of the line.
it("should not leak a trailing space when a css-in-html declaration value breaks", async () => {
const input = `\
export const BaseStyles = html\`
+1 -1
View File
@@ -39,7 +39,7 @@ pub const HTML_PLACEHOLDER_SUFFIX: &str = "_IN_JS";
/// `name` is an html-in-js placeholder, of any depth.
/// One in a `<script>` stands in for an expression of any precedence (`f((${a + b}) / 2)`),
/// so the parens around it in the source stay (Prettier's `parentheses/identifier.js`).
/// so the parens around it in the source stay.
pub fn is_html_placeholder(name: &str) -> bool {
name.strip_prefix(HTML_PLACEHOLDER_PREFIX)
.and_then(|id| id.strip_suffix(HTML_PLACEHOLDER_SUFFIX))
@@ -258,7 +258,7 @@ impl<'ast> JsFormatContext<'ast> {
self.embedded_in == Some(JsEmbeddedIn::HtmlEventHandler)
}
/// The quote a string prints in (Prettier's `printString`), the formatter's own too (a quoted property key):
/// The quote a string prints in, the formatter's own too (a quoted property key):
/// the configured one, but `'` in an HTML attribute.
/// A JSX attribute and a directive have their own (`FormatLiteralStringToken`).
pub(crate) fn string_quote_style(&self) -> QuoteStyle {
+1 -1
View File
@@ -184,7 +184,7 @@ pub fn format_fragment<'a>(
// this may not be enough for all cases.
// But it seems fine for almost all cases, so leave it for now.
// TODO: When Vue formats natively, share the HTML event handler's rule instead
// (`JsFormatContext::string_quote_style`, forced `'` as Prettier's `__isInHtmlAttribute`):
// (`JsFormatContext::string_quote_style`, `'` whatever the string holds):
// a string holding more `'` than `"` prints `"` here, which the attribute escapes as `&quot;`.
let options = JsFormatOptions { quote_style: QuoteStyle::Single, ..options };
@@ -75,7 +75,7 @@ impl NeedsParentheses<'_> for AstNode<'_, '_, IdentifierReference<'_>> {
return false;
}
// An html-in-js placeholder (reaching JS through HTML's `<script>` or `on*` attributes)
// stands in for an expression of any precedence, as Prettier's `parentheses/identifier.js` does
// stands in for an expression of any precedence: the parens around it in the source stay
if is_html_placeholder(self.name.as_str()) {
return is_parenthesized_in_source(self, f);
}
+1 -1
View File
@@ -46,7 +46,7 @@ impl<'a> FormatLiteralStringToken<'a> {
let options = f.options();
let source_type = f.context().source_type();
// In an HTML attribute, `'` always (Prettier's `__isInHtmlAttribute`), but a directive as usual
// In an HTML attribute, `'` whatever the string holds, but a directive as usual
let (chosen_quote_style, force_chosen_quote) = if self.jsx {
(options.jsx_quote_style, false)
} else if self.parent_kind == StringLiteralParentKind::Directive {
+1 -1
View File
@@ -177,7 +177,7 @@ pub enum CssEmbeddedIn {
/// A css-in-js template: SCSS grammar with `${}` placeholder markers ([`CssRoot::CssInJsTemplate`]).
CssInJsTemplate,
/// An HTML `style` attribute's value: its declarations print on one line when they fit,
/// the last `;` only when they do not (Prettier's `__isHTMLStyleAttribute`).
/// the last `;` only when they do not.
HtmlStyleAttribute,
}
+1 -1
View File
@@ -19,7 +19,7 @@ a dialect adds a field there, not a `Dialect` check at the use site.
### Preprocess
The facts Prettier's `print-preprocess` computes (CSS display, whitespace sensitivity, kept children, borrowed tag markers)
The facts the printer needs besides the AST (CSS display, whitespace sensitivity, kept children, borrowed tag markers)
are a table keyed by `NodeId` (`src/tree/`).
The AST is never rewritten.
+1 -1
View File
@@ -139,7 +139,7 @@ pub fn format_to_ir<'a>(
///
/// The host translates the parent's request into this;
/// `None` is a part formatted on its own (e.g. a JSDoc code block).
/// Embedded HTML leaves `class` and `allow` values as written (as Prettier does with a `parentParser`).
/// Embedded HTML leaves `style`, `on*`, `class` and `allow` values as written, as Prettier does.
#[derive(Clone, Copy, Debug, Default)]
pub struct HtmlishEmbeddedIn {
/// Indent the content when more than one node prints at the root
@@ -220,7 +220,7 @@ pub(super) fn quoted_piece<'a>(piece: &'a str, f: &HtmlishFormatter<'_, 'a>) ->
arena_cow_str(&piece.cow_replace('"', "&quot;"), f)
}
/// Prettier's `printExpand`: `content` on its own indented lines when the group breaks.
/// `content` in quotes, on its own indented lines when the group breaks.
pub(super) fn write_expanded<'a>(
content: &impl Format<'a, HtmlishFormatContext<'a>>,
f: &mut HtmlishFormatter<'_, 'a>,
@@ -1,4 +1,4 @@
//! What separates siblings (Prettier's `printChildren`), and the line break facts it reads from the source.
//! What separates siblings, and the line break facts it reads from the source.
use oxc_formatter_core::{
Format, GroupId,
@@ -16,7 +16,7 @@ use super::{
};
use crate::{context::HtmlishFormatContext, tree::Tree};
/// What separates two siblings (Prettier's `printBetweenLine`).
/// What separates two siblings.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Between {
Nothing,
@@ -218,7 +218,7 @@ pub fn write_children<'a>(
}
/// A separator between siblings; a kept blank line is `empty_line`
/// (Prettier's two `hardline`s, which would collapse into one here).
/// (two hard line breaks would collapse into one).
fn write_between(between: Between, blank: bool, f: &mut HtmlishFormatter<'_, '_>) {
match between {
Between::Nothing => {}
@@ -175,7 +175,7 @@ fn should_preserve_content(t: &Tree<'_>, id: NodeId) -> bool {
}
}
/// The source between the tags, adjusted for markers the edges borrow (Prettier's `getNodeContent`).
/// The source between the tags, adjusted for markers the edges borrow.
fn node_content<'a>(t: &'a Tree<'a>, id: NodeId) -> &'a str {
let item = &t[id];
let mut start = t.content_start(id) as usize;
@@ -163,7 +163,7 @@ pub fn write_embedded_content<'a>(
f: &mut HtmlishFormatter<'_, 'a>,
) -> bool {
let Some(language) = content_language(t, id) else { return false };
// A Markdown body starts at the line after the tag, dedented (Prettier's `dedentString`)
// A Markdown body starts at the line after the tag, dedented
let value = if language == "markdown" {
let value = value
.find('\n')
@@ -213,8 +213,8 @@ fn escape_script_end<'a>(
})
}
/// The language of the body of `<script>` / `<style>` element `id`, a name the dispatcher resolves
/// (Prettier's `inferScriptParser` / `inferStyleParser`); `None` for none (`<script src>`, a template type).
/// The language of the body of `<script>` / `<style>` element `id`, a name the dispatcher resolves;
/// `None` for none (`<script src>`, a template type).
fn content_language<'a>(t: &'a Tree<'a>, id: NodeId) -> Option<&'a str> {
let el = t.element(id)?;
let attribute = |name: &str| {
@@ -235,7 +235,7 @@ fn content_language<'a>(t: &'a Tree<'a>, id: NodeId) -> Option<&'a str> {
}
}
/// A `<script type>` (Prettier's `inferParserByTypeAttribute`).
/// The language a `<script type>` names.
fn script_type_language(kind: &str) -> Option<&str> {
Some(match kind {
"module" | "text/javascript" | "application/javascript" => "js",
@@ -73,7 +73,7 @@ fn write_child<'a>(t: &'a Tree<'a>, id: NodeId, f: &mut HtmlishFormatter<'_, 'a>
write!(f, [opening_tag_prefix(t, id), text(raw), closing_tag_suffix(t, id)]);
}
/// Prettier's `getEndLocation`: an unclosed element extends to its last child.
/// Where an ignored node's source ends: an unclosed element extends to its last child.
fn ignored_end(t: &Tree<'_>, id: NodeId) -> u32 {
let end = t.source_end(id);
match t.kind(id) {
@@ -63,7 +63,7 @@ pub fn write_text<'a>(t: &'a Tree<'a>, id: NodeId, f: &mut HtmlishFormatter<'_,
fill.finish();
}
/// Writes `value`'s lines, trimmed and dedented (Prettier's `htmlTrimPreserveIndentation` + `dedentString`),
/// Writes `value`'s lines, trimmed and dedented ([`trim_preserving_indentation`], [`dedent`]),
/// joined by line breaks: every blank line is kept, and prints without indentation.
pub fn write_dedented_lines<'a>(value: &'a str, f: &mut HtmlishFormatter<'_, 'a>) {
let mut breaks = 0;
@@ -102,7 +102,7 @@ fn words(value: &str, cdata: bool) -> impl Iterator<Item = &str> {
})
}
/// Prettier's `htmlTrimPreserveIndentation`: trailing whitespace and leading blank lines go.
/// `value` without its trailing whitespace and leading blank lines.
fn trim_preserving_indentation(value: &str) -> &str {
let value = value.trim_ascii_end();
let mut start = 0;
@@ -132,7 +132,7 @@ pub fn dedent_string<'a>(value: &'a str, f: &HtmlishFormatter<'_, 'a>) -> &'a st
out.into_str()
}
/// Prettier's `dedentString`: the lines with their common indentation removed.
/// The lines of `value` with their common indentation removed.
fn dedent(value: &str) -> impl Iterator<Item = &str> {
dedent_by(value, common_indentation(value))
}
@@ -7,7 +7,7 @@ use oxc_htmlish_parser::ast::NodeId;
use super::Builder;
impl Builder<'_> {
/// Texts and CDATA sections next to each other print as one text (Prettier's `mergeCdataIntoText`);
/// Texts and CDATA sections next to each other print as one text;
/// the source between them, whitespace included, is the merged value.
pub(super) fn merge_cdata(&mut self, parent: NodeId) {
let mut next = self.tree[parent].first_child;
@@ -38,7 +38,7 @@ impl Builder<'_> {
}
}
/// `a<b>x</b>c` prints as one text (Prettier's `mergeSimpleElementIntoText`):
/// `a<b>x</b>c` prints as one text:
/// the text before absorbs the element and the text after.
pub(super) fn merge_simple_elements(&mut self, parent: NodeId) {
let mut next = self.tree[parent].first_child;
@@ -218,7 +218,7 @@ impl Builder<'_> {
}
/// `<!--[if IE]><!--><html><!--<![endif]-->`: for the start comment, the element whose start tag absorbs it
/// together with the end comment, its first child (Prettier's `mergeIfConditionalStartEndCommentIntoElementOpeningTag`).
/// together with the end comment, its first child.
fn absorbing_element(&self, id: NodeId) -> Option<NodeId> {
let root = self.tree.root;
let start = &root[id];
@@ -250,8 +250,8 @@ impl Builder<'_> {
}
_ => {}
}
// CDATA is text to Prettier (`mergeCdataIntoText`),
// printed as written here (DIVERGENCES.md#cdata-as-written).
// CDATA reads as text, merged with the texts around it,
// and prints as written (DIVERGENCES.md#cdata-as-written).
let text_like =
matches!(node.kind, NodeKind::Text(_) | NodeKind::Comment(_) | NodeKind::Cdata);
let display = self.display(id);
@@ -1,4 +1,4 @@
/// The CSS `display` an element gets from the UA stylesheet (Prettier's table, `constants.evaluate.js`).
/// The CSS `display` an element gets from the UA stylesheet, as Prettier tabulates it.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Display {
#[default]
@@ -30,7 +30,7 @@ impl Display {
}
}
/// `<!-- display: block -->` (Prettier's `/^\s*display:\s*([a-z]+)\s*$/`).
/// `<!-- display: block -->`: `display:` and a lowercase word, whitespace around them.
pub fn display_comment(content: &str) -> Option<Display> {
let value = content.trim().strip_prefix("display:")?.trim_start();
if value.is_empty() || !value.bytes().all(|b| b.is_ascii_lowercase()) {
+8 -8
View File
@@ -1,4 +1,4 @@
//! The facts Prettier's `print-preprocess` attaches to its tree, as a table keyed by the parser's [`NodeId`].
//! The facts the printer needs besides the AST, as a table keyed by the parser's [`NodeId`].
//!
//! The AST is not rewritten; names, kinds and the tag table's verdict are read from it.
//! What the table adds:
@@ -24,7 +24,7 @@ pub use display::Display;
#[derive(Clone, Copy, Default)]
pub struct Facts {
/// Prettier's `sourceSpan`: a text in a whitespace-insensitive parent is trimmed.
/// The span the node prints: a text in a whitespace-insensitive parent is trimmed.
pub span: Span,
/// The parent, siblings and children that print.
pub parent: Option<NodeId>,
@@ -35,7 +35,7 @@ pub struct Facts {
pub display: Display,
/// Text and comments: printed without tags whose markers could be borrowed.
pub text_like: bool,
/// No children and no end tag: the start tag closes the node (Prettier's `isSelfClosing`).
/// No children and no end tag: the start tag closes the node.
/// Text-like nodes count too.
pub self_closing: bool,
/// CSS `white-space: pre*`: `pre`, `textarea`, `listing`, `plaintext`, `xmp`, outside SVG;
@@ -160,7 +160,7 @@ impl<'a> Tree<'a> {
matches!(self.kind(id), NodeKind::Interpolation(_))
}
/// A text or a CDATA section, which Prettier merges into text (`mergeCdataIntoText`).
/// A text or a CDATA section, which print as one text next to each other.
pub fn is_text(&self, id: NodeId) -> bool {
matches!(self.kind(id), NodeKind::Text(_) | NodeKind::Cdata)
}
@@ -245,7 +245,7 @@ impl<'a> Tree<'a> {
std::iter::successors(self[id].first_child, move |&child| self[child].next)
}
/// Where the content between the tags starts (Prettier's `startSourceSpan.end`).
/// Where the content between the tags starts.
pub fn content_start(&self, id: NodeId) -> u32 {
if self[id].condition.is_some()
&& let Some(end) = self.root[id].first_child
@@ -260,7 +260,7 @@ impl<'a> Tree<'a> {
}
}
/// Where the end tag starts, when there is one (Prettier's `endSourceSpan.start`).
/// Where the end tag starts, when there is one.
pub fn end_tag_start(&self, id: NodeId) -> Option<u32> {
match &self.root[id].kind {
NodeKind::Element(el) => el.end_tag.map(|tag| tag.span.start),
@@ -276,7 +276,7 @@ impl<'a> Tree<'a> {
self.element(id).is_some_and(|el| el.end_tag.is_none()) && !self[id].self_closing
}
/// Where Prettier's `sourceSpan` of the node ends:
/// Where the node's own source ends:
/// an element without an end tag ends at its start tag, children aside.
pub fn source_end(&self, id: NodeId) -> u32 {
match &self.root[id].kind {
@@ -286,7 +286,7 @@ impl<'a> Tree<'a> {
}
/// Line breaks in `[from, to)`, counted up to 2:
/// Prettier compares line numbers, so a gap is on the same line, the next line, or further.
/// a gap is on the same line, the next line, or further.
pub fn line_breaks(&self, from: u32, to: u32) -> usize {
if from >= to {
return 0;