diff options
author | Grzegorz Bizon <grzesiek.bizon@gmail.com> | 2016-03-11 11:49:04 +0100 |
---|---|---|
committer | Grzegorz Bizon <grzesiek.bizon@gmail.com> | 2016-03-17 07:39:16 +0100 |
commit | 4354bfaba55c9238d5245fe2f5823665790c9817 (patch) | |
tree | 08da489b53d7b5168d0432031dd32da70ca5218d | |
parent | fd8394faae25b54c4d9ac485a0ce746cffec3a0f (diff) | |
download | gitlab-ce-4354bfaba55c9238d5245fe2f5823665790c9817.tar.gz |
Add implementation of reference unfolder using banzai
-rw-r--r-- | lib/banzai/filter/reference_filter.rb | 1 | ||||
-rw-r--r-- | lib/gitlab/gfm/reference_unfolder.rb | 53 | ||||
-rw-r--r-- | spec/lib/gitlab/gfm/reference_unfolder_spec.rb | 32 |
3 files changed, 78 insertions, 8 deletions
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb index 3637b1bac94..132f0a4bd93 100644 --- a/lib/banzai/filter/reference_filter.rb +++ b/lib/banzai/filter/reference_filter.rb @@ -47,6 +47,7 @@ module Banzai # Returns a String def data_attribute(attributes = {}) attributes[:reference_filter] = self.class.name.demodulize + attributes.delete(:original) if context[:no_original_data] attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{escape_once(value)}") }.join(" ") end diff --git a/lib/gitlab/gfm/reference_unfolder.rb b/lib/gitlab/gfm/reference_unfolder.rb index 4969e8ebe85..57871c36eb4 100644 --- a/lib/gitlab/gfm/reference_unfolder.rb +++ b/lib/gitlab/gfm/reference_unfolder.rb @@ -8,23 +8,66 @@ module Gitlab def initialize(text, project) @text = text @project = project + @original = markdown(text) end def unfold(from_project) - referables.each_with_object(@text.dup) do |referable, text| - next unless referable.respond_to?(:to_reference) + return @text unless @text =~ references_pattern - pattern = /#{Regexp.escape(referable.to_reference)}/ - text.gsub!(pattern, referable.to_reference(from_project)) + unfolded = @text.gsub(references_pattern) do |reference| + unfold_reference(reference, Regexp.last_match, from_project) end + + unless substitution_valid?(unfolded) + raise StandardError, 'Invalid references unfolding!' + end + + unfolded end private + def unfold_reference(reference, match, from_project) + before = @text[0...match.begin(0)] + after = @text[match.end(0)...@text.length] + referable = find_referable(reference) + + return reference unless referable + cross_reference = referable.to_reference(from_project) + new_text = before + cross_reference + after + + substitution_valid?(new_text) ? cross_reference : reference + end + + def references_pattern + return @pattern if @pattern + + patterns = Gitlab::ReferenceExtractor::REFERABLES.map do |ref| + ref.to_s.classify.constantize.try(:reference_pattern) + end + + @pattern = Regexp.union(patterns.compact) + end + def referables + return @referables if @referables + extractor = Gitlab::ReferenceExtractor.new(@project) extractor.analyze(@text) - extractor.all + @referables = extractor.all + end + + def find_referable(reference) + referables.find { |ref| ref.to_reference == reference } + end + + def substitution_valid?(substituted) + @original == markdown(substituted) + end + + def markdown(text) + helper = Class.new.extend(GitlabMarkdownHelper) + helper.markdown(text, project: @project, no_original_data: true) end end end diff --git a/spec/lib/gitlab/gfm/reference_unfolder_spec.rb b/spec/lib/gitlab/gfm/reference_unfolder_spec.rb index 4f8b960350c..40cdb7e1452 100644 --- a/spec/lib/gitlab/gfm/reference_unfolder_spec.rb +++ b/spec/lib/gitlab/gfm/reference_unfolder_spec.rb @@ -33,10 +33,36 @@ describe Gitlab::Gfm::ReferenceUnfolder do end context 'description ambigous elements' do - let(:url) { 'http://gitlab.com/#1' } - let(:text) { "This references #1, but not #{url}" } + context 'url' do + let(:url) { 'http://gitlab.com/#1' } + let(:text) { "This references #1, but not #{url}" } - it { is_expected.to include url } + it { is_expected.to include url } + end + + context 'code' do + let(:text) { "#1, but not `[#1]`" } + it { is_expected.to eq "#{issue_first.to_reference(new_project)}, but not `[#1]`" } + end + + context 'code reverse' do + let(:text) { "not `#1`, but #1" } + it { is_expected.to eq "not `#1`, but #{issue_first.to_reference(new_project)}" } + end + + context 'code in random order' do + let(:text) { "#1, `#1`, #1, `#1`" } + let(:ref) { issue_first.to_reference(new_project) } + + it { is_expected.to eq "#{ref}, `#1`, #{ref}, `#1`" } + end + end + + context 'reference contains milestone' do + let(:milestone) { create(:milestone) } + let(:text) { "milestone ref: #{milestone.to_reference}" } + + it { is_expected.to eq text } end end end |