diff options
Diffstat (limited to 'lib/banzai/filter/custom_emoji_filter.rb')
-rw-r--r-- | lib/banzai/filter/custom_emoji_filter.rb | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/lib/banzai/filter/custom_emoji_filter.rb b/lib/banzai/filter/custom_emoji_filter.rb new file mode 100644 index 00000000000..1ee8f4e31e8 --- /dev/null +++ b/lib/banzai/filter/custom_emoji_filter.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module Banzai + module Filter + class CustomEmojiFilter < HTML::Pipeline::Filter + IGNORED_ANCESTOR_TAGS = %w(pre code tt).to_set + + def call + return doc unless context[:project] + return doc unless Feature.enabled?(:custom_emoji, context[:project]) + + doc.search(".//text()").each do |node| + content = node.to_html + + next if has_ancestor?(node, IGNORED_ANCESTOR_TAGS) + next unless content.include?(':') + next unless namespace && namespace.custom_emoji.any? + + html = custom_emoji_name_element_filter(content) + + node.replace(html) unless html == content + end + + doc + end + + def custom_emoji_pattern + @emoji_pattern ||= + /(?<=[^[:alnum:]:]|\n|^) + :(#{CustomEmoji::NAME_REGEXP}): + (?=[^[:alnum:]:]|$)/x + end + + def custom_emoji_name_element_filter(text) + text.gsub(custom_emoji_pattern) do |match| + name = Regexp.last_match[1] + custom_emoji = all_custom_emoji[name] + + if custom_emoji + Gitlab::Emoji.custom_emoji_tag(custom_emoji.name, custom_emoji.url) + else + match + end + end + end + + private + + def namespace + context[:project].namespace.root_ancestor + end + + def custom_emoji_candidates + doc.to_html.scan(/:(#{CustomEmoji::NAME_REGEXP}):/).flatten + end + + def all_custom_emoji + @all_custom_emoji ||= namespace.custom_emoji.by_name(custom_emoji_candidates).index_by(&:name) + end + end + end +end |