Class: Markbridge::Normalizer
- Inherits:
-
Object
- Object
- Markbridge::Normalizer
- Defined in:
- lib/markbridge/normalizer.rb,
lib/markbridge/normalizer/report.rb,
lib/markbridge/normalizer/walker.rb,
lib/markbridge/normalizer/rule_set.rb,
lib/markbridge/normalizer/text_projection.rb
Overview
Rewrites an AST so the renderer only gets markup the target format can
express. It runs once, between parse and render. The default rules are
CommonMark legality: no link inside a link, no block element inside an
inline container, and an inline-only code span. Each match resolves to a
strategy (+:keep+, :hoist_after, :unwrap, :textify, :drop, or a
callable) that the Walker applies.
Discourse-specific policy (for example, moving an image out of a link) is not built in. A consumer adds those with #rule.
Defined Under Namespace
Modules: TextProjection Classes: Report, RuleSet, Walker
Constant Summary collapse
- STRATEGIES =
The strategy symbols a rule may resolve to.
%i[keep hoist_after unwrap textify drop].freeze
- INLINE_CONTAINERS =
Containers that hold inline content only: a link's text (CommonMark ยง6.3), and emphasis and heading content.
[ AST::Url, AST::Bold, AST::Italic, AST::Strikethrough, AST::Underline, AST::Superscript, AST::Subscript, AST::Heading, ].freeze
- BLOCK_NODES =
AST nodes the Discourse renderer prints as block-level Markdown (their output has blank lines around it). One inside an inline container breaks that container, so it is moved out. Spoiler and single-line Code stay inline and are not listed (Code is handled by KEEP_INLINE_CODE).
[ AST::Quote, AST::Heading, AST::List, AST::ListItem, AST::Table, AST::TableRow, AST::TableCell, AST::Details, AST::Paragraph, AST::HorizontalRule, AST::Align, AST::Poll, AST::Event, ].freeze
- KEEP_INLINE_CODE =
A code span may stay inside an inline container while it is on one line. A fenced or multi-line block is moved out. This matches
RenderingInterface#block_context?: Code prints as a fenced block when a Text child has a newline (the language alone does not make it a block). lambda do |_boundary, node| block = node.children.any? { |c| c.instance_of?(AST::Text) && c.text.include?("\n") } block ? :hoist_after : :keep end
Class Method Summary collapse
-
.default ⇒ Normalizer
A fresh, customizable normalizer with the default rules.
-
.shared_default ⇒ Normalizer
The default normalizer, built once and frozen, reused across conversions.
Instance Method Summary collapse
- #freeze ⇒ Object
-
#initialize(rule_set) ⇒ Normalizer
constructor
A new instance of Normalizer.
-
#normalize(ast) ⇒ Array<Hash>
Rewrite
astin place so it satisfies the rules. -
#rule(parent:, child:, strategy:) ⇒ self
Add or override a rule.
-
#violations(ast) ⇒ Array<Hash>
List the violations in
astwithout changing it.
Constructor Details
#initialize(rule_set) ⇒ Normalizer
Returns a new instance of Normalizer.
119 120 121 |
# File 'lib/markbridge/normalizer.rb', line 119 def initialize(rule_set) @rules = rule_set end |
Class Method Details
.default ⇒ Normalizer
A fresh, customizable normalizer with the default rules. Add more with #rule.
83 84 85 |
# File 'lib/markbridge/normalizer.rb', line 83 def default new(build_rules) end |
.shared_default ⇒ Normalizer
The default normalizer, built once and frozen, reused across
conversions. #normalize and #violations keep no state on the
instance, so one frozen instance is safe to reuse, also across threads.
91 92 93 |
# File 'lib/markbridge/normalizer.rb', line 91 def shared_default @shared_default ||= default.freeze end |
Instance Method Details
#freeze ⇒ Object
157 158 159 160 |
# File 'lib/markbridge/normalizer.rb', line 157 def freeze @rules.freeze super end |
#normalize(ast) ⇒ Array<Hash>
Rewrite ast in place so it satisfies the rules.
141 142 143 144 145 |
# File 'lib/markbridge/normalizer.rb', line 141 def normalize(ast) report = Report.new Walker.new(@rules, report).call(ast) report.to_a end |
#rule(parent:, child:, strategy:) ⇒ self
Add or override a rule. Chainable. A rule for a (parent, child) pair
that already exists is replaced. Raises on a frozen (shared_default)
instance; build a fresh one with default.
131 132 133 134 |
# File 'lib/markbridge/normalizer.rb', line 131 def rule(parent:, child:, strategy:) @rules.add(parent:, child:, strategy:) self end |
#violations(ast) ⇒ Array<Hash>
List the violations in ast without changing it.
151 152 153 154 155 |
# File 'lib/markbridge/normalizer.rb', line 151 def violations(ast) found = [] collect_violations(ast, EMPTY_STACK, found) found end |