Class: Slamdown::Conversion::Normaliser::Content
- Inherits:
-
Object
- Object
- Slamdown::Conversion::Normaliser::Content
- Defined in:
- lib/slamdown/conversion/normaliser/content.rb
Overview
Owns format-neutral whitespace, phrasing-run and content-inspection rules shared by structural transformations.
Constant Summary collapse
- HTML_WHITESPACE =
/[\t\n\f\r ]+/.freeze
- LEADING_SPACE =
/\A +/.freeze
- TRAILING_SPACE =
/ +\z/.freeze
- BLOCK_SEMANTICS =
[ :paragraph, :heading, :blockquote, :ordered_list, :unordered_list, :list_item, :definition_list, :definition_term, :definition_description, :horizontal_rule, :code_block, :table, :table_head, :table_body, :table_foot, :table_row, :table_cell, :table_caption, :table_colgroup, :table_column ].freeze
Instance Method Summary collapse
- #block_node?(node) ⇒ Boolean
- #descendants(nodes) ⇒ Object
-
#initialize(node_factory) ⇒ Content
constructor
A new instance of Content.
-
#normalise_code_block(value) ⇒ Object
Removes editor-layout whitespace around code while retaining meaningful indentation in already-left-aligned blocks.
- #normalise_inline_sequence(nodes, trim:) ⇒ Object
- #normalise_text(value) ⇒ Object
- #plain_text(nodes) ⇒ Object
-
#split_around_blocks(nodes) ⇒ Object
Splits inline runs around block children so a phrasing-only block can be closed and reopened canonically.
- #textual_content?(nodes) ⇒ Boolean
- #visible_content?(nodes) ⇒ Boolean
- #whitespace_text?(node) ⇒ Boolean
- #wrap_phrasing_runs(nodes) ⇒ Object
Constructor Details
#initialize(node_factory) ⇒ Content
Returns a new instance of Content.
17 18 19 |
# File 'lib/slamdown/conversion/normaliser/content.rb', line 17 def initialize(node_factory) @node_factory = node_factory end |
Instance Method Details
#block_node?(node) ⇒ Boolean
147 148 149 |
# File 'lib/slamdown/conversion/normaliser/content.rb', line 147 def block_node?(node) node.category == :block || BLOCK_SEMANTICS.include?(node.semantic_kind) end |
#descendants(nodes) ⇒ Object
139 140 141 |
# File 'lib/slamdown/conversion/normaliser/content.rb', line 139 def descendants(nodes) nodes.flat_map { |node| [node] + descendants(node.children) } end |
#normalise_code_block(value) ⇒ Object
Removes editor-layout whitespace around code while retaining meaningful indentation in already-left-aligned blocks.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/slamdown/conversion/normaliser/content.rb', line 26 def normalise_code_block(value) lines = value.to_s.gsub("\r\n", "\n").gsub("\r", "\n").split("\n", -1) lines.shift while lines.first && lines.first.strip.empty? lines.pop while lines.last && lines.last.strip.empty? return "" if lines.empty? lines.map! { |line| line.sub(/[\t ]+\z/, "") } prefixes = lines.reject { |line| line.empty? }.map { |line| line[/\A[\t ]*/] } if !prefixes.empty? && prefixes.none?(&:empty?) width = prefixes.map { |prefix| indentation_width(prefix) }.min lines.map! { |line| remove_indentation(line, width) } end lines.join("\n") end |
#normalise_inline_sequence(nodes, trim:) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/slamdown/conversion/normaliser/content.rb', line 62 def normalise_inline_sequence(nodes, trim:) result = [] nodes.each do |node| if node.kind == :text && result.last && result.last.kind == :text result[-1] = result.last.with(:value => normalise_text(result.last.value + node.value)) else result << node end end if trim result[0] = result.first.with(:value => result.first.value.sub(LEADING_SPACE, "")) if result.first && result.first.kind == :text result[-1] = result.last.with(:value => result.last.value.sub(TRAILING_SPACE, "")) if result.last && result.last.kind == :text end result.reject { |node| node.kind == :text && node.value.empty? } end |
#normalise_text(value) ⇒ Object
21 22 23 |
# File 'lib/slamdown/conversion/normaliser/content.rb', line 21 def normalise_text(value) value.to_s.gsub("\u00A0", " ").gsub(HTML_WHITESPACE, " ") end |
#plain_text(nodes) ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/slamdown/conversion/normaliser/content.rb', line 102 def plain_text(nodes) nodes.map do |node| if node.kind == :text node.value elsif node.kind == :entity && node.value.is_a?(Hash) node.value[:character].to_s elsif node.value.is_a?(String) node.value else plain_text(node.children) end end.join end |
#split_around_blocks(nodes) ⇒ Object
Splits inline runs around block children so a phrasing-only block can be closed and reopened canonically.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/slamdown/conversion/normaliser/content.rb', line 42 def split_around_blocks(nodes) result = [] run = [] flush = lambda do result << run unless run.empty? run = [] end nodes.each do |node| if block_node?(node) flush.call result << node else run << node end end flush.call result end |
#textual_content?(nodes) ⇒ Boolean
116 117 118 119 120 121 122 123 |
# File 'lib/slamdown/conversion/normaliser/content.rb', line 116 def textual_content?(nodes) return true unless plain_text(nodes).strip.empty? nodes.any? do |node| image_alt = node.semantic_kind == :image ? Hash[node.attributes].fetch("alt", "").to_s : nil [:typographic_symbol, :smart_quote].include?(node.kind) || !image_alt.to_s.strip.empty? || textual_content?(node.children) end end |
#visible_content?(nodes) ⇒ Boolean
125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/slamdown/conversion/normaliser/content.rb', line 125 def visible_content?(nodes) nodes.any? do |node| if node.kind == :text !node.value.strip.empty? elsif [:entity, :typographic_symbol, :smart_quote].include?(node.kind) true elsif [:line_break, :horizontal_rule, :paragraph_boundary].include?(node.semantic_kind) false else textual_content?([node]) || [:image, :code_span].include?(node.semantic_kind) || node.representation == :html end end end |
#whitespace_text?(node) ⇒ Boolean
143 144 145 |
# File 'lib/slamdown/conversion/normaliser/content.rb', line 143 def whitespace_text?(node) node.kind == :text && node.value.strip.empty? end |
#wrap_phrasing_runs(nodes) ⇒ Object
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/slamdown/conversion/normaliser/content.rb', line 79 def wrap_phrasing_runs(nodes) result = [] run = [] flush = lambda do run = normalise_inline_sequence(run, :trim => true) result << @node_factory.paragraph(run, run.first.source_path) if visible_content?(run) run = [] end nodes.each do |node| if node.semantic_kind == :paragraph_boundary flush.call elsif block_node?(node) flush.call result << node else run << node end end flush.call result end |