From aa87fcd0034d24163d8001d28011e69fd705bd83 Mon Sep 17 00:00:00 2001
From: Gabriel Mazetto <gabriel@gitlab.com>
Date: Mon, 23 Aug 2021 19:39:42 +0200
Subject: [PATCH] Switch from `Gemojione` to `TanukiEmoji`

---
 Gemfile                                       |  2 +-
 Gemfile.lock                                  |  5 ++--
 app/helpers/emoji_helper.rb                   |  5 ++--
 app/helpers/reminder_emails_helper.rb         |  3 ++-
 config/application.rb                         |  2 +-
 db/fixtures/development/15_award_emoji.rb     |  6 ++---
 lib/banzai/filter/emoji_filter.rb             | 19 +++++---------
 lib/gitlab/emoji.rb                           | 26 +++++++++----------
 spec/lib/banzai/filter/emoji_filter_spec.rb   |  6 ++---
 .../banzai/pipeline/emoji_pipeline_spec.rb    |  6 +++--
 spec/lib/gitlab/emoji_spec.rb                 | 20 +++++++-------
 spec/models/custom_emoji_spec.rb              |  2 +-
 12 files changed, 49 insertions(+), 53 deletions(-)

diff --git a/Gemfile b/Gemfile
index 1e6648df48bfe..5e5ab5bd979c1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -290,7 +290,7 @@ gem 'autoprefixer-rails', '10.2.5.1'
 gem 'terser', '1.0.2'
 
 gem 'addressable', '~> 2.8'
-gem 'gemojione', '~> 3.3'
+gem 'tanuki_emoji', '~> 0.5'
 gem 'gon', '~> 6.4.0'
 gem 'request_store', '~> 1.5'
 gem 'base32', '~> 0.3.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 1fc0e3b713986..30e6bdcf2e4f7 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -436,8 +436,6 @@ GEM
       ruby-progressbar (~> 1.4)
     fuzzyurl (0.9.0)
     gemoji (3.0.1)
-    gemojione (3.3.0)
-      json
     get_process_mem (0.2.5)
       ffi (~> 1.0)
     gettext (3.3.6)
@@ -1246,6 +1244,7 @@ GEM
     sys-filesystem (1.1.9)
       ffi
     sysexits (1.2.0)
+    tanuki_emoji (0.5.0)
     temple (0.8.2)
     terminal-table (1.8.0)
       unicode-display_width (~> 1.1, >= 1.1.1)
@@ -1454,7 +1453,6 @@ DEPENDENCIES
   fog-rackspace (~> 0.1.1)
   fugit (~> 1.2.1)
   fuubar (~> 2.2.0)
-  gemojione (~> 3.3)
   gettext (~> 3.3)
   gettext_i18n_rails (~> 1.8.0)
   gettext_i18n_rails_js (~> 1.3)
@@ -1627,6 +1625,7 @@ DEPENDENCIES
   stackprof (~> 0.2.15)
   state_machines-activerecord (~> 0.8.0)
   sys-filesystem (~> 1.1.6)
+  tanuki_emoji (~> 0.5)
   terser (= 1.0.2)
   test-prof (~> 1.0.7)
   test_file_finder (~> 0.1.3)
diff --git a/app/helpers/emoji_helper.rb b/app/helpers/emoji_helper.rb
index 51b7fd7f3523b..c390924f7e355 100644
--- a/app/helpers/emoji_helper.rb
+++ b/app/helpers/emoji_helper.rb
@@ -1,7 +1,8 @@
 # frozen_string_literal: true
 
 module EmojiHelper
-  def emoji_icon(*args)
-    raw Gitlab::Emoji.gl_emoji_tag(*args)
+  def emoji_icon(emoji_name, *options)
+    emoji = TanukiEmoji.find_by_alpha_code(emoji_name)
+    raw Gitlab::Emoji.gl_emoji_tag(emoji, *options)
   end
 end
diff --git a/app/helpers/reminder_emails_helper.rb b/app/helpers/reminder_emails_helper.rb
index bffb3cf775129..132fc3b784cc4 100644
--- a/app/helpers/reminder_emails_helper.rb
+++ b/app/helpers/reminder_emails_helper.rb
@@ -7,7 +7,8 @@ def invitation_reminder_salutation(reminder_index, format: nil)
       s_('InviteReminderEmail|Invitation pending')
     when 1
       if format == :html
