instance method
visit_h3
Ruby on Rails edge
Since edge Private — implementation detail, not part of the public APISignature
visit_h3(node, child_values)
No documentation comment.
Parameters
-
nodereq -
child_valuesreq
Source
# File actiontext/lib/action_text/markdown_conversion.rb, line 202
def visit_h3(node, child_values) = visit__heading(node, child_values, 3)
def visit_h4(node, child_values) = visit__heading(node, child_values, 4)
def visit_h5(node, child_values) = visit__heading(node, child_values, 5)
def visit_h6(node, child_values) = visit__heading(node, child_values, 6)
def visit_blockquote(_node, child_values)
quoted = join_children(child_values).strip.lines.map { |line| "> #{line}" }.join
"#{quoted}\n\n"
end
def visit_ul(node, child_values)
items = list_item_lines(node, child_values, prefix: "- ")
"#{items}\n\n"
end
def visit_ol(node, child_values)
items = list_item_lines(node, child_values, prefix: ->(i) { "#{i + 1}. " })
"#{items}\n\n"
end
def visit_a(node, child_values)
inner = join_children(child_values)
if (href = node["href"]) && Rails::HTML::Sanitizer.allowed_uri?(href)
"[#{flatten_to_inline(inner)}](#{encode_href(href)})"
else
inner
end
end
def flatten_to_inline(text)
return text unless text.match?(/[\r\n]/)
text.gsub(/[\r\n]+/, " ")
end
def visit_tr(node, child_values)
# lexxy does not emit `thead`, so we need to infer header rows from `tr` contents
if node.element_children.all? { |cell| cell.name == "th" }
visit__table_header_row(node, child_values)
else
cells = child_values_for_elements(node, child_values).map { |v| stringify(v).strip }
"| #{cells.join(" | ")} |\n"
end
end
def visit_summary(_node, child_values)
"**#{join_children(child_values)}**\n\n"
end
def visit_br(_node, _child_values)
"\n"
end
def visit_hr(_node, _child_values)
"---\n\n"
end
# Attachment markdown is wrapped in <action-text-markdown> by Content#to_markdown so it passes
# through without text escaping.
def visit_action_text_markdown(_node, child_values)
join_children(child_values)
end
# Avoid including content from elements that aren't meaningful for markdown output
def visit__unsupported(_node, _child_values)
""
end
alias_method :visit_script, :visit__unsupported
alias_method :visit_style, :visit__unsupported
# These elements pass through their content (parent handlers use child_values directly)
def visit__passthrough(_node, child_values)
join_children(child_values)
end
alias_method :visit_li, :visit__passthrough
alias_method :visit_td, :visit__passthrough
alias_method :visit_th, :visit__passthrough
alias_method :visit_thead, :visit__passthrough
alias_method :visit_tbody, :visit__passthrough
def visit__table_header_row(node, child_values)
cells = child_values_for_elements(node, child_values).map { |v| stringify(v).strip }
row = "| #{cells.join(" | ")} |\n"
separator = "| #{Array.new(cells.size, "---").join(" | ")} |\n"
"#{row}#{separator}"
end
def list_item_lines(list_node, child_values, prefix:)
element_values = child_values_for_elements(list_node, child_values)
element_values.each_with_index.filter_map do |value, index|
text = stringify(value)
lines = text.split("\n").reject(&:blank?)
next if lines.empty?
bullet = prefix.respond_to?(:call) ? prefix.call(index) : prefix
format_list_item(lines, bullet)
end.join("\n")
end
def format_list_item(lines, bullet)
first, *rest = lines
leader = first.match?(LIST_BULLET) ? LIST_INDENT : bullet
([ leader + first ] + rest.map { |line| LIST_INDENT + line }).join("\n")
end
def join_children(child_values)
merged = []
child_values.each do |value|
# Merge adjacent bold/italic runs which Lexxy emits
if value.is_a?(Array) && (value[0] == :bold || value[0] == :italic)
if merged.last.is_a?(Array) && merged.last[0] == value[0]
merged.last[1] = merged.last[1] + value[1]
else
merged << [ value[0], value[1] ]
end
else
merged << value
end
end
parts = merged.map { |v| stringify(v) }
result = +""
parts.each do |part|
# Nested block elements (e.g., lists and blockquotes) need an initial newline injected
if !result.empty? && !result.end_with?("\n") && part.end_with?("\n\n")
result << "\n"
end
result << part
end
result
end
def child_values_for_elements(node, child_values)
node.children.zip(child_values).filter_map do |child, value|
value if child.element?
end
end
def stringify(value)
case value
when Array
case value[0]
when :bold then wrap_emphasis(value[1], "**")
when :italic then wrap_emphasis(value[1], "*")
else value.join
end
else
value.to_s
end
end
# Make sure `<strong> hello </strong>` becomes ` **hello** ` and not `** hello **`
# (the latter is not valid markdown).
def wrap_emphasis(text, marker)
leading = text[/\A\s*/]
trailing = text[/\s*\z/]
inner = text.strip
"#{leading}#{marker}#{inner}#{marker}#{trailing}"
end
def code_fence(content)
max_run = content.scan(/`{3,}/).map(&:length).max || 0
"`" * [3, max_run + 1].max
end
def inline_code(content)
max_run = content.scan(/`+/).map(&:length).max || 0
fence = "`" * [1, max_run + 1].max
if content.start_with?("`") || content.end_with?("`")
"#{fence} #{content} #{fence}"
else
"#{fence}#{content}#{fence}"
end
end
def strip_pretty_print_indentation(node)
content = node.content
return content unless content.include?("\n")
content
.sub(LEADING_PRETTY_PRINT_WHITESPACE, inline_sibling?(node.previous_sibling) ? " " : "")
.sub(TRAILING_PRETTY_PRINT_WHITESPACE, inline_sibling?(node.next_sibling) ? " " : "")
end
def significant_whitespace?(node)
inline_sibling?(node.previous_sibling) &&
inline_sibling?(node.next_sibling)
end
def inline_sibling?(sibling)
sibling&.text? || sibling&.name&.in?(INLINE_ELEMENTS)
end
def ancestor_named?(node, names, max_depth:)
current = node.parent
max_depth.times do
break unless current&.element?
return true if current.name.in?(names)
current = current.parent
end
false
end
def encode_href(href)
URI::RFC2396_PARSER.escape(href, ENCODE_HREF_CHARS)
end
def skip_markdown_escaping?(node)
node.parent&.name.in?(SKIP_ESCAPING_PARENTS)
end
Defined in actiontext/lib/action_text/markdown_conversion.rb line 202
· View on GitHub
· Improve this page
· Find usages on GitHub
Defined in ActionText::MarkdownConversion