From 35836faa0ecddb064c7befa221a9d28b7e5716e3 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Tue, 25 Aug 2026 12:55:39 -0400 Subject: [PATCH 1/4] fix: a one-word run is a label so its authored newlines hold and spaces never break --- crates/tsv_svelte/src/ast/internal.rs | 13 + crates/tsv_svelte/src/printer/helpers.rs | 10 +- .../src/printer/nodes/element_analysis.rs | 15 +- .../src/printer/nodes/fragment_doc.rs | 185 +- .../src/printer/nodes/fragment_text_doc.rs | 92 +- docs/conformance_prettier_svelte.md | 6 +- .../expected.json | 20 +- .../input.svelte | 2 +- .../prettier_variant_newlines.svelte | 2 +- .../expected.json | 34 +- .../input.svelte | 2 +- .../prettier_variant_newlines.svelte | 4 +- .../expected.json | 350 +-- .../input.svelte | 12 +- .../prettier_variant_newline.svelte | 12 +- .../expected.json | 86 +- .../input.svelte | 2 +- .../prettier_variant_newlines.svelte | 4 +- .../README.md | 2 +- .../expected.json | 502 ++-- .../input.svelte | 14 +- .../output_prettier.svelte | 22 +- .../prettier_variant_newline.svelte | 26 +- .../README.md | 2 +- .../expected.json | 254 +- .../input.svelte | 6 +- .../output_prettier.svelte | 8 +- .../prettier_variant_newline.svelte | 12 +- .../expected.json | 16 +- .../input.svelte | 2 +- .../prettier_variant_newlines.svelte | 2 +- .../README.md | 2 +- .../expected.json | 264 +- .../input.svelte | 10 +- .../prettier_variant_dangle.svelte | 10 +- .../unformatted_ours_newline.svelte | 10 +- .../variant_boundary_newline.svelte | 10 +- .../expected.json | 112 +- .../input.svelte | 8 +- .../output_prettier.svelte | 8 +- .../unformatted_ours_one_per_line.svelte | 8 +- .../README.md | 35 + .../expected.json | 794 ++++++ .../input.svelte | 43 + .../prettier_variant_dangle.svelte | 46 + .../unformatted_ours_hugged.svelte | 36 + .../expected.json | 142 +- .../input.svelte | 14 +- .../expected.json | 322 +-- .../input.svelte | 20 +- .../prettier_variant_newline_after.svelte | 30 +- .../prettier_variant_newline_before.svelte | 30 +- .../prettier_variant_newline_both.svelte | 30 +- .../README.md | 26 + .../expected.json | 147 ++ .../input.svelte | 12 + .../prettier_variant_newline.svelte | 14 + .../unformatted_ours_interior_blank.svelte | 16 + .../README.md | 23 + .../expected.json | 714 ++++++ .../input.svelte | 52 + .../prettier_variant_newline.svelte | 53 + .../variant_space.svelte | 45 + .../README.md | 29 + .../expected.json | 606 +++++ .../input.svelte | 28 + .../prettier_variant_space.svelte | 26 + .../README.md | 73 + .../expected.json | 2218 +++++++++++++++++ .../input.svelte | 139 ++ .../prettier_variant_newline.svelte | 149 ++ .../variant_space.svelte | 113 + .../README.md | 33 + .../expected.json | 536 ++++ .../input.svelte | 26 + .../output_prettier.svelte | 30 + .../unformatted_ours_space.svelte | 25 + .../README.md | 32 + .../expected.json | 1022 ++++++++ .../input.svelte | 67 + .../prettier_variant_newline.svelte | 71 + .../unformatted_ours_newline_in_node.svelte | 72 + .../README.md | 29 + .../expected.json | 1133 +++++++++ .../input.svelte | 67 + .../prettier_variant_newline.svelte | 77 + .../expected.json | 32 +- .../input.svelte | 2 +- .../prettier_variant_newlines.svelte | 4 +- .../unformatted_ours_spaces.svelte | 4 +- .../README.md | 2 +- .../expected.json | 374 +-- .../input.svelte | 20 +- .../prettier_variant_fitting_newline.svelte | 22 +- .../unformatted_glued_head.svelte | 20 +- .../variant_space.svelte | 20 +- .../README.md | 10 +- .../expected.json | 226 +- .../input.svelte | 14 +- .../prettier_variant_fitting_newline.svelte | 16 +- .../variant_welded.svelte | 14 +- .../README.md | 11 +- .../expected.json | 89 +- .../input.svelte | 4 +- .../output_prettier.svelte | 4 +- 105 files changed, 10561 insertions(+), 1733 deletions(-) create mode 100644 tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/README.md create mode 100644 tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/expected.json create mode 100644 tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/input.svelte create mode 100644 tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/prettier_variant_dangle.svelte create mode 100644 tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/unformatted_ours_hugged.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/README.md create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/expected.json create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/input.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/prettier_variant_newline.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/unformatted_ours_interior_blank.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/README.md create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/expected.json create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/input.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/prettier_variant_newline.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/variant_space.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/README.md create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/expected.json create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/input.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/prettier_variant_space.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/README.md create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/expected.json create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/input.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/prettier_variant_newline.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/variant_space.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/README.md create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/expected.json create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/input.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/output_prettier.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/unformatted_ours_space.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/README.md create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/expected.json create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/input.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/prettier_variant_newline.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/unformatted_ours_newline_in_node.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/README.md create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/expected.json create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/input.svelte create mode 100644 tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/prettier_variant_newline.svelte diff --git a/crates/tsv_svelte/src/ast/internal.rs b/crates/tsv_svelte/src/ast/internal.rs index f5b30625b..522a1e38c 100644 --- a/crates/tsv_svelte/src/ast/internal.rs +++ b/crates/tsv_svelte/src/ast/internal.rs @@ -1536,6 +1536,19 @@ pub fn split_collapsible_ws(s: &str) -> impl Iterator { s.split(is_collapsible_ws_char).filter(|w| !w.is_empty()) } +/// The `leading` (else trailing) edge whitespace run of a text node's `raw` — its +/// [`is_collapsible_ws_char`] prefix (else suffix), empty when that edge is content. The one +/// slice every edge question reads (a newline count, a blank-line presence, a boundary's +/// authoring), so the edge is delimited by the same class the fill collapses. +#[inline] +pub fn text_edge_ws(raw: &str, leading: bool) -> &str { + if leading { + &raw[..raw.len() - raw.trim_start_matches(is_collapsible_ws_char).len()] + } else { + &raw[raw.trim_end_matches(is_collapsible_ws_char).len()..] + } +} + /// Svelte Text node - raw text content /// /// Represents static text in the template or attribute values. diff --git a/crates/tsv_svelte/src/printer/helpers.rs b/crates/tsv_svelte/src/printer/helpers.rs index 668f3bf9f..fe0cf8bcb 100644 --- a/crates/tsv_svelte/src/printer/helpers.rs +++ b/crates/tsv_svelte/src/printer/helpers.rs @@ -1,7 +1,7 @@ //! Fragment analysis and child printing helpers use super::Printer; -use crate::ast::internal::{Fragment, FragmentNode, is_collapsible_ws_char}; +use crate::ast::internal::{Fragment, FragmentNode, text_edge_ws}; impl<'a> Printer<'a> { /// Check if a fragment's content is inline (huggable at both ends). @@ -53,12 +53,6 @@ impl<'a> Printer<'a> { let Some(FragmentNode::Text(text)) = node else { return false; }; - let raw = text.raw(self.source); - let run = if is_leading { - &raw[..raw.len() - raw.trim_start_matches(is_collapsible_ws_char).len()] - } else { - &raw[raw.trim_end_matches(is_collapsible_ws_char).len()..] - }; - run.contains('\n') + text_edge_ws(text.raw(self.source), is_leading).contains('\n') } } diff --git a/crates/tsv_svelte/src/printer/nodes/element_analysis.rs b/crates/tsv_svelte/src/printer/nodes/element_analysis.rs index 929cf07a7..bcc075e65 100644 --- a/crates/tsv_svelte/src/printer/nodes/element_analysis.rs +++ b/crates/tsv_svelte/src/printer/nodes/element_analysis.rs @@ -179,9 +179,10 @@ impl<'a> Printer<'a> { /// states the rule as "the presence of a `fill` to reflow into, **not the shape of the /// separator node**". A fill needs two things, and both are load-bearing: /// - /// - **prose to pack** — a content text, asked through the flow rule's own - /// [`Printer::is_run_prose`] so the two rules cannot drift (an NBSP-only node is a - /// separator wearing content's clothing and is not prose); + /// - **prose to pack** — a phrase, not a word: the run's [`Printer::prose_words`] maximum + /// graded by [`Printer::run_is_prose`], the flow rule's own count, so the two rules cannot + /// drift (an NBSP-only node is a separator wearing content's clothing and counts nothing; + /// a one-word run is a label whose lines are structure); /// - **a whitespace seam to reflow at** — glued content (`{expr}text`) is a single /// unbreakable unit. With nothing to reflow, the boundary is the only signal the author /// has, so it keeps its authored lines — `elements/inline_multiline_nontext`, where @@ -213,7 +214,10 @@ impl<'a> Printer<'a> { /// text below it onto one line, and deleted an authored blank line — two content-preservation /// breaks, not layout choices. The blank-line arm is asked of content texts too, since /// `breaks_inline_run` sees a blank only in a whitespace-ONLY node while `modern⏎⏎` - /// carries it in a content text's trailing run. + /// carries it in a content text's trailing run. The flow rule's own scan + /// (`Printer::scan_inline_run`) ends a run at that EDGE blank too; where the two readers part + /// is a blank INTERIOR to a text node (`text1⏎⏎text2`), which this arm sees (the whole text + /// is scanned) and a run scan cannot, a run being a partition of nodes. /// /// Because both conjuncts are about the *run*, the answer is independent of the separator's /// spelling and of how many siblings the run holds. That is the point: without it a prose @@ -258,7 +262,8 @@ impl<'a> Printer<'a> { // A pair is reflowable when the boundary between them is NOT glued — the whitespace lives on // one of the two texts' facing edges, so there is a break point to reflow at. Same predicate // the fragment path's glue decisions ask, negated (`Printer::text_glued_before` / `_after`). - run.iter().any(|n| self.is_run_prose(n)) && run.windows(2).any(|w| { + Printer::run_is_prose(run.iter().map(|n| self.prose_words(n)).max().unwrap_or(0)) + && run.windows(2).any(|w| { matches!(&w[0], FragmentNode::Text(t) if !Self::text_glued_after(t.raw(source))) || matches!(&w[1], FragmentNode::Text(t) if !Self::text_glued_before(t.raw(source))) }) diff --git a/crates/tsv_svelte/src/printer/nodes/fragment_doc.rs b/crates/tsv_svelte/src/printer/nodes/fragment_doc.rs index 8fb31d846..5fb373bea 100644 --- a/crates/tsv_svelte/src/printer/nodes/fragment_doc.rs +++ b/crates/tsv_svelte/src/printer/nodes/fragment_doc.rs @@ -14,7 +14,7 @@ use super::element_doc::MultilineCause; use super::fragment_text_doc::TextChildContext; use super::helpers::{is_control_flow_block, is_inline_content}; -use crate::ast::internal::{self, FragmentNode}; +use crate::ast::internal::{self, FragmentNode, text_edge_ws}; use crate::printer::Printer; use tsv_lang::doc::{DocBuf, arena::DocId}; use tsv_lang::is_format_ignore_directive; @@ -223,10 +223,10 @@ impl<'a> Printer<'a> { // each node once and keeps the total cost O(n). Reading it at the separator instead would // rescan per separator, and a mid-run `continue` (a glued run skips its tail) would leave // a later rescan blind to the prose *before* it in the same run. - let (mut run_end, mut run_has_prose) = (0usize, false); + let (mut run_end, mut run_words) = (0usize, 0usize); for (i, node) in trimmed_nodes.iter().enumerate() { if i >= run_end { - (run_end, run_has_prose) = self.scan_inline_run(trimmed_nodes, i); + (run_end, run_words) = self.scan_inline_run(trimmed_nodes, i); } if i > 0 && is_inline_content(&trimmed_nodes[i - 1]) { has_preceding_breakable = true; @@ -301,7 +301,8 @@ impl<'a> Printer<'a> { i, TextChildContext { cause, - run_has_prose, + run_has_prose: Self::run_is_prose(run_words), + run_has_text: Self::run_has_text(run_words), content_bounds, glued_prefix: pending_glued_prefix.take(), prev_sibling_head, @@ -681,41 +682,145 @@ impl<'a> Printer<'a> { } } - /// Whether `node` is **prose** — a text node carrying a word for a `fill` to pack. An - /// NBSP-only node is a separator wearing content's clothing - /// ([`Self::is_separator_like_text`]) and is not prose, for the same reason it is excluded - /// from the flow rule itself. + /// The number of **words** `node` gives a `fill` to pack — its prose. A content text counts + /// its collapsible-whitespace-separated words ([`internal::split_collapsible_ws`]) over its + /// SOURCE BYTES (`raw`) — the fill's own item split, so the count and the fill cannot + /// disagree about where a seam is: an NBSP-joined pair is one word because it is one fill + /// item, and so is an entity-encoded space (`text1 text2` — the entity's bytes print + /// verbatim, and neither tsv's fill nor prettier's breaks inside it; counting the decoded + /// characters instead promised a wrap point no fill has). A whitespace-only node and every + /// non-text node count zero — an expression tag renders as a value, not a word, so + /// `chars⏎{n}` is a label beside its value — and so does an NBSP-only node, a separator + /// wearing content's clothing ([`Self::is_separator_like_text`]), excluded from the flow rule + /// for the same reason. That one test reads the DECODED text on purpose: whether a node + /// renders as a separator is a render question, where a word count is a print question. /// - /// This is the run-level spelling of the node-local `!separator_like_text` test the content - /// text sites already make: at those sites the node *is* the run's fill, so asking about the - /// node and asking about the run are the same question. The sites that own no fill have to - /// ask it of the run instead — the whitespace-only separator here, and - /// `Printer::content_is_reflowable_fill` in `element_analysis.rs`, which decides whether an - /// element's render-free content boundary may select its layout. Both are the one "is there - /// a fill to reflow into?" question, so they share this predicate rather than each spelling - /// out what counts as prose. - pub(super) fn is_run_prose(&self, node: &FragmentNode<'_>) -> bool { - matches!(node, FragmentNode::Text(t) - if !t.is_collapsible_ws_only && !Self::is_separator_like_text(&t.data(self.source))) + /// The count is taken over a RUN — the most words any one node carries — and graded by + /// [`Self::run_is_prose`]: a run needs a + /// **phrase** to reflow into, so a run holding a single word is a label, not prose: + /// `⏎Delete⏎{n}`, `hue:⏎`, `⏎private`, `chars⏎{n}` are a caption + /// beside its icon, a field beside its unit, and their authored newlines are structure the + /// flow rule holds, exactly as it holds a prose-free run's. Two callers ask it of a run: + /// [`Self::scan_inline_run`], which feeds the flow rule's three sites (the whitespace-only + /// separator and a content text's two edge runs, through `TextChildContext::run_has_prose`), + /// and `Printer::content_is_reflowable_fill` in `element_analysis.rs`, which decides whether + /// an interior newline may select the element's layout. Both are the one "is there a fill to + /// reflow into?" question, so they share this count rather than each spelling out what + /// counts as prose. + /// + /// ⚠️ **Run-level, never boundary-local.** At a content text's edge run the node adjacent to + /// the sibling is often the one-word TAIL a previous wrap left behind + /// (`…x⏎prop.`) — prettier's own fill writes one every time a paragraph wraps + /// at an element, and holds it as authored on the next pass. A boundary-local "≥2 words" + /// reads that leftover as a label and holds it, which is the accretion ratchet the flow rule + /// exists to heal, run in reverse (measured: 32 of 40 corpus movers under the boundary-local + /// variant were NOT the authored form). Taken over the run, the count sees the sentence + /// the node belongs to, so a one-word node that ends a real sentence flows with it. Pinned + /// by `elements/inline_sibling_newline_label_hold_prettier_divergence` (the label shapes + /// held, the two-word cliff and the one-word sentence tail flowing). + pub(super) fn prose_words(&self, node: &FragmentNode<'_>) -> usize { + match node { + FragmentNode::Text(t) if !t.is_collapsible_ws_only => { + if Self::is_separator_like_text(&t.data(self.source)) { + 0 + } else { + internal::split_collapsible_ws(t.raw(self.source)).count() + } + } + _ => 0, + } + } + + /// Whether a run whose [`Self::prose_words`] total is `words` is **prose** — holds a fill to + /// reflow into. The cliff is two words: a run holding one is a label + /// ([`Self::prose_words`]), and two is where label and prose genuinely blur (`Remember me` + /// packs), so the boundary is stated here rather than approximated by a sentence heuristic + /// (`.!?`, ≥3 words — measured to hold wrap artifacts the two-word rule does not). + /// + /// The count is the run's **maximum** over its nodes, and only its TEXT counts. Three + /// alternatives were measured and rejected: counting an expression tag as a word packs a + /// label beside its value (`chars⏎{n}` — authored vertical structure); a sentence heuristic + /// (`.!?`, ≥3 words) holds prettier's own two-word wrap tails; and SUMMING the run's words + /// packs every list of one-word captions the moment it holds two + /// (`⏎Delete⏎⏎Edit` → one line) — words in two text nodes are separated by + /// a sibling, and that separation is the author's. The maximum's own cost is a sentence + /// spelled entirely as one-word fragments between siblings (`text1⏎a⏎text2`), + /// which holds; real prose has a two-word node somewhere in its run. + #[inline] + pub(super) fn run_is_prose(words: usize) -> bool { + words >= 2 + } + + /// Whether a run whose [`Self::prose_words`] maximum is `words` holds a **content text** at + /// all — a fill for a whitespace-only separator to sit in, prose or not. The gate the + /// SPACE-spelled separator before a tag keeps (`TextChildContext::run_has_text`): the prose + /// gate is a hold on an authored NEWLINE and must never turn a space into one, so a one-word + /// run's `text1 {a} {b}` defers to the per-width group exactly as a prose run's does + /// (`elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence`), and + /// only a prose-FREE run's tag pair (`{a} {b}`) breaks with its container. + #[inline] + pub(super) fn run_has_text(words: usize) -> bool { + words >= 1 } - /// Scan the inline run beginning at `start`: its exclusive end, and whether it holds prose - /// ([`Self::is_run_prose`]) — which is what puts a `fill` in the run for a flowing separator - /// to reflow into. + /// Scan the inline run beginning at `start`: its exclusive end, and the most words any one + /// of its nodes carries ([`Self::prose_words`]) — graded by [`Self::run_is_prose`] for the + /// newline hold (a `fill` to reflow into) and by [`Self::run_has_text`] for the space-spelled + /// tag separator (a fill to sit in at all). + /// + /// A run ends at a node [`Self::breaks_inline_run`] names, and at an authored blank line a + /// content text carries on its edge ([`Self::text_edge_has_blank`]) — the boundary set + /// `Printer::content_is_reflowable_fill` reads too, with one deliberate difference: that + /// predicate asks its blank question of the WHOLE text (`has_authored_blank_line`), this scan + /// of the two EDGES alone, since a run is a partition of nodes and cannot split one at an + /// interior blank. The two agree wherever a blank sits beside a sibling; a blank INSIDE a + /// text (`text1⏎⏎text2` in one node) makes the fill predicate answer "no fill" while this + /// scan still counts the node's words — the fill collapses that blank either way, so the + /// boundaries beside it flow with the run's prose. /// /// Called only when the caller's cursor reaches a fresh run, so the scans partition /// `trimmed_nodes` and cost O(n) across the whole fragment — not the O(n²) a per-separator /// rescan would cost on a long all-flowing run (a generated per-token `` list). A /// run-breaking node at `start` is its own one-node span: the loop stops immediately and the /// `max` advances the cursor past it, so the caller cannot stall on it. - fn scan_inline_run(&self, nodes: &[FragmentNode<'_>], start: usize) -> (usize, bool) { + fn scan_inline_run(&self, nodes: &[FragmentNode<'_>], start: usize) -> (usize, usize) { let mut end = start; - let mut has_prose = false; + let mut words = 0usize; while end < nodes.len() && !self.breaks_inline_run(&nodes[end]) { - has_prose = has_prose || self.is_run_prose(&nodes[end]); + // An authored blank line bounds the run wherever the parser put it. Between two + // non-text siblings it is a whitespace-only node `breaks_inline_run` sees; beside a + // text it is folded into that text's edge whitespace, so the text itself must end + // the run — BEFORE it for a leading blank, AFTER it for a trailing one. Without this + // a blank beside a text split the layout (the blank-line arms hold it) but not the + // count, and `text1 text2⏎a⏎⏎text3⏎b` flowed the one-word + // half with the prose half as one run where the same shape with a comment for a + // boundary held it. + if end > start && self.text_edge_has_blank(&nodes[end], true) { + break; + } + words = words.max(self.prose_words(&nodes[end])); end += 1; + if self.text_edge_has_blank(&nodes[end - 1], false) { + break; + } + } + (end.max(start + 1), words) + } + + /// Whether `node` is a content text whose `leading` (else trailing) edge whitespace carries + /// an authored blank line — the run boundary a blank spells when the parser folds it into + /// the text beside it rather than into a whitespace-only node ([`Self::scan_inline_run`]). + /// Reads `raw`: a blank is a question about the bytes, like every blank-line scan. + fn text_edge_has_blank(&self, node: &FragmentNode<'_>, leading: bool) -> bool { + match node { + FragmentNode::Text(t) if !t.is_collapsible_ws_only => { + text_edge_ws(t.raw(self.source), leading) + .matches('\n') + .count() + >= 2 + } + _ => false, } - (end.max(start + 1), has_prose) } /// Whether a **single-newline** separator beside `node` may collapse to a plain space. @@ -805,23 +910,35 @@ impl<'a> Printer<'a> { /// The flow rule asked of **one side** of a content text's boundary run — the leading and /// trailing halves of [`Printer::handle_text_child`], which are exact mirrors. /// - /// A SINGLE newline beside flowing inline content is a spelling difference only, so the run - /// reflows with the fill instead of pinning a hardline; a blank line (2+) still breaks, and a - /// separator-like node ([`Self::is_separator_like_text`]) never flows whatever sits beside it. + /// A SINGLE newline beside flowing inline content, in a run that holds prose to reflow into + /// (`run_has_prose` — [`Self::run_is_prose`] over the run, from [`Self::scan_inline_run`]), + /// is a spelling difference only, so the run reflows with the fill instead of pinning a + /// hardline; a blank line (2+) still breaks, a one-word run is a label whose lines are held + /// ([`Self::run_is_prose`]), and a separator-like node ([`Self::is_separator_like_text`]) + /// never flows whatever sits beside it — that last exclusion is the one NODE-local gate, and + /// it stays beside the run-level one on purpose: a separator-like node must not flow even + /// when its run holds prose elsewhere, or the fill re-reads a break it emitted itself (the + /// NBSP F1 break). /// /// One question, one predicate. The third site — the whitespace-only separator *between* two - /// siblings — asks the same rule of BOTH neighbours, plus its run's prose. Letting the sites - /// drift apart is what left a single run with two answers: the boundaries touching a text node - /// flowed while the one between two adjacent siblings did not - /// (`inline_adjacent_sibling_newline_flow_prettier_divergence`). + /// siblings — asks the same two gates (the run's prose, the neighbour's kind) of BOTH + /// neighbours. Letting the sites drift apart is what left a single run with two answers: the + /// boundaries touching a text node flowed while the one between two adjacent siblings did + /// not (`inline_adjacent_sibling_newline_flow_prettier_divergence`) — and, before the prose + /// gate was asked here at all, a one-word run's edge runs flowed while its standalone + /// separator held. #[inline] pub(super) fn boundary_newline_flows( &self, newlines: usize, + run_has_prose: bool, separator_like_text: bool, neighbour: Option<&FragmentNode<'_>>, ) -> bool { - newlines == 1 && !separator_like_text && self.neighbour_newline_flows(neighbour) + newlines == 1 + && run_has_prose + && !separator_like_text + && self.neighbour_newline_flows(neighbour) } /// Whether this text node is a **separator** wearing content's clothing: every character it diff --git a/crates/tsv_svelte/src/printer/nodes/fragment_text_doc.rs b/crates/tsv_svelte/src/printer/nodes/fragment_text_doc.rs index 0be1b7988..f7aa7b320 100644 --- a/crates/tsv_svelte/src/printer/nodes/fragment_text_doc.rs +++ b/crates/tsv_svelte/src/printer/nodes/fragment_text_doc.rs @@ -14,7 +14,7 @@ use super::element_doc::MultilineCause; use super::fragment_doc::{DeferredBoundary, text_starts_with_linebreak}; use super::helpers::{is_control_flow_block, is_inline_content}; -use crate::ast::internal::{FragmentNode, Text, is_collapsible_ws_char, split_collapsible_ws}; +use crate::ast::internal::{FragmentNode, Text, split_collapsible_ws, text_edge_ws}; use crate::printer::Printer; use smallvec::SmallVec; use tsv_lang::doc::{DocBuf, arena::DocId}; @@ -74,14 +74,26 @@ pub(super) struct TextChildContext { /// it is, *why* the layout went multiline. The cause is read by the sibling-newline flow rule /// alone; every other site asks only [`MultilineCause::is_multiline`]. pub(super) cause: MultilineCause, - /// Whether this node's inline run holds prose — one of the two gates on the sibling-newline - /// flow rule at its standalone-separator site (`cause` is the other). Computed once per run by - /// the caller ([`Printer::scan_inline_run`]) and only on the `multiline` path; `false` - /// everywhere else, which is inert because that site returns before reading it in the - /// non-multiline arm. Note "inert per arm" is NOT "the arms agree" — the inline arm reaches - /// the same separator through its own `next_is_tag` case, and the two emitting a different doc - /// for one logical separator is exactly the period-2 cycle `cause` exists to close. + /// Whether this node's inline run holds prose — [`Printer::run_is_prose`] over the run's + /// [`Printer::prose_words`] maximum — the prose gate of the sibling-newline flow rule at all + /// three of its sites: the standalone separator, and a content text's leading and trailing + /// runs (`Printer::boundary_newline_flows`). Computed once per run by the caller + /// ([`Printer::scan_inline_run`]); read only in the `multiline` arms, since a + /// newline-spelled boundary is a hardline question there alone. Note "inert per arm" is NOT + /// "the arms agree" — the inline arm reaches the same separator through its own + /// `next_is_tag` case, and the two emitting a different doc for one logical separator is + /// exactly the period-2 cycle `cause` exists to close. pub(super) run_has_prose: bool, + /// Whether this node's inline run holds a content text at all ([`Printer::run_has_text`]) — + /// the gate the SPACE-spelled whitespace-only separator before a tag keeps, in both arms of + /// the separator site. Split from `run_has_prose` on purpose: the prose gate decides whether + /// an authored NEWLINE may reflow (a one-word run holds it), and a hold must never become a + /// forced break — so a one-word run's space-spelled tag pair defers to the per-width group + /// as a prose run's does (`inline_sibling_newline_label_hold_tag_pair_space`), while a + /// prose-FREE run's keeps the bare `line` that breaks with the container. Both arms read + /// this one value, which is what keeps the width-broken and newline-authored twins of one + /// document on one layout. + pub(super) run_has_text: bool, /// The first and last index in `trimmed_nodes` that the whitespace rules see — the fragment's /// content bounds once every HOISTED node is skipped /// ([`FragmentNode::content_bounds`]). `handle_content_text_child`'s `is_first` / `is_last` are @@ -196,6 +208,7 @@ impl<'a> Printer<'a> { let TextChildContext { cause, run_has_prose, + run_has_text, content_bounds, prev_sibling_head, .. @@ -312,9 +325,18 @@ impl<'a> Printer<'a> { // Flowing converges a tag pair onto one line where prettier splits it — a deliberate // divergence in the same family as the rest of this rule, pinned by // `elements/inline_adjacent_sibling_newline_flow_prettier_divergence`. - let separator_flows = run_has_prose - && self.neighbour_newline_flows(prev_node) - && self.neighbour_newline_flows(next_node); + let neighbours_flow = + self.neighbour_newline_flows(prev_node) && self.neighbour_newline_flows(next_node); + let separator_flows = run_has_prose && neighbours_flow; + // The SPACE-spelled separator before a tag asks the weaker run gate — any content text in + // the run (`run_has_text`), not prose. The prose gate is a HOLD on an authored newline and + // must never turn a space into one: a one-word run's `text1 {a} {b}` packs per width + // exactly as a prose run's does (`inline_sibling_newline_label_hold_tag_pair_space`), and + // only a prose-FREE run's tag pair keeps the bare `line` that breaks with the container. + // Read by both arms below, so the width-broken and newline-authored twins agree. A + // flowing NEWLINE reaches the tag case too, re-spelled as the space by `ws_flows`; prose + // implies text, so it defers as before. + let tag_space_defers = run_has_text && neighbours_flow; if !multiline { // Before a tag the separator is a bare collapsible break — a space while // the fragment fits, a newline once it breaks — exactly as the multiline @@ -334,8 +356,8 @@ impl<'a> Printer<'a> { // Tier-2 element-expansion class, not this bug. A tag has no such structure // to protect, so the bare break is strictly better. // - // ⚠️ A FLOWING run is the exception, and it is the same question the multiline - // arm's `next_is_tag && separator_flows` case asks — asked here so one run's + // ⚠️ A run holding a content text is the exception, and it is the same question + // the multiline arm's `next_is_tag && tag_space_defers` case asks — asked here so one run's // interior does not depend on WHY its element went multiline. A width-broken // element and a newline-authored one lay the same prose run out identically: // both defer to the next sibling's per-width `group([line, tag])`, so the run @@ -358,8 +380,8 @@ impl<'a> Printer<'a> { // cure for that cycle is one policy in both arms, not a hold in both: a component is // inline flow content like a ``, so the pair packs per width from either // spelling (`inline_adjacent_component_flow`). - // The layout-keyed hold (`arm_hold` above). `run_has_prose` is not computed on this - // path, so the candidate asks the two neighbours directly — a tag follower always + // The layout-keyed hold (`arm_hold` above). The candidate does not read + // `run_has_prose`; it asks the two neighbours directly — a tag follower always // flows, an inline element or component flows by kind. A TAG follower that takes // the hold takes the wrap with it (the `!*hold_next_lead` below): its bare `line` // renders flat past a multiline predecessor (` {expr}` through a glued head), @@ -372,7 +394,7 @@ impl<'a> Printer<'a> { { arm_hold(child_docs, deferred); } - if next_is_tag && !separator_flows && !deferred.held { + if next_is_tag && !tag_space_defers && !deferred.held { child_docs.push(d.line()); } else { // Defer the separator to the next sibling, which leads with it. NOT only "the @@ -471,8 +493,9 @@ impl<'a> Printer<'a> { child_docs.push(d.hardline()); } child_docs.push(d.hardline()); - } else if next_is_tag && separator_flows { - // A flowing separator before a TAG. `trim_to_collapsible` above covers only a next + } else if next_is_tag && tag_space_defers { + // A space (or a flowing newline, re-spelled above) before a TAG in a run that holds a + // content text. `trim_to_collapsible` above covers only a next // inline *element*, so without this arm the boundary would fall to the bare `line` // below — which resolves all-or-nothing with the parent group, and the parent is // already broken whenever the fragment is multiline. The whole run would then hard- @@ -481,7 +504,7 @@ impl<'a> Printer<'a> { // to the next sibling gives the tag the same per-width `group([line, tag])` an inline // element gets, so the run reflows as one. // - // Gated on `separator_flows` — NOT on `next_is_tag` alone. A plain authored space + // Gated on `tag_space_defers` — NOT on `next_is_tag` alone. A plain authored space // before a tag keeps the bare `line`: its neighbour may be a **comment**, whose own // line is authorship (`` `{expr}` must not weld — `root_expressions_spaced`), // and the flow predicate is exactly what excludes it. @@ -508,6 +531,7 @@ impl<'a> Printer<'a> { ) { let TextChildContext { cause, + run_has_prose, content_bounds, glued_prefix, prev_sibling_head, @@ -623,16 +647,18 @@ impl<'a> Printer<'a> { // [`Self::is_separator_like_text`] for why such a node is excluded, and why this one // reads the decoded text where the rest of the path reads `raw`. // - // This is the node-local face of the flow rule's prose gate: here the text node IS the - // run's fill, so `!separator_like_text` answers the same question `run_has_prose` answers - // for the whitespace-only separator site, which owns no fill (see [`Self::is_run_prose`]). - // Node-local is the stricter reading and is the one that belongs here — a separator-like - // node must not flow even when its run holds prose elsewhere, or the fill re-reads a break - // it emitted itself (the NBSP F1 break). + // Two prose gates, one per axis. `run_has_prose` is the RUN-level one the separator site + // asks too — a fill needs a phrase, so a one-word run is a label whose newlines hold + // (see [`Self::prose_words`], and why the count is never boundary-local: this text is often + // the one-word tail a previous wrap left behind, and holding it alone is the accretion + // ratchet in reverse). `!separator_like_text` is the NODE-local one and stays beside it: + // a separator-like node must not flow even when its run holds prose elsewhere, or the + // fill re-reads a break it emitted itself (the NBSP F1 break). let separator_like_text = Self::is_separator_like_text(&text.data(self.source)); - let leading_run = &raw[..raw.len() - raw.trim_start_matches(is_collapsible_ws_char).len()]; + let leading_run = text_edge_ws(raw, true); let leading_newline_flows = self.boundary_newline_flows( leading_run.matches('\n').count(), + run_has_prose, separator_like_text, prev_node, ); @@ -662,8 +688,7 @@ impl<'a> Printer<'a> { // A blank line (2+ leading newlines) is preserved as `[hardline, hardline]` — // prettier's `splitTextToDocs` startsWithLinebreak(_, 2). A single newline → one // hardline. - let content_start = raw.len() - raw.trim_start_matches(is_collapsible_ws_char).len(); - if raw[..content_start].matches('\n').count() >= 2 { + if leading_run.matches('\n').count() >= 2 { child_docs.push(d.hardline()); } child_docs.push(d.hardline()); @@ -915,8 +940,7 @@ impl<'a> Printer<'a> { let mut trailing_line = false; // Count newlines in the trailing whitespace run (multiline structural-break detection). let trailing_ws_newlines = if has_trailing_ws { - let content_end = raw.trim_end_matches(is_collapsible_ws_char).len(); - raw[content_end..].matches('\n').count() + text_edge_ws(raw, false).matches('\n').count() } else { 0 }; @@ -926,8 +950,12 @@ impl<'a> Printer<'a> { // spelling difference only, so it falls through to the space arms below and reflows with // the fill. Blank lines, comments and block elements keep the structural hardline. See // [`Self::sibling_newline_flows`]. - let trailing_newline_flows = - self.boundary_newline_flows(trailing_ws_newlines, separator_like_text, next_node); + let trailing_newline_flows = self.boundary_newline_flows( + trailing_ws_newlines, + run_has_prose, + separator_like_text, + next_node, + ); if multiline && next_owns_line { // Mirror of the leading arm: the tag below supplies the line, so this run is trimmed // rather than printed. Reached at a fragment edge too, where `is_last` already trims — diff --git a/docs/conformance_prettier_svelte.md b/docs/conformance_prettier_svelte.md index 76facdcd5..0c25b6db5 100644 --- a/docs/conformance_prettier_svelte.md +++ b/docs/conformance_prettier_svelte.md @@ -68,7 +68,7 @@ the parser keeps the wider set deliberately. See **Fill after inline**: Prettier's fill algorithm allows lines to exceed print width when text follows an inline element closing tag. Prettier produces 111 char lines, tsv breaks at exactly 100 chars. -**Fill boundary**: When fill content exceeds print width, Prettier tolerates the overage while tsv breaks earlier. This is an emergent behavior from prettier-plugin-svelte's doc structure (separate fills per text node with `group([line, element])` wrappers). See also `inline_element_fill_100/` which shows both formatters match at exactly 100 chars. +**Fill boundary**: When fill content exceeds print width, Prettier tolerates the overage while tsv breaks earlier. This is an emergent behavior from prettier-plugin-svelte's doc structure (separate fills per text node with `group([line, element])` wrappers). See also `inline_element_fill_100/` which shows both formatters match at exactly 100 chars. The same tolerance at a one-word run's space-spelled sibling boundary — ` text1` and ` text1` at 101, which Prettier leaves and tsv wraps onto the newline spelling the label hold then keeps — is [inline_sibling_newline_label_hold_long](../tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/). **Fill after breaking attr**: When an inline element has breaking attributes (e.g., multiline values) and long trailing text follows, Prettier's `handleTextChild` early return for last-child text skips wrapping the previous element, so there's no break point between the closing tag and trailing text. Prettier allows the line to exceed print width (102 chars). tsv's fill correctly breaks trailing words at the print width boundary (100 chars). See also `multiline_value_inline/` which shows both formatters match when trailing text is short. @@ -290,9 +290,9 @@ Five things are **not** reshaped, because none is a mere spelling difference. A **The comment exclusion is held on each SIDE independently, and it is where this section's convergence deliberately stops rather than a divergence from prettier.** "Keeps its authored line" is the symmetric reading, and it is the weaker one: the newline *after* a comment holds the following run off the comment's line exactly as the newline *before* it holds the comment off the preceding run's, so a one-sided authoring is a fixed point on that side alone — `text1 ⏎text2` and `text1⏎ text2` both stay, at any width, beside the both-sided form — and beside the form with a newline on neither side, which stays hugged, because the rule HOLDS an authored line rather than FORCING one around a comment (that fourth authoring is the pair's null control: it varies the same dimension, so without it an implementation that always broke both sides would satisfy the other three). Prettier holds all four too, so there is nothing to catalog against it here; the exclusion's whole cost is that these authorings do not converge with each other, which is the price [§Comment Position Philosophy](./conformance_prettier.md#comment-position-philosophy) is already paid for. ⚠️ **It is comment-specific, and the discriminating control has to hold the SITE fixed** — an element or a tag in the identical position flows (`text1 a⏎text2` collapses). Two *other* gates in this rule suppress flow for every sibling kind at once (the prose gate below, and the reflowable-fill suppression the paragraph after it, which closes this rule by making the element multiline for a reason it does not read), so an element control run in a shape either one already catches keeps its lines regardless of what sits there, and reads back as "the newline rule is not about comments" when it is. — [inline_separator_comment_newline](../tests/fixtures/svelte/elements/inline_separator_comment_newline/) pins all four comment authorings, and is deliberately **not** a divergence fixture: prettier holds each one too, so there is nothing for an `output_prettier` to claim. The element twins of the one-sided pair are [inline_sibling_newline_flow](../tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/)'s `prettier_variant_newline_after` / `prettier_variant_newline_before`, which converge on its input — that pair beside these four is the whole control. ⚠️ **The exclusion is scoped to the comment's OWN two boundaries: it BOUNDS the run, it does not STERILIZE one.** The runs a comment separates keep flowing on their own — `text1 a text2⏎⏎text3 b text4` converges from every isolated authoring of those spans, with only the comment's two newlines held. Nothing distinguishes that from the wider reading ("a run holding a comment keeps every line") unless a control puts a flowing run *beside* the comment and re-authors it: a control whose run is already hugged in every file is a fixed point under either rule and cannot separate them, which is why [inline_sibling_newline_flow](../tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/)'s comment-boundary control is re-authored in all three `prettier_variant_*` files rather than held. The same is true of every other run-bounding sibling (that fixture's block-sibling control carries the parallel pin), so the property belongs to run boundaries in general and not to comments — what is comment-specific is only the *holding*. The practical consequence is that a comment tsv has broken onto its own line for **width** ([fill_break_before_comment_spaced_long](../tests/fixtures/svelte/elements/fill_break_before_comment_spaced_long_prettier_divergence/)) reaches a second fixed point when the run after it was authored on the next line — the same document, differing only in whether that run shares the comment's line. -**The rule's boundary is the presence of a `fill` to reflow into, not the shape of the separator node.** Flowing means reflowing a run per width, and a run reflows only where there is prose for a `fill` to pack. So the rule reaches a whitespace-only separator standing *between two non-text siblings* — `text1⏎a` and `ab` converge alike — **when that separator's inline run holds a content text**, and not otherwise. A run of pure elements or tags (``) keeps its authored lines: with no prose its newlines are the only structure the author has, collapsing them packs independent siblings onto one line, and on a short run the collapse cascades into the parent element's own hug decision — an outright idempotency break (`` re-hugs on the second pass once its children fold). A run is bounded by whatever owns its own line — a block element, a control-flow block, a comment — and by an authored blank line, so a fragment's prose does not license flow in a neighbouring run that has none. Bounding is not sterilizing, though: a run that *does* hold prose still flows right up against the bounding sibling, and the two spellings of that ("its own boundaries are held" vs "every line near it is held") are only separated by a control that re-authors the neighbouring run — see the comment-boundary note above. Two mechanics keep this sound and are load-bearing. First, a flowing single newline takes the **space arm verbatim** rather than a parallel one: emitting a different collapsible form for the newline than for the space it claims to equal makes the formatter write a newline that its next pass re-reads as flowable and collapses. Second, the boundary must defer to the next sibling for a **tag** as well as an element — a bare `line` in the child list resolves all-or-nothing with the parent group, and a multiline fragment's parent group is always broken, so without that the one boundary owned by a content text's `fill` would flow while the rest of the same run hard-broke. That deferral is gated on the flow predicate and not on "next is a tag", because a plain authored space before a tag may have a **comment** on its other side, whose line is authorship. Note `authoring_audit` is blind to the tag half: both spellings agree there whichever form the rule picks — on the flowed form now, on the hard-broken one under a narrower gate — so it scores them as converging either way. — [inline_sibling_newline_flow](../tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/) (an inline element, a component and all three tag kinds — expression, render and `{@html}`, one predicate arm each — plus all five controls) and [expressions/angle_escaped](../tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/) (the tag-separator case, where prettier's plain `line` holds each tag on its authored line), and the shapes the rule reaches across the suite: [fill_text_inline_collapse](../tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/), [root_text_newline_inline](../tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/), [block_text_multiline_collapse](../tests/fixtures/svelte/elements/block_text_multiline_collapse_prettier_divergence/), [inline_attrs_multiline_content](../tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/), [components/root_with_text](../tests/fixtures/svelte/components/root_with_text_prettier_divergence/). **The CONTAINER holding the run is not an axis either**, and no fixture above varies it to say so — nearly all put the flowing run in a block container, and the one inline container that carries one ([inline_attrs_multiline_content](../tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/)) reaches multiline past a 99-char attribute line, confounding the kind with the attribute wrap that fixture exists to test. An inline element reaches the multiline form by a *different boundary rule* — both-or-neither, rather than the block's leading boundary alone — so it is the kind where a container-keyed rule would show, and the run lays out identically once there: [container_kind_newline_flow](../tests/fixtures/svelte/elements/container_kind_newline_flow_prettier_divergence/) holds one run fixed and varies the container across all four kinds — `

- text1 {expr1} + text1 text2 {expr1}

- text1 {expr1} + text1 text2 {expr1} - text1 {expr1} + text1 text2 {expr1} - text1 {expr1} + text1 text2 {expr1} diff --git a/tests/fixtures/svelte/elements/container_kind_newline_flow_prettier_divergence/prettier_variant_newline.svelte b/tests/fixtures/svelte/elements/container_kind_newline_flow_prettier_divergence/prettier_variant_newline.svelte index 5d59a30f9..c0c87a878 100644 --- a/tests/fixtures/svelte/elements/container_kind_newline_flow_prettier_divergence/prettier_variant_newline.svelte +++ b/tests/fixtures/svelte/elements/container_kind_newline_flow_prettier_divergence/prettier_variant_newline.svelte @@ -17,36 +17,36 @@

- text1 {expr1} + text1 text2 {expr1}

- text1 {expr1} + text1 text2 {expr1} - text1 {expr1} + text1 text2 {expr1} - text1 {expr1} + text1 text2 {expr1} diff --git a/tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/expected.json index b9731cfbe..8fb6fdfdf 100644 --- a/tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 209, + "end": 215, "type": "Root", "fragment": { "type": "Fragment", @@ -23,7 +23,7 @@ { "type": "RegularElement", "start": 147, - "end": 208, + "end": 214, "name": "div", "name_loc": { "start": { @@ -44,49 +44,49 @@ { "type": "Text", "start": 152, - "end": 160, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "end": 166, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "RegularElement", - "start": 160, - "end": 177, + "start": 166, + "end": 183, "name": "a", "name_loc": { "start": { "line": 4, - "column": 8, - "character": 161 + "column": 14, + "character": 167 }, "end": { "line": 4, - "column": 9, - "character": 162 + "column": 15, + "character": 168 } }, "attributes": [ { "type": "Attribute", - "start": 163, - "end": 171, + "start": 169, + "end": 177, "name": "href", "name_loc": { "start": { "line": 4, - "column": 10, - "character": 163 + "column": 16, + "character": 169 }, "end": { "line": 4, - "column": 14, - "character": 167 + "column": 20, + "character": 173 } }, "value": [ { - "start": 169, - "end": 170, + "start": 175, + "end": 176, "type": "Text", "raw": "x", "data": "x" @@ -99,8 +99,8 @@ "nodes": [ { "type": "Text", - "start": 172, - "end": 173, + "start": 178, + "end": 179, "raw": "a", "data": "a" } @@ -109,50 +109,50 @@ }, { "type": "Text", - "start": 177, - "end": 184, - "raw": " text2 ", - "data": " text2 " + "start": 183, + "end": 190, + "raw": " text3 ", + "data": " text3 " }, { "type": "RegularElement", - "start": 184, - "end": 201, + "start": 190, + "end": 207, "name": "a", "name_loc": { "start": { "line": 4, - "column": 32, - "character": 185 + "column": 38, + "character": 191 }, "end": { "line": 4, - "column": 33, - "character": 186 + "column": 39, + "character": 192 } }, "attributes": [ { "type": "Attribute", - "start": 187, - "end": 195, + "start": 193, + "end": 201, "name": "href", "name_loc": { "start": { "line": 4, - "column": 34, - "character": 187 + "column": 40, + "character": 193 }, "end": { "line": 4, - "column": 38, - "character": 191 + "column": 44, + "character": 197 } }, "value": [ { - "start": 193, - "end": 194, + "start": 199, + "end": 200, "type": "Text", "raw": "y", "data": "y" @@ -165,8 +165,8 @@ "nodes": [ { "type": "Text", - "start": 196, - "end": 197, + "start": 202, + "end": 203, "raw": "b", "data": "b" } @@ -175,8 +175,8 @@ }, { "type": "Text", - "start": 201, - "end": 202, + "start": 207, + "end": 208, "raw": "\n", "data": "\n" } diff --git a/tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/input.svelte index 7cb7504de..3f2487131 100644 --- a/tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/input.svelte @@ -1,5 +1,5 @@
- text1 a text2 b + text1 text2 a text3 b
diff --git a/tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/prettier_variant_newlines.svelte b/tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/prettier_variant_newlines.svelte index 2d1500cc5..b50ef0c57 100644 --- a/tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/prettier_variant_newlines.svelte +++ b/tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/prettier_variant_newlines.svelte @@ -1,6 +1,6 @@
- text1 a - text2 b + text1 text2 a + text3 b
diff --git a/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/README.md index 941628670..0c2651cd1 100644 --- a/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/README.md +++ b/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/README.md @@ -13,7 +13,7 @@ collapses here is inter-node whitespace that renders as one space either way. Prettier's `isInlineElement` requires a `RegularElement`, so a component is neither inline nor block to it: the whitespace-only node before a component is printed as a plain `line` that breaks with the container, while the text-adjacent boundaries of the same run hug in its fill. From the **space** -spelling it therefore emits `text1 text2` whenever the container is multiline — +spelling it therefore emits `text1 text2 text3` whenever the container is multiline — one run, two answers, in a line that fits — which `output_prettier.svelte` records; from the **newline** spelling it holds every line (`prettier_variant_newline.svelte`, which tsv normalizes to `input.svelte`). The `` pair beside the first case is the parity assertion: both formatters diff --git a/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/expected.json index 66ab95a23..59a3fc677 100644 --- a/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 1784, + "end": 1827, "type": "Root", "fragment": { "type": "Fragment", @@ -23,7 +23,7 @@ { "type": "RegularElement", "start": 997, - "end": 1038, + "end": 1044, "name": "p", "name_loc": { "start": { @@ -44,25 +44,25 @@ { "type": "Text", "start": 1000, - "end": 1008, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "end": 1014, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "Component", - "start": 1008, - "end": 1017, + "start": 1014, + "end": 1023, "name": "Comp1", "name_loc": { "start": { "line": 15, - "column": 8, - "character": 1009 + "column": 14, + "character": 1015 }, "end": { "line": 15, - "column": 13, - "character": 1014 + "column": 19, + "character": 1020 } }, "attributes": [], @@ -73,26 +73,26 @@ }, { "type": "Text", - "start": 1017, - "end": 1018, + "start": 1023, + "end": 1024, "raw": " ", "data": " " }, { "type": "Component", - "start": 1018, - "end": 1027, + "start": 1024, + "end": 1033, "name": "Comp2", "name_loc": { "start": { "line": 15, - "column": 18, - "character": 1019 + "column": 24, + "character": 1025 }, "end": { "line": 15, - "column": 23, - "character": 1024 + "column": 29, + "character": 1030 } }, "attributes": [], @@ -103,36 +103,36 @@ }, { "type": "Text", - "start": 1027, - "end": 1034, - "raw": " text2\n", - "data": " text2\n" + "start": 1033, + "end": 1040, + "raw": " text3\n", + "data": " text3\n" } ] } }, { "type": "Text", - "start": 1038, - "end": 1039, + "start": 1044, + "end": 1045, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1039, - "end": 1102, + "start": 1045, + "end": 1114, "name": "p", "name_loc": { "start": { "line": 17, "column": 1, - "character": 1040 + "character": 1046 }, "end": { "line": 17, "column": 2, - "character": 1041 + "character": 1047 } }, "attributes": [], @@ -141,26 +141,26 @@ "nodes": [ { "type": "Text", - "start": 1042, - "end": 1050, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "start": 1048, + "end": 1062, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "RegularElement", - "start": 1050, - "end": 1070, + "start": 1062, + "end": 1082, "name": "span", "name_loc": { "start": { "line": 18, - "column": 8, - "character": 1051 + "column": 14, + "character": 1063 }, "end": { "line": 18, - "column": 12, - "character": 1055 + "column": 18, + "character": 1067 } }, "attributes": [], @@ -169,8 +169,8 @@ "nodes": [ { "type": "Text", - "start": 1056, - "end": 1063, + "start": 1068, + "end": 1075, "raw": "inline1", "data": "inline1" } @@ -179,26 +179,26 @@ }, { "type": "Text", - "start": 1070, - "end": 1071, + "start": 1082, + "end": 1083, "raw": " ", "data": " " }, { "type": "RegularElement", - "start": 1071, - "end": 1091, + "start": 1083, + "end": 1103, "name": "span", "name_loc": { "start": { "line": 18, - "column": 29, - "character": 1072 + "column": 35, + "character": 1084 }, "end": { "line": 18, - "column": 33, - "character": 1076 + "column": 39, + "character": 1088 } }, "attributes": [], @@ -207,8 +207,8 @@ "nodes": [ { "type": "Text", - "start": 1077, - "end": 1084, + "start": 1089, + "end": 1096, "raw": "inline2", "data": "inline2" } @@ -217,49 +217,49 @@ }, { "type": "Text", - "start": 1091, - "end": 1098, - "raw": " text2\n", - "data": " text2\n" + "start": 1103, + "end": 1110, + "raw": " text3\n", + "data": " text3\n" } ] } }, { "type": "Text", - "start": 1102, - "end": 1104, + "start": 1114, + "end": 1116, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 1104, - "end": 1189, + "start": 1116, + "end": 1201, "data": " kind order: an element before a component, and a component before an element " }, { "type": "Text", - "start": 1189, - "end": 1190, + "start": 1201, + "end": 1202, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1190, - "end": 1241, + "start": 1202, + "end": 1259, "name": "p", "name_loc": { "start": { "line": 22, "column": 1, - "character": 1191 + "character": 1203 }, "end": { "line": 22, "column": 2, - "character": 1192 + "character": 1204 } }, "attributes": [], @@ -268,26 +268,26 @@ "nodes": [ { "type": "Text", - "start": 1193, - "end": 1201, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "start": 1205, + "end": 1219, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "RegularElement", - "start": 1201, - "end": 1221, + "start": 1219, + "end": 1239, "name": "span", "name_loc": { "start": { "line": 23, - "column": 8, - "character": 1202 + "column": 14, + "character": 1220 }, "end": { "line": 23, - "column": 12, - "character": 1206 + "column": 18, + "character": 1224 } }, "attributes": [], @@ -296,8 +296,8 @@ "nodes": [ { "type": "Text", - "start": 1207, - "end": 1214, + "start": 1225, + "end": 1232, "raw": "inline1", "data": "inline1" } @@ -306,26 +306,26 @@ }, { "type": "Text", - "start": 1221, - "end": 1222, + "start": 1239, + "end": 1240, "raw": " ", "data": " " }, { "type": "Component", - "start": 1222, - "end": 1230, + "start": 1240, + "end": 1248, "name": "Comp", "name_loc": { "start": { "line": 23, - "column": 29, - "character": 1223 + "column": 35, + "character": 1241 }, "end": { "line": 23, - "column": 33, - "character": 1227 + "column": 39, + "character": 1245 } }, "attributes": [], @@ -336,36 +336,36 @@ }, { "type": "Text", - "start": 1230, - "end": 1237, - "raw": " text2\n", - "data": " text2\n" + "start": 1248, + "end": 1255, + "raw": " text3\n", + "data": " text3\n" } ] } }, { "type": "Text", - "start": 1241, - "end": 1242, + "start": 1259, + "end": 1260, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1242, - "end": 1293, + "start": 1260, + "end": 1317, "name": "p", "name_loc": { "start": { "line": 25, "column": 1, - "character": 1243 + "character": 1261 }, "end": { "line": 25, "column": 2, - "character": 1244 + "character": 1262 } }, "attributes": [], @@ -374,26 +374,26 @@ "nodes": [ { "type": "Text", - "start": 1245, - "end": 1253, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "start": 1263, + "end": 1277, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "Component", - "start": 1253, - "end": 1261, + "start": 1277, + "end": 1285, "name": "Comp", "name_loc": { "start": { "line": 26, - "column": 8, - "character": 1254 + "column": 14, + "character": 1278 }, "end": { "line": 26, - "column": 12, - "character": 1258 + "column": 18, + "character": 1282 } }, "attributes": [], @@ -404,26 +404,26 @@ }, { "type": "Text", - "start": 1261, - "end": 1262, + "start": 1285, + "end": 1286, "raw": " ", "data": " " }, { "type": "RegularElement", - "start": 1262, - "end": 1282, + "start": 1286, + "end": 1306, "name": "span", "name_loc": { "start": { "line": 26, - "column": 17, - "character": 1263 + "column": 23, + "character": 1287 }, "end": { "line": 26, - "column": 21, - "character": 1267 + "column": 27, + "character": 1291 } }, "attributes": [], @@ -432,8 +432,8 @@ "nodes": [ { "type": "Text", - "start": 1268, - "end": 1275, + "start": 1292, + "end": 1299, "raw": "inline1", "data": "inline1" } @@ -442,49 +442,49 @@ }, { "type": "Text", - "start": 1282, - "end": 1289, - "raw": " text2\n", - "data": " text2\n" + "start": 1306, + "end": 1313, + "raw": " text3\n", + "data": " text3\n" } ] } }, { "type": "Text", - "start": 1293, - "end": 1295, + "start": 1317, + "end": 1319, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 1295, - "end": 1328, + "start": 1319, + "end": 1352, "data": " components with children " }, { "type": "Text", - "start": 1328, - "end": 1329, + "start": 1352, + "end": 1353, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1329, - "end": 1396, + "start": 1353, + "end": 1426, "name": "p", "name_loc": { "start": { "line": 30, "column": 1, - "character": 1330 + "character": 1354 }, "end": { "line": 30, "column": 2, - "character": 1331 + "character": 1355 } }, "attributes": [], @@ -493,26 +493,26 @@ "nodes": [ { "type": "Text", - "start": 1332, - "end": 1340, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "start": 1356, + "end": 1370, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "Component", - "start": 1340, - "end": 1362, + "start": 1370, + "end": 1392, "name": "Comp1", "name_loc": { "start": { "line": 31, - "column": 8, - "character": 1341 + "column": 14, + "character": 1371 }, "end": { "line": 31, - "column": 13, - "character": 1346 + "column": 19, + "character": 1376 } }, "attributes": [], @@ -521,8 +521,8 @@ "nodes": [ { "type": "Text", - "start": 1347, - "end": 1354, + "start": 1377, + "end": 1384, "raw": "inline1", "data": "inline1" } @@ -531,26 +531,26 @@ }, { "type": "Text", - "start": 1362, - "end": 1363, + "start": 1392, + "end": 1393, "raw": " ", "data": " " }, { "type": "Component", - "start": 1363, - "end": 1385, + "start": 1393, + "end": 1415, "name": "Comp2", "name_loc": { "start": { "line": 31, - "column": 31, - "character": 1364 + "column": 37, + "character": 1394 }, "end": { "line": 31, - "column": 36, - "character": 1369 + "column": 42, + "character": 1399 } }, "attributes": [], @@ -559,8 +559,8 @@ "nodes": [ { "type": "Text", - "start": 1370, - "end": 1377, + "start": 1400, + "end": 1407, "raw": "inline2", "data": "inline2" } @@ -569,49 +569,49 @@ }, { "type": "Text", - "start": 1385, - "end": 1392, - "raw": " text2\n", - "data": " text2\n" + "start": 1415, + "end": 1422, + "raw": " text3\n", + "data": " text3\n" } ] } }, { "type": "Text", - "start": 1396, - "end": 1398, + "start": 1426, + "end": 1428, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 1398, - "end": 1492, + "start": 1428, + "end": 1522, "data": " prose only at the END of the run: every separator in the run flows, not just the last " }, { "type": "Text", - "start": 1492, - "end": 1493, + "start": 1522, + "end": 1523, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1493, - "end": 1537, + "start": 1523, + "end": 1574, "name": "p", "name_loc": { "start": { "line": 35, "column": 1, - "character": 1494 + "character": 1524 }, "end": { "line": 35, "column": 2, - "character": 1495 + "character": 1525 } }, "attributes": [], @@ -620,26 +620,26 @@ "nodes": [ { "type": "Text", - "start": 1496, - "end": 1498, + "start": 1526, + "end": 1528, "raw": "\n\t", "data": "\n\t" }, { "type": "Component", - "start": 1498, - "end": 1507, + "start": 1528, + "end": 1537, "name": "Comp1", "name_loc": { "start": { "line": 36, "column": 2, - "character": 1499 + "character": 1529 }, "end": { "line": 36, "column": 7, - "character": 1504 + "character": 1534 } }, "attributes": [], @@ -650,26 +650,26 @@ }, { "type": "Text", - "start": 1507, - "end": 1508, + "start": 1537, + "end": 1538, "raw": " ", "data": " " }, { "type": "Component", - "start": 1508, - "end": 1517, + "start": 1538, + "end": 1547, "name": "Comp2", "name_loc": { "start": { "line": 36, "column": 12, - "character": 1509 + "character": 1539 }, "end": { "line": 36, "column": 17, - "character": 1514 + "character": 1544 } }, "attributes": [], @@ -680,26 +680,26 @@ }, { "type": "Text", - "start": 1517, - "end": 1518, + "start": 1547, + "end": 1548, "raw": " ", "data": " " }, { "type": "Component", - "start": 1518, - "end": 1527, + "start": 1548, + "end": 1557, "name": "Comp3", "name_loc": { "start": { "line": 36, "column": 22, - "character": 1519 + "character": 1549 }, "end": { "line": 36, "column": 27, - "character": 1524 + "character": 1554 } }, "attributes": [], @@ -710,49 +710,49 @@ }, { "type": "Text", - "start": 1527, - "end": 1533, - "raw": " text\n", - "data": " text\n" + "start": 1557, + "end": 1570, + "raw": " text1 text2\n", + "data": " text1 text2\n" } ] } }, { "type": "Text", - "start": 1537, - "end": 1539, + "start": 1574, + "end": 1576, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 1539, - "end": 1626, + "start": 1576, + "end": 1663, "data": " control: multiline STRUCTURALLY (a block child), not by the run's own newlines " }, { "type": "Text", - "start": 1626, - "end": 1627, + "start": 1663, + "end": 1664, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1627, - "end": 1691, + "start": 1664, + "end": 1734, "name": "div", "name_loc": { "start": { "line": 40, "column": 1, - "character": 1628 + "character": 1665 }, "end": { "line": 40, "column": 4, - "character": 1631 + "character": 1668 } }, "attributes": [], @@ -761,26 +761,26 @@ "nodes": [ { "type": "Text", - "start": 1632, - "end": 1640, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "start": 1669, + "end": 1683, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "Component", - "start": 1640, - "end": 1649, + "start": 1683, + "end": 1692, "name": "Comp1", "name_loc": { "start": { "line": 41, - "column": 8, - "character": 1641 + "column": 14, + "character": 1684 }, "end": { "line": 41, - "column": 13, - "character": 1646 + "column": 19, + "character": 1689 } }, "attributes": [], @@ -791,26 +791,26 @@ }, { "type": "Text", - "start": 1649, - "end": 1650, + "start": 1692, + "end": 1693, "raw": " ", "data": " " }, { "type": "Component", - "start": 1650, - "end": 1659, + "start": 1693, + "end": 1702, "name": "Comp2", "name_loc": { "start": { "line": 41, - "column": 18, - "character": 1651 + "column": 24, + "character": 1694 }, "end": { "line": 41, - "column": 23, - "character": 1656 + "column": 29, + "character": 1699 } }, "attributes": [], @@ -821,26 +821,26 @@ }, { "type": "Text", - "start": 1659, - "end": 1667, - "raw": " text2\n\t", - "data": " text2\n\t" + "start": 1702, + "end": 1710, + "raw": " text3\n\t", + "data": " text3\n\t" }, { "type": "RegularElement", - "start": 1667, - "end": 1684, + "start": 1710, + "end": 1727, "name": "div", "name_loc": { "start": { "line": 42, "column": 2, - "character": 1668 + "character": 1711 }, "end": { "line": 42, "column": 5, - "character": 1671 + "character": 1714 } }, "attributes": [], @@ -849,8 +849,8 @@ "nodes": [ { "type": "Text", - "start": 1672, - "end": 1678, + "start": 1715, + "end": 1721, "raw": "block1", "data": "block1" } @@ -859,8 +859,8 @@ }, { "type": "Text", - "start": 1684, - "end": 1685, + "start": 1727, + "end": 1728, "raw": "\n", "data": "\n" } @@ -869,39 +869,39 @@ }, { "type": "Text", - "start": 1691, - "end": 1693, + "start": 1734, + "end": 1736, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 1693, - "end": 1752, + "start": 1736, + "end": 1795, "data": " control: a prose-free run keeps its authored lines " }, { "type": "Text", - "start": 1752, - "end": 1753, + "start": 1795, + "end": 1796, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1753, - "end": 1783, + "start": 1796, + "end": 1826, "name": "p", "name_loc": { "start": { "line": 46, "column": 1, - "character": 1754 + "character": 1797 }, "end": { "line": 46, "column": 2, - "character": 1755 + "character": 1798 } }, "attributes": [], @@ -910,26 +910,26 @@ "nodes": [ { "type": "Text", - "start": 1756, - "end": 1758, + "start": 1799, + "end": 1801, "raw": "\n\t", "data": "\n\t" }, { "type": "Component", - "start": 1758, - "end": 1767, + "start": 1801, + "end": 1810, "name": "Comp1", "name_loc": { "start": { "line": 47, "column": 2, - "character": 1759 + "character": 1802 }, "end": { "line": 47, "column": 7, - "character": 1764 + "character": 1807 } }, "attributes": [], @@ -940,26 +940,26 @@ }, { "type": "Text", - "start": 1767, - "end": 1769, + "start": 1810, + "end": 1812, "raw": "\n\t", "data": "\n\t" }, { "type": "Component", - "start": 1769, - "end": 1778, + "start": 1812, + "end": 1821, "name": "Comp2", "name_loc": { "start": { "line": 48, "column": 2, - "character": 1770 + "character": 1813 }, "end": { "line": 48, "column": 7, - "character": 1775 + "character": 1818 } }, "attributes": [], @@ -970,8 +970,8 @@ }, { "type": "Text", - "start": 1778, - "end": 1779, + "start": 1821, + "end": 1822, "raw": "\n", "data": "\n" } diff --git a/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/input.svelte index 500ad4c9d..64569d79c 100644 --- a/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/input.svelte @@ -12,33 +12,33 @@ are the author's only structure. -->

- text1 text2 + text1 text2 text3

- text1 inline1 inline2 text2 + text1 text2 inline1 inline2 text3

- text1 inline1 text2 + text1 text2 inline1 text3

- text1 inline1 text2 + text1 text2 inline1 text3

- text1 inline1 inline2 text2 + text1 text2 inline1 inline2 text3

- text + text1 text2

- text1 text2 + text1 text2 text3
block1
diff --git a/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/output_prettier.svelte b/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/output_prettier.svelte index 29704e689..01d83adf6 100644 --- a/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/output_prettier.svelte +++ b/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/output_prettier.svelte @@ -12,39 +12,39 @@ are the author's only structure. -->

- text1 - text2 + text1 text2 + text3

- text1 inline1 inline2 text2 + text1 text2 inline1 inline2 text3

- text1 inline1 - text2 + text1 text2 inline1 + text3

- text1 inline1 text2 + text1 text2 inline1 text3

- text1 inline1 - inline2 text2 + text1 text2 inline1 + inline2 text3

- text + text1 text2

- text1 - text2 + text1 text2 + text3
block1
diff --git a/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/prettier_variant_newline.svelte b/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/prettier_variant_newline.svelte index be75f9f6c..6563822b9 100644 --- a/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/prettier_variant_newline.svelte +++ b/tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/prettier_variant_newline.svelte @@ -12,38 +12,38 @@ are the author's only structure. -->

- text1 + text1 text2 - text2 + text3

- text1 + text1 text2 inline1 inline2 - text2 + text3

- text1 + text1 text2 inline1 - text2 + text3

- text1 + text1 text2 inline1 - text2 + text3

- text1 + text1 text2 inline1 inline2 - text2 + text3

@@ -51,15 +51,15 @@ - text + text1 text2

- text1 + text1 text2 - text2 + text3
block1
diff --git a/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/README.md index 68051aed8..fd95a2c77 100644 --- a/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/README.md +++ b/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/README.md @@ -10,7 +10,7 @@ Svelte 5 collapses inter-sibling whitespace to one space, so this separator is t whichever way it is spelled, and it flows exactly like the text-bounded ones beside it. Without that, one run reaches two answers: the boundaries touching a text node flow while the one between the two siblings does not, leaving a break in a line that fits — -`text1 inline1inline2 text2`, which is neither formatter's form. +`text1 text2 inline1inline2 text3`, which is neither formatter's form. - **element pair** — `` beside ``, prose in the run. Prettier agrees on this one. - **expression-tag pair** — `{expr1}` beside `{expr2}`. Prettier splits the pair here even though diff --git a/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/expected.json index aeb58825d..0e1bcef29 100644 --- a/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 1297, + "end": 1315, "type": "Root", "fragment": { "type": "Fragment", @@ -23,7 +23,7 @@ { "type": "RegularElement", "start": 905, - "end": 968, + "end": 974, "name": "p", "name_loc": { "start": { @@ -44,25 +44,25 @@ { "type": "Text", "start": 908, - "end": 916, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "end": 922, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "RegularElement", - "start": 916, - "end": 936, + "start": 922, + "end": 942, "name": "span", "name_loc": { "start": { "line": 14, - "column": 8, - "character": 917 + "column": 14, + "character": 923 }, "end": { "line": 14, - "column": 12, - "character": 921 + "column": 18, + "character": 927 } }, "attributes": [], @@ -71,8 +71,8 @@ "nodes": [ { "type": "Text", - "start": 922, - "end": 929, + "start": 928, + "end": 935, "raw": "inline1", "data": "inline1" } @@ -81,26 +81,26 @@ }, { "type": "Text", - "start": 936, - "end": 937, + "start": 942, + "end": 943, "raw": " ", "data": " " }, { "type": "RegularElement", - "start": 937, - "end": 957, + "start": 943, + "end": 963, "name": "span", "name_loc": { "start": { "line": 14, - "column": 29, - "character": 938 + "column": 35, + "character": 944 }, "end": { "line": 14, - "column": 33, - "character": 942 + "column": 39, + "character": 948 } }, "attributes": [], @@ -109,8 +109,8 @@ "nodes": [ { "type": "Text", - "start": 943, - "end": 950, + "start": 949, + "end": 956, "raw": "inline2", "data": "inline2" } @@ -119,36 +119,36 @@ }, { "type": "Text", - "start": 957, - "end": 964, - "raw": " text2\n", - "data": " text2\n" + "start": 963, + "end": 970, + "raw": " text3\n", + "data": " text3\n" } ] } }, { "type": "Text", - "start": 968, - "end": 969, + "start": 974, + "end": 975, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 969, - "end": 1006, + "start": 975, + "end": 1018, "name": "p", "name_loc": { "start": { "line": 16, "column": 1, - "character": 970 + "character": 976 }, "end": { "line": 16, "column": 2, - "character": 971 + "character": 977 } }, "attributes": [], @@ -157,27 +157,27 @@ "nodes": [ { "type": "Text", - "start": 972, - "end": 980, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "start": 978, + "end": 992, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "ExpressionTag", - "start": 980, - "end": 987, + "start": 992, + "end": 999, "expression": { "type": "Identifier", - "start": 981, - "end": 986, + "start": 993, + "end": 998, "loc": { "start": { "line": 17, - "column": 8 + "column": 14 }, "end": { "line": 17, - "column": 13 + "column": 19 } }, "name": "expr1" @@ -185,27 +185,27 @@ }, { "type": "Text", - "start": 987, - "end": 988, + "start": 999, + "end": 1000, "raw": " ", "data": " " }, { "type": "ExpressionTag", - "start": 988, - "end": 995, + "start": 1000, + "end": 1007, "expression": { "type": "Identifier", - "start": 989, - "end": 994, + "start": 1001, + "end": 1006, "loc": { "start": { "line": 17, - "column": 16 + "column": 22 }, "end": { "line": 17, - "column": 21 + "column": 27 } }, "name": "expr2" @@ -213,49 +213,49 @@ }, { "type": "Text", - "start": 995, - "end": 1002, - "raw": " text2\n", - "data": " text2\n" + "start": 1007, + "end": 1014, + "raw": " text3\n", + "data": " text3\n" } ] } }, { "type": "Text", - "start": 1006, - "end": 1008, + "start": 1018, + "end": 1020, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 1008, - "end": 1095, + "start": 1020, + "end": 1107, "data": " control: multiline STRUCTURALLY (a block child), not by the run's own newlines " }, { "type": "Text", - "start": 1095, - "end": 1096, + "start": 1107, + "end": 1108, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1096, - "end": 1182, + "start": 1108, + "end": 1200, "name": "div", "name_loc": { "start": { "line": 21, "column": 1, - "character": 1097 + "character": 1109 }, "end": { "line": 21, "column": 4, - "character": 1100 + "character": 1112 } }, "attributes": [], @@ -264,26 +264,26 @@ "nodes": [ { "type": "Text", - "start": 1101, - "end": 1109, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "start": 1113, + "end": 1127, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "RegularElement", - "start": 1109, - "end": 1129, + "start": 1127, + "end": 1147, "name": "span", "name_loc": { "start": { "line": 22, - "column": 8, - "character": 1110 + "column": 14, + "character": 1128 }, "end": { "line": 22, - "column": 12, - "character": 1114 + "column": 18, + "character": 1132 } }, "attributes": [], @@ -292,8 +292,8 @@ "nodes": [ { "type": "Text", - "start": 1115, - "end": 1122, + "start": 1133, + "end": 1140, "raw": "inline1", "data": "inline1" } @@ -302,26 +302,26 @@ }, { "type": "Text", - "start": 1129, - "end": 1130, + "start": 1147, + "end": 1148, "raw": " ", "data": " " }, { "type": "RegularElement", - "start": 1130, - "end": 1150, + "start": 1148, + "end": 1168, "name": "span", "name_loc": { "start": { "line": 22, - "column": 29, - "character": 1131 + "column": 35, + "character": 1149 }, "end": { "line": 22, - "column": 33, - "character": 1135 + "column": 39, + "character": 1153 } }, "attributes": [], @@ -330,8 +330,8 @@ "nodes": [ { "type": "Text", - "start": 1136, - "end": 1143, + "start": 1154, + "end": 1161, "raw": "inline2", "data": "inline2" } @@ -340,26 +340,26 @@ }, { "type": "Text", - "start": 1150, - "end": 1158, - "raw": " text2\n\t", - "data": " text2\n\t" + "start": 1168, + "end": 1176, + "raw": " text3\n\t", + "data": " text3\n\t" }, { "type": "RegularElement", - "start": 1158, - "end": 1175, + "start": 1176, + "end": 1193, "name": "div", "name_loc": { "start": { "line": 23, "column": 2, - "character": 1159 + "character": 1177 }, "end": { "line": 23, "column": 5, - "character": 1162 + "character": 1180 } }, "attributes": [], @@ -368,8 +368,8 @@ "nodes": [ { "type": "Text", - "start": 1163, - "end": 1169, + "start": 1181, + "end": 1187, "raw": "block1", "data": "block1" } @@ -378,8 +378,8 @@ }, { "type": "Text", - "start": 1175, - "end": 1176, + "start": 1193, + "end": 1194, "raw": "\n", "data": "\n" } @@ -388,39 +388,39 @@ }, { "type": "Text", - "start": 1182, - "end": 1184, + "start": 1200, + "end": 1202, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 1184, - "end": 1243, + "start": 1202, + "end": 1261, "data": " control: a prose-free run keeps its authored lines " }, { "type": "Text", - "start": 1243, - "end": 1244, + "start": 1261, + "end": 1262, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1244, - "end": 1296, + "start": 1262, + "end": 1314, "name": "p", "name_loc": { "start": { "line": 27, "column": 1, - "character": 1245 + "character": 1263 }, "end": { "line": 27, "column": 2, - "character": 1246 + "character": 1264 } }, "attributes": [], @@ -429,26 +429,26 @@ "nodes": [ { "type": "Text", - "start": 1247, - "end": 1249, + "start": 1265, + "end": 1267, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1249, - "end": 1269, + "start": 1267, + "end": 1287, "name": "span", "name_loc": { "start": { "line": 28, "column": 2, - "character": 1250 + "character": 1268 }, "end": { "line": 28, "column": 6, - "character": 1254 + "character": 1272 } }, "attributes": [], @@ -457,8 +457,8 @@ "nodes": [ { "type": "Text", - "start": 1255, - "end": 1262, + "start": 1273, + "end": 1280, "raw": "inline1", "data": "inline1" } @@ -467,26 +467,26 @@ }, { "type": "Text", - "start": 1269, - "end": 1271, + "start": 1287, + "end": 1289, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1271, - "end": 1291, + "start": 1289, + "end": 1309, "name": "span", "name_loc": { "start": { "line": 29, "column": 2, - "character": 1272 + "character": 1290 }, "end": { "line": 29, "column": 6, - "character": 1276 + "character": 1294 } }, "attributes": [], @@ -495,8 +495,8 @@ "nodes": [ { "type": "Text", - "start": 1277, - "end": 1284, + "start": 1295, + "end": 1302, "raw": "inline2", "data": "inline2" } @@ -505,8 +505,8 @@ }, { "type": "Text", - "start": 1291, - "end": 1292, + "start": 1309, + "end": 1310, "raw": "\n", "data": "\n" } diff --git a/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/input.svelte index 4fbdf4581..cc566a746 100644 --- a/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/input.svelte @@ -11,15 +11,15 @@ so the authored newlines are the author's only structure. -->

- text1 inline1 inline2 text2 + text1 text2 inline1 inline2 text3

- text1 {expr1} {expr2} text2 + text1 text2 {expr1} {expr2} text3

- text1 inline1 inline2 text2 + text1 text2 inline1 inline2 text3
block1
diff --git a/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/output_prettier.svelte b/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/output_prettier.svelte index aa75bc4a6..9f5068554 100644 --- a/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/output_prettier.svelte +++ b/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/output_prettier.svelte @@ -11,16 +11,16 @@ so the authored newlines are the author's only structure. -->

- text1 inline1 inline2 text2 + text1 text2 inline1 inline2 text3

- text1 {expr1} - {expr2} text2 + text1 text2 {expr1} + {expr2} text3

- text1 inline1 inline2 text2 + text1 text2 inline1 inline2 text3
block1
diff --git a/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/prettier_variant_newline.svelte b/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/prettier_variant_newline.svelte index 2b14d3cd8..6bbe8de8c 100644 --- a/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/prettier_variant_newline.svelte +++ b/tests/fixtures/svelte/elements/inline_adjacent_sibling_newline_flow_prettier_divergence/prettier_variant_newline.svelte @@ -11,24 +11,24 @@ so the authored newlines are the author's only structure. -->

- text1 + text1 text2 inline1 inline2 - text2 + text3

- text1 + text1 text2 {expr1} {expr2} - text2 + text3

- text1 + text1 text2 inline1 inline2 - text2 + text3
block1
diff --git a/tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/expected.json index a5ec55ca7..7f828e0f3 100644 --- a/tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 340, + "end": 347, "type": "Root", "fragment": { "type": "Fragment", @@ -23,7 +23,7 @@ { "type": "RegularElement", "start": 184, - "end": 339, + "end": 346, "name": "div", "name_loc": { "start": { @@ -51,7 +51,7 @@ { "type": "RegularElement", "start": 191, - "end": 332, + "end": 339, "name": "label", "name_loc": { "start": { @@ -212,17 +212,17 @@ { "type": "Text", "start": 317, - "end": 324, - "raw": " text\n\t", - "data": " text\n\t" + "end": 331, + "raw": " text1 text2\n\t", + "data": " text1 text2\n\t" } ] } }, { "type": "Text", - "start": 332, - "end": 333, + "start": 339, + "end": 340, "raw": "\n", "data": "\n" } diff --git a/tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/input.svelte index 395df3729..07015e37a 100644 --- a/tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/input.svelte @@ -2,6 +2,6 @@ are isolated by an authored newline, which flows back onto the content line -->
diff --git a/tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/prettier_variant_newlines.svelte b/tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/prettier_variant_newlines.svelte index b104cd62c..f074d2473 100644 --- a/tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/prettier_variant_newlines.svelte +++ b/tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/prettier_variant_newlines.svelte @@ -3,6 +3,6 @@
diff --git a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/README.md index 5172213eb..0d99dcb55 100644 --- a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/README.md +++ b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/README.md @@ -22,7 +22,7 @@ and both are already sanctioned: The last four cases pin the interior side of the second rule's predicate. What lets those newlines reflow rather than expand the element is that the content is a **fill to reflow into** — asked of the RUN, never of the separator's shape or -the sibling count. A separator carrying words (`text1`) is as much a fill as a space-only one, +the sibling count. A separator carrying words (`text1 text2`) is as much a fill as a space-only one, and a lone element beside prose is one too, so all of them must reach the same form the space-separated twin already reaches. Otherwise one document has two layouts depending on whether the author happened to put words between the siblings. diff --git a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/expected.json index 8c070195c..daf9df7a3 100644 --- a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 1547, + "end": 1577, "type": "Root", "fragment": { "type": "Fragment", @@ -451,7 +451,7 @@ { "type": "RegularElement", "start": 1266, - "end": 1342, + "end": 1348, "name": "div", "name_loc": { "start": { @@ -479,7 +479,7 @@ { "type": "RegularElement", "start": 1273, - "end": 1335, + "end": 1341, "name": "span", "name_loc": { "start": { @@ -551,49 +551,49 @@ { "type": "Text", "start": 1300, - "end": 1307, - "raw": " text1 ", - "data": " text1 " + "end": 1313, + "raw": " text1 text2 ", + "data": " text1 text2 " }, { "type": "Component", - "start": 1307, - "end": 1328, + "start": 1313, + "end": 1334, "name": "Comp", "name_loc": { "start": { "line": 24, - "column": 36, - "character": 1308 + "column": 42, + "character": 1314 }, "end": { "line": 24, - "column": 40, - "character": 1312 + "column": 46, + "character": 1318 } }, "attributes": [ { "type": "Attribute", - "start": 1313, - "end": 1325, + "start": 1319, + "end": 1331, "name": "prop", "name_loc": { "start": { "line": 24, - "column": 41, - "character": 1313 + "column": 47, + "character": 1319 }, "end": { "line": 24, - "column": 45, - "character": 1317 + "column": 51, + "character": 1323 } }, "value": [ { - "start": 1319, - "end": 1324, + "start": 1325, + "end": 1330, "type": "Text", "raw": "value", "data": "value" @@ -611,8 +611,8 @@ }, { "type": "Text", - "start": 1335, - "end": 1336, + "start": 1341, + "end": 1342, "raw": "\n", "data": "\n" } @@ -621,26 +621,26 @@ }, { "type": "Text", - "start": 1342, - "end": 1344, + "start": 1348, + "end": 1350, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 1344, - "end": 1391, + "start": 1350, + "end": 1403, "name": "div", "name_loc": { "start": { "line": 27, "column": 1, - "character": 1345 + "character": 1351 }, "end": { "line": 27, "column": 4, - "character": 1348 + "character": 1354 } }, "attributes": [], @@ -649,26 +649,26 @@ "nodes": [ { "type": "Text", - "start": 1349, - "end": 1351, + "start": 1355, + "end": 1357, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1351, - "end": 1384, + "start": 1357, + "end": 1396, "name": "span", "name_loc": { "start": { "line": 28, "column": 2, - "character": 1352 + "character": 1358 }, "end": { "line": 28, "column": 6, - "character": 1356 + "character": 1362 } }, "attributes": [], @@ -677,26 +677,26 @@ "nodes": [ { "type": "Text", - "start": 1357, - "end": 1363, - "raw": "text1 ", - "data": "text1 " + "start": 1363, + "end": 1375, + "raw": "text1 text2 ", + "data": "text1 text2 " }, { "type": "RegularElement", - "start": 1363, - "end": 1377, + "start": 1375, + "end": 1389, "name": "code", "name_loc": { "start": { "line": 28, - "column": 14, - "character": 1364 + "column": 20, + "character": 1376 }, "end": { "line": 28, - "column": 18, - "character": 1368 + "column": 24, + "character": 1380 } }, "attributes": [], @@ -705,8 +705,8 @@ "nodes": [ { "type": "Text", - "start": 1369, - "end": 1370, + "start": 1381, + "end": 1382, "raw": "a", "data": "a" } @@ -718,8 +718,8 @@ }, { "type": "Text", - "start": 1384, - "end": 1385, + "start": 1396, + "end": 1397, "raw": "\n", "data": "\n" } @@ -728,26 +728,26 @@ }, { "type": "Text", - "start": 1391, - "end": 1393, + "start": 1403, + "end": 1405, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 1393, - "end": 1433, + "start": 1405, + "end": 1451, "name": "div", "name_loc": { "start": { "line": 31, "column": 1, - "character": 1394 + "character": 1406 }, "end": { "line": 31, "column": 4, - "character": 1397 + "character": 1409 } }, "attributes": [], @@ -756,26 +756,26 @@ "nodes": [ { "type": "Text", - "start": 1398, - "end": 1400, + "start": 1410, + "end": 1412, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1400, - "end": 1426, + "start": 1412, + "end": 1444, "name": "span", "name_loc": { "start": { "line": 32, "column": 2, - "character": 1401 + "character": 1413 }, "end": { "line": 32, "column": 6, - "character": 1405 + "character": 1417 } }, "attributes": [], @@ -784,12 +784,12 @@ "nodes": [ { "type": "ExpressionTag", - "start": 1406, - "end": 1409, + "start": 1418, + "end": 1421, "expression": { "type": "Identifier", - "start": 1407, - "end": 1408, + "start": 1419, + "end": 1420, "loc": { "start": { "line": 32, @@ -805,27 +805,27 @@ }, { "type": "Text", - "start": 1409, - "end": 1416, - "raw": " text1 ", - "data": " text1 " + "start": 1421, + "end": 1434, + "raw": " text1 text2 ", + "data": " text1 text2 " }, { "type": "ExpressionTag", - "start": 1416, - "end": 1419, + "start": 1434, + "end": 1437, "expression": { "type": "Identifier", - "start": 1417, - "end": 1418, + "start": 1435, + "end": 1436, "loc": { "start": { "line": 32, - "column": 18 + "column": 24 }, "end": { "line": 32, - "column": 19 + "column": 25 } }, "name": "b" @@ -836,8 +836,8 @@ }, { "type": "Text", - "start": 1426, - "end": 1427, + "start": 1444, + "end": 1445, "raw": "\n", "data": "\n" } @@ -846,26 +846,26 @@ }, { "type": "Text", - "start": 1433, - "end": 1435, + "start": 1451, + "end": 1453, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 1435, - "end": 1482, + "start": 1453, + "end": 1506, "name": "div", "name_loc": { "start": { "line": 35, "column": 1, - "character": 1436 + "character": 1454 }, "end": { "line": 35, "column": 4, - "character": 1439 + "character": 1457 } }, "attributes": [], @@ -874,26 +874,26 @@ "nodes": [ { "type": "Text", - "start": 1440, - "end": 1442, + "start": 1458, + "end": 1460, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1442, - "end": 1475, + "start": 1460, + "end": 1499, "name": "span", "name_loc": { "start": { "line": 36, "column": 2, - "character": 1443 + "character": 1461 }, "end": { "line": 36, "column": 6, - "character": 1447 + "character": 1465 } }, "attributes": [], @@ -902,19 +902,19 @@ "nodes": [ { "type": "RegularElement", - "start": 1448, - "end": 1462, + "start": 1466, + "end": 1480, "name": "code", "name_loc": { "start": { "line": 36, "column": 8, - "character": 1449 + "character": 1467 }, "end": { "line": 36, "column": 12, - "character": 1453 + "character": 1471 } }, "attributes": [], @@ -923,8 +923,8 @@ "nodes": [ { "type": "Text", - "start": 1454, - "end": 1455, + "start": 1472, + "end": 1473, "raw": "a", "data": "a" } @@ -933,18 +933,18 @@ }, { "type": "Text", - "start": 1462, - "end": 1468, - "raw": " text1", - "data": " text1" + "start": 1480, + "end": 1492, + "raw": " text1 text2", + "data": " text1 text2" } ] } }, { "type": "Text", - "start": 1475, - "end": 1476, + "start": 1499, + "end": 1500, "raw": "\n", "data": "\n" } @@ -953,26 +953,26 @@ }, { "type": "Text", - "start": 1482, - "end": 1484, + "start": 1506, + "end": 1508, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 1484, - "end": 1546, + "start": 1508, + "end": 1576, "name": "div", "name_loc": { "start": { "line": 39, "column": 1, - "character": 1485 + "character": 1509 }, "end": { "line": 39, "column": 4, - "character": 1488 + "character": 1512 } }, "attributes": [], @@ -981,26 +981,26 @@ "nodes": [ { "type": "Text", - "start": 1489, - "end": 1491, + "start": 1513, + "end": 1515, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1491, - "end": 1539, + "start": 1515, + "end": 1569, "name": "span", "name_loc": { "start": { "line": 40, "column": 2, - "character": 1492 + "character": 1516 }, "end": { "line": 40, "column": 6, - "character": 1496 + "character": 1520 } }, "attributes": [], @@ -1009,19 +1009,19 @@ "nodes": [ { "type": "RegularElement", - "start": 1497, - "end": 1511, + "start": 1521, + "end": 1535, "name": "code", "name_loc": { "start": { "line": 40, "column": 8, - "character": 1498 + "character": 1522 }, "end": { "line": 40, "column": 12, - "character": 1502 + "character": 1526 } }, "attributes": [], @@ -1030,8 +1030,8 @@ "nodes": [ { "type": "Text", - "start": 1503, - "end": 1504, + "start": 1527, + "end": 1528, "raw": "a", "data": "a" } @@ -1040,26 +1040,26 @@ }, { "type": "Text", - "start": 1511, - "end": 1512, + "start": 1535, + "end": 1536, "raw": " ", "data": " " }, { "type": "RegularElement", - "start": 1512, - "end": 1526, + "start": 1536, + "end": 1550, "name": "code", "name_loc": { "start": { "line": 40, "column": 23, - "character": 1513 + "character": 1537 }, "end": { "line": 40, "column": 27, - "character": 1517 + "character": 1541 } }, "attributes": [], @@ -1068,8 +1068,8 @@ "nodes": [ { "type": "Text", - "start": 1518, - "end": 1519, + "start": 1542, + "end": 1543, "raw": "b", "data": "b" } @@ -1078,18 +1078,18 @@ }, { "type": "Text", - "start": 1526, - "end": 1532, - "raw": " text1", - "data": " text1" + "start": 1550, + "end": 1562, + "raw": " text1 text2", + "data": " text1 text2" } ] } }, { "type": "Text", - "start": 1539, - "end": 1540, + "start": 1569, + "end": 1570, "raw": "\n", "data": "\n" } diff --git a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/input.svelte index 5bb6d6a1b..9a6dd0b1c 100644 --- a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/input.svelte @@ -21,21 +21,21 @@
- text1 + text1 text2
- text1 a + text1 text2 a
- {a} text1 {b} + {a} text1 text2 {b}
- a text1 + a text1 text2
- a b text1 + a b text1 text2
diff --git a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/prettier_variant_dangle.svelte b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/prettier_variant_dangle.svelte index 56a7ad9c4..9d119361a 100644 --- a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/prettier_variant_dangle.svelte +++ b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/prettier_variant_dangle.svelte @@ -28,21 +28,21 @@
text1 + > text1 text2
text1 + >text1 text2 a
{a} text1 + >{a} text1 text2 {b}
@@ -50,13 +50,13 @@
a - text1
a - b text1b text1 text2
diff --git a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/unformatted_ours_newline.svelte b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/unformatted_ours_newline.svelte index baf449413..5f815de78 100644 --- a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/unformatted_ours_newline.svelte +++ b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/unformatted_ours_newline.svelte @@ -23,26 +23,26 @@
- text1 + text1 text2
- text1 + text1 text2 a
- {a} text1 + {a} text1 text2 {b}
a - text1 + text1 text2
a - b text1 + b text1 text2
diff --git a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/variant_boundary_newline.svelte b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/variant_boundary_newline.svelte index 4acae869f..5dd5e64b7 100644 --- a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/variant_boundary_newline.svelte +++ b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/variant_boundary_newline.svelte @@ -26,30 +26,30 @@
- text1 + text1 text2
- text1 a + text1 text2 a
- {a} text1 {b} + {a} text1 text2 {b}
- a text1 + a text1 text2
- a b text1 + a b text1 text2
diff --git a/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/expected.json index b3cc7683f..9c2ef0a5a 100644 --- a/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 773, + "end": 787, "type": "Root", "fragment": { "type": "Fragment", @@ -100,11 +100,11 @@ { "type": "ExpressionTag", "start": 58, - "end": 132, + "end": 125, "expression": { "type": "Identifier", "start": 59, - "end": 131, + "end": 124, "loc": { "start": { "line": 3, @@ -112,18 +112,18 @@ }, "end": { "line": 3, - "column": 85 + "column": 78 } }, - "name": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + "name": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } }, { "type": "Text", - "start": 132, + "start": 125, "end": 137, - "raw": " text", - "data": " text" + "raw": " text1 text2", + "data": " text1 text2" } ] } @@ -245,11 +245,11 @@ { "type": "ExpressionTag", "start": 258, - "end": 333, + "end": 326, "expression": { "type": "Identifier", "start": 259, - "end": 332, + "end": 325, "loc": { "start": { "line": 9, @@ -257,18 +257,18 @@ }, "end": { "line": 9, - "column": 80 + "column": 73 } }, - "name": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + "name": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } }, { "type": "Text", - "start": 333, + "start": 326, "end": 340, - "raw": " text\n\t", - "data": " text\n\t" + "raw": " text1 text2\n\t", + "data": " text1 text2\n\t" } ] } @@ -306,7 +306,7 @@ { "type": "RegularElement", "start": 432, - "end": 554, + "end": 561, "name": "div", "name_loc": { "start": { @@ -334,7 +334,7 @@ { "type": "RegularElement", "start": 439, - "end": 547, + "end": 554, "name": "small", "name_loc": { "start": { @@ -413,17 +413,17 @@ { "type": "Text", "start": 533, - "end": 539, - "raw": "text\n\t", - "data": "text\n\t" + "end": 546, + "raw": "text1 text2\n\t", + "data": "text1 text2\n\t" } ] } }, { "type": "Text", - "start": 547, - "end": 548, + "start": 554, + "end": 555, "raw": "\n", "data": "\n" } @@ -432,39 +432,39 @@ }, { "type": "Text", - "start": 554, - "end": 556, + "start": 561, + "end": 563, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 556, - "end": 725, + "start": 563, + "end": 732, "data": " The SAME run in an element made multiline by AUTHORED AIR rather than by width - the parity\n\tassertion: the interior does not depend on why the element expanded " }, { "type": "Text", - "start": 725, - "end": 726, + "start": 732, + "end": 733, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 726, - "end": 772, + "start": 733, + "end": 786, "name": "div", "name_loc": { "start": { "line": 22, "column": 1, - "character": 727 + "character": 734 }, "end": { "line": 22, "column": 4, - "character": 730 + "character": 737 } }, "attributes": [], @@ -473,26 +473,26 @@ "nodes": [ { "type": "Text", - "start": 731, - "end": 733, + "start": 738, + "end": 740, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 733, - "end": 765, + "start": 740, + "end": 779, "name": "small", "name_loc": { "start": { "line": 23, "column": 2, - "character": 734 + "character": 741 }, "end": { "line": 23, "column": 7, - "character": 739 + "character": 746 } }, "attributes": [], @@ -501,19 +501,19 @@ "nodes": [ { "type": "Text", - "start": 740, - "end": 743, + "start": 747, + "end": 750, "raw": "\n\t\t", "data": "\n\t\t" }, { "type": "ExpressionTag", - "start": 743, - "end": 746, + "start": 750, + "end": 753, "expression": { "type": "Identifier", - "start": 744, - "end": 745, + "start": 751, + "end": 752, "loc": { "start": { "line": 24, @@ -529,19 +529,19 @@ }, { "type": "Text", - "start": 746, - "end": 747, + "start": 753, + "end": 754, "raw": " ", "data": " " }, { "type": "ExpressionTag", - "start": 747, - "end": 750, + "start": 754, + "end": 757, "expression": { "type": "Identifier", - "start": 748, - "end": 749, + "start": 755, + "end": 756, "loc": { "start": { "line": 24, @@ -557,18 +557,18 @@ }, { "type": "Text", - "start": 750, - "end": 757, - "raw": " text\n\t", - "data": " text\n\t" + "start": 757, + "end": 771, + "raw": " text1 text2\n\t", + "data": " text1 text2\n\t" } ] } }, { "type": "Text", - "start": 765, - "end": 766, + "start": 779, + "end": 780, "raw": "\n", "data": "\n" } diff --git a/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/input.svelte index e8404bc38..6a68ec5c5 100644 --- a/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/input.svelte @@ -1,19 +1,19 @@
- {a} {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb} text + {a} {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb} text1 text2
- {a} {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb} text + {a} {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb} text1 text2
- {ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc}text + {ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc}text1 text2
@@ -21,6 +21,6 @@ assertion: the interior does not depend on why the element expanded -->
- {a} {b} text + {a} {b} text1 text2
diff --git a/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/output_prettier.svelte b/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/output_prettier.svelte index c198c1775..24ee70049 100644 --- a/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/output_prettier.svelte +++ b/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/output_prettier.svelte @@ -1,13 +1,13 @@
- {a} {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb} text + {a} {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb} text1 text2
{a} - {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb} text + {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb} text1 text2
@@ -15,7 +15,7 @@
- {ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc}text + {ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc}text1 text2
@@ -24,6 +24,6 @@
{a} - {b} text + {b} text1 text2
diff --git a/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/unformatted_ours_one_per_line.svelte b/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/unformatted_ours_one_per_line.svelte index c198c1775..24ee70049 100644 --- a/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/unformatted_ours_one_per_line.svelte +++ b/tests/fixtures/svelte/elements/inline_content_spaced_tags_tail_long_prettier_divergence/unformatted_ours_one_per_line.svelte @@ -1,13 +1,13 @@
- {a} {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb} text + {a} {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb} text1 text2
{a} - {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb} text + {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb} text1 text2
@@ -15,7 +15,7 @@
- {ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc}text + {ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc}text1 text2
@@ -24,6 +24,6 @@
{a} - {b} text + {b} text1 text2
diff --git a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/README.md new file mode 100644 index 000000000..dcad760b1 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/README.md @@ -0,0 +1,35 @@ +# inline_interior_newline_label_hold_prettier_divergence + +The prose gate read by the **interior** side of the render-free content boundary: whether an +authored newline inside a hugged inline element may select its layout. A run holding a single +word is a label with no fill to reflow into, so its whitespace-only newline is held +— and holding it expands the element (`ab text1⏎`), +since the hugged form has nowhere to put a hardline. Two one-word texts in one run are two labels, +not a phrase — the count is per node, never a sum — so +`ab text1 c text2` expands the same way. The +same newline in a run that is prose — one text carries two words — is the fill's own wrap point +and the element collapses (`{a} {b} text1 text2`). + +## Reason + +Design choice, the same "is there a fill?" answer +[inline_content_flow_collapse](../inline_content_flow_collapse_prettier_divergence/) gives for a +prose run, read with the prose gate of +[inline_sibling_newline_label_hold](../inline_sibling_newline_label_hold_prettier_divergence/): +every reader of "did the author break this content?" must consult one answer, or a wide fill +writes the very newline the other reader keys on and the two forms alternate. With no fill the +authored newline is the author's only structure, so the interior arm holds it exactly as the +sibling arms do. + +`unformatted_ours_hugged.svelte` is the hugged authoring of every case. tsv normalizes it to +`input.svelte` in one pass; prettier keeps the hug and dangles the closing delimiter +(`ab text1`) — its interior newline is held but never +selects the element's layout. + +⚠️ The hold reaches only the whitespace-only separator. The content-text arm's edge trim still +collapses the newline when it sits at a text node's edge (`text1⏎a` +→ `text1 a`), so the two spellings of a one-word interior newline do +not yet agree; that arm is tracked as open work and deliberately not pinned here. + +See +[conformance_prettier_svelte.md §Svelte: Inline content block-style](../../../../../docs/conformance_prettier_svelte.md#svelte-inline-content-block-style). diff --git a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/expected.json new file mode 100644 index 000000000..1e978a7a6 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/expected.json @@ -0,0 +1,794 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 1206, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Comment", + "start": 0, + "end": 413, + "data": "\n\tAn INTERIOR newline in a hugged inline element selects its layout only when the content is\n\tnot a reflowable fill. A run holding a single word has no fill to reflow into, so its\n\twhitespace-only newline is the author's structure and the element expands, boundaries and\n\tall; the same newline in a run that is prose — its text carries two words — is the\n\tfill's own wrap point, and the element collapses.\n" + }, + { + "type": "Text", + "start": 413, + "end": 415, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 415, + "end": 555, + "data": " a whitespace-only newline between two elements, two components, or two tags in a one-word\n run: held, and it selects the layout " + }, + { + "type": "Text", + "start": 555, + "end": 556, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 556, + "end": 625, + "name": "div", + "name_loc": { + "start": { + "line": 11, + "column": 1, + "character": 557 + }, + "end": { + "line": 11, + "column": 4, + "character": 560 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 561, + "end": 563, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 563, + "end": 618, + "name": "span", + "name_loc": { + "start": { + "line": 12, + "column": 2, + "character": 564 + }, + "end": { + "line": 12, + "column": 6, + "character": 568 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 569, + "end": 572, + "raw": "\n\t\t", + "data": "\n\t\t" + }, + { + "type": "RegularElement", + "start": 572, + "end": 586, + "name": "code", + "name_loc": { + "start": { + "line": 13, + "column": 3, + "character": 573 + }, + "end": { + "line": 13, + "column": 7, + "character": 577 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 578, + "end": 579, + "raw": "a", + "data": "a" + } + ] + } + }, + { + "type": "Text", + "start": 586, + "end": 589, + "raw": "\n\t\t", + "data": "\n\t\t" + }, + { + "type": "RegularElement", + "start": 589, + "end": 603, + "name": "code", + "name_loc": { + "start": { + "line": 14, + "column": 3, + "character": 590 + }, + "end": { + "line": 14, + "column": 7, + "character": 594 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 595, + "end": 596, + "raw": "b", + "data": "b" + } + ] + } + }, + { + "type": "Text", + "start": 603, + "end": 611, + "raw": " text1\n\t", + "data": " text1\n\t" + } + ] + } + }, + { + "type": "Text", + "start": 618, + "end": 619, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 625, + "end": 626, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 626, + "end": 685, + "name": "div", + "name_loc": { + "start": { + "line": 17, + "column": 1, + "character": 627 + }, + "end": { + "line": 17, + "column": 4, + "character": 630 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 631, + "end": 633, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 633, + "end": 678, + "name": "span", + "name_loc": { + "start": { + "line": 18, + "column": 2, + "character": 634 + }, + "end": { + "line": 18, + "column": 6, + "character": 638 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 639, + "end": 642, + "raw": "\n\t\t", + "data": "\n\t\t" + }, + { + "type": "Component", + "start": 642, + "end": 651, + "name": "Comp1", + "name_loc": { + "start": { + "line": 19, + "column": 3, + "character": 643 + }, + "end": { + "line": 19, + "column": 8, + "character": 648 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 651, + "end": 654, + "raw": "\n\t\t", + "data": "\n\t\t" + }, + { + "type": "Component", + "start": 654, + "end": 663, + "name": "Comp2", + "name_loc": { + "start": { + "line": 20, + "column": 3, + "character": 655 + }, + "end": { + "line": 20, + "column": 8, + "character": 660 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 663, + "end": 671, + "raw": " text1\n\t", + "data": " text1\n\t" + } + ] + } + }, + { + "type": "Text", + "start": 678, + "end": 679, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 685, + "end": 686, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 686, + "end": 733, + "name": "div", + "name_loc": { + "start": { + "line": 23, + "column": 1, + "character": 687 + }, + "end": { + "line": 23, + "column": 4, + "character": 690 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 691, + "end": 693, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 693, + "end": 726, + "name": "span", + "name_loc": { + "start": { + "line": 24, + "column": 2, + "character": 694 + }, + "end": { + "line": 24, + "column": 6, + "character": 698 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 699, + "end": 702, + "raw": "\n\t\t", + "data": "\n\t\t" + }, + { + "type": "ExpressionTag", + "start": 702, + "end": 705, + "expression": { + "type": "Identifier", + "start": 703, + "end": 704, + "loc": { + "start": { + "line": 25, + "column": 3 + }, + "end": { + "line": 25, + "column": 4 + } + }, + "name": "a" + } + }, + { + "type": "Text", + "start": 705, + "end": 708, + "raw": "\n\t\t", + "data": "\n\t\t" + }, + { + "type": "ExpressionTag", + "start": 708, + "end": 711, + "expression": { + "type": "Identifier", + "start": 709, + "end": 710, + "loc": { + "start": { + "line": 26, + "column": 3 + }, + "end": { + "line": 26, + "column": 4 + } + }, + "name": "b" + } + }, + { + "type": "Text", + "start": 711, + "end": 719, + "raw": " text1\n\t", + "data": " text1\n\t" + } + ] + } + }, + { + "type": "Text", + "start": 726, + "end": 727, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 733, + "end": 735, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 735, + "end": 928, + "data": " two one-word texts in one run are two labels, not a phrase: the count is per node, so the run\n is no fill and the newline still selects the layout (a sum would have read two words) " + }, + { + "type": "Text", + "start": 928, + "end": 929, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 929, + "end": 1019, + "name": "div", + "name_loc": { + "start": { + "line": 32, + "column": 1, + "character": 930 + }, + "end": { + "line": 32, + "column": 4, + "character": 933 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 934, + "end": 936, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 936, + "end": 1012, + "name": "span", + "name_loc": { + "start": { + "line": 33, + "column": 2, + "character": 937 + }, + "end": { + "line": 33, + "column": 6, + "character": 941 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 942, + "end": 945, + "raw": "\n\t\t", + "data": "\n\t\t" + }, + { + "type": "RegularElement", + "start": 945, + "end": 959, + "name": "code", + "name_loc": { + "start": { + "line": 34, + "column": 3, + "character": 946 + }, + "end": { + "line": 34, + "column": 7, + "character": 950 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 951, + "end": 952, + "raw": "a", + "data": "a" + } + ] + } + }, + { + "type": "Text", + "start": 959, + "end": 962, + "raw": "\n\t\t", + "data": "\n\t\t" + }, + { + "type": "RegularElement", + "start": 962, + "end": 976, + "name": "code", + "name_loc": { + "start": { + "line": 35, + "column": 3, + "character": 963 + }, + "end": { + "line": 35, + "column": 7, + "character": 967 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 968, + "end": 969, + "raw": "b", + "data": "b" + } + ] + } + }, + { + "type": "Text", + "start": 976, + "end": 983, + "raw": " text1 ", + "data": " text1 " + }, + { + "type": "RegularElement", + "start": 983, + "end": 997, + "name": "code", + "name_loc": { + "start": { + "line": 35, + "column": 24, + "character": 984 + }, + "end": { + "line": 35, + "column": 28, + "character": 988 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 989, + "end": 990, + "raw": "c", + "data": "c" + } + ] + } + }, + { + "type": "Text", + "start": 997, + "end": 1005, + "raw": " text2\n\t", + "data": " text2\n\t" + } + ] + } + }, + { + "type": "Text", + "start": 1012, + "end": 1013, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1019, + "end": 1021, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1021, + "end": 1158, + "data": " control: the same newline in a run whose text carries two words — the run is prose and the\n newline is the fill's wrap point " + }, + { + "type": "Text", + "start": 1158, + "end": 1159, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1159, + "end": 1205, + "name": "div", + "name_loc": { + "start": { + "line": 41, + "column": 1, + "character": 1160 + }, + "end": { + "line": 41, + "column": 4, + "character": 1163 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1164, + "end": 1166, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1166, + "end": 1198, + "name": "span", + "name_loc": { + "start": { + "line": 42, + "column": 2, + "character": 1167 + }, + "end": { + "line": 42, + "column": 6, + "character": 1171 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "ExpressionTag", + "start": 1172, + "end": 1175, + "expression": { + "type": "Identifier", + "start": 1173, + "end": 1174, + "loc": { + "start": { + "line": 42, + "column": 8 + }, + "end": { + "line": 42, + "column": 9 + } + }, + "name": "a" + } + }, + { + "type": "Text", + "start": 1175, + "end": 1176, + "raw": " ", + "data": " " + }, + { + "type": "ExpressionTag", + "start": 1176, + "end": 1179, + "expression": { + "type": "Identifier", + "start": 1177, + "end": 1178, + "loc": { + "start": { + "line": 42, + "column": 12 + }, + "end": { + "line": 42, + "column": 13 + } + }, + "name": "b" + } + }, + { + "type": "Text", + "start": 1179, + "end": 1191, + "raw": " text1 text2", + "data": " text1 text2" + } + ] + } + }, + { + "type": "Text", + "start": 1198, + "end": 1199, + "raw": "\n", + "data": "\n" + } + ] + } + } + ] + }, + "options": null, + "comments": [] +} diff --git a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/input.svelte new file mode 100644 index 000000000..9d24bd3c6 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/input.svelte @@ -0,0 +1,43 @@ + + + +
+ + a + b text1 + +
+
+ + + text1 + +
+
+ + {a} + {b} text1 + +
+ + +
+ + a + b text1 c text2 + +
+ + +
+ {a} {b} text1 text2 +
diff --git a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/prettier_variant_dangle.svelte b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/prettier_variant_dangle.svelte new file mode 100644 index 000000000..0e98b87b6 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/prettier_variant_dangle.svelte @@ -0,0 +1,46 @@ + + + +
+ a + b text1 +
+
+ + text1 +
+
+ {a} + {b} text1 +
+ + +
+ a + b text1 c text2 +
+ + +
+ {a} + {b} text1 text2 +
diff --git a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/unformatted_ours_hugged.svelte b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/unformatted_ours_hugged.svelte new file mode 100644 index 000000000..3d3fa9da3 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/unformatted_ours_hugged.svelte @@ -0,0 +1,36 @@ + + + +
+ a + b text1 +
+
+ + text1 +
+
+ {a} + {b} text1 +
+ + +
+ a + b text1 c text2 +
+ + +
+ {a} + {b} text1 text2 +
diff --git a/tests/fixtures/svelte/elements/inline_separator_comment_newline/expected.json b/tests/fixtures/svelte/elements/inline_separator_comment_newline/expected.json index e690de95d..209c35edb 100644 --- a/tests/fixtures/svelte/elements/inline_separator_comment_newline/expected.json +++ b/tests/fixtures/svelte/elements/inline_separator_comment_newline/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 1323, + "end": 1371, "type": "Root", "fragment": { "type": "Fragment", @@ -36,7 +36,7 @@ { "type": "RegularElement", "start": 647, - "end": 681, + "end": 693, "name": "p", "name_loc": { "start": { @@ -57,61 +57,61 @@ { "type": "Text", "start": 650, - "end": 659, - "raw": "\n\ttext1\n\t", - "data": "\n\ttext1\n\t" + "end": 665, + "raw": "\n\ttext1 text2\n\t", + "data": "\n\ttext1 text2\n\t" }, { "type": "Comment", - "start": 659, - "end": 669, + "start": 665, + "end": 675, "data": " c " }, { "type": "Text", - "start": 669, - "end": 677, - "raw": "\n\ttext2\n", - "data": "\n\ttext2\n" + "start": 675, + "end": 689, + "raw": "\n\ttext3 text4\n", + "data": "\n\ttext3 text4\n" } ] } }, { "type": "Text", - "start": 681, - "end": 683, + "start": 693, + "end": 695, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 683, - "end": 769, + "start": 695, + "end": 781, "data": "\n\tcontrol: newline AFTER only — the following run stays off the comment's line\n" }, { "type": "Text", - "start": 769, - "end": 770, + "start": 781, + "end": 782, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 770, - "end": 803, + "start": 782, + "end": 827, "name": "p", "name_loc": { "start": { "line": 23, "column": 1, - "character": 771 + "character": 783 }, "end": { "line": 23, "column": 2, - "character": 772 + "character": 784 } }, "attributes": [], @@ -120,62 +120,62 @@ "nodes": [ { "type": "Text", - "start": 773, - "end": 781, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "start": 785, + "end": 799, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "Comment", - "start": 781, - "end": 791, + "start": 799, + "end": 809, "data": " c " }, { "type": "Text", - "start": 791, - "end": 799, - "raw": "\n\ttext2\n", - "data": "\n\ttext2\n" + "start": 809, + "end": 823, + "raw": "\n\ttext3 text4\n", + "data": "\n\ttext3 text4\n" } ] } }, { "type": "Text", - "start": 803, - "end": 805, + "start": 827, + "end": 829, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 805, - "end": 892, + "start": 829, + "end": 916, "data": "\n\tcontrol: newline BEFORE only — the comment stays off the preceding run's line\n" }, { "type": "Text", - "start": 892, - "end": 893, + "start": 916, + "end": 917, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 893, - "end": 926, + "start": 917, + "end": 962, "name": "p", "name_loc": { "start": { "line": 31, "column": 1, - "character": 894 + "character": 918 }, "end": { "line": 31, "column": 2, - "character": 895 + "character": 919 } }, "attributes": [], @@ -184,62 +184,62 @@ "nodes": [ { "type": "Text", - "start": 896, - "end": 905, - "raw": "\n\ttext1\n\t", - "data": "\n\ttext1\n\t" + "start": 920, + "end": 935, + "raw": "\n\ttext1 text2\n\t", + "data": "\n\ttext1 text2\n\t" }, { "type": "Comment", - "start": 905, - "end": 915, + "start": 935, + "end": 945, "data": " c " }, { "type": "Text", - "start": 915, - "end": 922, - "raw": " text2\n", - "data": " text2\n" + "start": 945, + "end": 958, + "raw": " text3 text4\n", + "data": " text3 text4\n" } ] } }, { "type": "Text", - "start": 926, - "end": 928, + "start": 962, + "end": 964, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 928, - "end": 1292, + "start": 964, + "end": 1328, "data": "\n\tcontrol: NEITHER side — the rule HOLDS what was authored, it does not FORCE a break around a\n\tcomment, so the hugged authoring stays hugged. This is the null control of the pair above,\n\tvarying the same dimension (is there a newline at this boundary?): without it, an\n\timplementation that always broke both sides of a comment would pass every case above.\n" }, { "type": "Text", - "start": 1292, - "end": 1293, + "start": 1328, + "end": 1329, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1293, - "end": 1322, + "start": 1329, + "end": 1370, "name": "p", "name_loc": { "start": { "line": 42, "column": 1, - "character": 1294 + "character": 1330 }, "end": { "line": 42, "column": 2, - "character": 1295 + "character": 1331 } }, "attributes": [], @@ -248,23 +248,23 @@ "nodes": [ { "type": "Text", - "start": 1296, - "end": 1302, - "raw": "text1 ", - "data": "text1 " + "start": 1332, + "end": 1344, + "raw": "text1 text2 ", + "data": "text1 text2 " }, { "type": "Comment", - "start": 1302, - "end": 1312, + "start": 1344, + "end": 1354, "data": " c " }, { "type": "Text", - "start": 1312, - "end": 1318, - "raw": " text2", - "data": " text2" + "start": 1354, + "end": 1366, + "raw": " text3 text4", + "data": " text3 text4" } ] } diff --git a/tests/fixtures/svelte/elements/inline_separator_comment_newline/input.svelte b/tests/fixtures/svelte/elements/inline_separator_comment_newline/input.svelte index 6057e3cb7..815ed2080 100644 --- a/tests/fixtures/svelte/elements/inline_separator_comment_newline/input.svelte +++ b/tests/fixtures/svelte/elements/inline_separator_comment_newline/input.svelte @@ -12,25 +12,25 @@ control: newline on BOTH sides — the comment owns its line -->

- text1 + text1 text2 - text2 + text3 text4

- text1 - text2 + text1 text2 + text3 text4

- text1 - text2 + text1 text2 + text3 text4

-

text1 text2

+

text1 text2 text3 text4

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/expected.json index cbb4e0315..1e254088f 100644 --- a/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 2996, + "end": 3050, "type": "Root", "fragment": { "type": "Fragment", @@ -605,7 +605,7 @@ { "type": "RegularElement", "start": 2024, - "end": 2058, + "end": 2070, "name": "p", "name_loc": { "start": { @@ -626,61 +626,61 @@ { "type": "Text", "start": 2027, - "end": 2036, - "raw": "\n\ttext1\n\t", - "data": "\n\ttext1\n\t" + "end": 2042, + "raw": "\n\ttext1 text2\n\t", + "data": "\n\ttext1 text2\n\t" }, { "type": "Comment", - "start": 2036, - "end": 2046, + "start": 2042, + "end": 2052, "data": " c " }, { "type": "Text", - "start": 2046, - "end": 2054, - "raw": "\n\ttext2\n", - "data": "\n\ttext2\n" + "start": 2052, + "end": 2066, + "raw": "\n\ttext3 text4\n", + "data": "\n\ttext3 text4\n" } ] } }, { "type": "Text", - "start": 2058, - "end": 2060, + "start": 2070, + "end": 2072, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 2060, - "end": 2345, + "start": 2072, + "end": 2357, "data": " control: a comment BOUNDS the run without sterilizing it — it keeps its own line, and the\n inline runs on BOTH sides of it still flow. Without this the comment control above is equally\n satisfied by \"a run holding a comment keeps every line\", which is a different rule " }, { "type": "Text", - "start": 2345, - "end": 2346, + "start": 2357, + "end": 2358, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 2346, - "end": 2434, + "start": 2358, + "end": 2458, "name": "p", "name_loc": { "start": { "line": 56, "column": 1, - "character": 2347 + "character": 2359 }, "end": { "line": 56, "column": 2, - "character": 2348 + "character": 2360 } }, "attributes": [], @@ -689,26 +689,26 @@ "nodes": [ { "type": "Text", - "start": 2349, - "end": 2357, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "start": 2361, + "end": 2375, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "RegularElement", - "start": 2357, - "end": 2377, + "start": 2375, + "end": 2395, "name": "span", "name_loc": { "start": { "line": 57, - "column": 8, - "character": 2358 + "column": 14, + "character": 2376 }, "end": { "line": 57, - "column": 12, - "character": 2362 + "column": 18, + "character": 2380 } }, "attributes": [], @@ -717,8 +717,8 @@ "nodes": [ { "type": "Text", - "start": 2363, - "end": 2370, + "start": 2381, + "end": 2388, "raw": "inline1", "data": "inline1" } @@ -727,39 +727,39 @@ }, { "type": "Text", - "start": 2377, - "end": 2385, - "raw": " text2\n\t", - "data": " text2\n\t" + "start": 2395, + "end": 2403, + "raw": " text3\n\t", + "data": " text3\n\t" }, { "type": "Comment", - "start": 2385, - "end": 2395, + "start": 2403, + "end": 2413, "data": " c " }, { "type": "Text", - "start": 2395, - "end": 2403, - "raw": "\n\ttext3 ", - "data": "\n\ttext3 " + "start": 2413, + "end": 2427, + "raw": "\n\ttext4 text5 ", + "data": "\n\ttext4 text5 " }, { "type": "RegularElement", - "start": 2403, - "end": 2423, + "start": 2427, + "end": 2447, "name": "span", "name_loc": { "start": { "line": 59, - "column": 8, - "character": 2404 + "column": 14, + "character": 2428 }, "end": { "line": 59, - "column": 12, - "character": 2408 + "column": 18, + "character": 2432 } }, "attributes": [], @@ -768,8 +768,8 @@ "nodes": [ { "type": "Text", - "start": 2409, - "end": 2416, + "start": 2433, + "end": 2440, "raw": "inline2", "data": "inline2" } @@ -778,49 +778,49 @@ }, { "type": "Text", - "start": 2423, - "end": 2430, - "raw": " text4\n", - "data": " text4\n" + "start": 2447, + "end": 2454, + "raw": " text6\n", + "data": " text6\n" } ] } }, { "type": "Text", - "start": 2434, - "end": 2436, + "start": 2458, + "end": 2460, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 2436, - "end": 2647, + "start": 2460, + "end": 2671, "data": " control: a `
` bounds the run exactly as a comment does — its newline is a rendered\n line break, not a spelling of a space, so it keeps its own line while the runs on both\n sides still flow " }, { "type": "Text", - "start": 2647, - "end": 2648, + "start": 2671, + "end": 2672, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 2648, - "end": 2732, + "start": 2672, + "end": 2768, "name": "p", "name_loc": { "start": { "line": 65, "column": 1, - "character": 2649 + "character": 2673 }, "end": { "line": 65, "column": 2, - "character": 2650 + "character": 2674 } }, "attributes": [], @@ -829,26 +829,26 @@ "nodes": [ { "type": "Text", - "start": 2651, - "end": 2659, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "start": 2675, + "end": 2689, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "RegularElement", - "start": 2659, - "end": 2679, + "start": 2689, + "end": 2709, "name": "span", "name_loc": { "start": { "line": 66, - "column": 8, - "character": 2660 + "column": 14, + "character": 2690 }, "end": { "line": 66, - "column": 12, - "character": 2664 + "column": 18, + "character": 2694 } }, "attributes": [], @@ -857,8 +857,8 @@ "nodes": [ { "type": "Text", - "start": 2665, - "end": 2672, + "start": 2695, + "end": 2702, "raw": "inline1", "data": "inline1" } @@ -867,26 +867,26 @@ }, { "type": "Text", - "start": 2679, - "end": 2687, - "raw": " text2\n\t", - "data": " text2\n\t" + "start": 2709, + "end": 2717, + "raw": " text3\n\t", + "data": " text3\n\t" }, { "type": "RegularElement", - "start": 2687, - "end": 2693, + "start": 2717, + "end": 2723, "name": "br", "name_loc": { "start": { "line": 67, "column": 2, - "character": 2688 + "character": 2718 }, "end": { "line": 67, "column": 4, - "character": 2690 + "character": 2720 } }, "attributes": [], @@ -897,26 +897,26 @@ }, { "type": "Text", - "start": 2693, - "end": 2701, - "raw": "\n\ttext3 ", - "data": "\n\ttext3 " + "start": 2723, + "end": 2737, + "raw": "\n\ttext4 text5 ", + "data": "\n\ttext4 text5 " }, { "type": "RegularElement", - "start": 2701, - "end": 2721, + "start": 2737, + "end": 2757, "name": "span", "name_loc": { "start": { "line": 68, - "column": 8, - "character": 2702 + "column": 14, + "character": 2738 }, "end": { "line": 68, - "column": 12, - "character": 2706 + "column": 18, + "character": 2742 } }, "attributes": [], @@ -925,8 +925,8 @@ "nodes": [ { "type": "Text", - "start": 2707, - "end": 2714, + "start": 2743, + "end": 2750, "raw": "inline2", "data": "inline2" } @@ -935,49 +935,49 @@ }, { "type": "Text", - "start": 2721, - "end": 2728, - "raw": " text4\n", - "data": " text4\n" + "start": 2757, + "end": 2764, + "raw": " text6\n", + "data": " text6\n" } ] } }, { "type": "Text", - "start": 2732, - "end": 2734, + "start": 2768, + "end": 2770, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 2734, - "end": 2777, + "start": 2770, + "end": 2813, "data": " control: a blank line still breaks " }, { "type": "Text", - "start": 2777, - "end": 2778, + "start": 2813, + "end": 2814, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 2778, - "end": 2824, + "start": 2814, + "end": 2872, "name": "p", "name_loc": { "start": { "line": 72, "column": 1, - "character": 2779 + "character": 2815 }, "end": { "line": 72, "column": 2, - "character": 2780 + "character": 2816 } }, "attributes": [], @@ -986,26 +986,26 @@ "nodes": [ { "type": "Text", - "start": 2781, - "end": 2791, - "raw": "\n\ttext1\n\n\t", - "data": "\n\ttext1\n\n\t" + "start": 2817, + "end": 2833, + "raw": "\n\ttext1 text2\n\n\t", + "data": "\n\ttext1 text2\n\n\t" }, { "type": "RegularElement", - "start": 2791, - "end": 2811, + "start": 2833, + "end": 2853, "name": "span", "name_loc": { "start": { "line": 75, "column": 2, - "character": 2792 + "character": 2834 }, "end": { "line": 75, "column": 6, - "character": 2796 + "character": 2838 } }, "attributes": [], @@ -1014,8 +1014,8 @@ "nodes": [ { "type": "Text", - "start": 2797, - "end": 2804, + "start": 2839, + "end": 2846, "raw": "inline1", "data": "inline1" } @@ -1024,49 +1024,49 @@ }, { "type": "Text", - "start": 2811, - "end": 2820, - "raw": "\n\n\ttext2\n", - "data": "\n\n\ttext2\n" + "start": 2853, + "end": 2868, + "raw": "\n\n\ttext3 text4\n", + "data": "\n\n\ttext3 text4\n" } ] } }, { "type": "Text", - "start": 2824, - "end": 2826, + "start": 2872, + "end": 2874, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 2826, - "end": 2916, + "start": 2874, + "end": 2964, "data": " control: a block sibling takes its own line; the inline run before it still flows " }, { "type": "Text", - "start": 2916, - "end": 2917, + "start": 2964, + "end": 2965, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 2917, - "end": 2995, + "start": 2965, + "end": 3049, "name": "div", "name_loc": { "start": { "line": 81, "column": 1, - "character": 2918 + "character": 2966 }, "end": { "line": 81, "column": 4, - "character": 2921 + "character": 2969 } }, "attributes": [], @@ -1075,26 +1075,26 @@ "nodes": [ { "type": "Text", - "start": 2922, - "end": 2930, - "raw": "\n\ttext1 ", - "data": "\n\ttext1 " + "start": 2970, + "end": 2984, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " }, { "type": "RegularElement", - "start": 2930, - "end": 2950, + "start": 2984, + "end": 3004, "name": "span", "name_loc": { "start": { "line": 82, - "column": 8, - "character": 2931 + "column": 14, + "character": 2985 }, "end": { "line": 82, - "column": 12, - "character": 2935 + "column": 18, + "character": 2989 } }, "attributes": [], @@ -1103,8 +1103,8 @@ "nodes": [ { "type": "Text", - "start": 2936, - "end": 2943, + "start": 2990, + "end": 2997, "raw": "inline1", "data": "inline1" } @@ -1113,26 +1113,26 @@ }, { "type": "Text", - "start": 2950, - "end": 2958, - "raw": " text2\n\t", - "data": " text2\n\t" + "start": 3004, + "end": 3012, + "raw": " text3\n\t", + "data": " text3\n\t" }, { "type": "RegularElement", - "start": 2958, - "end": 2975, + "start": 3012, + "end": 3029, "name": "div", "name_loc": { "start": { "line": 83, "column": 2, - "character": 2959 + "character": 3013 }, "end": { "line": 83, "column": 5, - "character": 2962 + "character": 3016 } }, "attributes": [], @@ -1141,8 +1141,8 @@ "nodes": [ { "type": "Text", - "start": 2963, - "end": 2969, + "start": 3017, + "end": 3023, "raw": "block1", "data": "block1" } @@ -1151,10 +1151,10 @@ }, { "type": "Text", - "start": 2975, - "end": 2989, - "raw": "\n\ttext3 text4\n", - "data": "\n\ttext3 text4\n" + "start": 3029, + "end": 3043, + "raw": "\n\ttext4 text5\n", + "data": "\n\ttext4 text5\n" } ] } diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/input.svelte index 41339d6bb..b9c98f417 100644 --- a/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/input.svelte @@ -45,41 +45,41 @@

- text1 + text1 text2 - text2 + text3 text4

- text1 inline1 text2 + text1 text2 inline1 text3 - text3 inline2 text4 + text4 text5 inline2 text6

- text1 inline1 text2 + text1 text2 inline1 text3
- text3 inline2 text4 + text4 text5 inline2 text6

- text1 + text1 text2 inline1 - text2 + text3 text4

- text1 inline1 text2 + text1 text2 inline1 text3
block1
- text3 text4 + text4 text5
diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/prettier_variant_newline_after.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/prettier_variant_newline_after.svelte index d004753eb..bac1745b6 100644 --- a/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/prettier_variant_newline_after.svelte +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/prettier_variant_newline_after.svelte @@ -50,46 +50,46 @@

- text1 + text1 text2 - text2 + text3 text4

- text1 inline1 - text2 + text1 text2 inline1 + text3 - text3 inline2 - text4 + text4 text5 inline2 + text6

- text1 inline1 - text2 + text1 text2 inline1 + text3
- text3 inline2 - text4 + text4 text5 inline2 + text6

- text1 + text1 text2 inline1 - text2 + text3 text4

- text1 inline1 - text2 + text1 text2 inline1 + text3
block1
- text3 text4 + text4 text5
diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/prettier_variant_newline_before.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/prettier_variant_newline_before.svelte index 7de48260d..e388fea7b 100644 --- a/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/prettier_variant_newline_before.svelte +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/prettier_variant_newline_before.svelte @@ -50,46 +50,46 @@

- text1 + text1 text2 - text2 + text3 text4

- text1 - inline1 text2 + text1 text2 + inline1 text3 - text3 - inline2 text4 + text4 text5 + inline2 text6

- text1 - inline1 text2 + text1 text2 + inline1 text3
- text3 - inline2 text4 + text4 text5 + inline2 text6

- text1 + text1 text2 inline1 - text2 + text3 text4

- text1 - inline1 text2 + text1 text2 + inline1 text3
block1
- text3 text4 + text4 text5
diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/prettier_variant_newline_both.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/prettier_variant_newline_both.svelte index 6c64452be..39801f688 100644 --- a/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/prettier_variant_newline_both.svelte +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/prettier_variant_newline_both.svelte @@ -55,51 +55,51 @@

- text1 + text1 text2 - text2 + text3 text4

- text1 + text1 text2 inline1 - text2 - text3 + + text4 text5 inline2 - text4 + text6

- text1 + text1 text2 inline1 - text2 -
text3 +
+ text4 text5 inline2 - text4 + text6

- text1 + text1 text2 inline1 - text2 + text3 text4

- text1 + text1 text2 inline1 - text2 + text3
block1
- text3 text4 + text4 text5
diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/README.md new file mode 100644 index 000000000..47f7e5c50 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/README.md @@ -0,0 +1,26 @@ +# inline_sibling_newline_interior_blank_prettier_divergence + +The sibling-newline flow rule at the one shape where its two blank-line readers part: an +authored blank line **inside one text node** (`text1 text2⏎⏎text3 text4`, a single Text node). +An authored blank bounds a run wherever the parser puts it beside a sibling — a whitespace-only +node, or a content text's edge whitespace +([inline_sibling_newline_run_bounded](../inline_sibling_newline_run_bounded_prettier_divergence/)) +— but a run is a partition of nodes and cannot be split inside one, so an interior blank bounds +nothing: the node's four words make the run prose, and the sibling newlines on both sides of it +flow (`prettier_variant_newline.svelte`, which prettier keeps and tsv normalizes to +`input.svelte`). + +## Reason + +Design choice, and the honest one: the interior blank is collapsed by the fill under **both** +formatters (`unformatted_ours_interior_blank.svelte` carries it — tsv normalizes it to +`input.svelte`, prettier to `prettier_variant_newline.svelte`, the blank gone in each), so there +is no blank left for a run boundary to protect. Holding the sibling newlines on account of a +blank the output does not contain would key the layout on bytes the formatter erases — a layout +selected by nothing the next pass can see. The element-interior reader +(`content_is_reflowable_fill`) reads the whole text and does answer "no fill" for this node; the +two readers agree at every blank that sits beside a sibling and are documented to differ only +here. + +See +[conformance_prettier_svelte.md §Svelte: Inline content block-style](../../../../../docs/conformance_prettier_svelte.md#svelte-inline-content-block-style). diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/expected.json new file mode 100644 index 000000000..b11bf118c --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/expected.json @@ -0,0 +1,147 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 546, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Comment", + "start": 0, + "end": 393, + "data": "\n\tAn authored blank line INSIDE one text node (`text1 text2⏎⏎text3 text4`, one Text node) is\n\tnot a run boundary: the flow rule's scan partitions NODES and can bound a run only at a\n\tblank on a text's EDGE, while the fill collapses the interior blank either way — under\n\tboth formatters. So the sibling newlines beside such a node flow with the run's prose,\n\twhere prettier holds them.\n" + }, + { + "type": "Text", + "start": 393, + "end": 395, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 395, + "end": 469, + "data": " a two-word half on each side of the blank, beside inline siblings " + }, + { + "type": "Text", + "start": 469, + "end": 470, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 470, + "end": 545, + "name": "p", + "name_loc": { + "start": { + "line": 10, + "column": 1, + "character": 471 + }, + "end": { + "line": 10, + "column": 2, + "character": 472 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 473, + "end": 475, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 475, + "end": 495, + "name": "span", + "name_loc": { + "start": { + "line": 11, + "column": 2, + "character": 476 + }, + "end": { + "line": 11, + "column": 6, + "character": 480 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 481, + "end": 488, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 495, + "end": 520, + "raw": " text1 text2 text3 text4 ", + "data": " text1 text2 text3 text4 " + }, + { + "type": "RegularElement", + "start": 520, + "end": 540, + "name": "span", + "name_loc": { + "start": { + "line": 11, + "column": 47, + "character": 521 + }, + "end": { + "line": 11, + "column": 51, + "character": 525 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 526, + "end": 533, + "raw": "inline2", + "data": "inline2" + } + ] + } + }, + { + "type": "Text", + "start": 540, + "end": 541, + "raw": "\n", + "data": "\n" + } + ] + } + } + ] + }, + "options": null, + "comments": [] +} diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/input.svelte new file mode 100644 index 000000000..648ed3184 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/input.svelte @@ -0,0 +1,12 @@ + + + +

+ inline1 text1 text2 text3 text4 inline2 +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/prettier_variant_newline.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/prettier_variant_newline.svelte new file mode 100644 index 000000000..14e04a959 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/prettier_variant_newline.svelte @@ -0,0 +1,14 @@ + + + +

+ inline1 + text1 text2 text3 text4 + inline2 +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/unformatted_ours_interior_blank.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/unformatted_ours_interior_blank.svelte new file mode 100644 index 000000000..dbb047f2c --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/unformatted_ours_interior_blank.svelte @@ -0,0 +1,16 @@ + + + +

+ inline1 + text1 text2 + + text3 text4 + inline2 +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/README.md new file mode 100644 index 000000000..294555434 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/README.md @@ -0,0 +1,23 @@ +# inline_sibling_newline_label_hold_container_prettier_divergence + +The sibling-newline flow rule's prose gate, on its holding side, across container kinds: a +one-word run is a label ([inline_sibling_newline_label_hold](../inline_sibling_newline_label_hold_prettier_divergence/)) +and holds its authored newlines at the root, in an inline element, in a list item and a table +cell, and inside a control-flow block or a snippet body alike. The flowing side already pins that +the container is not an axis +([container_kind_newline_flow](../container_kind_newline_flow_prettier_divergence/)); this is the +same assertion for the hold, which a rule keyed on the container would fail from the other side. + +## Reason + +Design choice — one predicate, asked of the run and its neighbours, never of what encloses them. +Every held case is identical in both files and agreement with prettier. The control is the +divergence: a two-word run in the same inline container is prose and flows, which prettier holds +(`prettier_variant_newline.svelte`, normalized to `input.svelte` by tsv). + +`variant_space.svelte` is the null control: the rule holds an authored newline and never forces +one, so the space spelling of every label shape stays inline under both formatters in every +container (dual-stable). + +See +[conformance_prettier_svelte.md §Svelte: Inline content block-style](../../../../../docs/conformance_prettier_svelte.md#svelte-inline-content-block-style). diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/expected.json new file mode 100644 index 000000000..3ccdbf771 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/expected.json @@ -0,0 +1,714 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 1072, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Comment", + "start": 0, + "end": 487, + "data": "\n\tThe sibling-newline flow rule's prose gate does not read the container: a one-word run is a\n\tlabel and holds its authored newlines at the root, in an inline element, a block element, a\n\tlist item, a table cell, and inside a control-flow block or a snippet body alike, exactly as\n\tthe flowing side of the rule lays one run out identically across every container kind\n\t(`container_kind_newline_flow`). Prettier holds every label shape too; the flowing control is\n\tthe divergence.\n" + }, + { + "type": "Text", + "start": 487, + "end": 489, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 489, + "end": 509, + "data": " at the root " + }, + { + "type": "Text", + "start": 509, + "end": 510, + "raw": "\n", + "data": "\n" + }, + { + "type": "Component", + "start": 510, + "end": 519, + "name": "Comp1", + "name_loc": { + "start": { + "line": 11, + "column": 1, + "character": 511 + }, + "end": { + "line": 11, + "column": 6, + "character": 516 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 519, + "end": 526, + "raw": "\ntext1\n", + "data": "\ntext1\n" + }, + { + "type": "Component", + "start": 526, + "end": 535, + "name": "Comp2", + "name_loc": { + "start": { + "line": 13, + "column": 1, + "character": 527 + }, + "end": { + "line": 13, + "column": 6, + "character": 532 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 535, + "end": 537, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 537, + "end": 591, + "data": " in an inline element: an icon and its caption " + }, + { + "type": "Text", + "start": 591, + "end": 592, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 592, + "end": 627, + "name": "button", + "name_loc": { + "start": { + "line": 16, + "column": 1, + "character": 593 + }, + "end": { + "line": 16, + "column": 7, + "character": 599 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 600, + "end": 602, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 602, + "end": 610, + "name": "Icon", + "name_loc": { + "start": { + "line": 17, + "column": 2, + "character": 603 + }, + "end": { + "line": 17, + "column": 6, + "character": 607 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 610, + "end": 618, + "raw": "\n\ttext1\n", + "data": "\n\ttext1\n" + } + ] + } + }, + { + "type": "Text", + "start": 627, + "end": 629, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 629, + "end": 669, + "data": " in a list item and a table cell " + }, + { + "type": "Text", + "start": 669, + "end": 670, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 670, + "end": 712, + "name": "ul", + "name_loc": { + "start": { + "line": 22, + "column": 1, + "character": 671 + }, + "end": { + "line": 22, + "column": 3, + "character": 673 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 674, + "end": 676, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 676, + "end": 706, + "name": "li", + "name_loc": { + "start": { + "line": 23, + "column": 2, + "character": 677 + }, + "end": { + "line": 23, + "column": 4, + "character": 679 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 680, + "end": 683, + "raw": "\n\t\t", + "data": "\n\t\t" + }, + { + "type": "Component", + "start": 683, + "end": 691, + "name": "Icon", + "name_loc": { + "start": { + "line": 24, + "column": 3, + "character": 684 + }, + "end": { + "line": 24, + "column": 7, + "character": 688 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 691, + "end": 701, + "raw": "\n\t\ttext1\n\t", + "data": "\n\t\ttext1\n\t" + } + ] + } + }, + { + "type": "Text", + "start": 706, + "end": 707, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 712, + "end": 713, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 713, + "end": 803, + "name": "table", + "name_loc": { + "start": { + "line": 28, + "column": 1, + "character": 714 + }, + "end": { + "line": 28, + "column": 6, + "character": 719 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 720, + "end": 722, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 722, + "end": 794, + "name": "tbody", + "name_loc": { + "start": { + "line": 29, + "column": 2, + "character": 723 + }, + "end": { + "line": 29, + "column": 7, + "character": 728 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 729, + "end": 732, + "raw": "\n\t\t", + "data": "\n\t\t" + }, + { + "type": "RegularElement", + "start": 732, + "end": 784, + "name": "tr", + "name_loc": { + "start": { + "line": 30, + "column": 3, + "character": 733 + }, + "end": { + "line": 30, + "column": 5, + "character": 735 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 736, + "end": 740, + "raw": "\n\t\t\t", + "data": "\n\t\t\t" + }, + { + "type": "RegularElement", + "start": 740, + "end": 776, + "name": "td", + "name_loc": { + "start": { + "line": 31, + "column": 4, + "character": 741 + }, + "end": { + "line": 31, + "column": 6, + "character": 743 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 744, + "end": 749, + "raw": "\n\t\t\t\t", + "data": "\n\t\t\t\t" + }, + { + "type": "Component", + "start": 749, + "end": 757, + "name": "Icon", + "name_loc": { + "start": { + "line": 32, + "column": 5, + "character": 750 + }, + "end": { + "line": 32, + "column": 9, + "character": 754 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 757, + "end": 771, + "raw": "\n\t\t\t\ttext1\n\t\t\t", + "data": "\n\t\t\t\ttext1\n\t\t\t" + } + ] + } + }, + { + "type": "Text", + "start": 776, + "end": 779, + "raw": "\n\t\t", + "data": "\n\t\t" + } + ] + } + }, + { + "type": "Text", + "start": 784, + "end": 786, + "raw": "\n\t", + "data": "\n\t" + } + ] + } + }, + { + "type": "Text", + "start": 794, + "end": 795, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 803, + "end": 805, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 805, + "end": 868, + "data": " inside a control-flow block, and inside a snippet body " + }, + { + "type": "Text", + "start": 868, + "end": 869, + "raw": "\n", + "data": "\n" + }, + { + "type": "IfBlock", + "elseif": false, + "start": 869, + "end": 902, + "test": { + "type": "Identifier", + "start": 874, + "end": 878, + "loc": { + "start": { + "line": 40, + "column": 5 + }, + "end": { + "line": 40, + "column": 9 + } + }, + "name": "cond" + }, + "consequent": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 879, + "end": 881, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 881, + "end": 889, + "name": "Icon", + "name_loc": { + "start": { + "line": 41, + "column": 2, + "character": 882 + }, + "end": { + "line": 41, + "column": 6, + "character": 886 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 889, + "end": 897, + "raw": "\n\ttext1\n", + "data": "\n\ttext1\n" + } + ] + }, + "alternate": null + }, + { + "type": "Text", + "start": 902, + "end": 903, + "raw": "\n", + "data": "\n" + }, + { + "type": "SnippetBlock", + "start": 903, + "end": 947, + "expression": { + "type": "Identifier", + "name": "row", + "start": 913, + "end": 916, + "loc": { + "start": { + "line": 44, + "column": 10, + "character": 913 + }, + "end": { + "line": 44, + "column": 13, + "character": 916 + } + } + }, + "parameters": [], + "body": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 919, + "end": 921, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 921, + "end": 929, + "name": "Icon", + "name_loc": { + "start": { + "line": 45, + "column": 2, + "character": 922 + }, + "end": { + "line": 45, + "column": 6, + "character": 926 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 929, + "end": 937, + "raw": "\n\ttext1\n", + "data": "\n\ttext1\n" + } + ] + } + }, + { + "type": "Text", + "start": 947, + "end": 949, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 949, + "end": 1030, + "data": " control: a two-word run in the same inline container is prose, and flows " + }, + { + "type": "Text", + "start": 1030, + "end": 1031, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1031, + "end": 1071, + "name": "button", + "name_loc": { + "start": { + "line": 50, + "column": 1, + "character": 1032 + }, + "end": { + "line": 50, + "column": 7, + "character": 1038 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1039, + "end": 1041, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 1041, + "end": 1049, + "name": "Icon", + "name_loc": { + "start": { + "line": 51, + "column": 2, + "character": 1042 + }, + "end": { + "line": 51, + "column": 6, + "character": 1046 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 1049, + "end": 1062, + "raw": " text1 text2\n", + "data": " text1 text2\n" + } + ] + } + } + ] + }, + "options": null, + "comments": [] +} diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/input.svelte new file mode 100644 index 000000000..8217c0199 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/input.svelte @@ -0,0 +1,52 @@ + + + + +text1 + + + + + + +
    +
  • + + text1 +
  • +
+ + + + + + +
+ + text1 +
+ + +{#if cond} + + text1 +{/if} +{#snippet row()} + + text1 +{/snippet} + + + diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/prettier_variant_newline.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/prettier_variant_newline.svelte new file mode 100644 index 000000000..a8ee95ed4 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/prettier_variant_newline.svelte @@ -0,0 +1,53 @@ + + + + +text1 + + + + + + +
    +
  • + + text1 +
  • +
+ + + + + + +
+ + text1 +
+ + +{#if cond} + + text1 +{/if} +{#snippet row()} + + text1 +{/snippet} + + + diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/variant_space.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/variant_space.svelte new file mode 100644 index 000000000..4be49add2 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/variant_space.svelte @@ -0,0 +1,45 @@ + + + + text1 + + + + + +
    +
  • + text1 +
  • +
+ + + + + + +
+ text1 +
+ + +{#if cond} + text1 +{/if} +{#snippet row()} + text1 +{/snippet} + + + diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/README.md new file mode 100644 index 000000000..3efc8ae14 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/README.md @@ -0,0 +1,29 @@ +# inline_sibling_newline_label_hold_long_prettier_divergence + +The sibling-newline flow rule's prose gate +([inline_sibling_newline_label_hold](../inline_sibling_newline_label_hold_prettier_divergence/)) +at the print-width boundary. A one-word run is a label whose authored newline is held, but its +**space** spelling is still a fill: at 100 chars ` text1` stays inline, and at 101 the +fill wraps at the separator — onto the newline spelling, which is then the held form. Both the +component and the void-element (` text1`, the checkbox-and-caption shape) boundaries +are pinned at 100 and 101, and a tag too wide to fit even alone breaks its attributes instead, +the word staying hugged to the closing delimiter (`/> text1`), as prettier does too. + +## Reason + +Two claims, one of them a divergence. The **wrap** is tsv's print-width-as-hard-limit stance +(the Elements catalog's fill-boundary family): prettier's fill tolerates the 101-char line and +leaves the space spelling as it is, so `prettier_variant_space.svelte` — the space spelling of +every case — is a prettier fixed point, which tsv normalizes to `input.svelte`. The **hold** is +what the wrap lands on: once the fill has written `⏎text1`, the newline is a +one-word run's and the flow rule holds it, so the wrapped form never re-packs when the line +later has room — the same one-way ratchet prettier has at this shape, and the cost the label +hold accepts on purpose. This fixture pins that the width-driven wrap and the authored newline +are one spelling (`input.svelte` is both formatters' fixed point), so the ratchet is the +composition of two pinned rules rather than a third. + +See +[conformance_prettier_svelte.md §Svelte: Elements](../../../../../docs/conformance_prettier_svelte.md#svelte-elements) +(the fill-boundary family) and +[conformance_prettier_svelte.md §Svelte: Inline content block-style](../../../../../docs/conformance_prettier_svelte.md#svelte-inline-content-block-style) +(the prose gate). diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/expected.json new file mode 100644 index 000000000..c0a3c1744 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/expected.json @@ -0,0 +1,606 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 934, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Comment", + "start": 0, + "end": 76, + "data": " 100 chars - fits: the space spelling of a one-word run stays inline " + }, + { + "type": "Text", + "start": 76, + "end": 77, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 77, + "end": 185, + "name": "p", + "name_loc": { + "start": { + "line": 2, + "column": 1, + "character": 78 + }, + "end": { + "line": 2, + "column": 2, + "character": 79 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 80, + "end": 82, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 82, + "end": 174, + "name": "Comp", + "name_loc": { + "start": { + "line": 3, + "column": 2, + "character": 83 + }, + "end": { + "line": 3, + "column": 6, + "character": 87 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 88, + "end": 171, + "name": "prop", + "name_loc": { + "start": { + "line": 3, + "column": 7, + "character": 88 + }, + "end": { + "line": 3, + "column": 11, + "character": 92 + } + }, + "value": [ + { + "start": 94, + "end": 170, + "type": "Text", + "raw": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "data": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 174, + "end": 181, + "raw": " text1\n", + "data": " text1\n" + } + ] + } + }, + { + "type": "Text", + "start": 185, + "end": 187, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 187, + "end": 284, + "data": " 101 chars - overflows: the fill wraps at the separator, onto the held (newline) spelling " + }, + { + "type": "Text", + "start": 284, + "end": 285, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 285, + "end": 395, + "name": "p", + "name_loc": { + "start": { + "line": 7, + "column": 1, + "character": 286 + }, + "end": { + "line": 7, + "column": 2, + "character": 287 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 288, + "end": 290, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 290, + "end": 383, + "name": "Comp", + "name_loc": { + "start": { + "line": 8, + "column": 2, + "character": 291 + }, + "end": { + "line": 8, + "column": 6, + "character": 295 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 296, + "end": 380, + "name": "prop", + "name_loc": { + "start": { + "line": 8, + "column": 7, + "character": 296 + }, + "end": { + "line": 8, + "column": 11, + "character": 300 + } + }, + "value": [ + { + "start": 302, + "end": 379, + "type": "Text", + "raw": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "data": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 383, + "end": 391, + "raw": "\n\ttext1\n", + "data": "\n\ttext1\n" + } + ] + } + }, + { + "type": "Text", + "start": 395, + "end": 397, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 397, + "end": 451, + "data": " the same boundary at a void element: 100 fits " + }, + { + "type": "Text", + "start": 451, + "end": 452, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 452, + "end": 568, + "name": "label", + "name_loc": { + "start": { + "line": 13, + "column": 1, + "character": 453 + }, + "end": { + "line": 13, + "column": 6, + "character": 458 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 459, + "end": 461, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 461, + "end": 553, + "name": "input", + "name_loc": { + "start": { + "line": 14, + "column": 2, + "character": 462 + }, + "end": { + "line": 14, + "column": 7, + "character": 467 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 468, + "end": 483, + "name": "type", + "name_loc": { + "start": { + "line": 14, + "column": 8, + "character": 468 + }, + "end": { + "line": 14, + "column": 12, + "character": 472 + } + }, + "value": [ + { + "start": 474, + "end": 482, + "type": "Text", + "raw": "checkbox", + "data": "checkbox" + } + ] + }, + { + "type": "Attribute", + "start": 484, + "end": 550, + "name": "prop", + "name_loc": { + "start": { + "line": 14, + "column": 24, + "character": 484 + }, + "end": { + "line": 14, + "column": 28, + "character": 488 + } + }, + "value": [ + { + "start": 490, + "end": 549, + "type": "Text", + "raw": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "data": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 553, + "end": 560, + "raw": " text1\n", + "data": " text1\n" + } + ] + } + }, + { + "type": "Text", + "start": 568, + "end": 570, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 570, + "end": 588, + "data": " 101 wraps " + }, + { + "type": "Text", + "start": 588, + "end": 589, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 589, + "end": 707, + "name": "label", + "name_loc": { + "start": { + "line": 18, + "column": 1, + "character": 590 + }, + "end": { + "line": 18, + "column": 6, + "character": 595 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 596, + "end": 598, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 598, + "end": 691, + "name": "input", + "name_loc": { + "start": { + "line": 19, + "column": 2, + "character": 599 + }, + "end": { + "line": 19, + "column": 7, + "character": 604 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 605, + "end": 620, + "name": "type", + "name_loc": { + "start": { + "line": 19, + "column": 8, + "character": 605 + }, + "end": { + "line": 19, + "column": 12, + "character": 609 + } + }, + "value": [ + { + "start": 611, + "end": 619, + "type": "Text", + "raw": "checkbox", + "data": "checkbox" + } + ] + }, + { + "type": "Attribute", + "start": 621, + "end": 688, + "name": "prop", + "name_loc": { + "start": { + "line": 19, + "column": 24, + "character": 621 + }, + "end": { + "line": 19, + "column": 28, + "character": 625 + } + }, + "value": [ + { + "start": 627, + "end": 687, + "type": "Text", + "raw": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "data": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 691, + "end": 699, + "raw": "\n\ttext1\n", + "data": "\n\ttext1\n" + } + ] + } + }, + { + "type": "Text", + "start": 707, + "end": 709, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 709, + "end": 809, + "data": " the tag alone overflows: its attributes break, and the word stays hugged to the closing tag " + }, + { + "type": "Text", + "start": 809, + "end": 810, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 810, + "end": 933, + "name": "p", + "name_loc": { + "start": { + "line": 24, + "column": 1, + "character": 811 + }, + "end": { + "line": 24, + "column": 2, + "character": 812 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 813, + "end": 815, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 815, + "end": 922, + "name": "Comp", + "name_loc": { + "start": { + "line": 25, + "column": 2, + "character": 816 + }, + "end": { + "line": 25, + "column": 6, + "character": 820 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 823, + "end": 918, + "name": "prop", + "name_loc": { + "start": { + "line": 26, + "column": 2, + "character": 823 + }, + "end": { + "line": 26, + "column": 6, + "character": 827 + } + }, + "value": [ + { + "start": 829, + "end": 917, + "type": "Text", + "raw": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "data": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 922, + "end": 929, + "raw": " text1\n", + "data": " text1\n" + } + ] + } + } + ] + }, + "options": null, + "comments": [] +} diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/input.svelte new file mode 100644 index 000000000..55c364b50 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/input.svelte @@ -0,0 +1,28 @@ + +

+ text1 +

+ + +

+ + text1 +

+ + + + + + + + +

+ text1 +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/prettier_variant_space.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/prettier_variant_space.svelte new file mode 100644 index 000000000..790dc2d79 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/prettier_variant_space.svelte @@ -0,0 +1,26 @@ + +

+ text1 +

+ + +

+ text1 +

+ + + + + + + + +

+ text1 +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/README.md new file mode 100644 index 000000000..7bfc9f8f9 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/README.md @@ -0,0 +1,73 @@ +# inline_sibling_newline_label_hold_prettier_divergence + +The sibling-newline flow rule's **prose gate**: a run holding a single word is a label, not +prose, so the authored newlines beside it hold. tsv and prettier agree on every label shape; the +divergence is the flowing controls, where tsv converges the newline and space authorings and +prettier keeps each. + +## Reason + +Design choice — the boundary of the authoring-independence +[inline_sibling_newline_flow](../inline_sibling_newline_flow_prettier_divergence/) argues for. +Flowing means reflowing a run per width into a text `fill`, and a fill needs a phrase: a run is +prose when **one of its text nodes carries two words**. One word beside an element, a void +element, a component or a tag has nothing to reflow, so its newlines are the author's structure — +an icon and its caption, a field and its unit, a label and its value — and are held exactly as a +prose-free run's are. The question is asked of every text node in the **run** (between two +run-bounding siblings), never of the node at the boundary alone: a one-word node that ends a real +sentence flows with the sentence node it belongs to, where a boundary-local test would hold +prettier's own one-word wrap leftovers alone on their lines — the accretion the flow rule exists to +heal. The count is the **most words any one node carries**, never a sum over the run: words in +two different text nodes are separated by a sibling, and that separation is the author's, so two +one-word captions in one run (`text1⏎⏎text2⏎`, an icon-and-caption list) are +two labels and hold — a sum packs every such list the moment it holds two entries. The cost is a +sentence spelled entirely as one-word fragments between siblings, which holds; real prose has a +two-word node somewhere in its run. Two other alternatives were measured and rejected: counting +an expression tag as a word packs a label beside its value (`chars⏎{n}`), and a sentence +heuristic (three words, or `.!?`) holds prettier's own two-word wrap tails. + +## Cases + +Every label shape is held, in the multiline form both formatters keep: a word before a void +element, and one after; a word before an inline element, and one after; a word between two +components; a word beside a `{@render}` tag and beside an `{@html}` tag; a word before a tag, and +one after; an element, a word and a tag; a word ending a run of tags; a word after a tag (the +separator between the two tags holds with it); a one-word tail after an element whose own content +is prose — that content is the element's own run, so the tail's run holds one word; two +one-word captions in one run, beside void elements and beside tags, and a list of three — the +count is per node, so a run of one-word labels is a list of labels however many it holds; and +the count's own cost, stated: a sentence spelled entirely as one-word fragments between siblings +(`text1⏎inline1⏎text2`) is three labels and holds. Real prose has a two-word node +somewhere in its run — the corpus put one such sentence in 1105 files, and holding it returned +the file to its authored bytes. + +## Controls — what still flows + +- **The cliff** — a node carrying two words is prose, and the run flows. Two words is where label + and prose blur, so the boundary is stated on purpose. +- **Run-level, not boundary-local** — `text1 text2 text3 inline1 text4`: the + trailing one-word node ends a sentence, and the run holds it, so it flows with it — and the + phrase reaches every boundary in its run, however far from it + (`text1 text2 inline1 text3 inline2 text4` packs whole). +- **The cliff in a list** — ` text1 text2 text3`: one two-word caption is + prose at the cliff, and it packs the one-word captions in its run with it. That is the cost of + any count at the cliff (a per-node ≥3 was measured to hold prettier's own two-word wrap + tails), stated here so the choice is recorded rather than discovered. + +`prettier_variant_newline.svelte` is the isolated authoring of the four controls — prettier keeps +it stable, tsv normalizes it to `input.svelte`; the label shapes are identical in both files, +which is the point. + +`variant_space.svelte` is the null control: the rule HOLDS an authored newline, it never FORCES +one, so the space spelling of every label shape stays inline under tsv. This file carries the +shapes prettier keeps inline too — beside an element, a void element, a component, a render/html +tag, or inside a content text — so it is dual-stable. The one space spelling it leaves in its +newline form is a whitespace-only space between two **tags**: tsv keeps that packed as well (the +separator defers to its per-width group as a prose run's does), but prettier's bare `line` +between two tags breaks with the container, so that pair is its own fixture, +[inline_sibling_newline_label_hold_tag_pair_space](../inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/). +Without the null control an implementation that always broke a one-word run would satisfy every +other file here. + +See +[conformance_prettier_svelte.md §Svelte: Inline content block-style](../../../../../docs/conformance_prettier_svelte.md#svelte-inline-content-block-style). diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/expected.json new file mode 100644 index 000000000..b36341941 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/expected.json @@ -0,0 +1,2218 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 3351, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Comment", + "start": 0, + "end": 806, + "data": "\n\tA run holding a single word is a LABEL, not prose. Flowing means reflowing a run per width,\n\tand a fill needs a phrase to reflow into: the run's prose is the most words any ONE of its\n\ttext nodes carries, so one word beside an element, a void element, a component or a tag is a\n\tcaption beside its icon, a field beside its unit, a label beside its value — and the\n\tsibling-newline flow rule holds the authored newlines beside it exactly as it holds a\n\tprose-free run's. The count is asked of the RUN (every content text between two run-bounding\n\tsiblings), never of the node at the boundary alone: a one-word node that ends a real sentence\n\tflows with it.\n\tPrettier holds every label shape too, so those agree; the flowing controls are where tsv\n\tconverges the authorings and prettier keeps each.\n" + }, + { + "type": "Text", + "start": 806, + "end": 808, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 808, + "end": 867, + "data": " a word before a void element, and a word after one " + }, + { + "type": "Text", + "start": 867, + "end": 868, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 868, + "end": 915, + "name": "label", + "name_loc": { + "start": { + "line": 15, + "column": 1, + "character": 869 + }, + "end": { + "line": 15, + "column": 6, + "character": 874 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 875, + "end": 884, + "raw": "\n\ttext1\n\t", + "data": "\n\ttext1\n\t" + }, + { + "type": "RegularElement", + "start": 884, + "end": 906, + "name": "input", + "name_loc": { + "start": { + "line": 17, + "column": 2, + "character": 885 + }, + "end": { + "line": 17, + "column": 7, + "character": 890 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 891, + "end": 903, + "name": "type", + "name_loc": { + "start": { + "line": 17, + "column": 8, + "character": 891 + }, + "end": { + "line": 17, + "column": 12, + "character": 895 + } + }, + "value": [ + { + "start": 897, + "end": 902, + "type": "Text", + "raw": "range", + "data": "range" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 906, + "end": 907, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 915, + "end": 916, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 916, + "end": 966, + "name": "label", + "name_loc": { + "start": { + "line": 19, + "column": 1, + "character": 917 + }, + "end": { + "line": 19, + "column": 6, + "character": 922 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 923, + "end": 925, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 925, + "end": 950, + "name": "input", + "name_loc": { + "start": { + "line": 20, + "column": 2, + "character": 926 + }, + "end": { + "line": 20, + "column": 7, + "character": 931 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 932, + "end": 947, + "name": "type", + "name_loc": { + "start": { + "line": 20, + "column": 8, + "character": 932 + }, + "end": { + "line": 20, + "column": 12, + "character": 936 + } + }, + "value": [ + { + "start": 938, + "end": 946, + "type": "Text", + "raw": "checkbox", + "data": "checkbox" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 950, + "end": 958, + "raw": "\n\ttext1\n", + "data": "\n\ttext1\n" + } + ] + } + }, + { + "type": "Text", + "start": 966, + "end": 968, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 968, + "end": 1030, + "data": " a word before an inline element, and a word after one " + }, + { + "type": "Text", + "start": 1030, + "end": 1031, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1031, + "end": 1068, + "name": "p", + "name_loc": { + "start": { + "line": 25, + "column": 1, + "character": 1032 + }, + "end": { + "line": 25, + "column": 2, + "character": 1033 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1034, + "end": 1043, + "raw": "\n\ttext1\n\t", + "data": "\n\ttext1\n\t" + }, + { + "type": "RegularElement", + "start": 1043, + "end": 1063, + "name": "span", + "name_loc": { + "start": { + "line": 27, + "column": 2, + "character": 1044 + }, + "end": { + "line": 27, + "column": 6, + "character": 1048 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1049, + "end": 1056, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 1063, + "end": 1064, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1068, + "end": 1069, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1069, + "end": 1106, + "name": "p", + "name_loc": { + "start": { + "line": 29, + "column": 1, + "character": 1070 + }, + "end": { + "line": 29, + "column": 2, + "character": 1071 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1072, + "end": 1074, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1074, + "end": 1094, + "name": "span", + "name_loc": { + "start": { + "line": 30, + "column": 2, + "character": 1075 + }, + "end": { + "line": 30, + "column": 6, + "character": 1079 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1080, + "end": 1087, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 1094, + "end": 1102, + "raw": "\n\ttext1\n", + "data": "\n\ttext1\n" + } + ] + } + }, + { + "type": "Text", + "start": 1106, + "end": 1108, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1108, + "end": 1146, + "data": " a word between two components " + }, + { + "type": "Text", + "start": 1146, + "end": 1147, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1147, + "end": 1184, + "name": "p", + "name_loc": { + "start": { + "line": 35, + "column": 1, + "character": 1148 + }, + "end": { + "line": 35, + "column": 2, + "character": 1149 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1150, + "end": 1152, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 1152, + "end": 1161, + "name": "Comp1", + "name_loc": { + "start": { + "line": 36, + "column": 2, + "character": 1153 + }, + "end": { + "line": 36, + "column": 7, + "character": 1158 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 1161, + "end": 1170, + "raw": "\n\ttext1\n\t", + "data": "\n\ttext1\n\t" + }, + { + "type": "Component", + "start": 1170, + "end": 1179, + "name": "Comp2", + "name_loc": { + "start": { + "line": 38, + "column": 2, + "character": 1171 + }, + "end": { + "line": 38, + "column": 7, + "character": 1176 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 1179, + "end": 1180, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1184, + "end": 1186, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1186, + "end": 1245, + "data": " a word beside a render tag, and beside an html tag " + }, + { + "type": "Text", + "start": 1245, + "end": 1246, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1246, + "end": 1279, + "name": "p", + "name_loc": { + "start": { + "line": 42, + "column": 1, + "character": 1247 + }, + "end": { + "line": 42, + "column": 2, + "character": 1248 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1249, + "end": 1251, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RenderTag", + "start": 1251, + "end": 1267, + "expression": { + "type": "CallExpression", + "start": 1260, + "end": 1266, + "loc": { + "start": { + "line": 43, + "column": 10 + }, + "end": { + "line": 43, + "column": 16 + } + }, + "callee": { + "type": "Identifier", + "start": 1260, + "end": 1264, + "loc": { + "start": { + "line": 43, + "column": 10 + }, + "end": { + "line": 43, + "column": 14 + } + }, + "name": "icon" + }, + "arguments": [], + "optional": false + } + }, + { + "type": "Text", + "start": 1267, + "end": 1275, + "raw": "\n\ttext1\n", + "data": "\n\ttext1\n" + } + ] + } + }, + { + "type": "Text", + "start": 1279, + "end": 1280, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1280, + "end": 1308, + "name": "p", + "name_loc": { + "start": { + "line": 46, + "column": 1, + "character": 1281 + }, + "end": { + "line": 46, + "column": 2, + "character": 1282 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1283, + "end": 1285, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "HtmlTag", + "start": 1285, + "end": 1296, + "expression": { + "type": "Identifier", + "start": 1292, + "end": 1295, + "loc": { + "start": { + "line": 47, + "column": 8 + }, + "end": { + "line": 47, + "column": 11 + } + }, + "name": "svg" + } + }, + { + "type": "Text", + "start": 1296, + "end": 1304, + "raw": "\n\ttext1\n", + "data": "\n\ttext1\n" + } + ] + } + }, + { + "type": "Text", + "start": 1308, + "end": 1310, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1310, + "end": 1435, + "data": " a word before a tag, and a word after one: a tag renders as a value, and a label beside it\n holds like any other " + }, + { + "type": "Text", + "start": 1435, + "end": 1436, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1436, + "end": 1459, + "name": "p", + "name_loc": { + "start": { + "line": 53, + "column": 1, + "character": 1437 + }, + "end": { + "line": 53, + "column": 2, + "character": 1438 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1439, + "end": 1448, + "raw": "\n\ttext1\n\t", + "data": "\n\ttext1\n\t" + }, + { + "type": "ExpressionTag", + "start": 1448, + "end": 1454, + "expression": { + "type": "Identifier", + "start": 1449, + "end": 1453, + "loc": { + "start": { + "line": 55, + "column": 2 + }, + "end": { + "line": 55, + "column": 6 + } + }, + "name": "expr" + } + }, + { + "type": "Text", + "start": 1454, + "end": 1455, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1459, + "end": 1460, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1460, + "end": 1483, + "name": "p", + "name_loc": { + "start": { + "line": 57, + "column": 1, + "character": 1461 + }, + "end": { + "line": 57, + "column": 2, + "character": 1462 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1463, + "end": 1465, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 1465, + "end": 1471, + "expression": { + "type": "Identifier", + "start": 1466, + "end": 1470, + "loc": { + "start": { + "line": 58, + "column": 2 + }, + "end": { + "line": 58, + "column": 6 + } + }, + "name": "expr" + } + }, + { + "type": "Text", + "start": 1471, + "end": 1479, + "raw": "\n\ttext1\n", + "data": "\n\ttext1\n" + } + ] + } + }, + { + "type": "Text", + "start": 1483, + "end": 1485, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1485, + "end": 1551, + "data": " an element, a word and a tag; a word ending a run of tags " + }, + { + "type": "Text", + "start": 1551, + "end": 1552, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1552, + "end": 1585, + "name": "p", + "name_loc": { + "start": { + "line": 63, + "column": 1, + "character": 1553 + }, + "end": { + "line": 63, + "column": 2, + "character": 1554 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1555, + "end": 1557, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 1557, + "end": 1565, + "name": "Comp", + "name_loc": { + "start": { + "line": 64, + "column": 2, + "character": 1558 + }, + "end": { + "line": 64, + "column": 6, + "character": 1562 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 1565, + "end": 1574, + "raw": "\n\ttext1\n\t", + "data": "\n\ttext1\n\t" + }, + { + "type": "ExpressionTag", + "start": 1574, + "end": 1580, + "expression": { + "type": "Identifier", + "start": 1575, + "end": 1579, + "loc": { + "start": { + "line": 66, + "column": 2 + }, + "end": { + "line": 66, + "column": 6 + } + }, + "name": "expr" + } + }, + { + "type": "Text", + "start": 1580, + "end": 1581, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1585, + "end": 1586, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1586, + "end": 1627, + "name": "p", + "name_loc": { + "start": { + "line": 68, + "column": 1, + "character": 1587 + }, + "end": { + "line": 68, + "column": 2, + "character": 1588 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1589, + "end": 1591, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 1591, + "end": 1598, + "expression": { + "type": "Identifier", + "start": 1592, + "end": 1597, + "loc": { + "start": { + "line": 69, + "column": 2 + }, + "end": { + "line": 69, + "column": 7 + } + }, + "name": "expr1" + } + }, + { + "type": "Text", + "start": 1598, + "end": 1600, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 1600, + "end": 1607, + "expression": { + "type": "Identifier", + "start": 1601, + "end": 1606, + "loc": { + "start": { + "line": 70, + "column": 2 + }, + "end": { + "line": 70, + "column": 7 + } + }, + "name": "expr2" + } + }, + { + "type": "Text", + "start": 1607, + "end": 1609, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 1609, + "end": 1616, + "expression": { + "type": "Identifier", + "start": 1610, + "end": 1615, + "loc": { + "start": { + "line": 71, + "column": 2 + }, + "end": { + "line": 71, + "column": 7 + } + }, + "name": "expr3" + } + }, + { + "type": "Text", + "start": 1616, + "end": 1623, + "raw": " text1\n", + "data": " text1\n" + } + ] + } + }, + { + "type": "Text", + "start": 1627, + "end": 1629, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1629, + "end": 1706, + "data": " a word after a tag: the separator between the two tags holds with it " + }, + { + "type": "Text", + "start": 1706, + "end": 1707, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1707, + "end": 1739, + "name": "p", + "name_loc": { + "start": { + "line": 75, + "column": 1, + "character": 1708 + }, + "end": { + "line": 75, + "column": 2, + "character": 1709 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1710, + "end": 1718, + "raw": "\n\ttext1 ", + "data": "\n\ttext1 " + }, + { + "type": "ExpressionTag", + "start": 1718, + "end": 1725, + "expression": { + "type": "Identifier", + "start": 1719, + "end": 1724, + "loc": { + "start": { + "line": 76, + "column": 8 + }, + "end": { + "line": 76, + "column": 13 + } + }, + "name": "expr1" + } + }, + { + "type": "Text", + "start": 1725, + "end": 1727, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 1727, + "end": 1734, + "expression": { + "type": "Identifier", + "start": 1728, + "end": 1733, + "loc": { + "start": { + "line": 77, + "column": 2 + }, + "end": { + "line": 77, + "column": 7 + } + }, + "name": "expr2" + } + }, + { + "type": "Text", + "start": 1734, + "end": 1735, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1739, + "end": 1741, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1741, + "end": 1887, + "data": " a one-word tail after an element whose own content is prose: that content is the element's\n own run, so the tail's run holds one word " + }, + { + "type": "Text", + "start": 1887, + "end": 1888, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1888, + "end": 1947, + "name": "p", + "name_loc": { + "start": { + "line": 82, + "column": 1, + "character": 1889 + }, + "end": { + "line": 82, + "column": 2, + "character": 1890 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1891, + "end": 1893, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1893, + "end": 1928, + "name": "a", + "name_loc": { + "start": { + "line": 83, + "column": 2, + "character": 1894 + }, + "end": { + "line": 83, + "column": 3, + "character": 1895 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 1896, + "end": 1908, + "name": "href", + "name_loc": { + "start": { + "line": 83, + "column": 4, + "character": 1896 + }, + "end": { + "line": 83, + "column": 8, + "character": 1900 + } + }, + "value": [ + { + "start": 1902, + "end": 1907, + "type": "Text", + "raw": "/path", + "data": "/path" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1909, + "end": 1924, + "raw": "inline1 inline2", + "data": "inline1 inline2" + } + ] + } + }, + { + "type": "Text", + "start": 1928, + "end": 1936, + "raw": "\n\ttext1 ", + "data": "\n\ttext1 " + }, + { + "type": "ExpressionTag", + "start": 1936, + "end": 1942, + "expression": { + "type": "Identifier", + "start": 1937, + "end": 1941, + "loc": { + "start": { + "line": 84, + "column": 8 + }, + "end": { + "line": 84, + "column": 12 + } + }, + "name": "expr" + } + }, + { + "type": "Text", + "start": 1942, + "end": 1943, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1947, + "end": 1949, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1949, + "end": 2032, + "data": " control: the cliff — a node carrying two words is prose, and the run flows " + }, + { + "type": "Text", + "start": 2032, + "end": 2033, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 2033, + "end": 2070, + "name": "p", + "name_loc": { + "start": { + "line": 88, + "column": 1, + "character": 2034 + }, + "end": { + "line": 88, + "column": 2, + "character": 2035 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 2036, + "end": 2038, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 2038, + "end": 2046, + "name": "Comp", + "name_loc": { + "start": { + "line": 89, + "column": 2, + "character": 2039 + }, + "end": { + "line": 89, + "column": 6, + "character": 2043 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 2046, + "end": 2059, + "raw": " text1 text2 ", + "data": " text1 text2 " + }, + { + "type": "ExpressionTag", + "start": 2059, + "end": 2065, + "expression": { + "type": "Identifier", + "start": 2060, + "end": 2064, + "loc": { + "start": { + "line": 89, + "column": 23 + }, + "end": { + "line": 89, + "column": 27 + } + }, + "name": "expr" + } + }, + { + "type": "Text", + "start": 2065, + "end": 2066, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 2070, + "end": 2072, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 2072, + "end": 2169, + "data": " control: run-level, not boundary-local — a one-word node ending a sentence flows with it " + }, + { + "type": "Text", + "start": 2169, + "end": 2170, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 2170, + "end": 2224, + "name": "p", + "name_loc": { + "start": { + "line": 93, + "column": 1, + "character": 2171 + }, + "end": { + "line": 93, + "column": 2, + "character": 2172 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 2173, + "end": 2193, + "raw": "\n\ttext1 text2 text3 ", + "data": "\n\ttext1 text2 text3 " + }, + { + "type": "RegularElement", + "start": 2193, + "end": 2213, + "name": "span", + "name_loc": { + "start": { + "line": 94, + "column": 20, + "character": 2194 + }, + "end": { + "line": 94, + "column": 24, + "character": 2198 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 2199, + "end": 2206, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 2213, + "end": 2220, + "raw": " text4\n", + "data": " text4\n" + } + ] + } + }, + { + "type": "Text", + "start": 2224, + "end": 2226, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 2226, + "end": 2324, + "data": " control: run-level reaches every boundary in the run, however far it sits from the phrase " + }, + { + "type": "Text", + "start": 2324, + "end": 2325, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 2325, + "end": 2400, + "name": "p", + "name_loc": { + "start": { + "line": 98, + "column": 1, + "character": 2326 + }, + "end": { + "line": 98, + "column": 2, + "character": 2327 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 2328, + "end": 2342, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " + }, + { + "type": "RegularElement", + "start": 2342, + "end": 2362, + "name": "span", + "name_loc": { + "start": { + "line": 99, + "column": 14, + "character": 2343 + }, + "end": { + "line": 99, + "column": 18, + "character": 2347 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 2348, + "end": 2355, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 2362, + "end": 2369, + "raw": " text3 ", + "data": " text3 " + }, + { + "type": "RegularElement", + "start": 2369, + "end": 2389, + "name": "span", + "name_loc": { + "start": { + "line": 99, + "column": 41, + "character": 2370 + }, + "end": { + "line": 99, + "column": 45, + "character": 2374 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 2375, + "end": 2382, + "raw": "inline2", + "data": "inline2" + } + ] + } + }, + { + "type": "Text", + "start": 2389, + "end": 2396, + "raw": " text4\n", + "data": " text4\n" + } + ] + } + }, + { + "type": "Text", + "start": 2400, + "end": 2402, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 2402, + "end": 2593, + "data": " control: the cliff in a list — one two-word caption is prose, and it packs the one-word\n captions in its run with it: the cost of any count at the cliff, stated so it is recorded " + }, + { + "type": "Text", + "start": 2593, + "end": 2594, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 2594, + "end": 2645, + "name": "div", + "name_loc": { + "start": { + "line": 104, + "column": 1, + "character": 2595 + }, + "end": { + "line": 104, + "column": 4, + "character": 2598 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 2599, + "end": 2601, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 2601, + "end": 2610, + "name": "Comp1", + "name_loc": { + "start": { + "line": 105, + "column": 2, + "character": 2602 + }, + "end": { + "line": 105, + "column": 7, + "character": 2607 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 2610, + "end": 2617, + "raw": " text1 ", + "data": " text1 " + }, + { + "type": "Component", + "start": 2617, + "end": 2626, + "name": "Comp2", + "name_loc": { + "start": { + "line": 105, + "column": 18, + "character": 2618 + }, + "end": { + "line": 105, + "column": 23, + "character": 2623 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 2626, + "end": 2639, + "raw": " text2 text3\n", + "data": " text2 text3\n" + } + ] + } + }, + { + "type": "Text", + "start": 2645, + "end": 2647, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 2647, + "end": 2836, + "data": " two one-word captions in one run, and one-word fragments between tags: the count is the most\n words any ONE node carries, never a sum over the run, so these are labels and hold " + }, + { + "type": "Text", + "start": 2836, + "end": 2837, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 2837, + "end": 2915, + "name": "label", + "name_loc": { + "start": { + "line": 110, + "column": 1, + "character": 2838 + }, + "end": { + "line": 110, + "column": 6, + "character": 2843 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 2844, + "end": 2853, + "raw": "\n\ttext1\n\t", + "data": "\n\ttext1\n\t" + }, + { + "type": "RegularElement", + "start": 2853, + "end": 2875, + "name": "input", + "name_loc": { + "start": { + "line": 112, + "column": 2, + "character": 2854 + }, + "end": { + "line": 112, + "column": 7, + "character": 2859 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 2860, + "end": 2872, + "name": "type", + "name_loc": { + "start": { + "line": 112, + "column": 8, + "character": 2860 + }, + "end": { + "line": 112, + "column": 12, + "character": 2864 + } + }, + "value": [ + { + "start": 2866, + "end": 2871, + "type": "Text", + "raw": "range", + "data": "range" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 2875, + "end": 2884, + "raw": "\n\ttext2\n\t", + "data": "\n\ttext2\n\t" + }, + { + "type": "RegularElement", + "start": 2884, + "end": 2906, + "name": "input", + "name_loc": { + "start": { + "line": 114, + "column": 2, + "character": 2885 + }, + "end": { + "line": 114, + "column": 7, + "character": 2890 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 2891, + "end": 2903, + "name": "type", + "name_loc": { + "start": { + "line": 114, + "column": 8, + "character": 2891 + }, + "end": { + "line": 114, + "column": 12, + "character": 2895 + } + }, + "value": [ + { + "start": 2897, + "end": 2902, + "type": "Text", + "raw": "range", + "data": "range" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 2906, + "end": 2907, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 2915, + "end": 2916, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 2916, + "end": 2956, + "name": "p", + "name_loc": { + "start": { + "line": 116, + "column": 1, + "character": 2917 + }, + "end": { + "line": 116, + "column": 2, + "character": 2918 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 2919, + "end": 2928, + "raw": "\n\ttext1\n\t", + "data": "\n\ttext1\n\t" + }, + { + "type": "ExpressionTag", + "start": 2928, + "end": 2935, + "expression": { + "type": "Identifier", + "start": 2929, + "end": 2934, + "loc": { + "start": { + "line": 118, + "column": 2 + }, + "end": { + "line": 118, + "column": 7 + } + }, + "name": "expr1" + } + }, + { + "type": "Text", + "start": 2935, + "end": 2944, + "raw": "\n\ttext2\n\t", + "data": "\n\ttext2\n\t" + }, + { + "type": "ExpressionTag", + "start": 2944, + "end": 2951, + "expression": { + "type": "Identifier", + "start": 2945, + "end": 2950, + "loc": { + "start": { + "line": 120, + "column": 2 + }, + "end": { + "line": 120, + "column": 7 + } + }, + "name": "expr2" + } + }, + { + "type": "Text", + "start": 2951, + "end": 2952, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 2956, + "end": 2958, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 2958, + "end": 3045, + "data": " a list of one-word captions holds however many it holds: the count is per node " + }, + { + "type": "Text", + "start": 3045, + "end": 3046, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 3046, + "end": 3112, + "name": "div", + "name_loc": { + "start": { + "line": 124, + "column": 1, + "character": 3047 + }, + "end": { + "line": 124, + "column": 4, + "character": 3050 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 3051, + "end": 3053, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 3053, + "end": 3062, + "name": "Comp1", + "name_loc": { + "start": { + "line": 125, + "column": 2, + "character": 3054 + }, + "end": { + "line": 125, + "column": 7, + "character": 3059 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 3062, + "end": 3071, + "raw": "\n\ttext1\n\t", + "data": "\n\ttext1\n\t" + }, + { + "type": "Component", + "start": 3071, + "end": 3080, + "name": "Comp2", + "name_loc": { + "start": { + "line": 127, + "column": 2, + "character": 3072 + }, + "end": { + "line": 127, + "column": 7, + "character": 3077 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 3080, + "end": 3089, + "raw": "\n\ttext2\n\t", + "data": "\n\ttext2\n\t" + }, + { + "type": "Component", + "start": 3089, + "end": 3098, + "name": "Comp3", + "name_loc": { + "start": { + "line": 129, + "column": 2, + "character": 3090 + }, + "end": { + "line": 129, + "column": 7, + "character": 3095 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 3098, + "end": 3106, + "raw": "\n\ttext3\n", + "data": "\n\ttext3\n" + } + ] + } + }, + { + "type": "Text", + "start": 3112, + "end": 3114, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 3114, + "end": 3305, + "data": " the count's own cost, stated: a sentence spelled entirely as one-word fragments between\n siblings is three labels, and holds — real prose has a two-word node somewhere in its run " + }, + { + "type": "Text", + "start": 3305, + "end": 3306, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 3306, + "end": 3350, + "name": "p", + "name_loc": { + "start": { + "line": 135, + "column": 1, + "character": 3307 + }, + "end": { + "line": 135, + "column": 2, + "character": 3308 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 3309, + "end": 3318, + "raw": "\n\ttext1\n\t", + "data": "\n\ttext1\n\t" + }, + { + "type": "RegularElement", + "start": 3318, + "end": 3338, + "name": "span", + "name_loc": { + "start": { + "line": 137, + "column": 2, + "character": 3319 + }, + "end": { + "line": 137, + "column": 6, + "character": 3323 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 3324, + "end": 3331, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 3338, + "end": 3346, + "raw": "\n\ttext2\n", + "data": "\n\ttext2\n" + } + ] + } + } + ] + }, + "options": null, + "comments": [] +} diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/input.svelte new file mode 100644 index 000000000..9129ac530 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/input.svelte @@ -0,0 +1,139 @@ + + + + + + + +

+ text1 + inline1 +

+

+ inline1 + text1 +

+ + +

+ + text1 + +

+ + +

+ {@render icon()} + text1 +

+

+ {@html svg} + text1 +

+ + +

+ text1 + {expr} +

+

+ {expr} + text1 +

+ + +

+ + text1 + {expr} +

+

+ {expr1} + {expr2} + {expr3} text1 +

+ + +

+ text1 {expr1} + {expr2} +

+ + +

+ inline1 inline2 + text1 {expr} +

+ + +

+ text1 text2 {expr} +

+ + +

+ text1 text2 text3 inline1 text4 +

+ + +

+ text1 text2 inline1 text3 inline2 text4 +

+ + +
+ text1 text2 text3 +
+ + + +

+ text1 + {expr1} + text2 + {expr2} +

+ + +
+ + text1 + + text2 + + text3 +
+ + +

+ text1 + inline1 + text2 +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/prettier_variant_newline.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/prettier_variant_newline.svelte new file mode 100644 index 000000000..99d5ea5d0 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/prettier_variant_newline.svelte @@ -0,0 +1,149 @@ + + + + + + + +

+ text1 + inline1 +

+

+ inline1 + text1 +

+ + +

+ + text1 + +

+ + +

+ {@render icon()} + text1 +

+

+ {@html svg} + text1 +

+ + +

+ text1 + {expr} +

+

+ {expr} + text1 +

+ + +

+ + text1 + {expr} +

+

+ {expr1} + {expr2} + {expr3} text1 +

+ + +

+ text1 {expr1} + {expr2} +

+ + +

+ inline1 inline2 + text1 {expr} +

+ + +

+ + text1 text2 + {expr} +

+ + +

+ text1 text2 text3 inline1 + text4 +

+ + +

+ text1 text2 + inline1 + text3 + inline2 + text4 +

+ + +
+ + text1 + + text2 text3 +
+ + + +

+ text1 + {expr1} + text2 + {expr2} +

+ + +
+ + text1 + + text2 + + text3 +
+ + +

+ text1 + inline1 + text2 +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/variant_space.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/variant_space.svelte new file mode 100644 index 000000000..25a4d5330 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/variant_space.svelte @@ -0,0 +1,113 @@ + + + + + + + +

+ text1 inline1 +

+

+ inline1 text1 +

+ + +

+ text1 +

+ + +

+ {@render icon()} text1 +

+

+ {@html svg} text1 +

+ + +

+ text1 {expr} +

+

+ {expr} text1 +

+ + +

+ text1 {expr} +

+

+ {expr1} + {expr2} + {expr3} text1 +

+ + +

+ text1 {expr1} + {expr2} +

+ + +

+ inline1 inline2 text1 {expr} +

+ + +

+ text1 text2 {expr} +

+ + +

+ text1 text2 text3 inline1 text4 +

+ + +

+ text1 text2 inline1 text3 inline2 text4 +

+ + +
+ text1 text2 text3 +
+ + + +

+ text1 {expr1} text2 {expr2} +

+ + +
+ text1 text2 text3 +
+ + +

+ text1 inline1 text2 +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/README.md new file mode 100644 index 000000000..7fd908055 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/README.md @@ -0,0 +1,33 @@ +# inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence + +The sibling-newline flow rule's prose gate +([inline_sibling_newline_label_hold](../inline_sibling_newline_label_hold_prettier_divergence/)) +is a **hold**, never a forced break: it decides whether an authored newline may reflow, and says +nothing about an authored space. A one-word run holds its authored lines, but its SPACE-spelled +tag pair — `text1 {expr1} {expr2}`, `{expr1} {expr2} {expr3} text1`, and the escaped-angle-bracket +pair at the root — is a fill like a prose run's and packs per width. Prettier's bare `line` +between two tags breaks with the multiline container whatever the run holds, so it splits every +one of them (`output_prettier.svelte`). The control is a prose-free pair (`{expr1} {expr2}`, no +content text at all), which breaks with the container under both formatters +(`unformatted_ours_space.svelte`: tsv normalizes its space spelling to the split `input.svelte` +form, prettier to `output_prettier.svelte`). + +## Reason + +Design choice. The whitespace-only separator before a tag asks two different run gates for its two +spellings. The NEWLINE spelling asks the prose gate — is there a phrase to reflow into — and a +one-word run says no, so the newline is held as the author's structure. The SPACE spelling asks +only whether the run holds a content text at all — is there a fill for this separator to sit in — +so it defers to the next tag's per-width group and the pair packs, exactly as a prose run's does +([inline_adjacent_sibling_newline_flow](../inline_adjacent_sibling_newline_flow_prettier_divergence/), +the prose tag pair prettier also splits). Gating the space on the prose count would turn the hold +into a forced break — `Price: {currency} {amount}` authored on one line coming out on two — a +layout keyed on nothing the author wrote. Only a run with no content text keeps the bare `line` +that breaks with the container: with no fill at all, its authored spacing is the only structure it +has, and both formatters agree (the tag-separator rule of +[inline_content_spaced_tags_long](../inline_content_spaced_tags_long_prettier_divergence/)). +Both arms of the separator site read the same run fact, so a width-broken container and a +newline-authored one lay the pair out identically. + +See +[conformance_prettier_svelte.md §Svelte: Inline content block-style](../../../../../docs/conformance_prettier_svelte.md#svelte-inline-content-block-style). diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/expected.json new file mode 100644 index 000000000..9ccb8b098 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/expected.json @@ -0,0 +1,536 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 912, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Comment", + "start": 0, + "end": 420, + "data": "\n\tThe sibling-newline flow rule's prose gate is a HOLD on an authored newline, never a forced\n\tone: a one-word run keeps its authored lines, but its SPACE-spelled tag pair is a fill like a\n\tprose run's and packs per width, where prettier's bare `line` between two tags breaks with\n\tthe multiline container. Only a prose-FREE run's tag pair — no content text at all — takes\n\tthat break under tsv too: the control.\n" + }, + { + "type": "Text", + "start": 420, + "end": 422, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 422, + "end": 484, + "data": " a word before a spaced tag pair, and a word after one " + }, + { + "type": "Text", + "start": 484, + "end": 485, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 485, + "end": 516, + "name": "p", + "name_loc": { + "start": { + "line": 10, + "column": 1, + "character": 486 + }, + "end": { + "line": 10, + "column": 2, + "character": 487 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 488, + "end": 496, + "raw": "\n\ttext1 ", + "data": "\n\ttext1 " + }, + { + "type": "ExpressionTag", + "start": 496, + "end": 503, + "expression": { + "type": "Identifier", + "start": 497, + "end": 502, + "loc": { + "start": { + "line": 11, + "column": 8 + }, + "end": { + "line": 11, + "column": 13 + } + }, + "name": "expr1" + } + }, + { + "type": "Text", + "start": 503, + "end": 504, + "raw": " ", + "data": " " + }, + { + "type": "ExpressionTag", + "start": 504, + "end": 511, + "expression": { + "type": "Identifier", + "start": 505, + "end": 510, + "loc": { + "start": { + "line": 11, + "column": 16 + }, + "end": { + "line": 11, + "column": 21 + } + }, + "name": "expr2" + } + }, + { + "type": "Text", + "start": 511, + "end": 512, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 516, + "end": 517, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 517, + "end": 556, + "name": "p", + "name_loc": { + "start": { + "line": 13, + "column": 1, + "character": 518 + }, + "end": { + "line": 13, + "column": 2, + "character": 519 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 520, + "end": 522, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 522, + "end": 529, + "expression": { + "type": "Identifier", + "start": 523, + "end": 528, + "loc": { + "start": { + "line": 14, + "column": 2 + }, + "end": { + "line": 14, + "column": 7 + } + }, + "name": "expr1" + } + }, + { + "type": "Text", + "start": 529, + "end": 530, + "raw": " ", + "data": " " + }, + { + "type": "ExpressionTag", + "start": 530, + "end": 537, + "expression": { + "type": "Identifier", + "start": 531, + "end": 536, + "loc": { + "start": { + "line": 14, + "column": 10 + }, + "end": { + "line": 14, + "column": 15 + } + }, + "name": "expr2" + } + }, + { + "type": "Text", + "start": 537, + "end": 538, + "raw": " ", + "data": " " + }, + { + "type": "ExpressionTag", + "start": 538, + "end": 545, + "expression": { + "type": "Identifier", + "start": 539, + "end": 544, + "loc": { + "start": { + "line": 14, + "column": 18 + }, + "end": { + "line": 14, + "column": 23 + } + }, + "name": "expr3" + } + }, + { + "type": "Text", + "start": 545, + "end": 552, + "raw": " text1\n", + "data": " text1\n" + } + ] + } + }, + { + "type": "Text", + "start": 556, + "end": 558, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 558, + "end": 716, + "data": " at the root, which is always a multiline container: the escaped-angle-bracket pair, whose\n glued `div` / `code` words make the run a one-word run " + }, + { + "type": "Text", + "start": 716, + "end": 717, + "raw": "\n", + "data": "\n" + }, + { + "type": "ExpressionTag", + "start": 717, + "end": 722, + "expression": { + "type": "Literal", + "start": 718, + "end": 721, + "loc": { + "start": { + "line": 19, + "column": 1 + }, + "end": { + "line": 19, + "column": 4 + } + }, + "value": "<", + "raw": "'<'" + } + }, + { + "type": "Text", + "start": 722, + "end": 725, + "raw": "div", + "data": "div" + }, + { + "type": "ExpressionTag", + "start": 725, + "end": 730, + "expression": { + "type": "Literal", + "start": 726, + "end": 729, + "loc": { + "start": { + "line": 19, + "column": 9 + }, + "end": { + "line": 19, + "column": 12 + } + }, + "value": ">", + "raw": "'>'" + } + }, + { + "type": "Text", + "start": 730, + "end": 731, + "raw": " ", + "data": " " + }, + { + "type": "ExpressionTag", + "start": 731, + "end": 736, + "expression": { + "type": "Literal", + "start": 732, + "end": 735, + "loc": { + "start": { + "line": 19, + "column": 15 + }, + "end": { + "line": 19, + "column": 18 + } + }, + "value": "<", + "raw": "'<'" + } + }, + { + "type": "Text", + "start": 736, + "end": 740, + "raw": "code", + "data": "code" + }, + { + "type": "ExpressionTag", + "start": 740, + "end": 745, + "expression": { + "type": "Literal", + "start": 741, + "end": 744, + "loc": { + "start": { + "line": 19, + "column": 24 + }, + "end": { + "line": 19, + "column": 27 + } + }, + "value": ">", + "raw": "'>'" + } + }, + { + "type": "ExpressionTag", + "start": 745, + "end": 750, + "expression": { + "type": "Literal", + "start": 746, + "end": 749, + "loc": { + "start": { + "line": 19, + "column": 29 + }, + "end": { + "line": 19, + "column": 32 + } + }, + "value": "<", + "raw": "'<'" + } + }, + { + "type": "Text", + "start": 750, + "end": 755, + "raw": "/code", + "data": "/code" + }, + { + "type": "ExpressionTag", + "start": 755, + "end": 760, + "expression": { + "type": "Literal", + "start": 756, + "end": 759, + "loc": { + "start": { + "line": 19, + "column": 39 + }, + "end": { + "line": 19, + "column": 42 + } + }, + "value": ">", + "raw": "'>'" + } + }, + { + "type": "Text", + "start": 760, + "end": 762, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 762, + "end": 884, + "data": " control: a prose-free pair has no fill at all, and its space breaks with the container\n under both formatters " + }, + { + "type": "Text", + "start": 884, + "end": 885, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 885, + "end": 911, + "name": "p", + "name_loc": { + "start": { + "line": 23, + "column": 1, + "character": 886 + }, + "end": { + "line": 23, + "column": 2, + "character": 887 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 888, + "end": 890, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 890, + "end": 897, + "expression": { + "type": "Identifier", + "start": 891, + "end": 896, + "loc": { + "start": { + "line": 24, + "column": 2 + }, + "end": { + "line": 24, + "column": 7 + } + }, + "name": "expr1" + } + }, + { + "type": "Text", + "start": 897, + "end": 899, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 899, + "end": 906, + "expression": { + "type": "Identifier", + "start": 900, + "end": 905, + "loc": { + "start": { + "line": 25, + "column": 2 + }, + "end": { + "line": 25, + "column": 7 + } + }, + "name": "expr2" + } + }, + { + "type": "Text", + "start": 906, + "end": 907, + "raw": "\n", + "data": "\n" + } + ] + } + } + ] + }, + "options": null, + "comments": [] +} diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/input.svelte new file mode 100644 index 000000000..2c6ddc8b3 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/input.svelte @@ -0,0 +1,26 @@ + + + +

+ text1 {expr1} {expr2} +

+

+ {expr1} {expr2} {expr3} text1 +

+ + +{'<'}div{'>'} {'<'}code{'>'}{'<'}/code{'>'} + + +

+ {expr1} + {expr2} +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/output_prettier.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/output_prettier.svelte new file mode 100644 index 000000000..5a2d33dfa --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/output_prettier.svelte @@ -0,0 +1,30 @@ + + + +

+ text1 {expr1} + {expr2} +

+

+ {expr1} + {expr2} + {expr3} text1 +

+ + +{'<'}div{'>'} +{'<'}code{'>'}{'<'}/code{'>'} + + +

+ {expr1} + {expr2} +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/unformatted_ours_space.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/unformatted_ours_space.svelte new file mode 100644 index 000000000..a166306dd --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/unformatted_ours_space.svelte @@ -0,0 +1,25 @@ + + + +

+ text1 {expr1} {expr2} +

+

+ {expr1} {expr2} {expr3} text1 +

+ + +{'<'}div{'>'} {'<'}code{'>'}{'<'}/code{'>'} + + +

+ {expr1} {expr2} +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/README.md new file mode 100644 index 000000000..1d471e019 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/README.md @@ -0,0 +1,32 @@ +# inline_sibling_newline_prose_words_prettier_divergence + +What the sibling-newline flow rule counts as a **word** when it asks whether a run is prose +([inline_sibling_newline_label_hold](../inline_sibling_newline_label_hold_prettier_divergence/)): +a content text's collapsible-whitespace-separated words, split over the **source bytes** — +exactly the split the fill makes when it turns the text into items. So an NBSP-joined pair is one +word (one fill item), and so is an entity-encoded space (`text1 text2`: the entity's bytes +print verbatim, so the fill carries the pair as one unbreakable item — prettier's does too — and +there is no seam to reflow at); punctuation alone is a word, a hyphenated pair is one, a word +glued to a tag (`{expr}text1`) is still one, and punctuation alone between two tags (`{a}⏎·⏎{b}`) +is a one-word connector that holds. One word holds as a label; two is the cliff — at a void +element (` text1 text2`, the checkbox-and-caption shape where label and prose genuinely +blur) as at any other sibling — and a newline INSIDE a text node separates words exactly as a +space does, so `⏎text1⏎text2` is a two-word node and flows to ` text1 text2` +(`unformatted_ours_newline_in_node.svelte`, the newline authoring with that node split; prettier +reflows the interior newline but holds the sibling one, landing on `prettier_variant_newline`). + +## Reason + +Design choice. The count exists to answer one question — is there a fill to reflow into — and a +fill reflows only between its items, so the count splits the same text the fill splits: the +source bytes, never the decoded characters. (The rule's other text question, whether a node is a +separator wearing content's clothing, reads the DECODED text, because that one is a render +question — an NBSP renders as itself; see `inline_separator_nbsp_newline`. The two axes are +deliberately distinct.) Counting decoded characters instead would call `text1 text2` a +two-word phrase and flow the newline beside it on the promise of a wrap point that neither +formatter's fill has. Prettier holds every authored newline here; the held cases are agreement, +and the three controls are the divergence — `prettier_variant_newline.svelte` is their isolated +authoring, kept by prettier and normalized to `input.svelte` by tsv. + +See +[conformance_prettier_svelte.md §Svelte: Inline content block-style](../../../../../docs/conformance_prettier_svelte.md#svelte-inline-content-block-style). diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/expected.json new file mode 100644 index 000000000..72e29615c --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/expected.json @@ -0,0 +1,1022 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 1792, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Comment", + "start": 0, + "end": 609, + "data": "\n\tWhat the sibling-newline flow rule counts as a WORD: a content text's collapsible-whitespace-\n\tseparated words, split over the SOURCE BYTES exactly as the fill splits them into items. An\n\tNBSP-joined pair is one word (it is one fill item), and so is an entity-encoded space\n\t(` ` — the fill never breaks inside it); punctuation alone is a word, a hyphenated pair is\n\tone, and a word glued to a tag is still a word. A run holding one word is a label and holds;\n\ttwo words is the cliff, at a void element as at any sibling. Prettier holds every line here;\n\tthe flowing controls are the divergence.\n" + }, + { + "type": "Text", + "start": 609, + "end": 611, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 611, + "end": 651, + "data": " an NBSP-joined pair is one word " + }, + { + "type": "Text", + "start": 651, + "end": 652, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 652, + "end": 688, + "name": "p", + "name_loc": { + "start": { + "line": 12, + "column": 1, + "character": 653 + }, + "end": { + "line": 12, + "column": 2, + "character": 654 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 655, + "end": 657, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 657, + "end": 665, + "name": "Comp", + "name_loc": { + "start": { + "line": 13, + "column": 2, + "character": 658 + }, + "end": { + "line": 13, + "column": 6, + "character": 662 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 665, + "end": 684, + "raw": "\n\ttext1 text2\n", + "data": "\n\ttext1 text2\n" + } + ] + } + }, + { + "type": "Text", + "start": 688, + "end": 690, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 690, + "end": 945, + "data": " an entity-encoded space is one word too: the fill splits the source bytes and carries\n `text1 text2` as one unbreakable item (prettier does the same), so there is no seam to\n reflow at, and the pair holds exactly as the NBSP pair does " + }, + { + "type": "Text", + "start": 945, + "end": 946, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 946, + "end": 981, + "name": "p", + "name_loc": { + "start": { + "line": 20, + "column": 1, + "character": 947 + }, + "end": { + "line": 20, + "column": 2, + "character": 948 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 949, + "end": 951, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 951, + "end": 959, + "name": "Comp", + "name_loc": { + "start": { + "line": 21, + "column": 2, + "character": 952 + }, + "end": { + "line": 21, + "column": 6, + "character": 956 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 959, + "end": 977, + "raw": "\n\ttext1 text2\n", + "data": "\n\ttext1 text2\n" + } + ] + } + }, + { + "type": "Text", + "start": 981, + "end": 983, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 983, + "end": 1075, + "data": " punctuation alone is a word; a hyphenated pair is one; a word glued to a tag is one " + }, + { + "type": "Text", + "start": 1075, + "end": 1076, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1076, + "end": 1116, + "name": "p", + "name_loc": { + "start": { + "line": 26, + "column": 1, + "character": 1077 + }, + "end": { + "line": 26, + "column": 2, + "character": 1078 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1079, + "end": 1081, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1081, + "end": 1108, + "name": "a", + "name_loc": { + "start": { + "line": 27, + "column": 2, + "character": 1082 + }, + "end": { + "line": 27, + "column": 3, + "character": 1083 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 1084, + "end": 1096, + "name": "href", + "name_loc": { + "start": { + "line": 27, + "column": 4, + "character": 1084 + }, + "end": { + "line": 27, + "column": 8, + "character": 1088 + } + }, + "value": [ + { + "start": 1090, + "end": 1095, + "type": "Text", + "raw": "/path", + "data": "/path" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1097, + "end": 1104, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 1108, + "end": 1112, + "raw": "\n\t.\n", + "data": "\n\t.\n" + } + ] + } + }, + { + "type": "Text", + "start": 1116, + "end": 1117, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1117, + "end": 1148, + "name": "p", + "name_loc": { + "start": { + "line": 30, + "column": 1, + "character": 1118 + }, + "end": { + "line": 30, + "column": 2, + "character": 1119 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1120, + "end": 1122, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 1122, + "end": 1130, + "name": "Comp", + "name_loc": { + "start": { + "line": 31, + "column": 2, + "character": 1123 + }, + "end": { + "line": 31, + "column": 6, + "character": 1127 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 1130, + "end": 1144, + "raw": "\n\ttext1-text2\n", + "data": "\n\ttext1-text2\n" + } + ] + } + }, + { + "type": "Text", + "start": 1148, + "end": 1149, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1149, + "end": 1180, + "name": "p", + "name_loc": { + "start": { + "line": 34, + "column": 1, + "character": 1150 + }, + "end": { + "line": 34, + "column": 2, + "character": 1151 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1152, + "end": 1154, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 1154, + "end": 1162, + "name": "Comp", + "name_loc": { + "start": { + "line": 35, + "column": 2, + "character": 1155 + }, + "end": { + "line": 35, + "column": 6, + "character": 1159 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 1162, + "end": 1164, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 1164, + "end": 1170, + "expression": { + "type": "Identifier", + "start": 1165, + "end": 1169, + "loc": { + "start": { + "line": 36, + "column": 2 + }, + "end": { + "line": 36, + "column": 6 + } + }, + "name": "expr" + } + }, + { + "type": "Text", + "start": 1170, + "end": 1176, + "raw": "text1\n", + "data": "text1\n" + } + ] + } + }, + { + "type": "Text", + "start": 1180, + "end": 1182, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1182, + "end": 1214, + "data": " a word between two tags " + }, + { + "type": "Text", + "start": 1214, + "end": 1215, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1215, + "end": 1248, + "name": "p", + "name_loc": { + "start": { + "line": 40, + "column": 1, + "character": 1216 + }, + "end": { + "line": 40, + "column": 2, + "character": 1217 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1218, + "end": 1220, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 1220, + "end": 1227, + "expression": { + "type": "Identifier", + "start": 1221, + "end": 1226, + "loc": { + "start": { + "line": 41, + "column": 2 + }, + "end": { + "line": 41, + "column": 7 + } + }, + "name": "expr1" + } + }, + { + "type": "Text", + "start": 1227, + "end": 1236, + "raw": "\n\ttext1\n\t", + "data": "\n\ttext1\n\t" + }, + { + "type": "ExpressionTag", + "start": 1236, + "end": 1243, + "expression": { + "type": "Identifier", + "start": 1237, + "end": 1242, + "loc": { + "start": { + "line": 43, + "column": 2 + }, + "end": { + "line": 43, + "column": 7 + } + }, + "name": "expr2" + } + }, + { + "type": "Text", + "start": 1243, + "end": 1244, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1248, + "end": 1250, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1250, + "end": 1329, + "data": " punctuation alone between two tags is one word: a connector, and holds " + }, + { + "type": "Text", + "start": 1329, + "end": 1330, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1330, + "end": 1359, + "name": "p", + "name_loc": { + "start": { + "line": 47, + "column": 1, + "character": 1331 + }, + "end": { + "line": 47, + "column": 2, + "character": 1332 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1333, + "end": 1335, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 1335, + "end": 1342, + "expression": { + "type": "Identifier", + "start": 1336, + "end": 1341, + "loc": { + "start": { + "line": 48, + "column": 2 + }, + "end": { + "line": 48, + "column": 7 + } + }, + "name": "expr1" + } + }, + { + "type": "Text", + "start": 1342, + "end": 1347, + "raw": "\n\t·\n\t", + "data": "\n\t·\n\t" + }, + { + "type": "ExpressionTag", + "start": 1347, + "end": 1354, + "expression": { + "type": "Identifier", + "start": 1348, + "end": 1353, + "loc": { + "start": { + "line": 50, + "column": 2 + }, + "end": { + "line": 50, + "column": 7 + } + }, + "name": "expr2" + } + }, + { + "type": "Text", + "start": 1354, + "end": 1355, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1359, + "end": 1361, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1361, + "end": 1425, + "data": " control: punctuation and a word are two words, and flow " + }, + { + "type": "Text", + "start": 1425, + "end": 1426, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1426, + "end": 1484, + "name": "p", + "name_loc": { + "start": { + "line": 54, + "column": 1, + "character": 1427 + }, + "end": { + "line": 54, + "column": 2, + "character": 1428 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1429, + "end": 1431, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1431, + "end": 1458, + "name": "a", + "name_loc": { + "start": { + "line": 55, + "column": 2, + "character": 1432 + }, + "end": { + "line": 55, + "column": 3, + "character": 1433 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 1434, + "end": 1446, + "name": "href", + "name_loc": { + "start": { + "line": 55, + "column": 4, + "character": 1434 + }, + "end": { + "line": 55, + "column": 8, + "character": 1438 + } + }, + "value": [ + { + "start": 1440, + "end": 1445, + "type": "Text", + "raw": "/path", + "data": "/path" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1447, + "end": 1454, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 1458, + "end": 1465, + "raw": " , and ", + "data": " , and " + }, + { + "type": "RegularElement", + "start": 1465, + "end": 1479, + "name": "b", + "name_loc": { + "start": { + "line": 55, + "column": 36, + "character": 1466 + }, + "end": { + "line": 55, + "column": 37, + "character": 1467 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1468, + "end": 1475, + "raw": "inline2", + "data": "inline2" + } + ] + } + }, + { + "type": "Text", + "start": 1479, + "end": 1480, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1484, + "end": 1486, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1486, + "end": 1581, + "data": " control: the cliff at a void element — two words beside a checkbox are prose, and flow " + }, + { + "type": "Text", + "start": 1581, + "end": 1582, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1582, + "end": 1637, + "name": "label", + "name_loc": { + "start": { + "line": 59, + "column": 1, + "character": 1583 + }, + "end": { + "line": 59, + "column": 6, + "character": 1588 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1589, + "end": 1591, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1591, + "end": 1616, + "name": "input", + "name_loc": { + "start": { + "line": 60, + "column": 2, + "character": 1592 + }, + "end": { + "line": 60, + "column": 7, + "character": 1597 + } + }, + "attributes": [ + { + "type": "Attribute", + "start": 1598, + "end": 1613, + "name": "type", + "name_loc": { + "start": { + "line": 60, + "column": 8, + "character": 1598 + }, + "end": { + "line": 60, + "column": 12, + "character": 1602 + } + }, + "value": [ + { + "start": 1604, + "end": 1612, + "type": "Text", + "raw": "checkbox", + "data": "checkbox" + } + ] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 1616, + "end": 1629, + "raw": " text1 text2\n", + "data": " text1 text2\n" + } + ] + } + }, + { + "type": "Text", + "start": 1637, + "end": 1639, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1639, + "end": 1760, + "data": " control: a newline inside a text node separates words as a space does, so this node is\n two words, and flows " + }, + { + "type": "Text", + "start": 1760, + "end": 1761, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1761, + "end": 1791, + "name": "p", + "name_loc": { + "start": { + "line": 65, + "column": 1, + "character": 1762 + }, + "end": { + "line": 65, + "column": 2, + "character": 1763 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1764, + "end": 1766, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 1766, + "end": 1774, + "name": "Comp", + "name_loc": { + "start": { + "line": 66, + "column": 2, + "character": 1767 + }, + "end": { + "line": 66, + "column": 6, + "character": 1771 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 1774, + "end": 1787, + "raw": " text1 text2\n", + "data": " text1 text2\n" + } + ] + } + } + ] + }, + "options": null, + "comments": [] +} diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/input.svelte new file mode 100644 index 000000000..43965953b --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/input.svelte @@ -0,0 +1,67 @@ + + + +

+ + text1 text2 +

+ + +

+ + text1 text2 +

+ + +

+ inline1 + . +

+

+ + text1-text2 +

+

+ + {expr}text1 +

+ + +

+ {expr1} + text1 + {expr2} +

+ + +

+ {expr1} + · + {expr2} +

+ + +

+ inline1 , and inline2 +

+ + + + + +

+ text1 text2 +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/prettier_variant_newline.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/prettier_variant_newline.svelte new file mode 100644 index 000000000..77c0eca87 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/prettier_variant_newline.svelte @@ -0,0 +1,71 @@ + + + +

+ + text1 text2 +

+ + +

+ + text1 text2 +

+ + +

+ inline1 + . +

+

+ + text1-text2 +

+

+ + {expr}text1 +

+ + +

+ {expr1} + text1 + {expr2} +

+ + +

+ {expr1} + · + {expr2} +

+ + +

+ inline1 + , and + inline2 +

+ + + + + +

+ + text1 text2 +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/unformatted_ours_newline_in_node.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/unformatted_ours_newline_in_node.svelte new file mode 100644 index 000000000..3ae346e36 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/unformatted_ours_newline_in_node.svelte @@ -0,0 +1,72 @@ + + + +

+ + text1 text2 +

+ + +

+ + text1 text2 +

+ + +

+ inline1 + . +

+

+ + text1-text2 +

+

+ + {expr}text1 +

+ + +

+ {expr1} + text1 + {expr2} +

+ + +

+ {expr1} + · + {expr2} +

+ + +

+ inline1 + , and + inline2 +

+ + + + + +

+ + text1 + text2 +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/README.md new file mode 100644 index 000000000..29ffea969 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/README.md @@ -0,0 +1,29 @@ +# inline_sibling_newline_run_bounded_prettier_divergence + +The sibling-newline flow rule's prose count is taken per **run**, and this fixture pins where a +run ends: a comment, a block element, a control-flow block, a `
`, and an authored blank +line — in a whitespace-only node between two siblings *or* at either edge of a content text. +Every case puts a two-word run on one side of the boundary and a one-word run on the other: the +prose run flows right up to the boundary, and the label beyond it holds +([inline_sibling_newline_label_hold](../inline_sibling_newline_label_hold_prettier_divergence/)); +the same nodes with nothing between them are one run, and the label flows with the prose. + +## Reason + +Design choice. The count is what decides whether there is a fill to reflow into, and a fill +cannot cross a line-owning sibling or a blank line — prose on one side of a comment says nothing +about the label on the other. The blank-line boundary is the same one the fill predicate reads +for an element's interior, and it is a boundary wherever the blank is spelled: the parser puts +a blank between two elements in a whitespace-only node but a blank beside a text in that text's +edge whitespace, and one document must not count two ways by that accident. Bounding is not +sterilizing: the prose run flows up to the boundary in every case, which is what separates "the +boundary ends the run" from "a run near a boundary keeps every line". + +Prettier holds every authored newline here, so the held halves are agreement and the flowing +halves are the divergence: `prettier_variant_newline.svelte` is their isolated authoring — +prettier keeps it, tsv normalizes it to `input.svelte`. The unbounded control is the twin that +shows the boundary is what held the label: with nothing between them the nodes are one run, and +the one-word tail flows with the two-word head. + +See +[conformance_prettier_svelte.md §Svelte: Inline content block-style](../../../../../docs/conformance_prettier_svelte.md#svelte-inline-content-block-style). diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/expected.json new file mode 100644 index 000000000..acfdaa706 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/expected.json @@ -0,0 +1,1133 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 1844, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Comment", + "start": 0, + "end": 689, + "data": "\n\tThe sibling-newline flow rule counts prose per RUN, and a run ends at whatever owns its own\n\tline: a comment, a block element, a control-flow block, a `
`, and an authored blank\n\tline — whether that blank stands in a whitespace-only node between two siblings or at the\n\tedge of a content text. Every case puts a two-word run on one side of the boundary and a\n\tone-word run on the other: the prose flows right up to the boundary (bounding is not\n\tsterilizing), and the label beyond it holds, where the same nodes with no boundary between\n\tthem are one run and the label flows with the prose. Prettier holds every authored newline\n\there; the flowing halves are the divergence.\n" + }, + { + "type": "Text", + "start": 689, + "end": 691, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 691, + "end": 797, + "data": " a comment bounds the run: the prose run before it flows up to it, the one-word run after it holds " + }, + { + "type": "Text", + "start": 797, + "end": 798, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 798, + "end": 881, + "name": "p", + "name_loc": { + "start": { + "line": 13, + "column": 1, + "character": 799 + }, + "end": { + "line": 13, + "column": 2, + "character": 800 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 801, + "end": 815, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " + }, + { + "type": "RegularElement", + "start": 815, + "end": 835, + "name": "span", + "name_loc": { + "start": { + "line": 14, + "column": 14, + "character": 816 + }, + "end": { + "line": 14, + "column": 18, + "character": 820 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 821, + "end": 828, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 835, + "end": 837, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Comment", + "start": 837, + "end": 847, + "data": " c " + }, + { + "type": "Text", + "start": 847, + "end": 856, + "raw": "\n\ttext3\n\t", + "data": "\n\ttext3\n\t" + }, + { + "type": "RegularElement", + "start": 856, + "end": 876, + "name": "span", + "name_loc": { + "start": { + "line": 17, + "column": 2, + "character": 857 + }, + "end": { + "line": 17, + "column": 6, + "character": 861 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 862, + "end": 869, + "raw": "inline2", + "data": "inline2" + } + ] + } + }, + { + "type": "Text", + "start": 876, + "end": 877, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 881, + "end": 883, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 883, + "end": 945, + "data": " a blank line in a whitespace-only node bounds the run " + }, + { + "type": "Text", + "start": 945, + "end": 946, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 946, + "end": 1018, + "name": "p", + "name_loc": { + "start": { + "line": 21, + "column": 1, + "character": 947 + }, + "end": { + "line": 21, + "column": 2, + "character": 948 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 949, + "end": 963, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " + }, + { + "type": "RegularElement", + "start": 963, + "end": 983, + "name": "span", + "name_loc": { + "start": { + "line": 22, + "column": 14, + "character": 964 + }, + "end": { + "line": 22, + "column": 18, + "character": 968 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 969, + "end": 976, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 983, + "end": 986, + "raw": "\n\n\t", + "data": "\n\n\t" + }, + { + "type": "RegularElement", + "start": 986, + "end": 1006, + "name": "span", + "name_loc": { + "start": { + "line": 24, + "column": 2, + "character": 987 + }, + "end": { + "line": 24, + "column": 6, + "character": 991 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 992, + "end": 999, + "raw": "inline2", + "data": "inline2" + } + ] + } + }, + { + "type": "Text", + "start": 1006, + "end": 1014, + "raw": "\n\ttext3\n", + "data": "\n\ttext3\n" + } + ] + } + }, + { + "type": "Text", + "start": 1018, + "end": 1020, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1020, + "end": 1099, + "data": " a blank line at a content text's LEADING edge bounds the run before it " + }, + { + "type": "Text", + "start": 1099, + "end": 1100, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1100, + "end": 1172, + "name": "p", + "name_loc": { + "start": { + "line": 29, + "column": 1, + "character": 1101 + }, + "end": { + "line": 29, + "column": 2, + "character": 1102 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1103, + "end": 1117, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " + }, + { + "type": "RegularElement", + "start": 1117, + "end": 1137, + "name": "span", + "name_loc": { + "start": { + "line": 30, + "column": 14, + "character": 1118 + }, + "end": { + "line": 30, + "column": 18, + "character": 1122 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1123, + "end": 1130, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 1137, + "end": 1147, + "raw": "\n\n\ttext3\n\t", + "data": "\n\n\ttext3\n\t" + }, + { + "type": "RegularElement", + "start": 1147, + "end": 1167, + "name": "span", + "name_loc": { + "start": { + "line": 33, + "column": 2, + "character": 1148 + }, + "end": { + "line": 33, + "column": 6, + "character": 1152 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1153, + "end": 1160, + "raw": "inline2", + "data": "inline2" + } + ] + } + }, + { + "type": "Text", + "start": 1167, + "end": 1168, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1172, + "end": 1174, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1174, + "end": 1253, + "data": " a blank line at a content text's TRAILING edge bounds the run after it " + }, + { + "type": "Text", + "start": 1253, + "end": 1254, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1254, + "end": 1326, + "name": "p", + "name_loc": { + "start": { + "line": 37, + "column": 1, + "character": 1255 + }, + "end": { + "line": 37, + "column": 2, + "character": 1256 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1257, + "end": 1259, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1259, + "end": 1279, + "name": "span", + "name_loc": { + "start": { + "line": 38, + "column": 2, + "character": 1260 + }, + "end": { + "line": 38, + "column": 6, + "character": 1264 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1265, + "end": 1272, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 1279, + "end": 1294, + "raw": " text1 text2\n\n\t", + "data": " text1 text2\n\n\t" + }, + { + "type": "RegularElement", + "start": 1294, + "end": 1314, + "name": "span", + "name_loc": { + "start": { + "line": 40, + "column": 2, + "character": 1295 + }, + "end": { + "line": 40, + "column": 6, + "character": 1299 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1300, + "end": 1307, + "raw": "inline2", + "data": "inline2" + } + ] + } + }, + { + "type": "Text", + "start": 1314, + "end": 1322, + "raw": "\n\ttext3\n", + "data": "\n\ttext3\n" + } + ] + } + }, + { + "type": "Text", + "start": 1326, + "end": 1328, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1328, + "end": 1403, + "data": " a block element, a control-flow block and a `
` bound the run " + }, + { + "type": "Text", + "start": 1403, + "end": 1404, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1404, + "end": 1498, + "name": "div", + "name_loc": { + "start": { + "line": 45, + "column": 1, + "character": 1405 + }, + "end": { + "line": 45, + "column": 4, + "character": 1408 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1409, + "end": 1423, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " + }, + { + "type": "RegularElement", + "start": 1423, + "end": 1443, + "name": "span", + "name_loc": { + "start": { + "line": 46, + "column": 14, + "character": 1424 + }, + "end": { + "line": 46, + "column": 18, + "character": 1428 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1429, + "end": 1436, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 1443, + "end": 1445, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1445, + "end": 1462, + "name": "div", + "name_loc": { + "start": { + "line": 47, + "column": 2, + "character": 1446 + }, + "end": { + "line": 47, + "column": 5, + "character": 1449 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1450, + "end": 1456, + "raw": "block1", + "data": "block1" + } + ] + } + }, + { + "type": "Text", + "start": 1462, + "end": 1471, + "raw": "\n\ttext3\n\t", + "data": "\n\ttext3\n\t" + }, + { + "type": "RegularElement", + "start": 1471, + "end": 1491, + "name": "span", + "name_loc": { + "start": { + "line": 49, + "column": 2, + "character": 1472 + }, + "end": { + "line": 49, + "column": 6, + "character": 1476 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1477, + "end": 1484, + "raw": "inline2", + "data": "inline2" + } + ] + } + }, + { + "type": "Text", + "start": 1491, + "end": 1492, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1498, + "end": 1499, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1499, + "end": 1594, + "name": "p", + "name_loc": { + "start": { + "line": 51, + "column": 1, + "character": 1500 + }, + "end": { + "line": 51, + "column": 2, + "character": 1501 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1502, + "end": 1516, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " + }, + { + "type": "RegularElement", + "start": 1516, + "end": 1536, + "name": "span", + "name_loc": { + "start": { + "line": 52, + "column": 14, + "character": 1517 + }, + "end": { + "line": 52, + "column": 18, + "character": 1521 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1522, + "end": 1529, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 1536, + "end": 1538, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "IfBlock", + "elseif": false, + "start": 1538, + "end": 1560, + "test": { + "type": "Identifier", + "start": 1543, + "end": 1547, + "loc": { + "start": { + "line": 53, + "column": 6 + }, + "end": { + "line": 53, + "column": 10 + } + }, + "name": "cond" + }, + "consequent": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1548, + "end": 1555, + "raw": "inline2", + "data": "inline2" + } + ] + }, + "alternate": null + }, + { + "type": "Text", + "start": 1560, + "end": 1569, + "raw": "\n\ttext3\n\t", + "data": "\n\ttext3\n\t" + }, + { + "type": "RegularElement", + "start": 1569, + "end": 1589, + "name": "span", + "name_loc": { + "start": { + "line": 55, + "column": 2, + "character": 1570 + }, + "end": { + "line": 55, + "column": 6, + "character": 1574 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1575, + "end": 1582, + "raw": "inline3", + "data": "inline3" + } + ] + } + }, + { + "type": "Text", + "start": 1589, + "end": 1590, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1594, + "end": 1595, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1595, + "end": 1674, + "name": "p", + "name_loc": { + "start": { + "line": 57, + "column": 1, + "character": 1596 + }, + "end": { + "line": 57, + "column": 2, + "character": 1597 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1598, + "end": 1612, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " + }, + { + "type": "RegularElement", + "start": 1612, + "end": 1632, + "name": "span", + "name_loc": { + "start": { + "line": 58, + "column": 14, + "character": 1613 + }, + "end": { + "line": 58, + "column": 18, + "character": 1617 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1618, + "end": 1625, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 1632, + "end": 1634, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1634, + "end": 1640, + "name": "br", + "name_loc": { + "start": { + "line": 59, + "column": 2, + "character": 1635 + }, + "end": { + "line": 59, + "column": 4, + "character": 1637 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + }, + { + "type": "Text", + "start": 1640, + "end": 1649, + "raw": "\n\ttext3\n\t", + "data": "\n\ttext3\n\t" + }, + { + "type": "RegularElement", + "start": 1649, + "end": 1669, + "name": "span", + "name_loc": { + "start": { + "line": 61, + "column": 2, + "character": 1650 + }, + "end": { + "line": 61, + "column": 6, + "character": 1654 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1655, + "end": 1662, + "raw": "inline2", + "data": "inline2" + } + ] + } + }, + { + "type": "Text", + "start": 1669, + "end": 1670, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1674, + "end": 1676, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1676, + "end": 1773, + "data": " control: the same nodes with no boundary are one run, and the label flows with the prose " + }, + { + "type": "Text", + "start": 1773, + "end": 1774, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1774, + "end": 1843, + "name": "p", + "name_loc": { + "start": { + "line": 65, + "column": 1, + "character": 1775 + }, + "end": { + "line": 65, + "column": 2, + "character": 1776 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1777, + "end": 1791, + "raw": "\n\ttext1 text2 ", + "data": "\n\ttext1 text2 " + }, + { + "type": "RegularElement", + "start": 1791, + "end": 1811, + "name": "span", + "name_loc": { + "start": { + "line": 66, + "column": 14, + "character": 1792 + }, + "end": { + "line": 66, + "column": 18, + "character": 1796 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1797, + "end": 1804, + "raw": "inline1", + "data": "inline1" + } + ] + } + }, + { + "type": "Text", + "start": 1811, + "end": 1818, + "raw": " text3 ", + "data": " text3 " + }, + { + "type": "RegularElement", + "start": 1818, + "end": 1838, + "name": "span", + "name_loc": { + "start": { + "line": 66, + "column": 41, + "character": 1819 + }, + "end": { + "line": 66, + "column": 45, + "character": 1823 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1824, + "end": 1831, + "raw": "inline2", + "data": "inline2" + } + ] + } + }, + { + "type": "Text", + "start": 1838, + "end": 1839, + "raw": "\n", + "data": "\n" + } + ] + } + } + ] + }, + "options": null, + "comments": [] +} diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/input.svelte new file mode 100644 index 000000000..f20523a1e --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/input.svelte @@ -0,0 +1,67 @@ + + + +

+ text1 text2 inline1 + + text3 + inline2 +

+ + +

+ text1 text2 inline1 + + inline2 + text3 +

+ + +

+ text1 text2 inline1 + + text3 + inline2 +

+ + +

+ inline1 text1 text2 + + inline2 + text3 +

+ + +
+ text1 text2 inline1 +
block1
+ text3 + inline2 +
+

+ text1 text2 inline1 + {#if cond}inline2{/if} + text3 + inline3 +

+

+ text1 text2 inline1 +
+ text3 + inline2 +

+ + +

+ text1 text2 inline1 text3 inline2 +

diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/prettier_variant_newline.svelte b/tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/prettier_variant_newline.svelte new file mode 100644 index 000000000..fa5380ba5 --- /dev/null +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/prettier_variant_newline.svelte @@ -0,0 +1,77 @@ + + + +

+ text1 text2 + inline1 + + text3 + inline2 +

+ + +

+ text1 text2 + inline1 + + inline2 + text3 +

+ + +

+ text1 text2 + inline1 + + text3 + inline2 +

+ + +

+ inline1 + text1 text2 + + inline2 + text3 +

+ + +
+ text1 text2 + inline1 +
block1
+ text3 + inline2 +
+

+ text1 text2 + inline1 + {#if cond}inline2{/if} + text3 + inline3 +

+

+ text1 text2 + inline1 +
+ text3 + inline2 +

+ + +

+ text1 text2 + inline1 + text3 + inline2 +

diff --git a/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/expected.json index f796dcf0b..f7edbefd4 100644 --- a/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 33, + "end": 39, "type": "Root", "fragment": { "type": "Fragment", @@ -10,25 +10,25 @@ { "type": "Text", "start": 0, - "end": 6, - "raw": "text1 ", - "data": "text1 " + "end": 12, + "raw": "text1 text2 ", + "data": "text1 text2 " }, { "type": "RegularElement", - "start": 6, - "end": 26, + "start": 12, + "end": 32, "name": "span", "name_loc": { "start": { "line": 1, - "column": 7, - "character": 7 + "column": 13, + "character": 13 }, "end": { "line": 1, - "column": 11, - "character": 11 + "column": 17, + "character": 17 } }, "attributes": [], @@ -37,8 +37,8 @@ "nodes": [ { "type": "Text", - "start": 12, - "end": 19, + "start": 18, + "end": 25, "raw": "inline1", "data": "inline1" } @@ -47,10 +47,10 @@ }, { "type": "Text", - "start": 26, - "end": 32, - "raw": " text2", - "data": " text2" + "start": 32, + "end": 38, + "raw": " text3", + "data": " text3" } ] }, diff --git a/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/input.svelte index a0cb17a65..4fc18351e 100644 --- a/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/input.svelte @@ -1 +1 @@ -text1 inline1 text2 +text1 text2 inline1 text3 diff --git a/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/prettier_variant_newlines.svelte b/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/prettier_variant_newlines.svelte index 3b786db39..01d792c30 100644 --- a/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/prettier_variant_newlines.svelte +++ b/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/prettier_variant_newlines.svelte @@ -1,3 +1,3 @@ -text1 +text1 text2 inline1 -text2 +text3 diff --git a/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/unformatted_ours_spaces.svelte b/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/unformatted_ours_spaces.svelte index 3b157445a..8d96d41e4 100644 --- a/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/unformatted_ours_spaces.svelte +++ b/tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/unformatted_ours_spaces.svelte @@ -1,3 +1,3 @@ - text1 + text1 text2 inline1 - text2 + text3 diff --git a/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/README.md b/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/README.md index 0a2c88568..aee6d9ec0 100644 --- a/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/README.md +++ b/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/README.md @@ -15,7 +15,7 @@ the boundary's layout follows the unit's rendered layout, not the spelling alone and not repeated here). - After a unit that renders **inline**, the same newline is a spelling difference only and reflows with the fill — the fitting control, and the held sibling's own text tail - (`inline2 text2`): `prettier_variant_fitting_newline` pins those newline authorings, + (`inline2 text2 text3`): `prettier_variant_fitting_newline` pins those newline authorings, which prettier keeps stable and tsv packs. After a **breaking tag** the sibling hugs the tag's closing brace (`)} inline6`): the tag's break lands inside its own expression, so the tag-pile reading does not arise, and prettier's own-line authoring of it is in the diff --git a/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/expected.json index 2b89f57e8..3e0534130 100644 --- a/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 1752, + "end": 1794, "type": "Root", "fragment": { "type": "Fragment", @@ -36,7 +36,7 @@ { "type": "RegularElement", "start": 817, - "end": 888, + "end": 894, "name": "p", "name_loc": { "start": { @@ -171,48 +171,48 @@ { "type": "Text", "start": 877, - "end": 884, - "raw": " text2\n", - "data": " text2\n" + "end": 890, + "raw": " text2 text3\n", + "data": " text2 text3\n" } ] } }, { "type": "Text", - "start": 888, - "end": 890, + "start": 894, + "end": 896, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 890, - "end": 965, + "start": 896, + "end": 971, "data": " a component after a component with content, in an inline container " }, { "type": "Text", - "start": 965, - "end": 966, + "start": 971, + "end": 972, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 966, - "end": 1040, + "start": 972, + "end": 1052, "name": "span", "name_loc": { "start": { "line": 21, "column": 1, - "character": 967 + "character": 973 }, "end": { "line": 21, "column": 5, - "character": 971 + "character": 977 } }, "attributes": [], @@ -221,26 +221,26 @@ "nodes": [ { "type": "Text", - "start": 972, - "end": 981, - "raw": "\n\ttext3\n\t", - "data": "\n\ttext3\n\t" + "start": 978, + "end": 987, + "raw": "\n\ttext4\n\t", + "data": "\n\ttext4\n\t" }, { "type": "Component", - "start": 981, - "end": 1015, + "start": 987, + "end": 1021, "name": "Comp1", "name_loc": { "start": { "line": 23, "column": 2, - "character": 982 + "character": 988 }, "end": { "line": 23, "column": 7, - "character": 987 + "character": 993 } }, "attributes": [], @@ -249,26 +249,26 @@ "nodes": [ { "type": "Text", - "start": 988, - "end": 991, + "start": 994, + "end": 997, "raw": "\n\t\t", "data": "\n\t\t" }, { "type": "RegularElement", - "start": 991, - "end": 1005, + "start": 997, + "end": 1011, "name": "b", "name_loc": { "start": { "line": 24, "column": 3, - "character": 992 + "character": 998 }, "end": { "line": 24, "column": 4, - "character": 993 + "character": 999 } }, "attributes": [], @@ -277,8 +277,8 @@ "nodes": [ { "type": "Text", - "start": 994, - "end": 1001, + "start": 1000, + "end": 1007, "raw": "inline3", "data": "inline3" } @@ -287,8 +287,8 @@ }, { "type": "Text", - "start": 1005, - "end": 1007, + "start": 1011, + "end": 1013, "raw": "\n\t", "data": "\n\t" } @@ -297,26 +297,26 @@ }, { "type": "Text", - "start": 1015, - "end": 1017, + "start": 1021, + "end": 1023, "raw": "\n\t", "data": "\n\t" }, { "type": "Component", - "start": 1017, - "end": 1026, + "start": 1023, + "end": 1032, "name": "Comp2", "name_loc": { "start": { "line": 26, "column": 2, - "character": 1018 + "character": 1024 }, "end": { "line": 26, "column": 7, - "character": 1023 + "character": 1029 } }, "attributes": [], @@ -327,49 +327,49 @@ }, { "type": "Text", - "start": 1026, - "end": 1033, - "raw": " text4\n", - "data": " text4\n" + "start": 1032, + "end": 1045, + "raw": " text5 text6\n", + "data": " text5 text6\n" } ] } }, { "type": "Text", - "start": 1040, - "end": 1042, + "start": 1052, + "end": 1054, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 1042, - "end": 1115, + "start": 1054, + "end": 1127, "data": " a tag after an element with a structurally multiline tag content " }, { "type": "Text", - "start": 1115, - "end": 1116, + "start": 1127, + "end": 1128, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1116, - "end": 1177, + "start": 1128, + "end": 1195, "name": "p", "name_loc": { "start": { "line": 30, "column": 1, - "character": 1117 + "character": 1129 }, "end": { "line": 30, "column": 2, - "character": 1118 + "character": 1130 } }, "attributes": [], @@ -378,50 +378,50 @@ "nodes": [ { "type": "Text", - "start": 1119, - "end": 1128, - "raw": "\n\ttext5\n\t", - "data": "\n\ttext5\n\t" + "start": 1131, + "end": 1140, + "raw": "\n\ttext7\n\t", + "data": "\n\ttext7\n\t" }, { "type": "RegularElement", - "start": 1128, - "end": 1157, + "start": 1140, + "end": 1169, "name": "a", "name_loc": { "start": { "line": 32, "column": 2, - "character": 1129 + "character": 1141 }, "end": { "line": 32, "column": 3, - "character": 1130 + "character": 1142 } }, "attributes": [ { "type": "Attribute", - "start": 1131, - "end": 1140, + "start": 1143, + "end": 1152, "name": "href", "name_loc": { "start": { "line": 32, "column": 4, - "character": 1131 + "character": 1143 }, "end": { "line": 32, "column": 8, - "character": 1135 + "character": 1147 } }, "value": [ { - "start": 1137, - "end": 1139, + "start": 1149, + "end": 1151, "type": "Text", "raw": "/a", "data": "/a" @@ -434,19 +434,19 @@ "nodes": [ { "type": "Text", - "start": 1141, - "end": 1144, + "start": 1153, + "end": 1156, "raw": "\n\t\t", "data": "\n\t\t" }, { "type": "ExpressionTag", - "start": 1144, - "end": 1151, + "start": 1156, + "end": 1163, "expression": { "type": "Identifier", - "start": 1145, - "end": 1150, + "start": 1157, + "end": 1162, "loc": { "start": { "line": 33, @@ -462,8 +462,8 @@ }, { "type": "Text", - "start": 1151, - "end": 1153, + "start": 1163, + "end": 1165, "raw": "\n\t", "data": "\n\t" } @@ -472,19 +472,19 @@ }, { "type": "Text", - "start": 1157, - "end": 1159, + "start": 1169, + "end": 1171, "raw": "\n\t", "data": "\n\t" }, { "type": "ExpressionTag", - "start": 1159, - "end": 1166, + "start": 1171, + "end": 1178, "expression": { "type": "Identifier", - "start": 1160, - "end": 1165, + "start": 1172, + "end": 1177, "loc": { "start": { "line": 35, @@ -500,49 +500,49 @@ }, { "type": "Text", - "start": 1166, - "end": 1173, - "raw": " text6\n", - "data": " text6\n" + "start": 1178, + "end": 1191, + "raw": " text8 text9\n", + "data": " text8 text9\n" } ] } }, { "type": "Text", - "start": 1177, - "end": 1179, + "start": 1195, + "end": 1197, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 1179, - "end": 1245, + "start": 1197, + "end": 1263, "data": " a void element after an element block-styled by wide text " }, { "type": "Text", - "start": 1245, - "end": 1246, + "start": 1263, + "end": 1264, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1246, - "end": 1391, + "start": 1264, + "end": 1418, "name": "p", "name_loc": { "start": { "line": 39, "column": 1, - "character": 1247 + "character": 1265 }, "end": { "line": 39, "column": 2, - "character": 1248 + "character": 1266 } }, "attributes": [], @@ -551,26 +551,26 @@ "nodes": [ { "type": "Text", - "start": 1249, - "end": 1258, - "raw": "\n\ttext7\n\t", - "data": "\n\ttext7\n\t" + "start": 1267, + "end": 1277, + "raw": "\n\ttext10\n\t", + "data": "\n\ttext10\n\t" }, { "type": "RegularElement", - "start": 1258, - "end": 1369, + "start": 1277, + "end": 1388, "name": "code", "name_loc": { "start": { "line": 41, "column": 2, - "character": 1259 + "character": 1278 }, "end": { "line": 41, "column": 6, - "character": 1263 + "character": 1282 } }, "attributes": [], @@ -579,8 +579,8 @@ "nodes": [ { "type": "Text", - "start": 1264, - "end": 1362, + "start": 1283, + "end": 1381, "raw": "\n\t\talpha bravo charlie delta echo foxtrot golf hotel india juliett kilo lima mike november oscar\n\t", "data": "\n\t\talpha bravo charlie delta echo foxtrot golf hotel india juliett kilo lima mike november oscar\n\t" } @@ -589,26 +589,26 @@ }, { "type": "Text", - "start": 1369, - "end": 1371, + "start": 1388, + "end": 1390, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1371, - "end": 1380, + "start": 1390, + "end": 1399, "name": "input", "name_loc": { "start": { "line": 44, "column": 2, - "character": 1372 + "character": 1391 }, "end": { "line": 44, "column": 7, - "character": 1377 + "character": 1396 } }, "attributes": [], @@ -619,49 +619,49 @@ }, { "type": "Text", - "start": 1380, - "end": 1387, - "raw": " text8\n", - "data": " text8\n" + "start": 1399, + "end": 1414, + "raw": " text11 text12\n", + "data": " text11 text12\n" } ] } }, { "type": "Text", - "start": 1391, - "end": 1393, + "start": 1418, + "end": 1420, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 1393, - "end": 1477, + "start": 1420, + "end": 1504, "data": " control: after a unit that renders INLINE the newline reflows with the fill " }, { "type": "Text", - "start": 1477, - "end": 1478, + "start": 1504, + "end": 1505, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1478, - "end": 1536, + "start": 1505, + "end": 1571, "name": "p", "name_loc": { "start": { "line": 48, "column": 1, - "character": 1479 + "character": 1506 }, "end": { "line": 48, "column": 2, - "character": 1480 + "character": 1507 } }, "attributes": [], @@ -670,26 +670,26 @@ "nodes": [ { "type": "Text", - "start": 1481, - "end": 1489, - "raw": "\n\ttext9 ", - "data": "\n\ttext9 " + "start": 1508, + "end": 1524, + "raw": "\n\ttext13 text14 ", + "data": "\n\ttext13 text14 " }, { "type": "RegularElement", - "start": 1489, - "end": 1509, + "start": 1524, + "end": 1544, "name": "span", "name_loc": { "start": { "line": 49, - "column": 8, - "character": 1490 + "column": 16, + "character": 1525 }, "end": { "line": 49, - "column": 12, - "character": 1494 + "column": 20, + "character": 1529 } }, "attributes": [], @@ -698,8 +698,8 @@ "nodes": [ { "type": "Text", - "start": 1495, - "end": 1502, + "start": 1530, + "end": 1537, "raw": "inline4", "data": "inline4" } @@ -708,26 +708,26 @@ }, { "type": "Text", - "start": 1509, - "end": 1510, + "start": 1544, + "end": 1545, "raw": " ", "data": " " }, { "type": "RegularElement", - "start": 1510, - "end": 1524, + "start": 1545, + "end": 1559, "name": "b", "name_loc": { "start": { "line": 49, - "column": 29, - "character": 1511 + "column": 37, + "character": 1546 }, "end": { "line": 49, - "column": 30, - "character": 1512 + "column": 38, + "character": 1547 } }, "attributes": [], @@ -736,8 +736,8 @@ "nodes": [ { "type": "Text", - "start": 1513, - "end": 1520, + "start": 1548, + "end": 1555, "raw": "inline5", "data": "inline5" } @@ -746,49 +746,49 @@ }, { "type": "Text", - "start": 1524, - "end": 1532, - "raw": " text10\n", - "data": " text10\n" + "start": 1559, + "end": 1567, + "raw": " text15\n", + "data": " text15\n" } ] } }, { "type": "Text", - "start": 1536, - "end": 1538, + "start": 1571, + "end": 1573, "raw": "\n\n", "data": "\n\n" }, { "type": "Comment", - "start": 1538, - "end": 1593, + "start": 1573, + "end": 1628, "data": " control: after a breaking TAG the sibling hugs " }, { "type": "Text", - "start": 1593, - "end": 1594, + "start": 1628, + "end": 1629, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1594, - "end": 1751, + "start": 1629, + "end": 1793, "name": "p", "name_loc": { "start": { "line": 53, "column": 1, - "character": 1595 + "character": 1630 }, "end": { "line": 53, "column": 2, - "character": 1596 + "character": 1631 } }, "attributes": [], @@ -797,19 +797,19 @@ "nodes": [ { "type": "Text", - "start": 1597, - "end": 1607, - "raw": "\n\ttext11\n\t", - "data": "\n\ttext11\n\t" + "start": 1632, + "end": 1642, + "raw": "\n\ttext16\n\t", + "data": "\n\ttext16\n\t" }, { "type": "ExpressionTag", - "start": 1607, - "end": 1724, + "start": 1642, + "end": 1759, "expression": { "type": "CallExpression", - "start": 1608, - "end": 1723, + "start": 1643, + "end": 1758, "loc": { "start": { "line": 55, @@ -822,8 +822,8 @@ }, "callee": { "type": "Identifier", - "start": 1608, - "end": 1610, + "start": 1643, + "end": 1645, "loc": { "start": { "line": 55, @@ -839,8 +839,8 @@ "arguments": [ { "type": "Identifier", - "start": 1614, - "end": 1658, + "start": 1649, + "end": 1693, "loc": { "start": { "line": 56, @@ -855,8 +855,8 @@ }, { "type": "Identifier", - "start": 1662, - "end": 1720, + "start": 1697, + "end": 1755, "loc": { "start": { "line": 57, @@ -875,26 +875,26 @@ }, { "type": "Text", - "start": 1724, - "end": 1725, + "start": 1759, + "end": 1760, "raw": " ", "data": " " }, { "type": "RegularElement", - "start": 1725, - "end": 1739, + "start": 1760, + "end": 1774, "name": "b", "name_loc": { "start": { "line": 58, "column": 5, - "character": 1726 + "character": 1761 }, "end": { "line": 58, "column": 6, - "character": 1727 + "character": 1762 } }, "attributes": [], @@ -903,8 +903,8 @@ "nodes": [ { "type": "Text", - "start": 1728, - "end": 1735, + "start": 1763, + "end": 1770, "raw": "inline6", "data": "inline6" } @@ -913,10 +913,10 @@ }, { "type": "Text", - "start": 1739, - "end": 1747, - "raw": " text12\n", - "data": " text12\n" + "start": 1774, + "end": 1789, + "raw": " text17 text18\n", + "data": " text17 text18\n" } ] } diff --git a/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/input.svelte index f8f60c26a..bf0d7908c 100644 --- a/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/input.svelte @@ -14,46 +14,46 @@ inline1 - inline2 text2 + inline2 text2 text3

- text3 + text4 inline3 - text4 + text5 text6

- text5 + text7 {expr1} - {expr2} text6 + {expr2} text8 text9

- text7 + text10 alpha bravo charlie delta echo foxtrot golf hotel india juliett kilo lima mike november oscar - text8 + text11 text12

- text9 inline4 inline5 text10 + text13 text14 inline4 inline5 text15

- text11 + text16 {fn( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb - )} inline6 text12 + )} inline6 text17 text18

diff --git a/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/prettier_variant_fitting_newline.svelte b/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/prettier_variant_fitting_newline.svelte index 4d98e4292..ebe8b80f4 100644 --- a/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/prettier_variant_fitting_newline.svelte +++ b/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/prettier_variant_fitting_newline.svelte @@ -15,48 +15,48 @@ inline1
inline2 - text2 + text2 text3

- text3 + text4 inline3 - text4 + text5 text6

- text5 + text7 {expr1} - {expr2} text6 + {expr2} text8 text9

- text7 + text10 alpha bravo charlie delta echo foxtrot golf hotel india juliett kilo lima mike november oscar - text8 + text11 text12

- text9 inline4 - inline5 text10 + text13 text14 inline4 + inline5 text15

- text11 + text16 {fn( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb )} - inline6 text12 + inline6 text17 text18

diff --git a/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/unformatted_glued_head.svelte b/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/unformatted_glued_head.svelte index acd933220..d5accd723 100644 --- a/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/unformatted_glued_head.svelte +++ b/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/unformatted_glued_head.svelte @@ -13,44 +13,44 @@ inline1 - inline2 text2 + inline2 text2 text3

- text3 + text4 inline3 - text4 + text5 text6 -

text5 +

text7 {expr1} - {expr2} text6 + {expr2} text8 text9

-

text7 +

text10 alpha bravo charlie delta echo foxtrot golf hotel india juliett kilo lima mike november oscar - text8 + text11 text12

- text9 inline4 inline5 text10 + text13 text14 inline4 inline5 text15

- text11 + text16 {fn( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb - )} inline6 text12 + )} inline6 text17 text18

diff --git a/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/variant_space.svelte b/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/variant_space.svelte index b3770e034..4ecb2a814 100644 --- a/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/variant_space.svelte +++ b/tests/fixtures/svelte/elements/sibling_newline_after_multiline_prettier_divergence/variant_space.svelte @@ -13,45 +13,45 @@ text1 inline1 - inline2 text2 +
inline2 text2 text3

- text3 + text4 inline3 - text4 + text5 text6

- text5 + text7 {expr1} - {expr2} text6 + {expr2} text8 text9

- text7 + text10 alpha bravo charlie delta echo foxtrot golf hotel india juliett kilo lima mike november oscar - text8 + text11 text12

- text9 inline4 inline5 text10 + text13 text14 inline4 inline5 text15

- text11 + text16 {fn( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb - )} inline6 text12 + )} inline6 text17 text18

diff --git a/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/README.md b/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/README.md index e4d42cf02..16ca00789 100644 --- a/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/README.md +++ b/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/README.md @@ -5,13 +5,13 @@ sibling text is **preserved when the element renders multiline**, and **reflowed renders inline** — the boundary's layout follows the unit's rendered layout, not the spelling alone: -- `⏎text2` after a multiline-rendering `` keeps the text on its own line (the +- `⏎text3` after a multiline-rendering `` keeps the text on its own line (the author separated the prose from the tag pile; both forms are clean, so the authored - one is kept — Tier-2). The **space** spelling stays hugged (` text3.`), so the + one is kept — Tier-2). The **space** spelling stays hugged (` text4.`), so the boundary is dual-stable there: `variant_welded` pins the hugged form at the terminal boundaries, where prettier holds it too. - After an element that renders **inline**, the newline is a spelling difference only - and reflows with the fill (`text4 {expr3} text5` — the fitting + and reflows with the fill (`text5 text6 {expr3} text7` — the fitting control): `prettier_variant_fitting_newline` pins the newline authoring, which prettier keeps stable and tsv packs. @@ -21,7 +21,7 @@ forced break exactly when the element's subtree actually emitted one — layout- render, with no measurement change anywhere. Prettier preserves the authored spelling at every one of these boundaries regardless of the element's layout (with one exception it normalizes itself: a **non-terminal** -space-hugged tail after a multiline element, ` text2⏎ text3⏎ text3.`, `
text8.`) are its +preserves it, and tsv's welded terminal tails (` text4.`, `
text11.`) are its own terminal-hug stance. See [conformance_prettier_svelte.md §Svelte: Inline content block-style](../../../../../docs/conformance_prettier_svelte.md#svelte-inline-content-block-style). diff --git a/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/expected.json index 2bb278063..d01dcaf53 100644 --- a/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 219, + "end": 239, "type": "Root", "fragment": { "type": "Fragment", @@ -10,7 +10,7 @@ { "type": "RegularElement", "start": 0, - "end": 92, + "end": 98, "name": "p", "name_loc": { "start": { @@ -31,49 +31,49 @@ { "type": "Text", "start": 3, - "end": 12, - "raw": "\n\ttext1\n\t", - "data": "\n\ttext1\n\t" + "end": 18, + "raw": "\n\ttext1 text2\n\t", + "data": "\n\ttext1 text2\n\t" }, { "type": "RegularElement", - "start": 12, - "end": 41, + "start": 18, + "end": 47, "name": "a", "name_loc": { "start": { "line": 3, "column": 2, - "character": 13 + "character": 19 }, "end": { "line": 3, "column": 3, - "character": 14 + "character": 20 } }, "attributes": [ { "type": "Attribute", - "start": 15, - "end": 24, + "start": 21, + "end": 30, "name": "href", "name_loc": { "start": { "line": 3, "column": 4, - "character": 15 + "character": 21 }, "end": { "line": 3, "column": 8, - "character": 19 + "character": 25 } }, "value": [ { - "start": 21, - "end": 23, + "start": 27, + "end": 29, "type": "Text", "raw": "/x", "data": "/x" @@ -86,19 +86,19 @@ "nodes": [ { "type": "Text", - "start": 25, - "end": 28, + "start": 31, + "end": 34, "raw": "\n\t\t", "data": "\n\t\t" }, { "type": "ExpressionTag", - "start": 28, - "end": 35, + "start": 34, + "end": 41, "expression": { "type": "Identifier", - "start": 29, - "end": 34, + "start": 35, + "end": 40, "loc": { "start": { "line": 4, @@ -114,8 +114,8 @@ }, { "type": "Text", - "start": 35, - "end": 37, + "start": 41, + "end": 43, "raw": "\n\t", "data": "\n\t" } @@ -124,50 +124,50 @@ }, { "type": "Text", - "start": 41, - "end": 50, - "raw": "\n\ttext2\n\t", - "data": "\n\ttext2\n\t" + "start": 47, + "end": 56, + "raw": "\n\ttext3\n\t", + "data": "\n\ttext3\n\t" }, { "type": "RegularElement", - "start": 50, - "end": 79, + "start": 56, + "end": 85, "name": "a", "name_loc": { "start": { "line": 7, "column": 2, - "character": 51 + "character": 57 }, "end": { "line": 7, "column": 3, - "character": 52 + "character": 58 } }, "attributes": [ { "type": "Attribute", - "start": 53, - "end": 62, + "start": 59, + "end": 68, "name": "href", "name_loc": { "start": { "line": 7, "column": 4, - "character": 53 + "character": 59 }, "end": { "line": 7, "column": 8, - "character": 57 + "character": 63 } }, "value": [ { - "start": 59, - "end": 61, + "start": 65, + "end": 67, "type": "Text", "raw": "/y", "data": "/y" @@ -180,19 +180,19 @@ "nodes": [ { "type": "Text", - "start": 63, - "end": 66, + "start": 69, + "end": 72, "raw": "\n\t\t", "data": "\n\t\t" }, { "type": "ExpressionTag", - "start": 66, - "end": 73, + "start": 72, + "end": 79, "expression": { "type": "Identifier", - "start": 67, - "end": 72, + "start": 73, + "end": 78, "loc": { "start": { "line": 8, @@ -208,8 +208,8 @@ }, { "type": "Text", - "start": 73, - "end": 75, + "start": 79, + "end": 81, "raw": "\n\t", "data": "\n\t" } @@ -218,36 +218,36 @@ }, { "type": "Text", - "start": 79, - "end": 88, - "raw": "\n\ttext3.\n", - "data": "\n\ttext3.\n" + "start": 85, + "end": 94, + "raw": "\n\ttext4.\n", + "data": "\n\ttext4.\n" } ] } }, { "type": "Text", - "start": 92, - "end": 94, + "start": 98, + "end": 100, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 94, - "end": 140, + "start": 100, + "end": 152, "name": "p", "name_loc": { "start": { "line": 13, "column": 1, - "character": 95 + "character": 101 }, "end": { "line": 13, "column": 2, - "character": 96 + "character": 102 } }, "attributes": [], @@ -256,50 +256,50 @@ "nodes": [ { "type": "Text", - "start": 97, - "end": 105, - "raw": "\n\ttext4 ", - "data": "\n\ttext4 " + "start": 103, + "end": 117, + "raw": "\n\ttext5 text6 ", + "data": "\n\ttext5 text6 " }, { "type": "RegularElement", - "start": 105, - "end": 129, + "start": 117, + "end": 141, "name": "a", "name_loc": { "start": { "line": 14, - "column": 8, - "character": 106 + "column": 14, + "character": 118 }, "end": { "line": 14, - "column": 9, - "character": 107 + "column": 15, + "character": 119 } }, "attributes": [ { "type": "Attribute", - "start": 108, - "end": 117, + "start": 120, + "end": 129, "name": "href", "name_loc": { "start": { "line": 14, - "column": 10, - "character": 108 + "column": 16, + "character": 120 }, "end": { "line": 14, - "column": 14, - "character": 112 + "column": 20, + "character": 124 } }, "value": [ { - "start": 114, - "end": 116, + "start": 126, + "end": 128, "type": "Text", "raw": "/z", "data": "/z" @@ -312,20 +312,20 @@ "nodes": [ { "type": "ExpressionTag", - "start": 118, - "end": 125, + "start": 130, + "end": 137, "expression": { "type": "Identifier", - "start": 119, - "end": 124, + "start": 131, + "end": 136, "loc": { "start": { "line": 14, - "column": 21 + "column": 27 }, "end": { "line": 14, - "column": 26 + "column": 32 } }, "name": "expr3" @@ -336,36 +336,36 @@ }, { "type": "Text", - "start": 129, - "end": 136, - "raw": " text5\n", - "data": " text5\n" + "start": 141, + "end": 148, + "raw": " text7\n", + "data": " text7\n" } ] } }, { "type": "Text", - "start": 140, - "end": 142, + "start": 152, + "end": 154, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 142, - "end": 218, + "start": 154, + "end": 238, "name": "span", "name_loc": { "start": { "line": 17, "column": 1, - "character": 143 + "character": 155 }, "end": { "line": 17, "column": 5, - "character": 147 + "character": 159 } }, "attributes": [], @@ -374,26 +374,26 @@ "nodes": [ { "type": "Text", - "start": 148, - "end": 157, - "raw": "\n\ttext6\n\t", - "data": "\n\ttext6\n\t" + "start": 160, + "end": 175, + "raw": "\n\ttext8 text9\n\t", + "data": "\n\ttext8 text9\n\t" }, { "type": "Component", - "start": 157, - "end": 202, + "start": 175, + "end": 221, "name": "Comp", "name_loc": { "start": { "line": 19, "column": 2, - "character": 158 + "character": 176 }, "end": { "line": 19, "column": 6, - "character": 162 + "character": 180 } }, "attributes": [], @@ -402,20 +402,20 @@ "nodes": [ { "type": "Text", - "start": 163, - "end": 166, + "start": 181, + "end": 184, "raw": "\n\t\t", "data": "\n\t\t" }, { "type": "IfBlock", "elseif": false, - "start": 166, - "end": 193, + "start": 184, + "end": 212, "test": { "type": "Identifier", - "start": 171, - "end": 175, + "start": 189, + "end": 193, "loc": { "start": { "line": 20, @@ -433,10 +433,10 @@ "nodes": [ { "type": "Text", - "start": 176, - "end": 188, - "raw": "\n\t\t\ttext7\n\t\t", - "data": "\n\t\t\ttext7\n\t\t" + "start": 194, + "end": 207, + "raw": "\n\t\t\ttext10\n\t\t", + "data": "\n\t\t\ttext10\n\t\t" } ] }, @@ -444,8 +444,8 @@ }, { "type": "Text", - "start": 193, - "end": 195, + "start": 212, + "end": 214, "raw": "\n\t", "data": "\n\t" } @@ -454,10 +454,10 @@ }, { "type": "Text", - "start": 202, - "end": 211, - "raw": "\n\ttext8.\n", - "data": "\n\ttext8.\n" + "start": 221, + "end": 231, + "raw": "\n\ttext11.\n", + "data": "\n\ttext11.\n" } ] } diff --git a/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/input.svelte index b041ced4c..ee9e492fd 100644 --- a/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/input.svelte @@ -1,25 +1,25 @@

- text1 + text1 text2 {expr1} - text2 + text3 {expr2} - text3. + text4.

- text4 {expr3} text5 + text5 text6 {expr3} text7

- text6 + text8 text9 {#if cond} - text7 + text10 {/if} - text8. + text11. diff --git a/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/prettier_variant_fitting_newline.svelte b/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/prettier_variant_fitting_newline.svelte index ca0b1764c..8fefd2a19 100644 --- a/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/prettier_variant_fitting_newline.svelte +++ b/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/prettier_variant_fitting_newline.svelte @@ -1,27 +1,27 @@

- text1 + text1 text2 {expr1} - text2 + text3 {expr2} - text3. + text4.

- text4 + text5 text6 {expr3} - text5 + text7

- text6 + text8 text9 {#if cond} - text7 + text10 {/if} - text8. + text11. diff --git a/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/variant_welded.svelte b/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/variant_welded.svelte index f8b9406b6..5db6b5c33 100644 --- a/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/variant_welded.svelte +++ b/tests/fixtures/svelte/elements/tail_newline_after_multiline_prettier_divergence/variant_welded.svelte @@ -1,23 +1,23 @@

- text1 + text1 text2 {expr1} - text2 + text3 {expr2} - text3. + text4.

- text4 {expr3} text5 + text5 text6 {expr3} text7

- text6 + text8 text9 {#if cond} - text7 + text10 {/if} - text8. +
text11.
diff --git a/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/README.md b/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/README.md index 4d4be6ca4..cb9c07883 100644 --- a/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/README.md +++ b/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/README.md @@ -28,8 +28,15 @@ separated by a space or a newline, so tsv converges them; prettier holds a stabl ## Cases The one shape that diverges, and nothing else: two escaped-bracket tags on adjacent lines, in a -run whose glued `div` / `code` words are the prose the rule needs. The comment is load-bearing — -it bounds the run above, so the pair is the whole flowing run. +run whose `text1 text2` content is the prose the rule needs. The glued `div` / `code` words are +one word each, and a pair with nothing else in its run is a list of labels whose authored newline +both formatters hold — the flow rule's prose gate, +[inline_sibling_newline_label_hold](../../elements/inline_sibling_newline_label_hold_prettier_divergence/); +its space spelling is +[inline_sibling_newline_label_hold_tag_pair_space](../../elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/), +where tsv keeps the pair packed and prettier splits it — so the two-word content is what makes +the newline-authored run flow here. The comment is load-bearing — it bounds the run above, so the +pair and its content are the whole flowing run. The rest of the escaped-angle-bracket matrix lives in the sibling [angle_escaped](../angle_escaped/), where both formatters agree: those cases either have no diff --git a/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/expected.json b/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/expected.json index 1dd173b40..54c999565 100644 --- a/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 96, + "end": 130, "type": "Root", "fragment": { "type": "Fragment", @@ -10,24 +10,24 @@ { "type": "Comment", "start": 0, - "end": 51, - "data": " Two escaped-bracket tags on adjacent lines " + "end": 74, + "data": " Two escaped-bracket tags on adjacent lines, with prose in the run " }, { "type": "Text", - "start": 51, - "end": 52, + "start": 74, + "end": 75, "raw": "\n", "data": "\n" }, { "type": "ExpressionTag", - "start": 52, - "end": 57, + "start": 75, + "end": 80, "expression": { "type": "Literal", - "start": 53, - "end": 56, + "start": 76, + "end": 79, "loc": { "start": { "line": 2, @@ -44,19 +44,19 @@ }, { "type": "Text", - "start": 57, - "end": 60, + "start": 80, + "end": 83, "raw": "div", "data": "div" }, { "type": "ExpressionTag", - "start": 60, - "end": 65, + "start": 83, + "end": 88, "expression": { "type": "Literal", - "start": 61, - "end": 64, + "start": 84, + "end": 87, "loc": { "start": { "line": 2, @@ -73,19 +73,19 @@ }, { "type": "Text", - "start": 65, - "end": 66, + "start": 88, + "end": 89, "raw": " ", "data": " " }, { "type": "ExpressionTag", - "start": 66, - "end": 71, + "start": 89, + "end": 94, "expression": { "type": "Literal", - "start": 67, - "end": 70, + "start": 90, + "end": 93, "loc": { "start": { "line": 2, @@ -102,19 +102,19 @@ }, { "type": "Text", - "start": 71, - "end": 75, + "start": 94, + "end": 98, "raw": "code", "data": "code" }, { "type": "ExpressionTag", - "start": 75, - "end": 80, + "start": 98, + "end": 103, "expression": { "type": "Literal", - "start": 76, - "end": 79, + "start": 99, + "end": 102, "loc": { "start": { "line": 2, @@ -129,22 +129,29 @@ "raw": "'>'" } }, + { + "type": "Text", + "start": 103, + "end": 114, + "raw": "text1 text2", + "data": "text1 text2" + }, { "type": "ExpressionTag", - "start": 80, - "end": 85, + "start": 114, + "end": 119, "expression": { "type": "Literal", - "start": 81, - "end": 84, + "start": 115, + "end": 118, "loc": { "start": { "line": 2, - "column": 29 + "column": 40 }, "end": { "line": 2, - "column": 32 + "column": 43 } }, "value": "<", @@ -153,27 +160,27 @@ }, { "type": "Text", - "start": 85, - "end": 90, + "start": 119, + "end": 124, "raw": "/code", "data": "/code" }, { "type": "ExpressionTag", - "start": 90, - "end": 95, + "start": 124, + "end": 129, "expression": { "type": "Literal", - "start": 91, - "end": 94, + "start": 125, + "end": 128, "loc": { "start": { "line": 2, - "column": 39 + "column": 50 }, "end": { "line": 2, - "column": 42 + "column": 53 } }, "value": ">", diff --git a/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/input.svelte b/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/input.svelte index 9e879d220..9e0a09d2c 100644 --- a/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/input.svelte @@ -1,2 +1,2 @@ - -{'<'}div{'>'} {'<'}code{'>'}{'<'}/code{'>'} + +{'<'}div{'>'} {'<'}code{'>'}text1 text2{'<'}/code{'>'} diff --git a/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/output_prettier.svelte b/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/output_prettier.svelte index 2ee11a682..33b1783d9 100644 --- a/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/output_prettier.svelte +++ b/tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/output_prettier.svelte @@ -1,3 +1,3 @@ - + {'<'}div{'>'} -{'<'}code{'>'}{'<'}/code{'>'} +{'<'}code{'>'}text1 text2{'<'}/code{'>'} From 43209476f32842f3aac8feeb9d92daf99b0f2abf Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Tue, 25 Aug 2026 13:03:46 -0400 Subject: [PATCH 2/4] wip --- crates/tsv_svelte/src/ast/internal.rs | 8 +++++ .../src/printer/nodes/element_analysis.rs | 3 +- .../src/printer/nodes/fragment_doc.rs | 30 +++++++++------- .../src/printer/nodes/fragment_text_doc.rs | 34 +++++++++---------- 4 files changed, 42 insertions(+), 33 deletions(-) diff --git a/crates/tsv_svelte/src/ast/internal.rs b/crates/tsv_svelte/src/ast/internal.rs index 522a1e38c..21d68cab7 100644 --- a/crates/tsv_svelte/src/ast/internal.rs +++ b/crates/tsv_svelte/src/ast/internal.rs @@ -1549,6 +1549,14 @@ pub fn text_edge_ws(raw: &str, leading: bool) -> &str { } } +/// The number of newlines in a text node's `leading` (else trailing) edge whitespace run +/// ([`text_edge_ws`]) — `0` for a glued edge, `1` for an authored line break, `2+` for an +/// authored blank line. The one count every edge-newline question reads. +#[inline] +pub fn text_edge_newlines(raw: &str, leading: bool) -> usize { + text_edge_ws(raw, leading).matches('\n').count() +} + /// Svelte Text node - raw text content /// /// Represents static text in the template or attribute values. diff --git a/crates/tsv_svelte/src/printer/nodes/element_analysis.rs b/crates/tsv_svelte/src/printer/nodes/element_analysis.rs index bcc075e65..659644e66 100644 --- a/crates/tsv_svelte/src/printer/nodes/element_analysis.rs +++ b/crates/tsv_svelte/src/printer/nodes/element_analysis.rs @@ -262,8 +262,7 @@ impl<'a> Printer<'a> { // A pair is reflowable when the boundary between them is NOT glued — the whitespace lives on // one of the two texts' facing edges, so there is a break point to reflow at. Same predicate // the fragment path's glue decisions ask, negated (`Printer::text_glued_before` / `_after`). - Printer::run_is_prose(run.iter().map(|n| self.prose_words(n)).max().unwrap_or(0)) - && run.windows(2).any(|w| { + Printer::run_is_prose(self.run_prose_words(run)) && run.windows(2).any(|w| { matches!(&w[0], FragmentNode::Text(t) if !Self::text_glued_after(t.raw(source))) || matches!(&w[1], FragmentNode::Text(t) if !Self::text_glued_before(t.raw(source))) }) diff --git a/crates/tsv_svelte/src/printer/nodes/fragment_doc.rs b/crates/tsv_svelte/src/printer/nodes/fragment_doc.rs index 5fb373bea..e8dd63b5e 100644 --- a/crates/tsv_svelte/src/printer/nodes/fragment_doc.rs +++ b/crates/tsv_svelte/src/printer/nodes/fragment_doc.rs @@ -14,7 +14,7 @@ use super::element_doc::MultilineCause; use super::fragment_text_doc::TextChildContext; use super::helpers::{is_control_flow_block, is_inline_content}; -use crate::ast::internal::{self, FragmentNode, text_edge_ws}; +use crate::ast::internal::{self, FragmentNode, text_edge_newlines}; use crate::printer::Printer; use tsv_lang::doc::{DocBuf, arena::DocId}; use tsv_lang::is_format_ignore_directive; @@ -731,7 +731,7 @@ impl<'a> Printer<'a> { } } - /// Whether a run whose [`Self::prose_words`] total is `words` is **prose** — holds a fill to + /// Whether a run whose [`Self::prose_words`] maximum is `words` is **prose** — holds a fill to /// reflow into. The cliff is two words: a run holding one is a label /// ([`Self::prose_words`]), and two is where label and prose genuinely blur (`Remember me` /// packs), so the boundary is stated here rather than approximated by a sentence heuristic @@ -763,10 +763,18 @@ impl<'a> Printer<'a> { words >= 1 } - /// Scan the inline run beginning at `start`: its exclusive end, and the most words any one - /// of its nodes carries ([`Self::prose_words`]) — graded by [`Self::run_is_prose`] for the - /// newline hold (a `fill` to reflow into) and by [`Self::run_has_text`] for the space-spelled - /// tag separator (a fill to sit in at all). + /// The most words any one node of `nodes` carries ([`Self::prose_words`]) — the run count + /// [`Self::run_is_prose`] and [`Self::run_has_text`] grade. The one counter for both readers + /// of a run: [`Self::scan_inline_run`] over the run it has just bounded, and + /// `Printer::content_is_reflowable_fill` over an element's already-bounded content. + pub(super) fn run_prose_words(&self, nodes: &[FragmentNode<'_>]) -> usize { + nodes.iter().map(|n| self.prose_words(n)).max().unwrap_or(0) + } + + /// Scan the inline run beginning at `start`: its exclusive end, and its + /// [`Self::run_prose_words`] — graded by [`Self::run_is_prose`] for the newline hold (a + /// `fill` to reflow into) and by [`Self::run_has_text`] for the space-spelled tag separator + /// (a fill to sit in at all). /// /// A run ends at a node [`Self::breaks_inline_run`] names, and at an authored blank line a /// content text carries on its edge ([`Self::text_edge_has_blank`]) — the boundary set @@ -785,7 +793,6 @@ impl<'a> Printer<'a> { /// `max` advances the cursor past it, so the caller cannot stall on it. fn scan_inline_run(&self, nodes: &[FragmentNode<'_>], start: usize) -> (usize, usize) { let mut end = start; - let mut words = 0usize; while end < nodes.len() && !self.breaks_inline_run(&nodes[end]) { // An authored blank line bounds the run wherever the parser put it. Between two // non-text siblings it is a whitespace-only node `breaks_inline_run` sees; beside a @@ -798,13 +805,13 @@ impl<'a> Printer<'a> { if end > start && self.text_edge_has_blank(&nodes[end], true) { break; } - words = words.max(self.prose_words(&nodes[end])); end += 1; if self.text_edge_has_blank(&nodes[end - 1], false) { break; } } - (end.max(start + 1), words) + let end = end.max(start + 1); + (end, self.run_prose_words(&nodes[start..end])) } /// Whether `node` is a content text whose `leading` (else trailing) edge whitespace carries @@ -814,10 +821,7 @@ impl<'a> Printer<'a> { fn text_edge_has_blank(&self, node: &FragmentNode<'_>, leading: bool) -> bool { match node { FragmentNode::Text(t) if !t.is_collapsible_ws_only => { - text_edge_ws(t.raw(self.source), leading) - .matches('\n') - .count() - >= 2 + text_edge_newlines(t.raw(self.source), leading) >= 2 } _ => false, } diff --git a/crates/tsv_svelte/src/printer/nodes/fragment_text_doc.rs b/crates/tsv_svelte/src/printer/nodes/fragment_text_doc.rs index f7aa7b320..3ba36fba4 100644 --- a/crates/tsv_svelte/src/printer/nodes/fragment_text_doc.rs +++ b/crates/tsv_svelte/src/printer/nodes/fragment_text_doc.rs @@ -14,7 +14,7 @@ use super::element_doc::MultilineCause; use super::fragment_doc::{DeferredBoundary, text_starts_with_linebreak}; use super::helpers::{is_control_flow_block, is_inline_content}; -use crate::ast::internal::{FragmentNode, Text, split_collapsible_ws, text_edge_ws}; +use crate::ast::internal::{FragmentNode, Text, split_collapsible_ws, text_edge_newlines}; use crate::printer::Printer; use smallvec::SmallVec; use tsv_lang::doc::{DocBuf, arena::DocId}; @@ -311,9 +311,10 @@ impl<'a> Printer<'a> { // multiline arm's newline case re-spells itself as the space rather than emitting a // parallel form. // - // ⚠️ **Asked once, ahead of the multiline split, because both arms below need this - // same answer.** The question is about the RUN and its two neighbours; nothing in it - // depends on WHY the container went multiline. A conjunct on the cause would be dead in + // ⚠️ **The run and neighbour facts are asked once, ahead of the multiline split, because + // both arms below need the same answer** (`tag_space_defers`; the newline hold + // `separator_flows` is read by the multiline arm alone, the only place a newline is a + // hardline question). Nothing in either depends on WHY the container went multiline. A conjunct on the cause would be dead in // the arm the `!multiline` test already selected, and wrong as a narrowing: the flow // rule's other two call sites — a content text's leading and trailing runs — carry no // cause gate at all, so a container-keyed one half-applies the rule. Within a single @@ -357,9 +358,9 @@ impl<'a> Printer<'a> { // to protect, so the bare break is strictly better. // // ⚠️ A run holding a content text is the exception, and it is the same question - // the multiline arm's `next_is_tag && tag_space_defers` case asks — asked here so one run's - // interior does not depend on WHY its element went multiline. A width-broken - // element and a newline-authored one lay the same prose run out identically: + // the multiline arm's `next_is_tag && tag_space_defers` case asks — asked here so + // one run's interior does not depend on WHY its element went multiline. A + // width-broken element and a newline-authored one lay the same run out identically: // both defer to the next sibling's per-width `group([line, tag])`, so the run // packs. Without this the two modes hold contradictory interior policies — the // bare `line` resolves all-or-nothing with the parent group, which is already @@ -655,9 +656,9 @@ impl<'a> Printer<'a> { // a separator-like node must not flow even when its run holds prose elsewhere, or the // fill re-reads a break it emitted itself (the NBSP F1 break). let separator_like_text = Self::is_separator_like_text(&text.data(self.source)); - let leading_run = text_edge_ws(raw, true); + let leading_newlines = text_edge_newlines(raw, true); let leading_newline_flows = self.boundary_newline_flows( - leading_run.matches('\n').count(), + leading_newlines, run_has_prose, separator_like_text, prev_node, @@ -667,7 +668,7 @@ impl<'a> Printer<'a> { // boundary — the tag's own break_after is the line. Checked ahead of the // `splitTextToDocs` linebreak arm below, which would double it. trim_left = true; - if leading_run.matches('\n').count() >= 2 { + if leading_newlines >= 2 { child_docs.push(d.hardline()); } } else if multiline @@ -688,7 +689,7 @@ impl<'a> Printer<'a> { // A blank line (2+ leading newlines) is preserved as `[hardline, hardline]` — // prettier's `splitTextToDocs` startsWithLinebreak(_, 2). A single newline → one // hardline. - if leading_run.matches('\n').count() >= 2 { + if leading_newlines >= 2 { child_docs.push(d.hardline()); } child_docs.push(d.hardline()); @@ -720,7 +721,7 @@ impl<'a> Printer<'a> { // the space); in an inline container (`multiline` false) the hardline arms above // are skipped, so a blank-line run can arrive too — the exact count keeps a blank // out of the layout-keyed rule, which is defined over the single-newline spelling. - let authored_newline = leading_run.matches('\n').count() == 1; + let authored_newline = leading_newlines == 1; let glued_head = self.leading_boundary_glued(trimmed_nodes, prev_sibling_head, content_bounds.0); if authored_newline && !prev_is_tag && !separator_like_text && !glued_head { @@ -938,12 +939,9 @@ impl<'a> Printer<'a> { // this handler's `handle_whitespace_of_prev_text`) so the next element gets wrapped with // group([line, element]). let mut trailing_line = false; - // Count newlines in the trailing whitespace run (multiline structural-break detection). - let trailing_ws_newlines = if has_trailing_ws { - text_edge_ws(raw, false).matches('\n').count() - } else { - 0 - }; + // Newlines in the trailing whitespace run (multiline structural-break detection); `0` for + // a glued edge, since the edge run is then empty. + let trailing_ws_newlines = text_edge_newlines(raw, false); let mut trailing_hardlines = 0usize; // The third face of the same rule (after the whitespace-only separator and a content // text's leading run): a SINGLE trailing newline before flowing inline content is a From 15b85bb56a1768e38ddae631184e02b9eec89b16 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Tue, 25 Aug 2026 14:15:10 -0400 Subject: [PATCH 3/4] wip --- .../src/cli/commands/authoring_audit.rs | 16 +- .../src/printer/nodes/element_analysis.rs | 70 +- .../src/printer/nodes/fragment_doc.rs | 84 ++- .../src/printer/nodes/fragment_text_doc.rs | 23 +- crates/tsv_svelte/src/printer/text.rs | 98 ++- docs/conformance_prettier_svelte.md | 2 +- .../README.md | 13 +- .../expected.json | 420 ++++++------ .../input.svelte | 4 +- .../prettier_variant_dangle.svelte | 4 +- .../unformatted_ours_newline.svelte | 4 +- .../variant_boundary_newline.svelte | 4 +- .../README.md | 22 +- .../expected.json | 623 +++++++++++++++++- .../input.svelte | 28 + .../prettier_variant_dangle.svelte | 31 + .../unformatted_ours_hugged.svelte | 23 + .../README.md | 6 +- 18 files changed, 1134 insertions(+), 341 deletions(-) diff --git a/crates/tsv_debug/src/cli/commands/authoring_audit.rs b/crates/tsv_debug/src/cli/commands/authoring_audit.rs index 22d27bfe6..96d7828bc 100644 --- a/crates/tsv_debug/src/cli/commands/authoring_audit.rs +++ b/crates/tsv_debug/src/cli/commands/authoring_audit.rs @@ -70,7 +70,7 @@ use std::path::{Path, PathBuf}; use tsv_cli::cli::format_source::format_source; use tsv_cli::cli::input::ParserType; -use tsv_svelte::ast::internal::{FragmentNode, is_collapsible_ws_char}; +use tsv_svelte::ast::internal::{FragmentNode, is_collapsible_ws_char, text_edge_ws}; use crate::audit::vacuity::check_graded_nonzero; use crate::cli::CliError; @@ -235,7 +235,7 @@ fn collect_sites(nodes: &[FragmentNode<'_>], src: &str, ws_sig: bool, out: &mut } else { // Content text: its leading run is a boundary iff a previous // sibling exists; its trailing run iff a next sibling exists. - let lead_len = raw.len() - raw.trim_start_matches(is_collapsible_ws_char).len(); + let lead_len = text_edge_ws(raw, true).len(); if i > 0 && lead_len > 0 && let Some((had_nl, flip)) = flip_run(&raw[..lead_len]) @@ -248,7 +248,7 @@ fn collect_sites(nodes: &[FragmentNode<'_>], src: &str, ws_sig: bool, out: &mut flipped: flip, }); } - let trail_len = raw.len() - raw.trim_end_matches(is_collapsible_ws_char).len(); + let trail_len = text_edge_ws(raw, false).len(); if i + 1 < len && trail_len > 0 && let Some((had_nl, flip)) = flip_run(&raw[raw.len() - trail_len..]) @@ -307,19 +307,13 @@ fn collect_boundary_sites( // for the trailing run. The two can't overlap: the fragment has non-whitespace content // between them. let lead = match first { - FragmentNode::Text(t) => { - let raw = t.raw(src); - raw.len() - raw.trim_start_matches(is_collapsible_ws_char).len() - } + FragmentNode::Text(t) => text_edge_ws(t.raw(src), true).len(), _ => 0, }; push_boundary_forms(content_start, content_start + lead, src, kinds.0, out); let trail = match last { - FragmentNode::Text(t) => { - let raw = t.raw(src); - raw.len() - raw.trim_end_matches(is_collapsible_ws_char).len() - } + FragmentNode::Text(t) => text_edge_ws(t.raw(src), false).len(), _ => 0, }; push_boundary_forms(content_end - trail, content_end, src, kinds.1, out); diff --git a/crates/tsv_svelte/src/printer/nodes/element_analysis.rs b/crates/tsv_svelte/src/printer/nodes/element_analysis.rs index 659644e66..30a54ec10 100644 --- a/crates/tsv_svelte/src/printer/nodes/element_analysis.rs +++ b/crates/tsv_svelte/src/printer/nodes/element_analysis.rs @@ -262,10 +262,12 @@ impl<'a> Printer<'a> { // A pair is reflowable when the boundary between them is NOT glued — the whitespace lives on // one of the two texts' facing edges, so there is a break point to reflow at. Same predicate // the fragment path's glue decisions ask, negated (`Printer::text_glued_before` / `_after`). - Printer::run_is_prose(self.run_prose_words(run)) && run.windows(2).any(|w| { + // The glue test leads: it short-circuits on the first unglued pair, where the word count + // has to reach the run's widest text before it can stop. + run.windows(2).any(|w| { matches!(&w[0], FragmentNode::Text(t) if !Self::text_glued_after(t.raw(source))) || matches!(&w[1], FragmentNode::Text(t) if !Self::text_glued_before(t.raw(source))) - }) + }) && Printer::run_is_prose(self.run_prose_words(run)) } /// Check if element content has source breaks (newlines) that should trigger multiline. @@ -369,9 +371,15 @@ impl<'a> Printer<'a> { // arms, and computed only once one can be reached (every boundary-air case above returns // before the scan). let is_fill = self.content_is_reflowable_fill(run); + // The run's own content edges — where the ELEMENT's boundary air lives. Not `0` and + // `run.len() - 1`: `trimmed_content_run` drops only whitespace-only text, so a HOISTED + // node (`{@debug}`, ``, `{@const}`, `{#snippet}`) sits at a real index and the + // text beside it is the effective edge. That is exactly `blocks/hoisted_boundary_convergence`. + let content_edges = + FragmentNode::content_bounds(run).unwrap_or((0, run.len().saturating_sub(1))); // Check for newlines in content between first and last non-whitespace nodes - run.iter().any(|n| { + run.iter().enumerate().any(|(idx, n)| { let FragmentNode::Text(t) = n else { return false; }; @@ -386,21 +394,40 @@ impl<'a> Printer<'a> { // living in different nodes. See [`Self::content_is_reflowable_fill`]. !is_fill && t.has_newline() } else { - // Text with content: exclude the boundary collapsible-whitespace runs - // on BOTH edges, whatever the node's position. An NBSP or form feed is content, - // so the trim keeps it attached. + // Text with content. An NBSP or form feed is content, so every trim below + // keeps it attached. + // + // ⚠️ **Two different reasons trim a text's edge run, and only ONE of them is the + // fill's** — keeping them apart is the whole of this arm. + // + // (a) **The element's own boundary air**, at `content_edges`. A newline there is + // the boundary question, answered by `boundary.both()` above; letting it reach + // this interior scan makes a ONE-SIDED boundary expand the element, which + // `elements/boundary_air_one_sided` pins as collapsing. Unconditional — it has + // nothing to do with what the content is made of. + // + // (b) **An edge facing a SIBLING**, which is the fill's separator: the fill owns + // it either way, reflowing it to a space when the run fits and to a break when it + // does not, so its spelling is not the element-expansion signal. Trimming just + // the fragment-edge sides (the old `is_first_content`/`is_last_content` match) + // left a middle text's separator run counted, which made `<span><code>a</code> + // b,⏎<code>c</code></span>` report SourceBreaks: the element went block-style on + // pass 1, the fill then reflowed that very newline away, and pass 2 — seeing no + // newline left — collapsed it inline. Two mechanisms reading one newline and + // answering differently, the same class [`MultilineCause`] closed at the + // separator-flow site (conformance_prettier_svelte.md §Svelte: Inline content + // block-style). // - // The edge run is a *separator* between this text and its neighbour, and the - // fill owns it either way — it reflows to a space when the run fits and to a - // break when it does not. So its spelling is not the element-expansion signal; - // only a newline strictly INSIDE the text's own content is. Trimming just the - // fragment-edge sides (the old `is_first_content`/`is_last_content` match) left a - // middle text's separator run counted, which made `<span><code>a</code> b,⏎<code>c - // </code></span>` report SourceBreaks: the element went block-style on pass 1, the - // fill then reflowed that very newline away, and pass 2 — seeing no newline left — - // collapsed it inline. Two mechanisms reading one newline and answering - // differently, the same class [`MultilineCause`] closed at the separator-flow site - // (conformance_prettier_svelte.md §Svelte: Inline content block-style). + // But (b)'s argument is the FILL's, so it is gated on `is_fill` like every other + // reader of that answer. Hoisting it out of the guard was invisible while any + // content text made its run a fill; once a ONE-WORD run became a label with no + // fill ([`Self::content_is_reflowable_fill`]), the hoist started answering the + // same physical newline two ways — held in a whitespace-only node, collapsed at + // a content text's edge — keyed on nothing but which node the parser folded it + // into. That is the accident the run scan's own blank-line boundary rules out + // one layer up (`Printer::scan_inline_run`), and prettier holds the newline at + // both spellings. Pinned by + // `elements/inline_interior_newline_label_hold_prettier_divergence`. // // The same argument reaches one step further inside a FILL, where the fill owns // the text's interior too: a newline there is one the fill itself wrapped in on a @@ -408,7 +435,14 @@ impl<'a> Printer<'a> { // two-mechanisms-one-newline bug, merely relocated from the edge run to the // middle of a sentence. That is the F1 break the suppression exists to stop — // see [`Self::content_is_reflowable_fill`]. - !is_fill && raw.trim_matches(is_collapsible_ws_char).contains('\n') + let mut scan = raw; + if idx == content_edges.0 || is_fill { + scan = scan.trim_start_matches(is_collapsible_ws_char); + } + if idx == content_edges.1 || is_fill { + scan = scan.trim_end_matches(is_collapsible_ws_char); + } + !is_fill && scan.contains('\n') } }) } diff --git a/crates/tsv_svelte/src/printer/nodes/fragment_doc.rs b/crates/tsv_svelte/src/printer/nodes/fragment_doc.rs index e8dd63b5e..5060819f3 100644 --- a/crates/tsv_svelte/src/printer/nodes/fragment_doc.rs +++ b/crates/tsv_svelte/src/printer/nodes/fragment_doc.rs @@ -14,8 +14,8 @@ use super::element_doc::MultilineCause; use super::fragment_text_doc::TextChildContext; use super::helpers::{is_control_flow_block, is_inline_content}; -use crate::ast::internal::{self, FragmentNode, text_edge_newlines}; -use crate::printer::Printer; +use crate::ast::internal::{self, FragmentNode}; +use crate::printer::{Printer, text}; use tsv_lang::doc::{DocBuf, arena::DocId}; use tsv_lang::is_format_ignore_directive; @@ -302,7 +302,7 @@ impl<'a> Printer<'a> { TextChildContext { cause, run_has_prose: Self::run_is_prose(run_words), - run_has_text: Self::run_has_text(run_words), + run_has_word: Self::run_has_word(run_words), content_bounds, glued_prefix: pending_glued_prefix.take(), prev_sibling_head, @@ -718,13 +718,15 @@ impl<'a> Printer<'a> { /// the node belongs to, so a one-word node that ends a real sentence flows with it. Pinned /// by `elements/inline_sibling_newline_label_hold_prettier_divergence` (the label shapes /// held, the two-word cliff and the one-word sentence tail flowing). - pub(super) fn prose_words(&self, node: &FragmentNode<'_>) -> usize { + fn prose_words(&self, node: &FragmentNode<'_>) -> usize { match node { FragmentNode::Text(t) if !t.is_collapsible_ws_only => { if Self::is_separator_like_text(&t.data(self.source)) { 0 } else { - internal::split_collapsible_ws(t.raw(self.source)).count() + internal::split_collapsible_ws(t.raw(self.source)) + .take(Self::PROSE_WORDS_CAP) + .count() } } _ => 0, @@ -748,32 +750,55 @@ impl<'a> Printer<'a> { /// which holds; real prose has a two-word node somewhere in its run. #[inline] pub(super) fn run_is_prose(words: usize) -> bool { - words >= 2 + words >= Self::PROSE_WORDS_CAP } - /// Whether a run whose [`Self::prose_words`] maximum is `words` holds a **content text** at - /// all — a fill for a whitespace-only separator to sit in, prose or not. The gate the - /// SPACE-spelled separator before a tag keeps (`TextChildContext::run_has_text`): the prose - /// gate is a hold on an authored NEWLINE and must never turn a space into one, so a one-word - /// run's `text1 {a} {b}` defers to the per-width group exactly as a prose run's does + /// The word count [`Self::prose_words`] and [`Self::run_prose_words`] **saturate** at — the + /// prose cliff [`Self::run_is_prose`] grades, and therefore the most either counter ever + /// needs to know. The two graders test `>= 1` and `>= 2` and nothing else, so a count capped + /// here is indistinguishable from the true one, and neither counter walks a long paragraph + /// to learn a number it would discard. + /// + /// The same trade `internal::Text::newline_count` makes one crate-module over, for the same + /// reason: the printer only ever asks `== 0` / `>= 1` / `>= 2` of it. + const PROSE_WORDS_CAP: usize = 2; + + /// Whether a run whose [`Self::prose_words`] maximum is `words` holds a **word** — one thing + /// for a fill to pack, prose or not. The gate the SPACE-spelled separator before a tag keeps + /// (`TextChildContext::run_has_word`): the prose gate is a hold on an authored NEWLINE and + /// must never turn a space into one, so a one-word run's `text1 {a} {b}` defers to the + /// per-width group exactly as a prose run's does /// (`elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence`), and - /// only a prose-FREE run's tag pair (`{a} {b}`) breaks with its container. + /// only a WORDLESS run's tag pair (`{a} {b}`) breaks with its container. + /// + /// A word, not "a content text": the two part on a content text that carries none, which is + /// a text whose *decoded* form is all whitespace — an ` ` node, or an entity-spelled + /// space (` `, the `inline_separator_entity_newline` case). Such a node is a separator + /// wearing content's clothing ([`Self::is_separator_like_text`]) and gives the separator + /// nothing to sit in, so the run answers `false` here even though a `Text` is present. #[inline] - pub(super) fn run_has_text(words: usize) -> bool { + pub(super) fn run_has_word(words: usize) -> bool { words >= 1 } /// The most words any one node of `nodes` carries ([`Self::prose_words`]) — the run count - /// [`Self::run_is_prose`] and [`Self::run_has_text`] grade. The one counter for both readers + /// [`Self::run_is_prose`] and [`Self::run_has_word`] grade. The one counter for both readers /// of a run: [`Self::scan_inline_run`] over the run it has just bounded, and /// `Printer::content_is_reflowable_fill` over an element's already-bounded content. pub(super) fn run_prose_words(&self, nodes: &[FragmentNode<'_>]) -> usize { - nodes.iter().map(|n| self.prose_words(n)).max().unwrap_or(0) + let mut words = 0; + for node in nodes { + words = words.max(self.prose_words(node)); + if words >= Self::PROSE_WORDS_CAP { + break; + } + } + words } /// Scan the inline run beginning at `start`: its exclusive end, and its /// [`Self::run_prose_words`] — graded by [`Self::run_is_prose`] for the newline hold (a - /// `fill` to reflow into) and by [`Self::run_has_text`] for the space-spelled tag separator + /// `fill` to reflow into) and by [`Self::run_has_word`] for the space-spelled tag separator /// (a fill to sit in at all). /// /// A run ends at a node [`Self::breaks_inline_run`] names, and at an authored blank line a @@ -801,12 +826,14 @@ impl<'a> Printer<'a> { // a blank beside a text split the layout (the blank-line arms hold it) but not the // count, and `text1 text2⏎<span>a</span>⏎⏎text3⏎<span>b</span>` flowed the one-word // half with the prose half as one run where the same shape with a comment for a - // boundary held it. + // boundary held it. Both edges are asked of the SAME node, so the trailing answer + // is taken before the cursor moves past it. if end > start && self.text_edge_has_blank(&nodes[end], true) { break; } + let ends_run = self.text_edge_has_blank(&nodes[end], false); end += 1; - if self.text_edge_has_blank(&nodes[end - 1], false) { + if ends_run { break; } } @@ -817,11 +844,26 @@ impl<'a> Printer<'a> { /// Whether `node` is a content text whose `leading` (else trailing) edge whitespace carries /// an authored blank line — the run boundary a blank spells when the parser folds it into /// the text beside it rather than into a whitespace-only node ([`Self::scan_inline_run`]). - /// Reads `raw`: a blank is a question about the bytes, like every blank-line scan. + /// + /// The node-unwrap alone: the blank question itself is the printer's one seam pair, + /// [`text::has_leading_blank_line`] / [`text::has_trailing_blank_line`], asked of the whole + /// `raw` so this caller never holds an edge slice of its own. Counting newlines inside a + /// slice of [`internal::text_edge_ws`] answers the same thing and is the near miss that + /// module's doc names — it is a newline TOTAL, correct only because the slice happens to be + /// pure whitespace. + /// + /// Pre-gated on the parse-time `newline_count` (saturating at 2, source-free), which bounds + /// either edge run: no blank line anywhere in `raw` means none at an edge of it, so the + /// common node never reaches `source` at all. fn text_edge_has_blank(&self, node: &FragmentNode<'_>, leading: bool) -> bool { match node { - FragmentNode::Text(t) if !t.is_collapsible_ws_only => { - text_edge_newlines(t.raw(self.source), leading) >= 2 + FragmentNode::Text(t) if !t.is_collapsible_ws_only && t.newline_count >= 2 => { + let raw = t.raw(self.source); + if leading { + text::has_leading_blank_line(raw) + } else { + text::has_trailing_blank_line(raw) + } } _ => false, } diff --git a/crates/tsv_svelte/src/printer/nodes/fragment_text_doc.rs b/crates/tsv_svelte/src/printer/nodes/fragment_text_doc.rs index 3ba36fba4..ce110c377 100644 --- a/crates/tsv_svelte/src/printer/nodes/fragment_text_doc.rs +++ b/crates/tsv_svelte/src/printer/nodes/fragment_text_doc.rs @@ -84,16 +84,16 @@ pub(super) struct TextChildContext { /// `next_is_tag` case, and the two emitting a different doc for one logical separator is /// exactly the period-2 cycle `cause` exists to close. pub(super) run_has_prose: bool, - /// Whether this node's inline run holds a content text at all ([`Printer::run_has_text`]) — - /// the gate the SPACE-spelled whitespace-only separator before a tag keeps, in both arms of - /// the separator site. Split from `run_has_prose` on purpose: the prose gate decides whether + /// Whether this node's inline run holds a word at all ([`Printer::run_has_word`]) — the gate + /// the SPACE-spelled whitespace-only separator before a tag keeps, in both arms of the + /// separator site. Split from `run_has_prose` on purpose: the prose gate decides whether /// an authored NEWLINE may reflow (a one-word run holds it), and a hold must never become a /// forced break — so a one-word run's space-spelled tag pair defers to the per-width group /// as a prose run's does (`inline_sibling_newline_label_hold_tag_pair_space`), while a - /// prose-FREE run's keeps the bare `line` that breaks with the container. Both arms read + /// WORDLESS run's keeps the bare `line` that breaks with the container. Both arms read /// this one value, which is what keeps the width-broken and newline-authored twins of one /// document on one layout. - pub(super) run_has_text: bool, + pub(super) run_has_word: bool, /// The first and last index in `trimmed_nodes` that the whitespace rules see — the fragment's /// content bounds once every HOISTED node is skipped /// ([`FragmentNode::content_bounds`]). `handle_content_text_child`'s `is_first` / `is_last` are @@ -208,7 +208,7 @@ impl<'a> Printer<'a> { let TextChildContext { cause, run_has_prose, - run_has_text, + run_has_word, content_bounds, prev_sibling_head, .. @@ -314,8 +314,9 @@ impl<'a> Printer<'a> { // ⚠️ **The run and neighbour facts are asked once, ahead of the multiline split, because // both arms below need the same answer** (`tag_space_defers`; the newline hold // `separator_flows` is read by the multiline arm alone, the only place a newline is a - // hardline question). Nothing in either depends on WHY the container went multiline. A conjunct on the cause would be dead in - // the arm the `!multiline` test already selected, and wrong as a narrowing: the flow + // hardline question). Nothing in either depends on WHY the container went multiline. A + // conjunct on the cause would be dead in the arm the `!multiline` test already selected, + // and wrong as a narrowing: the flow // rule's other two call sites — a content text's leading and trailing runs — carry no // cause gate at all, so a container-keyed one half-applies the rule. Within a single // run the boundaries touching a text node would flow while the one between two adjacent @@ -329,15 +330,15 @@ impl<'a> Printer<'a> { let neighbours_flow = self.neighbour_newline_flows(prev_node) && self.neighbour_newline_flows(next_node); let separator_flows = run_has_prose && neighbours_flow; - // The SPACE-spelled separator before a tag asks the weaker run gate — any content text in - // the run (`run_has_text`), not prose. The prose gate is a HOLD on an authored newline and + // The SPACE-spelled separator before a tag asks the weaker run gate — any word in the + // run (`run_has_word`), not prose. The prose gate is a HOLD on an authored newline and // must never turn a space into one: a one-word run's `text1 {a} {b}` packs per width // exactly as a prose run's does (`inline_sibling_newline_label_hold_tag_pair_space`), and // only a prose-FREE run's tag pair keeps the bare `line` that breaks with the container. // Read by both arms below, so the width-broken and newline-authored twins agree. A // flowing NEWLINE reaches the tag case too, re-spelled as the space by `ws_flows`; prose // implies text, so it defers as before. - let tag_space_defers = run_has_text && neighbours_flow; + let tag_space_defers = run_has_word && neighbours_flow; if !multiline { // Before a tag the separator is a bare collapsible break — a space while // the fragment fits, a newline once it breaks — exactly as the multiline diff --git a/crates/tsv_svelte/src/printer/text.rs b/crates/tsv_svelte/src/printer/text.rs index 6c186d95a..4a7fad489 100644 --- a/crates/tsv_svelte/src/printer/text.rs +++ b/crates/tsv_svelte/src/printer/text.rs @@ -4,7 +4,7 @@ //! (Whitespace *collapsing* itself lives in the doc-based content path — //! `build_text_fill_doc_trimmed` and friends.) //! -//! ⚠️ **Two "is there a blank line" predicates exist, and they are NOT interchangeable.** +//! ⚠️ **Three "is there a blank line" predicates exist, and they are NOT interchangeable.** //! Each answers a different question; picking the wrong one is how a formatter fabricates //! whitespace the author never wrote. Name the question at the call site: //! @@ -12,14 +12,24 @@ //! | --- | --- | --- | //! | [`has_authored_blank_line`] | did the author write a blank line *somewhere* in this text? | run, anywhere | //! | [`has_leading_blank_line`] | did the author write a blank line *before this text's first content*? | run, leading only | +//! | [`has_trailing_blank_line`] | did the author write a blank line *after this text's last content*? | run, trailing only | //! -//! Both scan for a **run** — two newlines separated by horizontal whitespace only. A newline -//! *total* (`count >= 2`) is not a third option: two SEPARATE single breaks (`\ntext1\ntext2`) +//! The last two are the two halves of one question — *is there a blank line at this SEAM* — and +//! a caller that has a side in hand (a boundary's `leading` flag) picks between them rather than +//! reaching for the `anywhere` scan: a blank *inside* the text belongs to the text, and relaying +//! it to the seam relocates the author's blank rather than preserving it. +//! +//! All three scan for a **run** — two newlines separated by horizontal whitespace only. A newline +//! *total* (`count >= 2`) is not a fourth option: two SEPARATE single breaks (`\ntext1\ntext2`) //! reach it with no blank line present, so a total coincides with the authoring signal only on a //! string that is nothing but whitespace, and every call site that would reach such a //! predicate is a string with content in it — trailing text after a `format-ignore` //! range end, a range marker between a section comment and its section. Don't introduce it: a -//! caller that "knows" its string is pure whitespace is one refactor away from being wrong. +//! caller that "knows" its string is pure whitespace is one refactor away from being wrong. The +//! near miss to watch for is a caller that has already sliced an edge run +//! ([`crate::ast::internal::text_edge_ws`]) and counts newlines in *that* — correct only because +//! the slice is pure whitespace by construction, which is the same one refactor away. Ask the +//! seam predicate instead; it takes the whole `raw` and does its own scoping. //! //! A module doc rather than a plain comment on purpose: the table's intra-doc links are then //! checked by `docs:audit`, so a rename can't rot it silently. @@ -28,8 +38,8 @@ /// horizontal whitespace only. /// /// The Tier-2 authoring signal for "did the author leave a blank line inside this text". -/// Contrast [`has_leading_blank_line`], which asks the same of the *leading* whitespace run -/// alone. +/// Contrast the two seam predicates, [`has_leading_blank_line`] and +/// [`has_trailing_blank_line`], which ask the same of one edge whitespace run alone. pub(crate) fn has_authored_blank_line(raw: &str) -> bool { let mut newlines = 0u32; for b in raw.bytes() { @@ -60,15 +70,18 @@ pub(crate) fn has_authored_blank_line(raw: &str) -> bool { /// of resetting the run. On a whitespace-only string the two therefore agree — the leading run /// is the whole string. /// -/// Kept as its own scan rather than folded into its sibling. Both folds are correct and both +/// Kept as its own scan rather than folded into its siblings. Both folds are correct and both /// were measured to GROW `.text` (`objcopy -O binary --only-section=.text target/corpus/tsv`, -/// against this pair): delegating to `has_authored_blank_line(leading_collapsible_ws(raw))` -/// costs +128 bytes, and one `const LEADING_ONLY: bool` scan behind two wrappers costs +112 — -/// `#[inline]` recovers neither. The same behaviour-neutral-but-not-code-neutral trade the -/// `is_horizontal_ws` note below records, and declined for the same reason. What the duplication -/// actually buys back is small: the two bodies differ in exactly one `match` arm, each doc -/// comment names the other, and `leading_blank_line_stops_at_the_first_content_byte` pins both -/// the case where they differ and the whitespace-only case where they must agree. +/// against this pair): delegating to +/// `has_authored_blank_line(internal::text_edge_ws(raw, true))` costs +128 bytes, and one +/// `const LEADING_ONLY: bool` scan behind two wrappers costs +112 — `#[inline]` recovers +/// neither. The same behaviour-neutral-but-not-code-neutral trade the `is_horizontal_ws` note +/// below records, and declined for the same reason, [`has_trailing_blank_line`] included. What +/// the duplication actually buys back is small: the bodies differ in exactly one `match` arm +/// (and, for the trailing one, the scan direction), each doc comment names the others, and +/// `leading_blank_line_stops_at_the_first_content_byte` / +/// `trailing_blank_line_stops_at_the_last_content_byte` pin both the case where they differ and +/// the whitespace-only case where they must agree. pub(crate) fn has_leading_blank_line(raw: &str) -> bool { let mut newlines = 0u32; for b in raw.bytes() { @@ -87,10 +100,38 @@ pub(crate) fn has_leading_blank_line(raw: &str) -> bool { false } +/// Whether `raw` **ends** with an authored blank line — two newlines, separated by horizontal +/// whitespace only, after the last content byte. +/// +/// The mirror of [`has_leading_blank_line`], asked at the seam that FOLLOWS a text rather than +/// the one that precedes it: "did the author leave a blank line immediately before the thing +/// that follows this text?" Scoped to the trailing run for the same reason — a blank *inside* +/// the text (`text1\n\ntext2\n`) belongs to the text, not to the seam. +/// +/// Identical to [`has_leading_blank_line`] with the scan reversed; see that one for why the +/// three bodies are hand-rolled rather than folded. On a whitespace-only string all three agree. +pub(crate) fn has_trailing_blank_line(raw: &str) -> bool { + let mut newlines = 0u32; + for b in raw.bytes().rev() { + match b { + b'\n' => { + newlines += 1; + if newlines >= 2 { + return true; + } + } + b if is_horizontal_ws(b) => {} + // Content ends the trailing run — an earlier blank is the text's, not the seam's. + _ => return false, + } + } + false +} + /// Whether `b` is **horizontal** collapsible whitespace — whitespace that does not end a line. /// /// [`crate::ast::internal::is_collapsible_ws`] minus the line feed (`[ \t\r]`). The callers are -/// the two run scans above: does this byte let the newline run continue rather than end it. A +/// the three run scans above: does this byte let the newline run continue rather than end it. A /// form feed is content, so it breaks the run — an FF between two newlines is not a blank line, /// matching the compiler's own class (`regex_not_whitespace` = `/[^ \t\r\n]/`). /// @@ -108,7 +149,7 @@ fn is_horizontal_ws(b: u8) -> bool { #[cfg(test)] mod tests { - use super::{has_authored_blank_line, has_leading_blank_line}; + use super::{has_authored_blank_line, has_leading_blank_line, has_trailing_blank_line}; #[test] fn authored_blank_line_wants_a_run_not_a_total() { @@ -134,4 +175,29 @@ mod tests { assert_eq!(has_leading_blank_line(ws), has_authored_blank_line(ws)); } } + + #[test] + fn trailing_blank_line_stops_at_the_last_content_byte() { + assert!(has_trailing_blank_line("text\n\n")); + assert!(has_trailing_blank_line("text\t\n\t\n")); + assert!(!has_trailing_blank_line("text1\ntext2\n")); + // The discriminator: a blank INSIDE the text belongs to the text, not the seam. + assert!(!has_trailing_blank_line("text1\n\ntext2\n")); + assert!(has_authored_blank_line("text1\n\ntext2\n")); + // A form feed is content, so it breaks the run — same class as the siblings. + assert!(!has_trailing_blank_line("text\n\u{c}\n")); + // The exact mirror of the leading scan. + for s in ["\n\ntext", "text\n\n", "\ntext1\n\ntext2\n", "a", ""] { + let reversed: String = s.chars().rev().collect(); + assert_eq!( + has_trailing_blank_line(s), + has_leading_blank_line(&reversed) + ); + } + // On a whitespace-only string all three agree — the trailing run is the whole string. + for ws in ["\n\n", "\n \n", "\n", "", "\t"] { + assert_eq!(has_trailing_blank_line(ws), has_authored_blank_line(ws)); + assert_eq!(has_trailing_blank_line(ws), has_leading_blank_line(ws)); + } + } } diff --git a/docs/conformance_prettier_svelte.md b/docs/conformance_prettier_svelte.md index 0c25b6db5..d39a81bc5 100644 --- a/docs/conformance_prettier_svelte.md +++ b/docs/conformance_prettier_svelte.md @@ -290,7 +290,7 @@ Five things are **not** reshaped, because none is a mere spelling difference. A **The comment exclusion is held on each SIDE independently, and it is where this section's convergence deliberately stops rather than a divergence from prettier.** "Keeps its authored line" is the symmetric reading, and it is the weaker one: the newline *after* a comment holds the following run off the comment's line exactly as the newline *before* it holds the comment off the preceding run's, so a one-sided authoring is a fixed point on that side alone — `text1 <!-- c -->⏎text2` and `text1⏎<!-- c --> text2` both stay, at any width, beside the both-sided form — and beside the form with a newline on neither side, which stays hugged, because the rule HOLDS an authored line rather than FORCING one around a comment (that fourth authoring is the pair's null control: it varies the same dimension, so without it an implementation that always broke both sides would satisfy the other three). Prettier holds all four too, so there is nothing to catalog against it here; the exclusion's whole cost is that these authorings do not converge with each other, which is the price [§Comment Position Philosophy](./conformance_prettier.md#comment-position-philosophy) is already paid for. ⚠️ **It is comment-specific, and the discriminating control has to hold the SITE fixed** — an element or a tag in the identical position flows (`text1 <span>a</span>⏎text2` collapses). Two *other* gates in this rule suppress flow for every sibling kind at once (the prose gate below, and the reflowable-fill suppression the paragraph after it, which closes this rule by making the element multiline for a reason it does not read), so an element control run in a shape either one already catches keeps its lines regardless of what sits there, and reads back as "the newline rule is not about comments" when it is. — [inline_separator_comment_newline](../tests/fixtures/svelte/elements/inline_separator_comment_newline/) pins all four comment authorings, and is deliberately **not** a divergence fixture: prettier holds each one too, so there is nothing for an `output_prettier` to claim. The element twins of the one-sided pair are [inline_sibling_newline_flow](../tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/)'s `prettier_variant_newline_after` / `prettier_variant_newline_before`, which converge on its input — that pair beside these four is the whole control. ⚠️ **The exclusion is scoped to the comment's OWN two boundaries: it BOUNDS the run, it does not STERILIZE one.** The runs a comment separates keep flowing on their own — `text1 <span>a</span> text2⏎<!-- c -->⏎text3 <span>b</span> text4` converges from every isolated authoring of those spans, with only the comment's two newlines held. Nothing distinguishes that from the wider reading ("a run holding a comment keeps every line") unless a control puts a flowing run *beside* the comment and re-authors it: a control whose run is already hugged in every file is a fixed point under either rule and cannot separate them, which is why [inline_sibling_newline_flow](../tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/)'s comment-boundary control is re-authored in all three `prettier_variant_*` files rather than held. The same is true of every other run-bounding sibling (that fixture's block-sibling control carries the parallel pin), so the property belongs to run boundaries in general and not to comments — what is comment-specific is only the *holding*. The practical consequence is that a comment tsv has broken onto its own line for **width** ([fill_break_before_comment_spaced_long](../tests/fixtures/svelte/elements/fill_break_before_comment_spaced_long_prettier_divergence/)) reaches a second fixed point when the run after it was authored on the next line — the same document, differing only in whether that run shares the comment's line. -**The rule's boundary is the presence of a `fill` to reflow into, not the shape of the separator node.** Flowing means reflowing a run per width, and a run reflows only where there is prose for a `fill` to pack. So the rule reaches a whitespace-only separator standing *between two non-text siblings* — `text1⏎<span>a</span>` and `<span>a</span>⏎<span>b</span>` converge alike — **when that separator's inline run holds prose, which is at least two words**, and not otherwise. A run of pure elements or tags (`<Comp {a} />⏎<Comp {b} />`) keeps its authored lines: with no prose its newlines are the only structure the author has, collapsing them packs independent siblings onto one line, and on a short run the collapse cascades into the parent element's own hug decision — an outright idempotency break (`<mrow>` re-hugs on the second pass once its children fold). **A run holding a single word is a label, not prose, and keeps its lines the same way** — `<Comp />⏎Delete⏎{n}`, `hue:⏎<input />`, `<input />⏎private`, `chars⏎{n}`, `{@render icon()}⏎text1` are a caption beside its icon, a field beside its unit, a label beside its value, and a fill needs a phrase to reflow into; prettier holds every such shape too, so the hold is agreement. Only the run's TEXT counts: an expression tag renders as a value, and a word beside one is a label like any other. The count is **run-level and never node-local**: at a content text's edge run the node beside the sibling is often the one-word tail a previous wrap left behind (`…<code>x</code>⏎prop.`), which a node-local test would hold alone on its line — the accretion the flow rule exists to heal, run in reverse — where the run's total sees the sentence it belongs to, so a one-word node ending a real sentence flows with it. The cliff is two words (`Remember me` still packs), where label and prose genuinely blur, and the count is the most words any **one** text node carries, never a sum over the run: words in two different text nodes are separated by a sibling, and that separation is the author's, so two one-word captions in one run (`text1⏎<input />⏎text2⏎<input />`, an icon-and-caption list) are two labels and hold — a sum packs every such list the moment it holds two entries. The cost is a sentence spelled entirely as one-word fragments between siblings (`text1⏎<span>inline1</span>⏎text2`), which holds; real prose has a two-word node somewhere in its run. The cliff's own cost is the mirror: one two-word caption in a list of one-word captions is prose, and packs its neighbours with it (`<Comp1 />⏎text1⏎<Comp2 />⏎text2 text3` → one line) — pinned as a control so it is recorded, not discovered. The gate is asked identically at all three of the rule's sites — the whitespace-only separator and a content text's leading and trailing runs — and **it is a hold, never a forced break**: it decides whether an authored newline may reflow, and says nothing about an authored space. So a one-word run's space spelling stays inline everywhere — beside an element, a void element, a component, a render/html tag, inside a content text, and between two **tags**, where the whitespace-only separator keeps deferring to the per-width group as a prose run's does: the space-spelled tag separator reads the weaker run gate, *any* content text in the run, and only a prose-free `{a} {b}` keeps the bare `line` that breaks with the container (the tag-separator rule above). Prettier's `line` between two tags breaks with the container whatever the run holds, so `text1 {a} {b}` in a multiline container is a divergence of the same family as the prose tag pair below — tsv packs where prettier splits. — [inline_sibling_newline_label_hold](../tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/) (every label shape held, the caption list and the fragmented sentence among them; the two-word cliff, the one-word sentence tail, the phrase's reach across a whole run and the cliff in a list flowing as `prettier_variant_newline`; `variant_space` the null control), [inline_sibling_newline_label_hold_tag_pair_space](../tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/) (the space-spelled tag pair before and after a word, and at the root; the prose-free pair as the control that still splits), and [inline_sibling_newline_label_hold_long](../tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/) (the hold at the print-width boundary: the space spelling stays at 100 and wraps at 101 onto the newline spelling, which is then held — the one-way ratchet the hold accepts, prettier's too, though prettier tolerates the 101-char line itself; the wrap is the Elements catalog's fill-boundary divergence). What counts as a word is the fill's own item split over the SOURCE BYTES — an NBSP-joined pair is one word, and so is an entity-encoded space (`text1 text2` is one unbreakable fill item under both formatters, so the count must not promise a seam the fill has not got; the render-side question, whether a node is a separator in content's clothing, reads the decoded text, and the two axes are deliberately distinct), punctuation alone is a word, a hyphenated pair is one, a word glued to a tag is one — and the cliff is the same at a void element (`<input /> text1 text2`, the checkbox-and-caption shape): [inline_sibling_newline_prose_words](../tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/). The container is not an axis on the holding side either — the root, an inline element, a list item, a table cell, a control-flow block body and a snippet body hold a one-word run alike: [inline_sibling_newline_label_hold_container](../tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/). A run is bounded by whatever owns its own line — a block element, a control-flow block, a comment, a `<br />` — and by an authored blank line **wherever the parser put it**: in a whitespace-only node between two siblings, or folded into the edge whitespace of the content text beside it, so that one document does not count two ways by that accident. The count is per run, so a fragment's prose does not license flow in a neighbouring run that has none — a two-word run on one side of a boundary flows right up to it, and the one-word run on the other holds: [inline_sibling_newline_run_bounded](../tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/) (every boundary kind, the blank in both spellings, prose on one side and a label on the other in each, the prose half flowing as `prettier_variant_newline`; and the unbounded twin, where the label flows with the prose). An authored blank INSIDE one text node is not a boundary — a run is a partition of nodes — and the fill collapses it under both formatters, so the sibling newlines beside such a node flow with its prose where prettier holds them: [inline_sibling_newline_interior_blank](../tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/). Bounding is not sterilizing, though: a run that *does* hold prose still flows right up against the bounding sibling, and the two spellings of that ("its own boundaries are held" vs "every line near it is held") are only separated by a control that re-authors the neighbouring run — see the comment-boundary note above. Two mechanics keep this sound and are load-bearing. First, a flowing single newline takes the **space arm verbatim** rather than a parallel one: emitting a different collapsible form for the newline than for the space it claims to equal makes the formatter write a newline that its next pass re-reads as flowable and collapses. Second, the boundary must defer to the next sibling for a **tag** as well as an element — a bare `line` in the child list resolves all-or-nothing with the parent group, and a multiline fragment's parent group is always broken, so without that the one boundary owned by a content text's `fill` would flow while the rest of the same run hard-broke. That deferral is gated on the flow predicate and not on "next is a tag", because a plain authored space before a tag may have a **comment** on its other side, whose line is authorship. Note `authoring_audit` is blind to the tag half: both spellings agree there whichever form the rule picks — on the flowed form now, on the hard-broken one under a narrower gate — so it scores them as converging either way. — [inline_sibling_newline_flow](../tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/) (an inline element, a component and all three tag kinds — expression, render and `{@html}`, one predicate arm each — plus all five controls) and [expressions/angle_escaped](../tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/) (the tag-separator case, where prettier's plain `line` holds each tag on its authored line), and the shapes the rule reaches across the suite: [fill_text_inline_collapse](../tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/), [root_text_newline_inline](../tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/), [block_text_multiline_collapse](../tests/fixtures/svelte/elements/block_text_multiline_collapse_prettier_divergence/), [inline_attrs_multiline_content](../tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/), [components/root_with_text](../tests/fixtures/svelte/components/root_with_text_prettier_divergence/). **The CONTAINER holding the run is not an axis either**, and no fixture above varies it to say so — nearly all put the flowing run in a block container, and the one inline container that carries one ([inline_attrs_multiline_content](../tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/)) reaches multiline past a 99-char attribute line, confounding the kind with the attribute wrap that fixture exists to test. An inline element reaches the multiline form by a *different boundary rule* — both-or-neither, rather than the block's leading boundary alone — so it is the kind where a container-keyed rule would show, and the run lays out identically once there: [container_kind_newline_flow](../tests/fixtures/svelte/elements/container_kind_newline_flow_prettier_divergence/) holds one run fixed and varies the container across all four kinds — `<button>` / `<label>`, `<p>`, a component, and a `svelte:*` special element on both boundary arities (`<svelte:element>`, `<svelte:head>`), the pair that pins the SpecialElement projection onto the same element analysis — with a prose-free run in an inline container as the null control — the one run whose authored lines survive under both formatters, which is what keeps "the container does not decide" from being satisfied by a rule that never consults the run either. **The SIBLING kind reaches one arm none of those fixtures does**: a `svelte:*` special element, which answers the flow question by the same block/inline classification a plain element does — the inline ones (`<svelte:element>`, `<slot>`, `<svelte:boundary>`) flow like a `<span>`, and the four global ones own their line. [special_element_newline_flow](../tests/fixtures/svelte/elements/special_element_newline_flow_prettier_divergence/) pins both halves, the block one as the control that keeps "a special element flows" from being satisfied by a rule that never asks the classification — it is root-only, since Svelte rejects those four inside an element or a block. Its second `prettier_variant_*` carries the classification's own divergence from the layout side: prettier has no `svelte:*` in its block list, so it keeps a **space**-authored `<svelte:window />` on the text line where tsv gives it its own line from either spelling. **A COMPONENT is the other sibling kind the predicate admits, and it is one follower kind with the inline element at every site that prints this separator.** Prettier's `isInlineElement` admits only a `RegularElement`, so the whitespace-only node before a component is printed as a plain `line` that breaks with the container while the text-adjacent boundaries of the same run hug — one run, two answers, in a line that fits (`text1 <Comp1 />⏎<Comp2 /> text2`, from the **space** spelling as much as the newline one), and the answer turns on which kind happens to sit on which side of the gap (`text1 <span>a</span>⏎<Comp /> text2` splits where `text1 <Comp />⏎<span>a</span> text2` packs). tsv gives the component the same inline-sibling wrap an element takes, in **both** multiline arms of the separator site: a component pair beside prose packs per width from either spelling, and an authored **space** before a component is kept where an element would keep it — in a prose-free run, after a comment, after a control-flow block, and after a predecessor that renders multiline (the component hugs the closing tag as an element does — for the **space** spelling; the newline spelling there is the layout-keyed hold above, for both kinds alike). Holding it in only one arm is a two-pass cycle, not a policy: the one time the arms disagreed, a preceding sibling that rendered multiline left the closing tag at a short column, the wrap fit the component after it, and the emitted container newline made the next pass take the other arm and split the pair. — [inline_adjacent_component_flow](../tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/) (the prose runs, with the element pair beside them, both kind orders, components with children, prose only at the run's end, and the structural and prose-free controls), [inline_adjacent_component_space](../tests/fixtures/svelte/elements/inline_adjacent_component_space_prettier_divergence/) (the space spelling with no prose — prose-free pair, after a comment, after a block, at the root — where the newline spelling is dual-stable, `variant_newline`), [inline_adjacent_component_after_multiline](../tests/fixtures/svelte/elements/inline_adjacent_component_after_multiline_prettier_divergence/) (the multiline predecessor, whose prose-free control keeps its line). Where this rule and the render-free content boundary meet on one element, the two compose to a single form — the flowed content with the boundaries hugged — and must settle in **one pass**, since the flow and the collapse it enables are one decision ([inline_content_flow_collapse](../tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/), which pins prettier's two stable forms alongside it). An element-separator run where both formatters agree once tsv flows it is pinned by `tests/fixtures/svelte/script/escapes` and `tests/fixtures/svelte/style/escapes`. +**The rule's boundary is the presence of a `fill` to reflow into, not the shape of the separator node.** Flowing means reflowing a run per width, and a run reflows only where there is prose for a `fill` to pack. So the rule reaches a whitespace-only separator standing *between two non-text siblings* — `text1⏎<span>a</span>` and `<span>a</span>⏎<span>b</span>` converge alike — **when that separator's inline run holds prose, which is at least two words**, and not otherwise. A run of pure elements or tags (`<Comp {a} />⏎<Comp {b} />`) keeps its authored lines: with no prose its newlines are the only structure the author has, collapsing them packs independent siblings onto one line, and on a short run the collapse cascades into the parent element's own hug decision — an outright idempotency break (`<mrow>` re-hugs on the second pass once its children fold). **A run holding a single word is a label, not prose, and keeps its lines the same way** — `<Comp />⏎Delete⏎{n}`, `hue:⏎<input />`, `<input />⏎private`, `chars⏎{n}`, `{@render icon()}⏎text1` are a caption beside its icon, a field beside its unit, a label beside its value, and a fill needs a phrase to reflow into; prettier holds every such shape too, so the hold is agreement. Only the run's TEXT counts: an expression tag renders as a value, and a word beside one is a label like any other. The count is **run-level and never node-local**: at a content text's edge run the node beside the sibling is often the one-word tail a previous wrap left behind (`…<code>x</code>⏎prop.`), which a node-local test would hold alone on its line — the accretion the flow rule exists to heal, run in reverse — where the run's total sees the sentence it belongs to, so a one-word node ending a real sentence flows with it. The cliff is two words (`Remember me` still packs), where label and prose genuinely blur, and the count is the most words any **one** text node carries, never a sum over the run: words in two different text nodes are separated by a sibling, and that separation is the author's, so two one-word captions in one run (`text1⏎<input />⏎text2⏎<input />`, an icon-and-caption list) are two labels and hold — a sum packs every such list the moment it holds two entries. The cost is a sentence spelled entirely as one-word fragments between siblings (`text1⏎<span>inline1</span>⏎text2`), which holds; real prose has a two-word node somewhere in its run. The cliff's own cost is the mirror: one two-word caption in a list of one-word captions is prose, and packs its neighbours with it (`<Comp1 />⏎text1⏎<Comp2 />⏎text2 text3` → one line) — pinned as a control so it is recorded, not discovered. The gate is asked identically at all three of the rule's sites — the whitespace-only separator and a content text's leading and trailing runs — and **it is a hold, never a forced break**: it decides whether an authored newline may reflow, and says nothing about an authored space. So a one-word run's space spelling stays inline everywhere — beside an element, a void element, a component, a render/html tag, inside a content text, and between two **tags**, where the whitespace-only separator keeps deferring to the per-width group as a prose run's does: the space-spelled tag separator reads the weaker run gate, *any* word in the run, and only a wordless `{a} {b}` keeps the bare `line` that breaks with the container (the tag-separator rule above). Prettier's `line` between two tags breaks with the container whatever the run holds, so `text1 {a} {b}` in a multiline container is a divergence of the same family as the prose tag pair below — tsv packs where prettier splits. — [inline_sibling_newline_label_hold](../tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_prettier_divergence/) (every label shape held, the caption list and the fragmented sentence among them; the two-word cliff, the one-word sentence tail, the phrase's reach across a whole run and the cliff in a list flowing as `prettier_variant_newline`; `variant_space` the null control), [inline_sibling_newline_label_hold_tag_pair_space](../tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/) (the space-spelled tag pair before and after a word, and at the root; the prose-free pair as the control that still splits), and [inline_sibling_newline_label_hold_long](../tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_long_prettier_divergence/) (the hold at the print-width boundary: the space spelling stays at 100 and wraps at 101 onto the newline spelling, which is then held — the one-way ratchet the hold accepts, prettier's too, though prettier tolerates the 101-char line itself; the wrap is the Elements catalog's fill-boundary divergence). What counts as a word is the fill's own item split over the SOURCE BYTES — an NBSP-joined pair is one word, and so is an entity-encoded space (`text1 text2` is one unbreakable fill item under both formatters, so the count must not promise a seam the fill has not got; the render-side question, whether a node is a separator in content's clothing, reads the decoded text, and the two axes are deliberately distinct), punctuation alone is a word, a hyphenated pair is one, a word glued to a tag is one — and the cliff is the same at a void element (`<input /> text1 text2`, the checkbox-and-caption shape): [inline_sibling_newline_prose_words](../tests/fixtures/svelte/elements/inline_sibling_newline_prose_words_prettier_divergence/). The container is not an axis on the holding side either — the root, an inline element, a list item, a table cell, a control-flow block body and a snippet body hold a one-word run alike: [inline_sibling_newline_label_hold_container](../tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_container_prettier_divergence/). A run is bounded by whatever owns its own line — a block element, a control-flow block, a comment, a `<br />` — and by an authored blank line **wherever the parser put it**: in a whitespace-only node between two siblings, or folded into the edge whitespace of the content text beside it, so that one document does not count two ways by that accident. The count is per run, so a fragment's prose does not license flow in a neighbouring run that has none — a two-word run on one side of a boundary flows right up to it, and the one-word run on the other holds: [inline_sibling_newline_run_bounded](../tests/fixtures/svelte/elements/inline_sibling_newline_run_bounded_prettier_divergence/) (every boundary kind, the blank in both spellings, prose on one side and a label on the other in each, the prose half flowing as `prettier_variant_newline`; and the unbounded twin, where the label flows with the prose). An authored blank INSIDE one text node is not a boundary — a run is a partition of nodes — and the fill collapses it under both formatters, so the sibling newlines beside such a node flow with its prose where prettier holds them: [inline_sibling_newline_interior_blank](../tests/fixtures/svelte/elements/inline_sibling_newline_interior_blank_prettier_divergence/). Bounding is not sterilizing, though: a run that *does* hold prose still flows right up against the bounding sibling, and the two spellings of that ("its own boundaries are held" vs "every line near it is held") are only separated by a control that re-authors the neighbouring run — see the comment-boundary note above. Two mechanics keep this sound and are load-bearing. First, a flowing single newline takes the **space arm verbatim** rather than a parallel one: emitting a different collapsible form for the newline than for the space it claims to equal makes the formatter write a newline that its next pass re-reads as flowable and collapses. Second, the boundary must defer to the next sibling for a **tag** as well as an element — a bare `line` in the child list resolves all-or-nothing with the parent group, and a multiline fragment's parent group is always broken, so without that the one boundary owned by a content text's `fill` would flow while the rest of the same run hard-broke. That deferral is gated on the flow predicate and not on "next is a tag", because a plain authored space before a tag may have a **comment** on its other side, whose line is authorship. Note `authoring_audit` is blind to the tag half: both spellings agree there whichever form the rule picks — on the flowed form now, on the hard-broken one under a narrower gate — so it scores them as converging either way. — [inline_sibling_newline_flow](../tests/fixtures/svelte/elements/inline_sibling_newline_flow_prettier_divergence/) (an inline element, a component and all three tag kinds — expression, render and `{@html}`, one predicate arm each — plus all five controls) and [expressions/angle_escaped](../tests/fixtures/svelte/expressions/angle_escaped_prettier_divergence/) (the tag-separator case, where prettier's plain `line` holds each tag on its authored line), and the shapes the rule reaches across the suite: [fill_text_inline_collapse](../tests/fixtures/svelte/elements/fill_text_inline_collapse_prettier_divergence/), [root_text_newline_inline](../tests/fixtures/svelte/elements/root_text_newline_inline_prettier_divergence/), [block_text_multiline_collapse](../tests/fixtures/svelte/elements/block_text_multiline_collapse_prettier_divergence/), [inline_attrs_multiline_content](../tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/), [components/root_with_text](../tests/fixtures/svelte/components/root_with_text_prettier_divergence/). **The CONTAINER holding the run is not an axis either**, and no fixture above varies it to say so — nearly all put the flowing run in a block container, and the one inline container that carries one ([inline_attrs_multiline_content](../tests/fixtures/svelte/elements/inline_attrs_multiline_content_prettier_divergence/)) reaches multiline past a 99-char attribute line, confounding the kind with the attribute wrap that fixture exists to test. An inline element reaches the multiline form by a *different boundary rule* — both-or-neither, rather than the block's leading boundary alone — so it is the kind where a container-keyed rule would show, and the run lays out identically once there: [container_kind_newline_flow](../tests/fixtures/svelte/elements/container_kind_newline_flow_prettier_divergence/) holds one run fixed and varies the container across all four kinds — `<button>` / `<label>`, `<p>`, a component, and a `svelte:*` special element on both boundary arities (`<svelte:element>`, `<svelte:head>`), the pair that pins the SpecialElement projection onto the same element analysis — with a prose-free run in an inline container as the null control — the one run whose authored lines survive under both formatters, which is what keeps "the container does not decide" from being satisfied by a rule that never consults the run either. **The SIBLING kind reaches one arm none of those fixtures does**: a `svelte:*` special element, which answers the flow question by the same block/inline classification a plain element does — the inline ones (`<svelte:element>`, `<slot>`, `<svelte:boundary>`) flow like a `<span>`, and the four global ones own their line. [special_element_newline_flow](../tests/fixtures/svelte/elements/special_element_newline_flow_prettier_divergence/) pins both halves, the block one as the control that keeps "a special element flows" from being satisfied by a rule that never asks the classification — it is root-only, since Svelte rejects those four inside an element or a block. Its second `prettier_variant_*` carries the classification's own divergence from the layout side: prettier has no `svelte:*` in its block list, so it keeps a **space**-authored `<svelte:window />` on the text line where tsv gives it its own line from either spelling. **A COMPONENT is the other sibling kind the predicate admits, and it is one follower kind with the inline element at every site that prints this separator.** Prettier's `isInlineElement` admits only a `RegularElement`, so the whitespace-only node before a component is printed as a plain `line` that breaks with the container while the text-adjacent boundaries of the same run hug — one run, two answers, in a line that fits (`text1 <Comp1 />⏎<Comp2 /> text2`, from the **space** spelling as much as the newline one), and the answer turns on which kind happens to sit on which side of the gap (`text1 <span>a</span>⏎<Comp /> text2` splits where `text1 <Comp />⏎<span>a</span> text2` packs). tsv gives the component the same inline-sibling wrap an element takes, in **both** multiline arms of the separator site: a component pair beside prose packs per width from either spelling, and an authored **space** before a component is kept where an element would keep it — in a prose-free run, after a comment, after a control-flow block, and after a predecessor that renders multiline (the component hugs the closing tag as an element does — for the **space** spelling; the newline spelling there is the layout-keyed hold above, for both kinds alike). Holding it in only one arm is a two-pass cycle, not a policy: the one time the arms disagreed, a preceding sibling that rendered multiline left the closing tag at a short column, the wrap fit the component after it, and the emitted container newline made the next pass take the other arm and split the pair. — [inline_adjacent_component_flow](../tests/fixtures/svelte/elements/inline_adjacent_component_flow_prettier_divergence/) (the prose runs, with the element pair beside them, both kind orders, components with children, prose only at the run's end, and the structural and prose-free controls), [inline_adjacent_component_space](../tests/fixtures/svelte/elements/inline_adjacent_component_space_prettier_divergence/) (the space spelling with no prose — prose-free pair, after a comment, after a block, at the root — where the newline spelling is dual-stable, `variant_newline`), [inline_adjacent_component_after_multiline](../tests/fixtures/svelte/elements/inline_adjacent_component_after_multiline_prettier_divergence/) (the multiline predecessor, whose prose-free control keeps its line). Where this rule and the render-free content boundary meet on one element, the two compose to a single form — the flowed content with the boundaries hugged — and must settle in **one pass**, since the flow and the collapse it enables are one decision ([inline_content_flow_collapse](../tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/), which pins prettier's two stable forms alongside it). **The prose gate reaches that interior side too, and reaches it at BOTH spellings of the newline.** An authored newline between two inline siblings is folded by the parser into a whitespace-only node when nothing else sits there and into the edge whitespace of the content text beside it when something does, so a rule that reads only one of them gives one document two layouts by that accident — the same accident the run scan rules out at its blank-line boundary above. A one-word run is a label with no fill, so its interior newline is the author's structure and expands the hugged element (`<span>⏎<code>a</code>⏎<code>b</code> text1⏎</span>`), and it does so equally when the newline sits at a content text's edge (`<span>text1⏎<code>a</code></span>`, `<span><code>a</code> <code>b</code>,⏎<code>c</code></span>` — a lone `,` is a word like any other). Prettier holds the newline at both spellings, so the hold is agreement and the divergence stays the block-style-vs-dangle spelling. The two-word twin is the control: the run is prose, the newline is the fill's own wrap point, and the element collapses. — [inline_interior_newline_label_hold](../tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/) (both spellings held, with the prose twin of each as the control). The mechanism is one gate, not two: the interior scan trims a text's edge run for two different reasons — the element's own boundary air, which is the boundary question and is excluded unconditionally (keyed on the fragment's CONTENT bounds, since a hoisted `{@debug}` / `<title>` at the edge leaves the text beside it as the effective first or last), and an edge facing a SIBLING, which is the fill's separator and is therefore excluded only where there IS a fill. An element-separator run where both formatters agree once tsv flows it is pinned by `tests/fixtures/svelte/script/escapes` and `tests/fixtures/svelte/style/escapes`. **That same "is there a `fill`?" question decides whether an authored newline in the run's INTERIOR may select the layout** — never the content boundary, whose air the per-kind boundary rule honors regardless of what the content is made of (both boundaries for an inline element or a component, the leading one for a block: [inline_boundary_air](../tests/fixtures/svelte/elements/inline_boundary_air/) / [boundary_air_one_sided](../tests/fixtures/svelte/elements/boundary_air_one_sided_prettier_divergence/)). It is asked of the RUN, never of the separator's shape, the sibling count, or the container's kind. A fill needs two things: **prose to pack** (the run-level count above — at least two words, a one-word run being a label — shared verbatim) and **a whitespace seam to reflow at**. With both, an interior newline is the fill's own wrap point rather than authoring, so the element collapses when it fits — `<span>text1⏎text2 <code>a</code></span>` reflows to the one-line form, its `<p>` and `<Comp>` twins identically (expanding a block or a component on an interior newline, as an inline element never is, would be a split keyed on the container rather than on anything the rule is about), and a ONE-boundary newline reaches the same answer through the arity above, since the edge run it lands in is trimmed before the interior is read ([content_interior_newline_collapse](../tests/fixtures/svelte/elements/content_interior_newline_collapse/), [boundary_air_one_sided](../tests/fixtures/svelte/elements/boundary_air_one_sided_prettier_divergence/); prettier agrees with the collapse in every kind). A pure-sibling run reaches **no** fill answer, and there is deliberately no third path granting it one. `<span>{a}\t{b}</span>` holds no prose, so it has nothing to reflow into, and a pure-sibling run the author DID break keeps its lines for the same reason: with no fill, those lines are the only structure the author has. A `{a} {b}` disjunct granting such a run the fill answer (on the reading that the author left the siblings on one line themselves) would be inert: every reader of "did the author break this content?" is unified onto the two interior-newline arms, which ask the question *of a text node carrying a newline*, and a run such a disjunct accepts has none — every newline-bearing node in it would have to be prose, and a run with prose already reaches the fill answer through the prose path. ⚠️ **A separator's *spelling* still may not pick a layout** — that equality is carried by the boundary and separator paths rather than by a fill answer for content that has no fill: a space and a tab render the same and reach one form across all three sibling kinds, where testing a literal space lets the spelling pick the layout and one document reaches two — [inline_separator_tab](../tests/fixtures/svelte/elements/inline_separator_tab_prettier_divergence/), whose hugged-boundary control isolates it: the tab alone is never the trigger, only the tab *with* a newline-authored boundary that has a layout left to select. **A spelling reaches that rule through what it DECODES to, not through its bytes**, so an entity-encoded space or tab does too: ` ` decodes to whitespace the compiler collapses, which makes it interchangeable with the plain space beside it — and only the *layout* converges, since the entity's own bytes print verbatim (respelling one is a content edit) — [inline_separator_entity_collapse](../tests/fixtures/svelte/elements/inline_separator_entity_collapse_prettier_divergence/). An ` ` decodes to a non-breaking space, which renders as itself and never collapses, so it does not reach the rule at all ([inline_separator_nbsp_newline](../tests/fixtures/svelte/elements/inline_separator_nbsp_newline/)), and neither does an entity in a run the author *did* break, which holds no prose to reflow ([inline_separator_entity_newline](../tests/fixtures/svelte/elements/inline_separator_entity_newline/)). So the two questions read different texts on purpose and neither may borrow the other's axis: the whitespace scalars that decide what PRINTS read `raw` (matching prettier's `node.raw || node.data`), while the separator questions that decide LAYOUT — is this node the run's prose, is this separator interchangeable with a space — read the decoded data. ⚠️ The authority for "same render" is rule 1 for an element or component pair and the **browser's** own collapse for a tag pair, because `clean_nodes` skips the collapse whenever a neighbor is an `ExpressionTag` — `{a}⇥{b}` reaches compiled output with its tab, where `<code>a</code>⇥<code>b</code>` compiles to a space. Both are render-identical under `white-space: normal`, which is the model tsv's render oracle implements and the one this whole section already assumes ([conformance_svelte.md §Template Whitespace](./conformance_svelte.md#template-whitespace-clean_nodes)); do not restate the tag case as a rule-1 consequence, it is not one. Without a seam the content is one unbreakable unit with nothing to reflow, so the boundary is the only signal the author has and it keeps its authored lines — glued `<a>⏎{expr}text⏎</a>` stays multiline, and prettier agrees ([elements/inline_multiline_nontext](../tests/fixtures/svelte/elements/inline_multiline_nontext/)). Two consequences are load-bearing. First, the run boundary is the *same* one the flow rule uses: a comment, a control-flow block, a block element or an authored blank line ends the run, and content holding one is structured rather than flowed — without that, a `<!-- … -->` and the text beneath it flatten onto one line and an authored blank line is deleted, content-preservation breaks rather than layout choices. Second, **every** reader of "did the author break this content?" must consult this one answer. The predicate suppresses a multiline signal, and a suppressed element lays its content out as one `fill` — which, once the content is wide, wraps a prose text ACROSS LINES and so writes the very newline the other readers key on. A reader left out reports the break on the next pass, the multiline arm then freezes the authored newlines as hardlines, and the two arrangements alternate forever: a real F1 break on real prose tables, which is why a narrower predicate gating only the boundary arm is not an option. — [inline_content_flow_collapse](../tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/) carries the whole class (element pair, table cell, component pair, expression-tag pair, a lone element with prose leading and trailing, and an element pair followed by prose). The holding side of the same answer — a one-word run (or two one-word texts in one run: the count is per node), whose interior whitespace-only newline is the author's structure and so EXPANDS the hugged element, boundaries and all, where the same newline in a run whose text carries two words is the fill's wrap point and collapses — is [inline_interior_newline_label_hold](../tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/); prettier keeps the hug and dangles the closing delimiter from the hugged authoring, its interior newline held but never selecting the layout. ⚠️ Only the whitespace-only arm holds it: the content-text arm's edge trim still collapses a one-word interior newline at a text node's edge (`<span>text1⏎<code>a</code></span>`), so the two spellings do not yet agree — open work, deliberately unpinned. That last case is what put the whitespace-ONLY arm on the shared answer: its newline lands in a whitespace-only separator node rather than inside a content text, so the run's prose and its break live in *different* nodes — the one shape the content-text arm cannot reach, and the last arm to be left out. The two interior arms are the predicate's only layout readers, for every container kind alike; the boundary arms honor their air without consulting it. diff --git a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/README.md index 0d99dcb55..7c6b1c8d0 100644 --- a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/README.md +++ b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/README.md @@ -27,8 +27,17 @@ and a lone element beside prose is one too, so all of them must reach the same f space-separated twin already reaches. Otherwise one document has two layouts depending on whether the author happened to put words between the siblings. -The predicate's other conjunct is a **whitespace seam** to reflow at, and the negative is pinned -next door: glued content (`<a>{expr}text</a>`) is a single unbreakable unit with nothing to +The predicate's **prose** conjunct has its negative pinned next door: a run holding a single word +is a label with no fill to reflow into, so its interior newline is the author's structure and +selects the element's layout instead of collapsing — see +[inline_interior_newline_label_hold](../inline_interior_newline_label_hold_prettier_divergence/), +which carries this fixture's own shape with a one-word separator (`<code>a</code> <code>b</code>,`) +beside it. That is why the separators here carry **two** words: a one-word separator would make +these cases labels, and the fixture would be asserting the collapse on a run that has no fill — +green by way of the trim rather than by the rule it names. + +The predicate's other conjunct is a **whitespace seam** to reflow at, and its negative is pinned +next door too: glued content (`<a>{expr}text</a>`) is a single unbreakable unit with nothing to reflow, so there the boundary *is* the only signal and the authored lines stay — see [inline_multiline_nontext](../inline_multiline_nontext/), where prettier agrees. diff --git a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/expected.json index daf9df7a3..5c42299ca 100644 --- a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 1577, + "end": 1589, "type": "Root", "fragment": { "type": "Fragment", @@ -23,7 +23,7 @@ { "type": "RegularElement", "start": 1092, - "end": 1179, + "end": 1185, "name": "div", "name_loc": { "start": { @@ -51,7 +51,7 @@ { "type": "RegularElement", "start": 1099, - "end": 1172, + "end": 1178, "name": "span", "name_loc": { "start": { @@ -141,25 +141,25 @@ { "type": "Text", "start": 1134, - "end": 1136, - "raw": ", ", - "data": ", " + "end": 1142, + "raw": ", text1 ", + "data": ", text1 " }, { "type": "RegularElement", - "start": 1136, - "end": 1150, + "start": 1142, + "end": 1156, "name": "code", "name_loc": { "start": { "line": 16, - "column": 39, - "character": 1137 + "column": 45, + "character": 1143 }, "end": { "line": 16, - "column": 43, - "character": 1141 + "column": 49, + "character": 1147 } }, "attributes": [], @@ -168,8 +168,8 @@ "nodes": [ { "type": "Text", - "start": 1142, - "end": 1143, + "start": 1148, + "end": 1149, "raw": "c", "data": "c" } @@ -178,26 +178,26 @@ }, { "type": "Text", - "start": 1150, - "end": 1151, + "start": 1156, + "end": 1157, "raw": " ", "data": " " }, { "type": "RegularElement", - "start": 1151, - "end": 1165, + "start": 1157, + "end": 1171, "name": "code", "name_loc": { "start": { "line": 16, - "column": 54, - "character": 1152 + "column": 60, + "character": 1158 }, "end": { "line": 16, - "column": 58, - "character": 1156 + "column": 64, + "character": 1162 } }, "attributes": [], @@ -206,8 +206,8 @@ "nodes": [ { "type": "Text", - "start": 1157, - "end": 1158, + "start": 1163, + "end": 1164, "raw": "d", "data": "d" } @@ -219,8 +219,8 @@ }, { "type": "Text", - "start": 1172, - "end": 1173, + "start": 1178, + "end": 1179, "raw": "\n", "data": "\n" } @@ -229,26 +229,26 @@ }, { "type": "Text", - "start": 1179, - "end": 1181, + "start": 1185, + "end": 1187, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 1181, - "end": 1264, + "start": 1187, + "end": 1276, "name": "div", "name_loc": { "start": { "line": 19, "column": 1, - "character": 1182 + "character": 1188 }, "end": { "line": 19, "column": 4, - "character": 1185 + "character": 1191 } }, "attributes": [], @@ -257,26 +257,26 @@ "nodes": [ { "type": "Text", - "start": 1186, - "end": 1188, + "start": 1192, + "end": 1194, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1188, - "end": 1257, + "start": 1194, + "end": 1269, "name": "td", "name_loc": { "start": { "line": 20, "column": 2, - "character": 1189 + "character": 1195 }, "end": { "line": 20, "column": 4, - "character": 1191 + "character": 1197 } }, "attributes": [], @@ -285,19 +285,19 @@ "nodes": [ { "type": "RegularElement", - "start": 1192, - "end": 1206, + "start": 1198, + "end": 1212, "name": "code", "name_loc": { "start": { "line": 20, "column": 6, - "character": 1193 + "character": 1199 }, "end": { "line": 20, "column": 10, - "character": 1197 + "character": 1203 } }, "attributes": [], @@ -306,8 +306,8 @@ "nodes": [ { "type": "Text", - "start": 1198, - "end": 1199, + "start": 1204, + "end": 1205, "raw": "a", "data": "a" } @@ -316,26 +316,26 @@ }, { "type": "Text", - "start": 1206, - "end": 1207, + "start": 1212, + "end": 1213, "raw": " ", "data": " " }, { "type": "RegularElement", - "start": 1207, - "end": 1221, + "start": 1213, + "end": 1227, "name": "code", "name_loc": { "start": { "line": 20, "column": 21, - "character": 1208 + "character": 1214 }, "end": { "line": 20, "column": 25, - "character": 1212 + "character": 1218 } }, "attributes": [], @@ -344,8 +344,8 @@ "nodes": [ { "type": "Text", - "start": 1213, - "end": 1214, + "start": 1219, + "end": 1220, "raw": "b", "data": "b" } @@ -354,26 +354,26 @@ }, { "type": "Text", - "start": 1221, - "end": 1223, - "raw": ", ", - "data": ", " + "start": 1227, + "end": 1235, + "raw": ", text1 ", + "data": ", text1 " }, { "type": "RegularElement", - "start": 1223, - "end": 1237, + "start": 1235, + "end": 1249, "name": "code", "name_loc": { "start": { "line": 20, - "column": 37, - "character": 1224 + "column": 43, + "character": 1236 }, "end": { "line": 20, - "column": 41, - "character": 1228 + "column": 47, + "character": 1240 } }, "attributes": [], @@ -382,8 +382,8 @@ "nodes": [ { "type": "Text", - "start": 1229, - "end": 1230, + "start": 1241, + "end": 1242, "raw": "c", "data": "c" } @@ -392,26 +392,26 @@ }, { "type": "Text", - "start": 1237, - "end": 1238, + "start": 1249, + "end": 1250, "raw": " ", "data": " " }, { "type": "RegularElement", - "start": 1238, - "end": 1252, + "start": 1250, + "end": 1264, "name": "code", "name_loc": { "start": { "line": 20, - "column": 52, - "character": 1239 + "column": 58, + "character": 1251 }, "end": { "line": 20, - "column": 56, - "character": 1243 + "column": 62, + "character": 1255 } }, "attributes": [], @@ -420,8 +420,8 @@ "nodes": [ { "type": "Text", - "start": 1244, - "end": 1245, + "start": 1256, + "end": 1257, "raw": "d", "data": "d" } @@ -433,8 +433,8 @@ }, { "type": "Text", - "start": 1257, - "end": 1258, + "start": 1269, + "end": 1270, "raw": "\n", "data": "\n" } @@ -443,26 +443,26 @@ }, { "type": "Text", - "start": 1264, - "end": 1266, + "start": 1276, + "end": 1278, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 1266, - "end": 1348, + "start": 1278, + "end": 1360, "name": "div", "name_loc": { "start": { "line": 23, "column": 1, - "character": 1267 + "character": 1279 }, "end": { "line": 23, "column": 4, - "character": 1270 + "character": 1282 } }, "attributes": [], @@ -471,26 +471,26 @@ "nodes": [ { "type": "Text", - "start": 1271, - "end": 1273, + "start": 1283, + "end": 1285, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1273, - "end": 1341, + "start": 1285, + "end": 1353, "name": "span", "name_loc": { "start": { "line": 24, "column": 2, - "character": 1274 + "character": 1286 }, "end": { "line": 24, "column": 6, - "character": 1278 + "character": 1290 } }, "attributes": [], @@ -499,43 +499,43 @@ "nodes": [ { "type": "Component", - "start": 1279, - "end": 1300, + "start": 1291, + "end": 1312, "name": "Comp", "name_loc": { "start": { "line": 24, "column": 8, - "character": 1280 + "character": 1292 }, "end": { "line": 24, "column": 12, - "character": 1284 + "character": 1296 } }, "attributes": [ { "type": "Attribute", - "start": 1285, - "end": 1297, + "start": 1297, + "end": 1309, "name": "prop", "name_loc": { "start": { "line": 24, "column": 13, - "character": 1285 + "character": 1297 }, "end": { "line": 24, "column": 17, - "character": 1289 + "character": 1301 } }, "value": [ { - "start": 1291, - "end": 1296, + "start": 1303, + "end": 1308, "type": "Text", "raw": "value", "data": "value" @@ -550,50 +550,50 @@ }, { "type": "Text", - "start": 1300, - "end": 1313, + "start": 1312, + "end": 1325, "raw": " text1 text2 ", "data": " text1 text2 " }, { "type": "Component", - "start": 1313, - "end": 1334, + "start": 1325, + "end": 1346, "name": "Comp", "name_loc": { "start": { "line": 24, "column": 42, - "character": 1314 + "character": 1326 }, "end": { "line": 24, "column": 46, - "character": 1318 + "character": 1330 } }, "attributes": [ { "type": "Attribute", - "start": 1319, - "end": 1331, + "start": 1331, + "end": 1343, "name": "prop", "name_loc": { "start": { "line": 24, "column": 47, - "character": 1319 + "character": 1331 }, "end": { "line": 24, "column": 51, - "character": 1323 + "character": 1335 } }, "value": [ { - "start": 1325, - "end": 1330, + "start": 1337, + "end": 1342, "type": "Text", "raw": "value", "data": "value" @@ -611,8 +611,8 @@ }, { "type": "Text", - "start": 1341, - "end": 1342, + "start": 1353, + "end": 1354, "raw": "\n", "data": "\n" } @@ -621,26 +621,26 @@ }, { "type": "Text", - "start": 1348, - "end": 1350, + "start": 1360, + "end": 1362, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 1350, - "end": 1403, + "start": 1362, + "end": 1415, "name": "div", "name_loc": { "start": { "line": 27, "column": 1, - "character": 1351 + "character": 1363 }, "end": { "line": 27, "column": 4, - "character": 1354 + "character": 1366 } }, "attributes": [], @@ -649,26 +649,26 @@ "nodes": [ { "type": "Text", - "start": 1355, - "end": 1357, + "start": 1367, + "end": 1369, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1357, - "end": 1396, + "start": 1369, + "end": 1408, "name": "span", "name_loc": { "start": { "line": 28, "column": 2, - "character": 1358 + "character": 1370 }, "end": { "line": 28, "column": 6, - "character": 1362 + "character": 1374 } }, "attributes": [], @@ -677,26 +677,26 @@ "nodes": [ { "type": "Text", - "start": 1363, - "end": 1375, + "start": 1375, + "end": 1387, "raw": "text1 text2 ", "data": "text1 text2 " }, { "type": "RegularElement", - "start": 1375, - "end": 1389, + "start": 1387, + "end": 1401, "name": "code", "name_loc": { "start": { "line": 28, "column": 20, - "character": 1376 + "character": 1388 }, "end": { "line": 28, "column": 24, - "character": 1380 + "character": 1392 } }, "attributes": [], @@ -705,8 +705,8 @@ "nodes": [ { "type": "Text", - "start": 1381, - "end": 1382, + "start": 1393, + "end": 1394, "raw": "a", "data": "a" } @@ -718,8 +718,8 @@ }, { "type": "Text", - "start": 1396, - "end": 1397, + "start": 1408, + "end": 1409, "raw": "\n", "data": "\n" } @@ -728,26 +728,26 @@ }, { "type": "Text", - "start": 1403, - "end": 1405, + "start": 1415, + "end": 1417, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 1405, - "end": 1451, + "start": 1417, + "end": 1463, "name": "div", "name_loc": { "start": { "line": 31, "column": 1, - "character": 1406 + "character": 1418 }, "end": { "line": 31, "column": 4, - "character": 1409 + "character": 1421 } }, "attributes": [], @@ -756,26 +756,26 @@ "nodes": [ { "type": "Text", - "start": 1410, - "end": 1412, + "start": 1422, + "end": 1424, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1412, - "end": 1444, + "start": 1424, + "end": 1456, "name": "span", "name_loc": { "start": { "line": 32, "column": 2, - "character": 1413 + "character": 1425 }, "end": { "line": 32, "column": 6, - "character": 1417 + "character": 1429 } }, "attributes": [], @@ -784,12 +784,12 @@ "nodes": [ { "type": "ExpressionTag", - "start": 1418, - "end": 1421, + "start": 1430, + "end": 1433, "expression": { "type": "Identifier", - "start": 1419, - "end": 1420, + "start": 1431, + "end": 1432, "loc": { "start": { "line": 32, @@ -805,19 +805,19 @@ }, { "type": "Text", - "start": 1421, - "end": 1434, + "start": 1433, + "end": 1446, "raw": " text1 text2 ", "data": " text1 text2 " }, { "type": "ExpressionTag", - "start": 1434, - "end": 1437, + "start": 1446, + "end": 1449, "expression": { "type": "Identifier", - "start": 1435, - "end": 1436, + "start": 1447, + "end": 1448, "loc": { "start": { "line": 32, @@ -836,8 +836,8 @@ }, { "type": "Text", - "start": 1444, - "end": 1445, + "start": 1456, + "end": 1457, "raw": "\n", "data": "\n" } @@ -846,26 +846,26 @@ }, { "type": "Text", - "start": 1451, - "end": 1453, + "start": 1463, + "end": 1465, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 1453, - "end": 1506, + "start": 1465, + "end": 1518, "name": "div", "name_loc": { "start": { "line": 35, "column": 1, - "character": 1454 + "character": 1466 }, "end": { "line": 35, "column": 4, - "character": 1457 + "character": 1469 } }, "attributes": [], @@ -874,26 +874,26 @@ "nodes": [ { "type": "Text", - "start": 1458, - "end": 1460, + "start": 1470, + "end": 1472, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1460, - "end": 1499, + "start": 1472, + "end": 1511, "name": "span", "name_loc": { "start": { "line": 36, "column": 2, - "character": 1461 + "character": 1473 }, "end": { "line": 36, "column": 6, - "character": 1465 + "character": 1477 } }, "attributes": [], @@ -902,19 +902,19 @@ "nodes": [ { "type": "RegularElement", - "start": 1466, - "end": 1480, + "start": 1478, + "end": 1492, "name": "code", "name_loc": { "start": { "line": 36, "column": 8, - "character": 1467 + "character": 1479 }, "end": { "line": 36, "column": 12, - "character": 1471 + "character": 1483 } }, "attributes": [], @@ -923,8 +923,8 @@ "nodes": [ { "type": "Text", - "start": 1472, - "end": 1473, + "start": 1484, + "end": 1485, "raw": "a", "data": "a" } @@ -933,8 +933,8 @@ }, { "type": "Text", - "start": 1480, - "end": 1492, + "start": 1492, + "end": 1504, "raw": " text1 text2", "data": " text1 text2" } @@ -943,8 +943,8 @@ }, { "type": "Text", - "start": 1499, - "end": 1500, + "start": 1511, + "end": 1512, "raw": "\n", "data": "\n" } @@ -953,26 +953,26 @@ }, { "type": "Text", - "start": 1506, - "end": 1508, + "start": 1518, + "end": 1520, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 1508, - "end": 1576, + "start": 1520, + "end": 1588, "name": "div", "name_loc": { "start": { "line": 39, "column": 1, - "character": 1509 + "character": 1521 }, "end": { "line": 39, "column": 4, - "character": 1512 + "character": 1524 } }, "attributes": [], @@ -981,26 +981,26 @@ "nodes": [ { "type": "Text", - "start": 1513, - "end": 1515, + "start": 1525, + "end": 1527, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1515, - "end": 1569, + "start": 1527, + "end": 1581, "name": "span", "name_loc": { "start": { "line": 40, "column": 2, - "character": 1516 + "character": 1528 }, "end": { "line": 40, "column": 6, - "character": 1520 + "character": 1532 } }, "attributes": [], @@ -1009,19 +1009,19 @@ "nodes": [ { "type": "RegularElement", - "start": 1521, - "end": 1535, + "start": 1533, + "end": 1547, "name": "code", "name_loc": { "start": { "line": 40, "column": 8, - "character": 1522 + "character": 1534 }, "end": { "line": 40, "column": 12, - "character": 1526 + "character": 1538 } }, "attributes": [], @@ -1030,8 +1030,8 @@ "nodes": [ { "type": "Text", - "start": 1527, - "end": 1528, + "start": 1539, + "end": 1540, "raw": "a", "data": "a" } @@ -1040,26 +1040,26 @@ }, { "type": "Text", - "start": 1535, - "end": 1536, + "start": 1547, + "end": 1548, "raw": " ", "data": " " }, { "type": "RegularElement", - "start": 1536, - "end": 1550, + "start": 1548, + "end": 1562, "name": "code", "name_loc": { "start": { "line": 40, "column": 23, - "character": 1537 + "character": 1549 }, "end": { "line": 40, "column": 27, - "character": 1541 + "character": 1553 } }, "attributes": [], @@ -1068,8 +1068,8 @@ "nodes": [ { "type": "Text", - "start": 1542, - "end": 1543, + "start": 1554, + "end": 1555, "raw": "b", "data": "b" } @@ -1078,8 +1078,8 @@ }, { "type": "Text", - "start": 1550, - "end": 1562, + "start": 1562, + "end": 1574, "raw": " text1 text2", "data": " text1 text2" } @@ -1088,8 +1088,8 @@ }, { "type": "Text", - "start": 1569, - "end": 1570, + "start": 1581, + "end": 1582, "raw": "\n", "data": "\n" } diff --git a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/input.svelte index 9a6dd0b1c..795d28e0e 100644 --- a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/input.svelte @@ -13,11 +13,11 @@ its authored lines, pinned by `inline_multiline_nontext`). --> <div> - <span><code>a</code> <code>b</code>, <code>c</code> <code>d</code></span> + <span><code>a</code> <code>b</code>, text1 <code>c</code> <code>d</code></span> </div> <div> - <td><code>a</code> <code>b</code>, <code>c</code> <code>d</code></td> + <td><code>a</code> <code>b</code>, text1 <code>c</code> <code>d</code></td> </div> <div> diff --git a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/prettier_variant_dangle.svelte b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/prettier_variant_dangle.svelte index 9d119361a..d89b8e2b9 100644 --- a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/prettier_variant_dangle.svelte +++ b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/prettier_variant_dangle.svelte @@ -14,14 +14,14 @@ --> <div> <span - ><code>a</code> <code>b</code>, + ><code>a</code> <code>b</code>, text1 <code>c</code> <code>d</code></span > </div> <div> <td - ><code>a</code> <code>b</code>, + ><code>a</code> <code>b</code>, text1 <code>c</code> <code>d</code></td > </div> diff --git a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/unformatted_ours_newline.svelte b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/unformatted_ours_newline.svelte index 5f815de78..e1f77b751 100644 --- a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/unformatted_ours_newline.svelte +++ b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/unformatted_ours_newline.svelte @@ -13,12 +13,12 @@ its authored lines, pinned by `inline_multiline_nontext`). --> <div> - <span><code>a</code> <code>b</code>, + <span><code>a</code> <code>b</code>, text1 <code>c</code> <code>d</code></span> </div> <div> - <td><code>a</code> <code>b</code>, + <td><code>a</code> <code>b</code>, text1 <code>c</code> <code>d</code></td> </div> diff --git a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/variant_boundary_newline.svelte b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/variant_boundary_newline.svelte index 5dd5e64b7..df308accf 100644 --- a/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/variant_boundary_newline.svelte +++ b/tests/fixtures/svelte/elements/inline_content_flow_collapse_prettier_divergence/variant_boundary_newline.svelte @@ -14,13 +14,13 @@ --> <div> <span> - <code>a</code> <code>b</code>, <code>c</code> <code>d</code> + <code>a</code> <code>b</code>, text1 <code>c</code> <code>d</code> </span> </div> <div> <td> - <code>a</code> <code>b</code>, <code>c</code> <code>d</code> + <code>a</code> <code>b</code>, text1 <code>c</code> <code>d</code> </td> </div> diff --git a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/README.md index dcad760b1..d5d76fbad 100644 --- a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/README.md +++ b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/README.md @@ -26,10 +26,24 @@ sibling arms do. (`<span⏎><code>a</code>⏎<code>b</code> text1</span⏎>`) — its interior newline is held but never selects the element's layout. -⚠️ The hold reaches only the whitespace-only separator. The content-text arm's edge trim still -collapses the newline when it sits at a text node's edge (`<span>text1⏎<code>a</code></span>` -→ `<span>text1 <code>a</code></span>`), so the two spellings of a one-word interior newline do -not yet agree; that arm is tracked as open work and deliberately not pinned here. +The hold reaches **both spellings of the newline**, which is the point: an authored newline +between two inline siblings is folded by the parser into a whitespace-only node when nothing else +sits there, and into the edge whitespace of the content text beside it when something does. One +document must not get two layouts by that accident — the same reason the run scan bounds a run at +an authored blank line wherever the parser put it +([inline_sibling_newline_run_bounded](../inline_sibling_newline_run_bounded_prettier_divergence/)). +So `<span>text1⏎<code>a</code></span>`, `<span><code>a</code>⏎text1</span>` and +`<span><code>a</code> <code>b</code>,⏎<code>c</code></span>` hold exactly as the whitespace-only +cases above do; a lone `,` is a word like any other, since a word is a run of non-collapsible +whitespace and nothing else +([inline_sibling_newline_prose_words](../inline_sibling_newline_prose_words_prettier_divergence/)). +Prettier holds the newline at both spellings too, so the hold is agreement and the divergence +stays the dangle-vs-block-style spelling this fixture already owns. + +The control is the two-word twin of the same edge (`<span>text1 text2 <code>a</code></span>`): +the run is prose, the newline is the fill's own wrap point, and the element collapses — the +positive case [inline_content_flow_collapse](../inline_content_flow_collapse_prettier_divergence/) +is built on. See [conformance_prettier_svelte.md §Svelte: Inline content block-style](../../../../../docs/conformance_prettier_svelte.md#svelte-inline-content-block-style). diff --git a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/expected.json b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/expected.json index 1e978a7a6..265fea013 100644 --- a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/expected.json +++ b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/expected.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 1206, + "end": 1898, "type": "Root", "fragment": { "type": "Fragment", @@ -659,31 +659,582 @@ { "type": "Comment", "start": 1021, - "end": 1158, + "end": 1303, + "data": " the same one-word interior newline spelled at a CONTENT TEXT's edge rather than in a\n whitespace-only node: one class, one answer, whichever node the parser folded the newline\n into. A lone `,` is a word like any other, so its run is a label and the newline is held " + }, + { + "type": "Text", + "start": 1303, + "end": 1304, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1304, + "end": 1383, + "name": "div", + "name_loc": { + "start": { + "line": 42, + "column": 1, + "character": 1305 + }, + "end": { + "line": 42, + "column": 4, + "character": 1308 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1309, + "end": 1311, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1311, + "end": 1376, + "name": "span", + "name_loc": { + "start": { + "line": 43, + "column": 2, + "character": 1312 + }, + "end": { + "line": 43, + "column": 6, + "character": 1316 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1317, + "end": 1320, + "raw": "\n\t\t", + "data": "\n\t\t" + }, + { + "type": "RegularElement", + "start": 1320, + "end": 1334, + "name": "code", + "name_loc": { + "start": { + "line": 44, + "column": 3, + "character": 1321 + }, + "end": { + "line": 44, + "column": 7, + "character": 1325 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1326, + "end": 1327, + "raw": "a", + "data": "a" + } + ] + } + }, + { + "type": "Text", + "start": 1334, + "end": 1335, + "raw": " ", + "data": " " + }, + { + "type": "RegularElement", + "start": 1335, + "end": 1349, + "name": "code", + "name_loc": { + "start": { + "line": 44, + "column": 18, + "character": 1336 + }, + "end": { + "line": 44, + "column": 22, + "character": 1340 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1341, + "end": 1342, + "raw": "b", + "data": "b" + } + ] + } + }, + { + "type": "Text", + "start": 1349, + "end": 1353, + "raw": ",\n\t\t", + "data": ",\n\t\t" + }, + { + "type": "RegularElement", + "start": 1353, + "end": 1367, + "name": "code", + "name_loc": { + "start": { + "line": 45, + "column": 3, + "character": 1354 + }, + "end": { + "line": 45, + "column": 7, + "character": 1358 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1359, + "end": 1360, + "raw": "c", + "data": "c" + } + ] + } + }, + { + "type": "Text", + "start": 1367, + "end": 1369, + "raw": "\n\t", + "data": "\n\t" + } + ] + } + }, + { + "type": "Text", + "start": 1376, + "end": 1377, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1383, + "end": 1384, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1384, + "end": 1438, + "name": "div", + "name_loc": { + "start": { + "line": 48, + "column": 1, + "character": 1385 + }, + "end": { + "line": 48, + "column": 4, + "character": 1388 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1389, + "end": 1391, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1391, + "end": 1431, + "name": "span", + "name_loc": { + "start": { + "line": 49, + "column": 2, + "character": 1392 + }, + "end": { + "line": 49, + "column": 6, + "character": 1396 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1397, + "end": 1408, + "raw": "\n\t\ttext1\n\t\t", + "data": "\n\t\ttext1\n\t\t" + }, + { + "type": "RegularElement", + "start": 1408, + "end": 1422, + "name": "code", + "name_loc": { + "start": { + "line": 51, + "column": 3, + "character": 1409 + }, + "end": { + "line": 51, + "column": 7, + "character": 1413 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1414, + "end": 1415, + "raw": "a", + "data": "a" + } + ] + } + }, + { + "type": "Text", + "start": 1422, + "end": 1424, + "raw": "\n\t", + "data": "\n\t" + } + ] + } + }, + { + "type": "Text", + "start": 1431, + "end": 1432, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1438, + "end": 1439, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1439, + "end": 1493, + "name": "div", + "name_loc": { + "start": { + "line": 54, + "column": 1, + "character": 1440 + }, + "end": { + "line": 54, + "column": 4, + "character": 1443 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1444, + "end": 1446, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1446, + "end": 1486, + "name": "span", + "name_loc": { + "start": { + "line": 55, + "column": 2, + "character": 1447 + }, + "end": { + "line": 55, + "column": 6, + "character": 1451 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1452, + "end": 1455, + "raw": "\n\t\t", + "data": "\n\t\t" + }, + { + "type": "RegularElement", + "start": 1455, + "end": 1469, + "name": "code", + "name_loc": { + "start": { + "line": 56, + "column": 3, + "character": 1456 + }, + "end": { + "line": 56, + "column": 7, + "character": 1460 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1461, + "end": 1462, + "raw": "a", + "data": "a" + } + ] + } + }, + { + "type": "Text", + "start": 1469, + "end": 1479, + "raw": "\n\t\ttext1\n\t", + "data": "\n\t\ttext1\n\t" + } + ] + } + }, + { + "type": "Text", + "start": 1486, + "end": 1487, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1493, + "end": 1495, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1495, + "end": 1657, + "data": " control: the same content-text edge in a run whose text carries two words — prose, so the\n newline is the fill's wrap point and the element collapses " + }, + { + "type": "Text", + "start": 1657, + "end": 1658, + "raw": "\n", + "data": "\n" + }, + { + "type": "RegularElement", + "start": 1658, + "end": 1711, + "name": "div", + "name_loc": { + "start": { + "line": 63, + "column": 1, + "character": 1659 + }, + "end": { + "line": 63, + "column": 4, + "character": 1662 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1663, + "end": 1665, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 1665, + "end": 1704, + "name": "span", + "name_loc": { + "start": { + "line": 64, + "column": 2, + "character": 1666 + }, + "end": { + "line": 64, + "column": 6, + "character": 1670 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1671, + "end": 1683, + "raw": "text1 text2 ", + "data": "text1 text2 " + }, + { + "type": "RegularElement", + "start": 1683, + "end": 1697, + "name": "code", + "name_loc": { + "start": { + "line": 64, + "column": 20, + "character": 1684 + }, + "end": { + "line": 64, + "column": 24, + "character": 1688 + } + }, + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 1689, + "end": 1690, + "raw": "a", + "data": "a" + } + ] + } + } + ] + } + }, + { + "type": "Text", + "start": 1704, + "end": 1705, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 1711, + "end": 1713, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Comment", + "start": 1713, + "end": 1850, "data": " control: the same newline in a run whose text carries two words — the run is prose and the\n newline is the fill's wrap point " }, { "type": "Text", - "start": 1158, - "end": 1159, + "start": 1850, + "end": 1851, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 1159, - "end": 1205, + "start": 1851, + "end": 1897, "name": "div", "name_loc": { "start": { - "line": 41, + "line": 69, "column": 1, - "character": 1160 + "character": 1852 }, "end": { - "line": 41, + "line": 69, "column": 4, - "character": 1163 + "character": 1855 } }, "attributes": [], @@ -692,26 +1243,26 @@ "nodes": [ { "type": "Text", - "start": 1164, - "end": 1166, + "start": 1856, + "end": 1858, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 1166, - "end": 1198, + "start": 1858, + "end": 1890, "name": "span", "name_loc": { "start": { - "line": 42, + "line": 70, "column": 2, - "character": 1167 + "character": 1859 }, "end": { - "line": 42, + "line": 70, "column": 6, - "character": 1171 + "character": 1863 } }, "attributes": [], @@ -720,19 +1271,19 @@ "nodes": [ { "type": "ExpressionTag", - "start": 1172, - "end": 1175, + "start": 1864, + "end": 1867, "expression": { "type": "Identifier", - "start": 1173, - "end": 1174, + "start": 1865, + "end": 1866, "loc": { "start": { - "line": 42, + "line": 70, "column": 8 }, "end": { - "line": 42, + "line": 70, "column": 9 } }, @@ -741,26 +1292,26 @@ }, { "type": "Text", - "start": 1175, - "end": 1176, + "start": 1867, + "end": 1868, "raw": " ", "data": " " }, { "type": "ExpressionTag", - "start": 1176, - "end": 1179, + "start": 1868, + "end": 1871, "expression": { "type": "Identifier", - "start": 1177, - "end": 1178, + "start": 1869, + "end": 1870, "loc": { "start": { - "line": 42, + "line": 70, "column": 12 }, "end": { - "line": 42, + "line": 70, "column": 13 } }, @@ -769,8 +1320,8 @@ }, { "type": "Text", - "start": 1179, - "end": 1191, + "start": 1871, + "end": 1883, "raw": " text1 text2", "data": " text1 text2" } @@ -779,8 +1330,8 @@ }, { "type": "Text", - "start": 1198, - "end": 1199, + "start": 1890, + "end": 1891, "raw": "\n", "data": "\n" } diff --git a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/input.svelte b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/input.svelte index 9d24bd3c6..0e4ce1df2 100644 --- a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/input.svelte +++ b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/input.svelte @@ -36,6 +36,34 @@ </span> </div> +<!-- the same one-word interior newline spelled at a CONTENT TEXT's edge rather than in a + whitespace-only node: one class, one answer, whichever node the parser folded the newline + into. A lone `,` is a word like any other, so its run is a label and the newline is held --> +<div> + <span> + <code>a</code> <code>b</code>, + <code>c</code> + </span> +</div> +<div> + <span> + text1 + <code>a</code> + </span> +</div> +<div> + <span> + <code>a</code> + text1 + </span> +</div> + +<!-- control: the same content-text edge in a run whose text carries two words — prose, so the + newline is the fill's wrap point and the element collapses --> +<div> + <span>text1 text2 <code>a</code></span> +</div> + <!-- control: the same newline in a run whose text carries two words — the run is prose and the newline is the fill's wrap point --> <div> diff --git a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/prettier_variant_dangle.svelte b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/prettier_variant_dangle.svelte index 0e98b87b6..751fa93d5 100644 --- a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/prettier_variant_dangle.svelte +++ b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/prettier_variant_dangle.svelte @@ -36,6 +36,37 @@ > </div> +<!-- the same one-word interior newline spelled at a CONTENT TEXT's edge rather than in a + whitespace-only node: one class, one answer, whichever node the parser folded the newline + into. A lone `,` is a word like any other, so its run is a label and the newline is held --> +<div> + <span + ><code>a</code> <code>b</code>, + <code>c</code></span + > +</div> +<div> + <span + >text1 + <code>a</code></span + > +</div> +<div> + <span + ><code>a</code> + text1</span + > +</div> + +<!-- control: the same content-text edge in a run whose text carries two words — prose, so the + newline is the fill's wrap point and the element collapses --> +<div> + <span + >text1 text2 + <code>a</code></span + > +</div> + <!-- control: the same newline in a run whose text carries two words — the run is prose and the newline is the fill's wrap point --> <div> diff --git a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/unformatted_ours_hugged.svelte b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/unformatted_ours_hugged.svelte index 3d3fa9da3..e5d921b84 100644 --- a/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/unformatted_ours_hugged.svelte +++ b/tests/fixtures/svelte/elements/inline_interior_newline_label_hold_prettier_divergence/unformatted_ours_hugged.svelte @@ -28,6 +28,29 @@ <code>b</code> text1 <code>c</code> text2</span> </div> +<!-- the same one-word interior newline spelled at a CONTENT TEXT's edge rather than in a + whitespace-only node: one class, one answer, whichever node the parser folded the newline + into. A lone `,` is a word like any other, so its run is a label and the newline is held --> +<div> + <span><code>a</code> <code>b</code>, + <code>c</code></span> +</div> +<div> + <span>text1 + <code>a</code></span> +</div> +<div> + <span><code>a</code> + text1</span> +</div> + +<!-- control: the same content-text edge in a run whose text carries two words — prose, so the + newline is the fill's wrap point and the element collapses --> +<div> + <span>text1 text2 + <code>a</code></span> +</div> + <!-- control: the same newline in a run whose text carries two words — the run is prose and the newline is the fill's wrap point --> <div> diff --git a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/README.md b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/README.md index 7fd908055..13e7f8d34 100644 --- a/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/README.md +++ b/tests/fixtures/svelte/elements/inline_sibling_newline_label_hold_tag_pair_space_prettier_divergence/README.md @@ -7,7 +7,7 @@ nothing about an authored space. A one-word run holds its authored lines, but it tag pair — `text1 {expr1} {expr2}`, `{expr1} {expr2} {expr3} text1`, and the escaped-angle-bracket pair at the root — is a fill like a prose run's and packs per width. Prettier's bare `line` between two tags breaks with the multiline container whatever the run holds, so it splits every -one of them (`output_prettier.svelte`). The control is a prose-free pair (`{expr1} {expr2}`, no +one of them (`output_prettier.svelte`). The control is a wordless pair (`{expr1} {expr2}`, no content text at all), which breaks with the container under both formatters (`unformatted_ours_space.svelte`: tsv normalizes its space spelling to the split `input.svelte` form, prettier to `output_prettier.svelte`). @@ -17,12 +17,12 @@ form, prettier to `output_prettier.svelte`). Design choice. The whitespace-only separator before a tag asks two different run gates for its two spellings. The NEWLINE spelling asks the prose gate — is there a phrase to reflow into — and a one-word run says no, so the newline is held as the author's structure. The SPACE spelling asks -only whether the run holds a content text at all — is there a fill for this separator to sit in — +only whether the run holds a word at all — is there a fill for this separator to sit in — so it defers to the next tag's per-width group and the pair packs, exactly as a prose run's does ([inline_adjacent_sibling_newline_flow](../inline_adjacent_sibling_newline_flow_prettier_divergence/), the prose tag pair prettier also splits). Gating the space on the prose count would turn the hold into a forced break — `Price: {currency} {amount}` authored on one line coming out on two — a -layout keyed on nothing the author wrote. Only a run with no content text keeps the bare `line` +layout keyed on nothing the author wrote. Only a run with no word in it keeps the bare `line` that breaks with the container: with no fill at all, its authored spacing is the only structure it has, and both formatters agree (the tag-separator rule of [inline_content_spaced_tags_long](../inline_content_spaced_tags_long_prettier_divergence/)). From 229ba1a82104d05e90cdec4a464e62f652eefca4 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson <mail@ryanatkn.com> Date: Tue, 25 Aug 2026 14:30:49 -0400 Subject: [PATCH 4/4] fix --- crates/tsv_svelte/src/printer/nodes/element_analysis.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/tsv_svelte/src/printer/nodes/element_analysis.rs b/crates/tsv_svelte/src/printer/nodes/element_analysis.rs index 30a54ec10..c6638f20b 100644 --- a/crates/tsv_svelte/src/printer/nodes/element_analysis.rs +++ b/crates/tsv_svelte/src/printer/nodes/element_analysis.rs @@ -375,8 +375,10 @@ impl<'a> Printer<'a> { // `run.len() - 1`: `trimmed_content_run` drops only whitespace-only text, so a HOISTED // node (`{@debug}`, `<title>`, `{@const}`, `{#snippet}`) sits at a real index and the // text beside it is the effective edge. That is exactly `blocks/hoisted_boundary_convergence`. + // The `None` arm (every node hoisted) is inert rather than load-bearing: no hoisted kind + // is a `Text`, so the scan below never reaches a node to compare these against. let content_edges = - FragmentNode::content_bounds(run).unwrap_or((0, run.len().saturating_sub(1))); + FragmentNode::content_bounds(run).unwrap_or_else(|| (0, run.len().saturating_sub(1))); // Check for newlines in content between first and last non-whitespace nodes run.iter().enumerate().any(|(idx, n)| {