diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index 949dd5d26b10cdfef0cd58b2a0ed96a502b1a984..1f8558f5ad0061a94b7fc6553dd76438186a3d96 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -8,151 +8,68 @@ module Gitlab
       @current_user = current_user
     end
 
-    def can?(user, action, subject)
-      Ability.abilities.allowed?(user, action, subject)
-    end
-
     def analyze(text)
-      text = text.dup
-
-      # Remove preformatted/code blocks so that references are not included
-      text.gsub!(/^```.*?^```/m, '')
-      text.gsub!(/[^`]`[^`]*?`[^`]/, '')
-
-      @references = Hash.new { |hash, type| hash[type] = [] }
-      parse_references(text)
+      @_text = text.dup
     end
 
-    # Given a valid project, resolve the extracted identifiers of the requested type to
-    # model objects.
-
     def users
-      references[:user].uniq.map do |project, identifier|
-        if identifier == "all"
-          project.team.members.flatten
-        elsif namespace = Namespace.find_by(path: identifier)
-          if namespace.is_a?(Group)
-            namespace.users if can?(current_user, :read_group, namespace)
-          else
-            namespace.owner
-          end
-        end
-      end.flatten.compact.uniq
+      result = pipeline_result(:user)
+      result[:references][:user].flatten.compact.uniq
     end
 
     def labels
-      references[:label].uniq.map do |project, identifier|
-        project.labels.where(id: identifier).first
-      end.compact.uniq
+      result = pipeline_result(:label)
+      result[:references][:label].compact.uniq
     end
 
     def issues
-      references[:issue].uniq.map do |project, identifier|
-        if project.default_issues_tracker?
-          project.issues.where(iid: identifier).first
-        end
-      end.compact.uniq
+      # TODO (rspeicher): What about external issues?
+
+      result = pipeline_result(:issue)
+      result[:references][:issue].compact.uniq
     end
 
     def merge_requests
-      references[:merge_request].uniq.map do |project, identifier|
-        project.merge_requests.where(iid: identifier).first
-      end.compact.uniq
+      result = pipeline_result(:merge_request)
+      result[:references][:merge_request].compact.uniq
     end
 
     def snippets
-      references[:snippet].uniq.map do |project, identifier|
-        project.snippets.where(id: identifier).first
-      end.compact.uniq
+      result = pipeline_result(:snippet)
+      result[:references][:snippet].compact.uniq
     end
 
     def commits
-      references[:commit].uniq.map do |project, identifier|
-        repo = project.repository
-        repo.commit(identifier) if repo
-      end.compact.uniq
+      result = pipeline_result(:commit)
+      result[:references][:commit].compact.uniq
     end
 
     def commit_ranges
-      references[:commit_range].uniq.map do |project, identifier|
-        repo = project.repository
-        if repo
-          from_id, to_id = identifier.split(/\.{2,3}/, 2)
-          [repo.commit(from_id), repo.commit(to_id)]
-        end
-      end.compact.uniq
+      result = pipeline_result(:commit_range)
+      result[:references][:commit_range].compact.uniq
     end
 
     private
 
-    NAME_STR = Gitlab::Regex::NAMESPACE_REGEX_STR
-    PROJ_STR = "(?<project>#{NAME_STR}/#{NAME_STR})"
-
-    REFERENCE_PATTERN = %r{
-      (?<prefix>\W)?                         # Prefix
-      (                                      # Reference
-         @(?<user>#{NAME_STR})               # User name
-        |~(?<label>\d+)                      # Label ID
-        |(?<issue>([A-Z\-]+-)\d+)            # JIRA Issue ID
-        |#{PROJ_STR}?\#(?<issue>([a-zA-Z\-]+-)?\d+) # Issue ID
-        |#{PROJ_STR}?!(?<merge_request>\d+)  # MR ID
-        |\$(?<snippet>\d+)                   # Snippet ID
-        |(#{PROJ_STR}@)?(?<commit_range>[\h]{6,40}\.{2,3}[\h]{6,40}) # Commit range
-        |(#{PROJ_STR}@)?(?<commit>[\h]{6,40}) # Commit ID
-      )
-      (?<suffix>\W)?                         # Suffix
-    }x.freeze
-
-    TYPES = %i(user issue label merge_request snippet commit commit_range).freeze
-
-    def parse_references(text, project = @project)
-      # parse reference links
-      text.gsub!(REFERENCE_PATTERN) do |match|
-        type = TYPES.detect { |t| $~[t].present? }
-
-        actual_project = project
-        project_prefix = nil
-        project_path = $LAST_MATCH_INFO[:project]
-        if project_path
-          actual_project = ::Project.find_with_namespace(project_path)
-          actual_project = nil unless can?(current_user, :read_project, actual_project)
-          project_prefix = project_path
-        end
-
-        parse_result($LAST_MATCH_INFO, type,
-                     actual_project, project_prefix) || match
-      end
-    end
-
-    # Called from #parse_references.  Attempts to build a gitlab reference
-    # link.  Returns nil if +type+ is nil, if the match string is an HTML
-    # entity, if the reference is invalid, or if the matched text includes an
-    # invalid project path.
-    def parse_result(match_info, type, project, project_prefix)
-      prefix = match_info[:prefix]
-      suffix = match_info[:suffix]
-
-      return nil if html_entity?(prefix, suffix) || type.nil?
-      return nil if project.nil? && !project_prefix.nil?
-
-      identifier = match_info[type]
-      ref_link = reference_link(type, identifier, project, project_prefix)
-
-      if ref_link
-        "#{prefix}#{ref_link}#{suffix}"
-      else
-        nil
-      end
-    end
-
-    # Return true if the +prefix+ and +suffix+ indicate that the matched string
-    # is an HTML entity like &amp;
-    def html_entity?(prefix, suffix)
-      prefix && suffix && prefix[0] == '&' && suffix[-1] == ';'
-    end
-
-    def reference_link(type, identifier, project, _)
-      references[type] << [project, identifier]
+    # Instantiate and call HTML::Pipeline with a single reference filter type,
+    # returning the result
+    #
+    # filter_type - Symbol reference type (e.g., :commit, :issue, etc.)
+    #
+    # Returns the results Hash
+    def pipeline_result(filter_type)
+      klass  = filter_type.to_s.camelize + 'ReferenceFilter'
+      filter = "Gitlab::Markdown::#{klass}".constantize
+
+      context = {
+        project: project,
+        current_user: current_user,
+        # We don't actually care about the links generated
+        only_path: true
+      }
+
+      pipeline = HTML::Pipeline.new([filter], context)
+      pipeline.call(@_text)
     end
   end
 end
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 0f4ea2a24eddbb4e747639ec27d34bfb1ac981ea..e339f378d543181fffd5a1fd088753ec4763d960 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -4,80 +4,6 @@ describe Gitlab::ReferenceExtractor do
   let(:project) { create(:project) }
   subject { Gitlab::ReferenceExtractor.new(project, project.creator) }
 
-  it 'extracts username references' do
-    subject.analyze('this contains a @user reference')
-    expect(subject.references[:user]).to eq([[project, 'user']])
-  end
-
-  it 'extracts issue references' do
-    subject.analyze('this one talks about issue #1234')
-    expect(subject.references[:issue]).to eq([[project, '1234']])
-  end
-
-  it 'extracts JIRA issue references' do
-    subject.analyze('this one talks about issue JIRA-1234')
-    expect(subject.references[:issue]).to eq([[project, 'JIRA-1234']])
-  end
-
-  it 'extracts merge request references' do
-    subject.analyze("and here's !43, a merge request")
-    expect(subject.references[:merge_request]).to eq([[project, '43']])
-  end
-
-  it 'extracts snippet ids' do
-    subject.analyze('snippets like $12 get extracted as well')
-    expect(subject.references[:snippet]).to eq([[project, '12']])
-  end
-
-  it 'extracts commit shas' do
-    subject.analyze('commit shas 98cf0ae3 are pulled out as Strings')
-    expect(subject.references[:commit]).to eq([[project, '98cf0ae3']])
-  end
-
-  it 'extracts commit ranges' do
-    subject.analyze('here you go, a commit range: 98cf0ae3...98cf0ae4')
-    expect(subject.references[:commit_range]).to eq([[project, '98cf0ae3...98cf0ae4']])
-  end
-
-  it 'extracts multiple references and preserves their order' do
-    subject.analyze('@me and @you both care about this')
-    expect(subject.references[:user]).to eq([
-      [project, 'me'],
-      [project, 'you']
-    ])
-  end
-
-  it 'leaves the original note unmodified' do
-    text = 'issue #123 is just the worst, @user'
-    subject.analyze(text)
-    expect(text).to eq('issue #123 is just the worst, @user')
-  end
-
-  it 'extracts no references for <pre>..</pre> blocks' do
-    subject.analyze("<pre>def puts '#1 issue'\nend\n</pre>```")
-    expect(subject.issues).to be_blank
-  end
-
-  it 'extracts no references for <code>..</code> blocks' do
-    subject.analyze("<code>def puts '!1 request'\nend\n</code>```")
-    expect(subject.merge_requests).to be_blank
-  end
-
-  it 'extracts no references for code blocks with language' do
-    subject.analyze("this code:\n```ruby\ndef puts '#1 issue'\nend\n```")
-    expect(subject.issues).to be_blank
-  end
-
-  it 'extracts issue references for invalid code blocks' do
-    subject.analyze('test: ```this one talks about issue #1234```')
-    expect(subject.references[:issue]).to eq([[project, '1234']])
-  end
-
-  it 'handles all possible kinds of references' do
-    accessors = described_class::TYPES.map { |t| "#{t}s".to_sym }
-    expect(subject).to respond_to(*accessors)
-  end
-
   it 'accesses valid user objects' do
     @u_foo = create(:user, username: 'foo')
     @u_bar = create(:user, username: 'bar')