-        s_('InviteReminderEmail|Hey there %{wave_emoji}').html_safe % { wave_emoji: Gitlab::Emoji.gl_emoji_tag('wave') }
+        wave_emoji_tag = Gitlab::Emoji.gl_emoji_tag(TanukiEmoji.find_by_alpha_code('wave'))
+        s_('InviteReminderEmail|Hey there %{wave_emoji}').html_safe % { wave_emoji: wave_emoji_tag }
       else
         s_('InviteReminderEmail|Hey there!')
       end
diff --git a/config/application.rb b/config/application.rb
index 83a3033a40db7..99760ea2b1a9c 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -198,7 +198,7 @@ class Application < Rails::Application
     config.assets.enabled = true
 
     # Support legacy unicode file named img emojis, `1F939.png`
-    config.assets.paths << Gemojione.images_path
+    config.assets.paths << TanukiEmoji.images_path
     config.assets.paths << "#{config.root}/vendor/assets/fonts"
 
     config.assets.precompile << "application_utilities.css"
diff --git a/db/fixtures/development/15_award_emoji.rb b/db/fixtures/development/15_award_emoji.rb
index 2b69a6c26abff..efad7e92bb0b1 100644
--- a/db/fixtures/development/15_award_emoji.rb
+++ b/db/fixtures/development/15_award_emoji.rb
@@ -1,15 +1,13 @@
 require './spec/support/sidekiq_middleware'
 
 Gitlab::Seeder.quiet do
-  EMOJI = Gitlab::Emoji.emojis.keys
-
   def seed_award_emoji(klass)
     klass.order(Gitlab::Database.random).limit(klass.count / 2).each do |awardable|
       awardable.project.authorized_users.where('project_authorizations.access_level > ?', Gitlab::Access::GUEST).sample(2).each do |user|
-        AwardEmojis::AddService.new(awardable, EMOJI.sample, user).execute
+        AwardEmojis::AddService.new(awardable, TanukiEmoji.index.all.sample.name, user).execute
 
         awardable.notes.user.sample(2).each do |note|
-          AwardEmojis::AddService.new(note, EMOJI.sample, user).execute
+          AwardEmojis::AddService.new(note, TanukiEmoji.index.all.sample.name, user).execute
         end
 
         print '.'
diff --git a/lib/banzai/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb
index 9d24bf028b645..d8c9fd0a7f00c 100644
--- a/lib/banzai/filter/emoji_filter.rb
+++ b/lib/banzai/filter/emoji_filter.rb
@@ -8,7 +8,6 @@ module Filter
     # Based on HTML::Pipeline::EmojiFilter
     class EmojiFilter < HTML::Pipeline::Filter
       IGNORED_ANCESTOR_TAGS = %w(pre code tt).to_set
-      IGNORE_UNICODE_EMOJIS = %w(™ © ®).freeze
 
       def call
         doc.xpath('descendant-or-self::text()').each do |node|
@@ -35,7 +34,8 @@ def call
       def emoji_name_element_unicode_filter(text)
         text.gsub(emoji_pattern) do |match|
           name = Regexp.last_match(1)
-          Gitlab::Emoji.gl_emoji_tag(name)
+          emoji = TanukiEmoji.find_by_alpha_code(name)
+          Gitlab::Emoji.gl_emoji_tag(emoji)
         end
       end
 
@@ -46,26 +46,19 @@ def emoji_name_element_unicode_filter(text)
       # Returns a String with unicode emoji replaced with gl-emoji unicode.
       def emoji_unicode_element_unicode_filter(text)
         text.gsub(emoji_unicode_pattern) do |moji|
-          emoji_info = Gitlab::Emoji.emojis_by_moji[moji]
-          Gitlab::Emoji.gl_emoji_tag(emoji_info['name'])
+          emoji = TanukiEmoji.find_by_codepoints(moji)
+          Gitlab::Emoji.gl_emoji_tag(emoji)
         end
       end
 
       # Build a regexp that matches all valid :emoji: names.
       def self.emoji_pattern
