diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb
index b8d2673c1a67f7739adb81c35368ec1660d0a638..15f7da5d934aadb75663b6978a7d63788a46d442 100644
--- a/lib/banzai/filter/autolink_filter.rb
+++ b/lib/banzai/filter/autolink_filter.rb
@@ -26,7 +26,7 @@ module Banzai
# in the generated link.
#
# Rubular: http://rubular.com/r/cxjPyZc7Sb
- LINK_PATTERN = %r{([a-z][a-z0-9\+\.-]+://\S+)(?])}
# Text matching LINK_PATTERN inside these elements will not be linked
IGNORE_PARENTS = %w(a code kbd pre script style).to_set
@@ -54,15 +54,13 @@ module Banzai
#
# `@doc` will be re-parsed with the HTML String from Rinku.
def rinku_parse
- # Convert the options from a Hash to a String that Rinku expects
- options = tag_options(link_options)
-
# NOTE: We don't parse email links because it will erroneously match
# external Commit and CommitRange references.
#
# The final argument tells Rinku to link short URLs that don't include a
# period (e.g., http://localhost:3000/)
- rinku = Rinku.auto_link(html, :urls, options, IGNORE_PARENTS.to_a, 1)
+ mode = context[:autolink_emails] ? :all : :urls
+ rinku = Rinku.auto_link(html, mode, tag_options(link_options), IGNORE_PARENTS.to_a, 1)
return if rinku == html
@@ -111,9 +109,9 @@ module Banzai
# order to be output literally rather than escaped.
match.gsub!(/((?:&[\w#]+;)+)\z/, '')
dropped = ($1 || '').html_safe
+ match = ERB::Util.html_escape_once(match)
- options = link_options.merge(href: match)
- content_tag(:a, match, options) + dropped
+ %{#{match}#{dropped}}.html_safe
end
def autolink_filter(text)
diff --git a/lib/banzai/pipeline/autolink_pipeline.rb b/lib/banzai/pipeline/autolink_pipeline.rb
new file mode 100644
index 0000000000000000000000000000000000000000..53f2da5c7b5861e318cc7bbbd3110b8c8e29c001
--- /dev/null
+++ b/lib/banzai/pipeline/autolink_pipeline.rb
@@ -0,0 +1,12 @@
+module Banzai
+ module Pipeline
+ class AutolinkPipeline < BasePipeline
+ def self.filters
+ @filters ||= FilterArray[
+ Filter::AutolinkFilter,
+ Filter::ExternalLinkFilter
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb
index 83bc230df3e1d4df12e50f4792e12953cb70d082..e25d0f060200fd58099129a0cd39bb8976f37380 100644
--- a/lib/gitlab/highlight.rb
+++ b/lib/gitlab/highlight.rb
@@ -25,7 +25,7 @@ module Gitlab
def highlight(text, continue: true, plain: false)
highlighted_text = highlight_text(text, continue: continue, plain: plain)
highlighted_text = link_dependencies(text, highlighted_text) if blob_name
- highlighted_text
+ autolink_strings(text, highlighted_text)
end
def lexer
@@ -67,5 +67,43 @@ module Gitlab
def link_dependencies(text, highlighted_text)
Gitlab::DependencyLinker.link(blob_name, text, highlighted_text)
end
+
+ def autolink_strings(text, highlighted_text)
+ raw_lines = text.lines
+
+ # TODO: Don't run pre-processing pipeline, because this may break the highlighting
+ linked_text = Banzai.render(
+ ERB::Util.html_escape(text),
+ pipeline: :autolink,
+ autolink_emails: true
+ ).html_safe
+
+ linked_lines = linked_text.lines
+
+ highlighted_lines = highlighted_text.lines
+
+ highlighted_lines.map!.with_index do |rich_line, i|
+ matches = []
+ linked_lines[i].scan(/(?]+>)(?[^<]+)(?<\/a>)/) { matches << Regexp.last_match }
+ next rich_line if matches.empty?
+
+ raw_line = raw_lines[i]
+ marked_line = rich_line.html_safe
+
+ matches.each do |match|
+ marker = StringRegexMarker.new(raw_line, marked_line)
+
+ regex = /#{Regexp.escape(match[:content])}/
+
+ marked_line = marker.mark(regex) do |text, left:, right:|
+ "#{match[:start]}#{text}#{match[:end]}"
+ end
+ end
+
+ marked_line
+ end
+
+ highlighted_lines.join.html_safe
+ end
end
end
diff --git a/spec/lib/banzai/filter/autolink_filter_spec.rb b/spec/lib/banzai/filter/autolink_filter_spec.rb
index a6d2ea11fcc697c2e619e55692f74fdbc0e04563..7fdcd1148aa2d9a387242c724e3fcf45064bec27 100644
--- a/spec/lib/banzai/filter/autolink_filter_spec.rb
+++ b/spec/lib/banzai/filter/autolink_filter_spec.rb
@@ -130,6 +130,15 @@ describe Banzai::Filter::AutolinkFilter, lib: true do
doc = filter("See #{link}...")
expect(doc.at_css('a').text).to eq link
+
+ doc = filter("See #{link}\"")
+ expect(doc.at_css('a').text).to eq link
+
+ doc = filter("See #{link}'")
+ expect(doc.at_css('a').text).to eq link
+
+ doc = filter("See #{link})")
+ expect(doc.at_css('a').text).to eq link
end
it 'does not include trailing HTML entities' do
diff --git a/spec/lib/gitlab/diff/inline_diff_marker_spec.rb b/spec/lib/gitlab/diff/inline_diff_marker_spec.rb
index 95da344802dc2820a16442d98f2c0001e550ecb8..e24241e70598ebfc4803f12dc69385fd7f1e0029 100644
--- a/spec/lib/gitlab/diff/inline_diff_marker_spec.rb
+++ b/spec/lib/gitlab/diff/inline_diff_marker_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::Diff::InlineDiffMarker, lib: true do
end
end
- context "when the text text is not html safe" do
+ context "when the rich text is not html safe" do
let(:raw) { "abc 'def'" }
let(:inline_diffs) { [2..5] }
let(:subject) { described_class.new(raw).mark(inline_diffs) }
diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb
index e57b3053871b211826e046463801d816e505fa93..4a106fc675c08f8a7bf62107047caee744e13409 100644
--- a/spec/lib/gitlab/highlight_spec.rb
+++ b/spec/lib/gitlab/highlight_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::Highlight, lib: true do
describe '.highlight_lines' do
let(:lines) do
- Gitlab::Highlight.highlight_lines(project.repository, commit.id, 'files/ruby/popen.rb')
+ described_class.highlight_lines(project.repository, commit.id, 'files/ruby/popen.rb')
end
it 'highlights all the lines properly' do
@@ -59,7 +59,7 @@ describe Gitlab::Highlight, lib: true do
end
describe '#highlight' do
- subject { described_class.highlight(file_name, file_content, nowrap: false) }
+ subject { described_class.highlight(file_name, file_content) }
it 'links dependencies via DependencyLinker' do
expect(Gitlab::DependencyLinker).to receive(:link).
@@ -67,5 +67,66 @@ describe Gitlab::Highlight, lib: true do
described_class.highlight('file.name', 'Contents')
end
+
+ context "plain text file" do
+ let(:file_name) { "example.txt" }
+ let(:file_content) do
+ <<-CONTENT.strip_heredoc
+ URL: http://www.google.com
+ Email: hello@example.com
+ CONTENT
+ end
+
+ it "links URLs" do
+ expect(subject).to include(%{http://www.google.com})
+ end
+
+ it "links emails" do
+ expect(subject).to include(%{hello@example.com})
+ end
+ end
+
+ context "file with highlighting" do
+ let(:file_name) { "example.rb" }
+ let(:file_content) do
+ <<-CONTENT.strip_heredoc
+ # URL in comment: http://www.google.com
+ # Email in comment: hello@example.com
+
+ "URL in string: http://www.google.com"
+ "Email in string: hello@example.com"
+
+ #
+ # http://www.google.com
+ CONTENT
+ end
+
+ context "in a comment" do
+ it "links URLs" do
+ expect(subject).to include(%{URL in comment: http://www.google.com})
+ end
+
+ it "links emails" do
+ expect(subject).to include(%{Email in comment: hello@example.com})
+ end
+ end
+
+ context "in a string" do
+ it "links URLs" do
+ expect(subject).to include(%{URL in string: http://www.google.com})
+ end
+
+ it "links emails" do
+ expect(subject).to include(%{Email in string: hello@example.com})
+ end
+ end
+
+ context 'in HTML/XML tags' do
+ it "links URLs" do
+ expect(subject).to include(%{<http://www.google.com>})
+ expect(subject).to include(%{<url>http://www.google.com</url>})
+ end
+ end
+ end
end
end