Class: Markbridge::Normalizer::Walker

Inherits:
Object
  • Object
show all
Defined in:
lib/markbridge/normalizer/walker.rb

Overview

The parent-aware tree-rewriting engine. One Walker is built per #normalize call (holding that call's RuleSet and Report; no state is kept on the Normalizer). It changes the tree in place via AST::Element#replace_children, touching only elements whose children actually changed.

The main rule is resolve-before-descend: a child's strategy is resolved against its current ancestor stack first, and a moved subtree (hoist or unwrap) is then walked against the ancestor stack it will have in its new place. So a node that leaves a link does not see the link while its own inside is normalized. That keeps a legally nested quote-in-quote or image-in-quote intact, and it means a second normalize reports nothing.

Hoisting: a node taken out of an inline container is moved up, tagged with its boundary (the outermost matching ancestor, compared by identity), and placed right after that boundary, at the boundary's parent. A wrapper left empty by a hoist or drop is removed (see PRUNE_WHEN_EMPTY), so no empty ** ** markers stay.

For speed, the ancestor stack is one shared array that is pushed and popped, and an element's children list is copied only when a child first changes (copy-on-write). A subtree with no violations allocates nothing and is left as it was, so the pass can run by default.

Constant Summary collapse

EMPTY =
[].freeze
PRUNE_WHEN_EMPTY =

Wrappers that mean nothing once they are empty, so an empty one is removed instead of kept. AST::Url is not here on purpose: an empty link still renders as a bare URL, so a hoist that empties a link keeps the link.

[
  AST::Bold,
  AST::Italic,
  AST::Underline,
  AST::Strikethrough,
  AST::Superscript,
  AST::Subscript,
  AST::Color,
  AST::Size,
  AST::Align,
  AST::Email,
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(rule_set, report) ⇒ Walker

Returns a new instance of Walker.

Parameters:



50
51
52
53
# File 'lib/markbridge/normalizer/walker.rb', line 50

def initialize(rule_set, report)
  @rules = rule_set
  @report = report
end

Instance Method Details

#call(document) ⇒ Object

Normalize +document+'s subtree in place.

Parameters:



57
58
59
60
61
62
63
# File 'lib/markbridge/normalizer/walker.rb', line 57

def call(document)
  _element, bubble = normalize_element(document, [])
  # Defensive: anything that never reached its boundary becomes a
  # trailing sibling rather than being lost. Well-formed rule tables
  # do not get here.
  bubble.each { |node, _boundary| document << node }
end