-        @emoji_pattern ||=
-          %r{(?<=[^[:alnum:]:]|\n|^)
-          :(#{Gitlab::Emoji.emojis_names.map { |name| Regexp.escape(name) }.join('|')}):
-          (?=[^[:alnum:]:]|$)}x
+        @emoji_pattern ||= TanukiEmoji.index.alpha_code_pattern
       end
 
       # Build a regexp that matches all valid unicode emojis names.
       def self.emoji_unicode_pattern
-        @emoji_unicode_pattern ||=
-          begin
-            filtered_emojis = Gitlab::Emoji.emojis_unicodes - IGNORE_UNICODE_EMOJIS
-            /(#{filtered_emojis.map { |moji| Regexp.escape(moji) }.join('|')})/
-          end
+        @emoji_unicode_pattern ||= TanukiEmoji.index.codepoints_pattern
       end
 
       private
diff --git a/lib/gitlab/emoji.rb b/lib/gitlab/emoji.rb
index 2b5f465d3c531..4ec0a2e4cdf50 100644
--- a/lib/gitlab/emoji.rb
+++ b/lib/gitlab/emoji.rb
@@ -5,11 +5,11 @@ module Emoji
     extend self
 
     def emojis
-      Gemojione.index.instance_variable_get(:@emoji_by_name)
+      TanukiEmoji.index.instance_variable_get(:@name_index)
     end
 
     def emojis_by_moji
-      Gemojione.index.instance_variable_get(:@emoji_by_moji)
+      TanukiEmoji.index.instance_variable_get(:@codepoints_index)
     end
 
     def emojis_unicodes
@@ -25,11 +25,11 @@ def emojis_aliases
     end
 
     def emoji_filename(name)
-      emojis[name]["unicode"]
+      TanukiEmoji.find_by_alpha_code(name).image_name
     end
 
     def emoji_unicode_filename(moji)
-      emojis_by_moji[moji]["unicode"]
+      TanukiEmoji.find_by_codepoints(moji).image_name
     end
 
     def emoji_unicode_version(name)
@@ -55,22 +55,22 @@ def emoji_image_tag(name, src)
     end
 
     def emoji_exists?(name)
-      emojis.has_key?(name)
+      TanukiEmoji.find_by_alpha_code(name)
     end
 
     # CSS sprite fallback takes precedence over image fallback
-    def gl_emoji_tag(name, options = {})
-      emoji_name = emojis_aliases[name] || name
-      emoji_info = emojis[emoji_name]
-      return unless emoji_info
+    # @param [TanukiEmoji::Character] emoji
+    # @param [Hash] options
+    def gl_emoji_tag(emoji, options = {})
+      return unless emoji
 
       data = {
-        name: emoji_name,
-        unicode_version: emoji_unicode_version(emoji_name)
+        name: emoji.name,
+        unicode_version: emoji_unicode_version(emoji.name)
       }
-      options = { title: emoji_info['description'], data: data }.merge(options)
+      options = { title: emoji.description, data: data }.merge(options)
 
-      ActionController::Base.helpers.content_tag('gl-emoji', emoji_info['moji'], options)
+      ActionController::Base.helpers.content_tag('gl-emoji', emoji.codepoints, options)
     end
 
     def custom_emoji_tag(name, image_source)
diff --git a/spec/lib/banzai/filter/emoji_filter_spec.rb b/spec/lib/banzai/filter/emoji_filter_spec.rb
index cb0b470eaa1f5..d621f63211b5d 100644
--- a/spec/lib/banzai/filter/emoji_filter_spec.rb
+++ b/spec/lib/banzai/filter/emoji_filter_spec.rb
@@ -28,9 +28,9 @@
   it 'replaces name versions of trademark, copyright, and registered trademark' do
     doc = filter('<p>:tm: :copyright: :registered:</p>')
 
-    expect(doc.css('gl-emoji')[0].text).to eq 'â„¢'
-    expect(doc.css('gl-emoji')[1].text).to eq '©'
-    expect(doc.css('gl-emoji')[2].text).to eq '®'
+    expect(doc.css('gl-emoji')[0].text).to eq '™️'
+    expect(doc.css('gl-emoji')[1].text).to eq '©️'
+    expect(doc.css('gl-emoji')[2].text).to eq '®️'
   end
 
   it 'correctly encodes the URL' do
diff --git a/spec/lib/banzai/pipeline/emoji_pipeline_spec.rb b/spec/lib/banzai/pipeline/emoji_pipeline_spec.rb
index 6de9d65f1b26b..8103846d4f77e 100644
--- a/spec/lib/banzai/pipeline/emoji_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/emoji_pipeline_spec.rb
@@ -3,18 +3,20 @@
 require 'spec_helper'
 
 RSpec.describe Banzai::Pipeline::EmojiPipeline do
+  let(:emoji) { TanukiEmoji.find_by_alpha_code('100') }
+
   def parse(text)
     described_class.to_html(text, {})
   end
 
   it 'replaces emoji' do
-    expected_result = "Hello world #{Gitlab::Emoji.gl_emoji_tag('100')}"
+    expected_result = "Hello world #{Gitlab::Emoji.gl_emoji_tag(emoji)}"
 
     expect(parse('Hello world :100:')).to eq(expected_result)
   end
 
   it 'filters out HTML tags' do
-    expected_result = "Hello &lt;b&gt;world&lt;/b&gt; #{Gitlab::Emoji.gl_emoji_tag('100')}"
+    expected_result = "Hello &lt;b&gt;world&lt;/b&gt; #{Gitlab::Emoji.gl_emoji_tag(emoji)}"
 
     expect(parse('Hello <b>world</b> :100:')).to eq(expected_result)
   end
diff --git a/spec/lib/gitlab/emoji_spec.rb b/spec/lib/gitlab/emoji_spec.rb
index 8f855489c12aa..8d03bfa98d843 100644
--- a/spec/lib/gitlab/emoji_spec.rb
+++ b/spec/lib/gitlab/emoji_spec.rb
@@ -3,16 +3,16 @@
 require 'spec_helper'
 
 RSpec.describe Gitlab::Emoji do
-  let_it_be(:emojis) { Gemojione.index.instance_variable_get(:@emoji_by_name) }
-  let_it_be(:emojis_by_moji) { Gemojione.index.instance_variable_get(:@emoji_by_moji) }
+  let_it_be(:emojis) { TanukiEmoji.index.all.index_by(&:name) }
+  let_it_be(:emojis_by_moji) { TanukiEmoji.index.all.index_by(&:codepoints) }
   let_it_be(:emoji_unicode_versions_by_name) { Gitlab::Json.parse(File.read(Rails.root.join('fixtures', 'emojis', 'emoji-unicode-version-map.json'))) }
   let_it_be(:emojis_aliases) { Gitlab::Json.parse(File.read(Rails.root.join('fixtures', 'emojis', 'aliases.json'))) }
 
   describe '.emojis' do
     it 'returns emojis' do
-      current_emojis = described_class.emojis
+      current_emojis = described_class.emojis.values
 
-      expect(current_emojis).to eq(emojis)
+      expect(current_emojis).to match_array(emojis)
     end
   end
 
@@ -53,7 +53,7 @@
       # "100" => {"unicode"=>"1F4AF"...}
       emoji_filename = described_class.emoji_filename('100')
 
-      expect(emoji_filename).to eq(emojis['100']['unicode'])
+      expect(emoji_filename).to eq("emoji_u#{TanukiEmoji.find_by_alpha_code('100').hex}.png")
     end
   end
 
@@ -61,7 +61,7 @@
     it 'returns emoji unicode filename' do
       emoji_unicode_filename = described_class.emoji_unicode_filename('💯')
 
-      expect(emoji_unicode_filename).to eq(emojis_by_moji['💯']['unicode'])
+      expect(emoji_unicode_filename).to eq("emoji_u#{TanukiEmoji.find_by_codepoints('💯').hex}.png")
     end
   end
 
@@ -120,13 +120,15 @@
 
   describe '.gl_emoji_tag' do
     it 'returns gl emoji tag if emoji is found' do
-      gl_tag = described_class.gl_emoji_tag('small_airplane')
+      emoji = TanukiEmoji.find_by_alpha_code('small_airplane')
+      gl_tag = described_class.gl_emoji_tag(emoji)
 
       expect(gl_tag).to eq('<gl-emoji title="small airplane" data-name="airplane_small" data-unicode-version="7.0">🛩</gl-emoji>')
     end
 
-    it 'returns nil if emoji name is not found' do
-      gl_tag = described_class.gl_emoji_tag('random')
+    it 'returns nil if emoji is not found' do
+      emoji = TanukiEmoji.find_by_alpha_code('random')
+      gl_tag = described_class.gl_emoji_tag(emoji)
 
       expect(gl_tag).to be_nil
     end
diff --git a/spec/models/custom_emoji_spec.rb b/spec/models/custom_emoji_spec.rb
index 4a8b671bab7b6..01252a58681b1 100644
--- a/spec/models/custom_emoji_spec.rb
+++ b/spec/models/custom_emoji_spec.rb
@@ -14,7 +14,7 @@
   end
 
   describe 'exclusion of duplicated emoji' do
-    let(:emoji_name) { Gitlab::Emoji.emojis_names.sample }
+    let(:emoji_name) { TanukiEmoji.index.all.sample.name }
     let(:group) { create(:group, :private) }
 
     it 'disallows emoji names of built-in emoji' do
-- 
GitLab