Differ
(before, after) → patches, under the one contract that matters (design/live.md §1.1): applying the patches to a DOM holding before produces the DOM that rendering after would. The round trip harness in research/harnesses/live-roundtrip holds it against the real client applier; RoundTripSuite holds it against the reference applier on every generated case.
The recursion prefers the finest patch that is still correct — a text edit over a subtree replace, an attribute set over either — because replacing a node destroys focus, caret, scroll and playback state the user can see (design/live.md §1.1 on §4.6). It bails to a coarser move at exactly two seams, each for a stated reason: a changed tag name or node kind is Patch.ReplaceNode, because no finer target survives the change; and a child list holding raw markup diffs as one Patch.SetChildren, because one Raw tree child can parse into any number of DOM nodes, so no sibling index past it can be trusted (design/live.md §2.2).
Child lists are diffed positionally: index against index, trailing surplus removed or appended. "Insert one row at the top" therefore patches every row, which is the cost design/live.md accepts until M5's keyed reconciliation; the keys are already carried, loudly validated, and diffed as ordinary attributes until then.
Attributes are compared as name → value maps, not as ordered lists: attribute order has no DOM meaning, setAttribute cannot reorder what exists, and a differ that chased order would replace nodes for a difference no browser can observe. For the same reason a bare attribute and an empty-valued one (disabled vs disabled="") are the same attribute here, as they are in the DOM.
Every function returns its patches; nothing accumulates by mutation. Concatenation order is the application-order contract Patch documents: within one child list, in-place updates first, then removals highest index first, then the append.
Attributes
- Source
- Differ.scala
- Graph
-
- Supertypes
- Self type
-
Differ.type