Commit 4354bfab authored by Grzegorz Bizon's avatar Grzegorz Bizon

Add implementation of reference unfolder using banzai

parent fd8394fa
...@@ -47,6 +47,7 @@ module Banzai ...@@ -47,6 +47,7 @@ module Banzai
# Returns a String # Returns a String
def data_attribute(attributes = {}) def data_attribute(attributes = {})
attributes[:reference_filter] = self.class.name.demodulize 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(" ") attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{escape_once(value)}") }.join(" ")
end end
......
...@@ -8,23 +8,66 @@ module Gitlab ...@@ -8,23 +8,66 @@ module Gitlab
def initialize(text, project) def initialize(text, project)
@text = text @text = text
@project = project @project = project
@original = markdown(text)
end end
def unfold(from_project) def unfold(from_project)
referables.each_with_object(@text.dup) do |referable, text| return @text unless @text =~ references_pattern
next unless referable.respond_to?(:to_reference)
pattern = /#{Regexp.escape(referable.to_reference)}/ unfolded = @text.gsub(references_pattern) do |reference|
text.gsub!(pattern, referable.to_reference(from_project)) unfold_reference(reference, Regexp.last_match, from_project)
end end
unless substitution_valid?(unfolded)
raise StandardError, 'Invalid references unfolding!'
end
unfolded
end end
private 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 def referables
return @referables if @referables
extractor = Gitlab::ReferenceExtractor.new(@project) extractor = Gitlab::ReferenceExtractor.new(@project)
extractor.analyze(@text) 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 end
end end
......
...@@ -33,10 +33,36 @@ describe Gitlab::Gfm::ReferenceUnfolder do ...@@ -33,10 +33,36 @@ describe Gitlab::Gfm::ReferenceUnfolder do
end end
context 'description ambigous elements' do context 'description ambigous elements' do
let(:url) { 'http://gitlab.com/#1' } context 'url' do
let(:text) { "This references #1, but not #{url}" } 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 end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment