diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index 1610f3689..d2caf042a 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -8,6 +8,8 @@ class Formatter include ActionView::Helpers::TextHelper + DISALLOWED_BOUNDING_REGEX = /[[:alnum:]:]/.freeze + def format(status, **options) if status.reblog? prepend_reblog = status.reblog.account.acct @@ -140,54 +142,49 @@ class Formatter emoji_map = emojis.each_with_object({}) { |e, h| h[e.shortcode] = [full_asset_url(e.image.url), full_asset_url(e.image.url(:static))] } - i = -1 - tag_open_index = nil - inside_shortname = false - shortname_start_index = -1 - invisible_depth = 0 + tree = Nokogiri::HTML.fragment(html) + tree.xpath('./text()|.//text()[not(ancestor[@class="invisible"])]').to_a.each do |node| + i = -1 + inside_shortname = false + shortname_start_index = -1 + last_index = 0 + text = node.content + result = Nokogiri::XML::NodeSet.new(tree.document) - while i + 1 < html.size - i += 1 + while i + 1 < text.size + i += 1 - if invisible_depth.zero? && inside_shortname && html[i] == ':' - shortcode = html[shortname_start_index + 1..i - 1] - emoji = emoji_map[shortcode] + if inside_shortname && text[i] == ':' + inside_shortname = false + shortcode = text[shortname_start_index + 1..i - 1] + char_after = text[i + 1] + + next unless (char_after.nil? || !DISALLOWED_BOUNDING_REGEX.match?(char_after)) && (emoji = emoji_map[shortcode]) - if emoji original_url, static_url = emoji - replacement = begin + + result << Nokogiri::XML::Text.new(text[last_index..shortname_start_index - 1], tree.document) if shortname_start_index.positive? + + result << Nokogiri::HTML.fragment( if animate image_tag(original_url, draggable: false, class: 'emojione', alt: ":#{shortcode}:", title: ":#{shortcode}:") else image_tag(original_url, draggable: false, class: 'emojione custom-emoji', alt: ":#{shortcode}:", title: ":#{shortcode}:", data: { original: original_url, static: static_url }) end - end - before_html = shortname_start_index.positive? ? html[0..shortname_start_index - 1] : '' - html = before_html + replacement + html[i + 1..-1] - i += replacement.size - (shortcode.size + 2) - 1 - else - i -= 1 - end + ) - inside_shortname = false - elsif tag_open_index && html[i] == '>' - tag = html[tag_open_index..i] - tag_open_index = nil - if invisible_depth.positive? - invisible_depth += count_tag_nesting(tag) - elsif tag == '