Merge branch 'dm-dependency-linker-gemfile' into 'master'
Autolink package names in Gemfile See merge request !11224
This commit is contained in:
commit
ec1a3c093f
24 changed files with 514 additions and 148 deletions
|
@ -185,6 +185,11 @@ $dark-il: #de935f;
|
|||
color: $dark-highlight-color !important;
|
||||
}
|
||||
|
||||
// Links to URLs, emails, or dependencies
|
||||
.line a {
|
||||
color: $dark-na;
|
||||
}
|
||||
|
||||
.hll { background-color: $dark-hll-bg; }
|
||||
.c { color: $dark-c; } /* Comment */
|
||||
.err { color: $dark-err; } /* Error */
|
||||
|
|
|
@ -185,6 +185,11 @@ $monokai-gi: #a6e22e;
|
|||
color: $black !important;
|
||||
}
|
||||
|
||||
// Links to URLs, emails, or dependencies
|
||||
.line a {
|
||||
color: $monokai-k;
|
||||
}
|
||||
|
||||
.hll { background-color: $monokai-hll; }
|
||||
.c { color: $monokai-c; } /* Comment */
|
||||
.err { color: $monokai-err-color; background-color: $monokai-err-bg; } /* Error */
|
||||
|
|
|
@ -188,6 +188,11 @@ $solarized-dark-il: #2aa198;
|
|||
background-color: $solarized-dark-highlight !important;
|
||||
}
|
||||
|
||||
// Links to URLs, emails, or dependencies
|
||||
.line a {
|
||||
color: $solarized-dark-kd;
|
||||
}
|
||||
|
||||
/* Solarized Dark
|
||||
|
||||
For use with Jekyll and Pygments
|
||||
|
|
|
@ -196,6 +196,11 @@ $solarized-light-il: #2aa198;
|
|||
background-color: $solarized-light-highlight !important;
|
||||
}
|
||||
|
||||
// Links to URLs, emails, or dependencies
|
||||
.line a {
|
||||
color: $solarized-light-kd;
|
||||
}
|
||||
|
||||
/* Solarized Light
|
||||
|
||||
For use with Jekyll and Pygments
|
||||
|
|
|
@ -203,6 +203,11 @@ $white-gc-bg: #eaf2f5;
|
|||
background-color: $white-highlight !important;
|
||||
}
|
||||
|
||||
// Links to URLs, emails, or dependencies
|
||||
.line a {
|
||||
color: $white-nb;
|
||||
}
|
||||
|
||||
.hll { background-color: $white-hll-bg; }
|
||||
.c { color: $white-c; font-style: italic; }
|
||||
.err { color: $white-err; background-color: $white-err-bg; }
|
||||
|
|
|
@ -291,8 +291,8 @@ module SystemNoteService
|
|||
|
||||
old_diffs, new_diffs = Gitlab::Diff::InlineDiff.new(old_title, new_title).inline_diffs
|
||||
|
||||
marked_old_title = Gitlab::Diff::InlineDiffMarker.new(old_title).mark(old_diffs, mode: :deletion, markdown: true)
|
||||
marked_new_title = Gitlab::Diff::InlineDiffMarker.new(new_title).mark(new_diffs, mode: :addition, markdown: true)
|
||||
marked_old_title = Gitlab::Diff::InlineDiffMarkdownMarker.new(old_title).mark(old_diffs, mode: :deletion)
|
||||
marked_new_title = Gitlab::Diff::InlineDiffMarkdownMarker.new(new_title).mark(new_diffs, mode: :addition)
|
||||
|
||||
body = "changed title from **#{marked_old_title}** to **#{marked_new_title}**"
|
||||
|
||||
|
|
4
changelogs/unreleased/dm-dependency-linker-gemfile.yml
Normal file
4
changelogs/unreleased/dm-dependency-linker-gemfile.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Autolink package names in Gemfile
|
||||
merge_request:
|
||||
author:
|
18
lib/gitlab/dependency_linker.rb
Normal file
18
lib/gitlab/dependency_linker.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
module Gitlab
|
||||
module DependencyLinker
|
||||
LINKERS = [
|
||||
GemfileLinker,
|
||||
].freeze
|
||||
|
||||
def self.linker(blob_name)
|
||||
LINKERS.find { |linker| linker.support?(blob_name) }
|
||||
end
|
||||
|
||||
def self.link(blob_name, plain_text, highlighted_text)
|
||||
linker = linker(blob_name)
|
||||
return highlighted_text unless linker
|
||||
|
||||
linker.link(plain_text, highlighted_text)
|
||||
end
|
||||
end
|
||||
end
|
103
lib/gitlab/dependency_linker/base_linker.rb
Normal file
103
lib/gitlab/dependency_linker/base_linker.rb
Normal file
|
@ -0,0 +1,103 @@
|
|||
module Gitlab
|
||||
module DependencyLinker
|
||||
class BaseLinker
|
||||
def self.link(plain_text, highlighted_text)
|
||||
new(plain_text, highlighted_text).link
|
||||
end
|
||||
|
||||
attr_accessor :plain_text, :highlighted_text
|
||||
|
||||
def initialize(plain_text, highlighted_text)
|
||||
@plain_text = plain_text
|
||||
@highlighted_text = highlighted_text
|
||||
end
|
||||
|
||||
def link
|
||||
link_dependencies
|
||||
|
||||
highlighted_lines.join.html_safe
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def package_url(name)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def link_dependencies
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def package_link(name, url = package_url(name))
|
||||
return name unless url
|
||||
|
||||
%{<a href="#{ERB::Util.html_escape_once(url)}" rel="noopener noreferrer" target="_blank">#{ERB::Util.html_escape_once(name)}</a>}
|
||||
end
|
||||
|
||||
# Links package names in a method call or assignment string argument.
|
||||
#
|
||||
# Example:
|
||||
# link_method_call("gem")
|
||||
# # Will link `package` in `gem "package"`, `gem("package")` and `gem = "package"`
|
||||
#
|
||||
# link_method_call("gem", "specific_package")
|
||||
# # Will link `specific_package` in `gem "specific_package"`
|
||||
#
|
||||
# link_method_call("github", /[^\/]+\/[^\/]+/)
|
||||
# # Will link `user/repo` in `github "user/repo"`, but not `github "package"`
|
||||
#
|
||||
# link_method_call(%w[add_dependency add_development_dependency])
|
||||
# # Will link `spec.add_dependency "package"` and `spec.add_development_dependency "package"`
|
||||
#
|
||||
# link_method_call("name")
|
||||
# # Will link `package` in `self.name = "package"`
|
||||
def link_method_call(method_names, value = nil, &url_proc)
|
||||
value =
|
||||
case value
|
||||
when String
|
||||
Regexp.escape(value)
|
||||
when nil
|
||||
/[^'"]+/
|
||||
else
|
||||
value
|
||||
end
|
||||
|
||||
method_names = Array(method_names).map { |name| Regexp.escape(name) }
|
||||
|
||||
regex = %r{
|
||||
#{Regexp.union(method_names)} # Method name
|
||||
\s* # Whitespace
|
||||
[(=]? # Opening brace or equals sign
|
||||
\s* # Whitespace
|
||||
['"](?<name>#{value})['"] # Package name in quotes
|
||||
}x
|
||||
|
||||
link_regex(regex, &url_proc)
|
||||
end
|
||||
|
||||
# Links package names based on regex.
|
||||
#
|
||||
# Example:
|
||||
# link_regex(/(github:|:github =>)\s*['"](?<name>[^'"]+)['"]/)
|
||||
# # Will link `user/repo` in `github: "user/repo"` or `:github => "user/repo"`
|
||||
def link_regex(regex)
|
||||
highlighted_lines.map!.with_index do |rich_line, i|
|
||||
marker = StringRegexMarker.new(plain_lines[i], rich_line.html_safe)
|
||||
|
||||
marker.mark(regex, group: :name) do |text, left:, right:|
|
||||
url = block_given? ? yield(text) : package_url(text)
|
||||
package_link(text, url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def plain_lines
|
||||
@plain_lines ||= plain_text.lines
|
||||
end
|
||||
|
||||
def highlighted_lines
|
||||
@highlighted_lines ||= highlighted_text.lines
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
31
lib/gitlab/dependency_linker/gemfile_linker.rb
Normal file
31
lib/gitlab/dependency_linker/gemfile_linker.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
module Gitlab
|
||||
module DependencyLinker
|
||||
class GemfileLinker < BaseLinker
|
||||
def self.support?(blob_name)
|
||||
blob_name == 'Gemfile' || blob_name == 'gems.rb'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def link_dependencies
|
||||
# Link `gem "package_name"` to https://rubygems.org/gems/package_name
|
||||
link_method_call("gem")
|
||||
|
||||
# Link `github: "user/repo"` to https://github.com/user/repo
|
||||
link_regex(/(github:|:github\s*=>)\s*['"](?<name>[^'"]+)['"]/) do |name|
|
||||
"https://github.com/#{name}"
|
||||
end
|
||||
|
||||
# Link `git: "https://gitlab.example.com/user/repo"` to https://gitlab.example.com/user/repo
|
||||
link_regex(%r{(git:|:git\s*=>)\s*['"](?<name>https?://[^'"]+)['"]}) { |url| url }
|
||||
|
||||
# Link `source "https://rubygems.org"` to https://rubygems.org
|
||||
link_method_call("source", %r{https?://[^'"]+}) { |url| url }
|
||||
end
|
||||
|
||||
def package_url(name)
|
||||
"https://rubygems.org/gems/#{name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
17
lib/gitlab/diff/inline_diff_markdown_marker.rb
Normal file
17
lib/gitlab/diff/inline_diff_markdown_marker.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
module Gitlab
|
||||
module Diff
|
||||
class InlineDiffMarkdownMarker < Gitlab::StringRangeMarker
|
||||
MARKDOWN_SYMBOLS = {
|
||||
addition: "+",
|
||||
deletion: "-"
|
||||
}.freeze
|
||||
|
||||
def mark(line_inline_diffs, mode: nil)
|
||||
super(line_inline_diffs) do |text, left:, right:|
|
||||
symbol = MARKDOWN_SYMBOLS[mode]
|
||||
"{#{symbol}#{text}#{symbol}}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,137 +1,21 @@
|
|||
module Gitlab
|
||||
module Diff
|
||||
class InlineDiffMarker
|
||||
MARKDOWN_SYMBOLS = {
|
||||
addition: "+",
|
||||
deletion: "-"
|
||||
}.freeze
|
||||
|
||||
attr_accessor :raw_line, :rich_line
|
||||
|
||||
def initialize(raw_line, rich_line = raw_line)
|
||||
@raw_line = raw_line
|
||||
@rich_line = ERB::Util.html_escape(rich_line)
|
||||
end
|
||||
|
||||
def mark(line_inline_diffs, mode: nil, markdown: false)
|
||||
return rich_line unless line_inline_diffs
|
||||
|
||||
marker_ranges = []
|
||||
line_inline_diffs.each do |inline_diff_range|
|
||||
# Map the inline-diff range based on the raw line to character positions in the rich line
|
||||
inline_diff_positions = position_mapping[inline_diff_range].flatten
|
||||
# Turn the array of character positions into ranges
|
||||
marker_ranges.concat(collapse_ranges(inline_diff_positions))
|
||||
class InlineDiffMarker < Gitlab::StringRangeMarker
|
||||
def mark(line_inline_diffs, mode: nil)
|
||||
super(line_inline_diffs) do |text, left:, right:|
|
||||
%{<span class="#{html_class_names(left, right, mode)}">#{text}</span>}
|
||||
end
|
||||
|
||||
offset = 0
|
||||
|
||||
# Mark each range
|
||||
marker_ranges.each_with_index do |range, index|
|
||||
before_content =
|
||||
if markdown
|
||||
"{#{MARKDOWN_SYMBOLS[mode]}"
|
||||
else
|
||||
"<span class='#{html_class_names(marker_ranges, mode, index)}'>"
|
||||
end
|
||||
after_content =
|
||||
if markdown
|
||||
"#{MARKDOWN_SYMBOLS[mode]}}"
|
||||
else
|
||||
"</span>"
|
||||
end
|
||||
offset = insert_around_range(rich_line, range, before_content, after_content, offset)
|
||||
end
|
||||
|
||||
rich_line.html_safe
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def html_class_names(marker_ranges, mode, index)
|
||||
def html_class_names(left, right, mode)
|
||||
class_names = ["idiff"]
|
||||
class_names << "left" if index == 0
|
||||
class_names << "right" if index == marker_ranges.length - 1
|
||||
class_names << "left" if left
|
||||
class_names << "right" if right
|
||||
class_names << mode if mode
|
||||
class_names.join(" ")
|
||||
end
|
||||
|
||||
# Mapping of character positions in the raw line, to the rich (highlighted) line
|
||||
def position_mapping
|
||||
@position_mapping ||= begin
|
||||
mapping = []
|
||||
rich_pos = 0
|
||||
(0..raw_line.length).each do |raw_pos|
|
||||
rich_char = rich_line[rich_pos]
|
||||
|
||||
# The raw and rich lines are the same except for HTML tags,
|
||||
# so skip over any `<...>` segment
|
||||
while rich_char == '<'
|
||||
until rich_char == '>'
|
||||
rich_pos += 1
|
||||
rich_char = rich_line[rich_pos]
|
||||
end
|
||||
|
||||
rich_pos += 1
|
||||
rich_char = rich_line[rich_pos]
|
||||
end
|
||||
|
||||
# multi-char HTML entities in the rich line correspond to a single character in the raw line
|
||||
if rich_char == '&'
|
||||
multichar_mapping = [rich_pos]
|
||||
until rich_char == ';'
|
||||
rich_pos += 1
|
||||
multichar_mapping << rich_pos
|
||||
rich_char = rich_line[rich_pos]
|
||||
end
|
||||
|
||||
mapping[raw_pos] = multichar_mapping
|
||||
else
|
||||
mapping[raw_pos] = rich_pos
|
||||
end
|
||||
|
||||
rich_pos += 1
|
||||
end
|
||||
|
||||
mapping
|
||||
end
|
||||
end
|
||||
|
||||
# Takes an array of integers, and returns an array of ranges covering the same integers
|
||||
def collapse_ranges(positions)
|
||||
return [] if positions.empty?
|
||||
ranges = []
|
||||
|
||||
start = prev = positions[0]
|
||||
range = start..prev
|
||||
positions[1..-1].each do |pos|
|
||||
if pos == prev + 1
|
||||
range = start..pos
|
||||
prev = pos
|
||||
else
|
||||
ranges << range
|
||||
start = prev = pos
|
||||
range = start..prev
|
||||
end
|
||||
end
|
||||
ranges << range
|
||||
|
||||
ranges
|
||||
end
|
||||
|
||||
# Inserts tags around the characters identified by the given range
|
||||
def insert_around_range(text, range, before, after, offset = 0)
|
||||
# Just to be sure
|
||||
return offset if offset + range.end + 1 > text.length
|
||||
|
||||
text.insert(offset + range.begin, before)
|
||||
offset += before.length
|
||||
|
||||
text.insert(offset + range.end + 1, after)
|
||||
offset += after.length
|
||||
|
||||
offset
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,8 @@ module Gitlab
|
|||
highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe)
|
||||
end
|
||||
|
||||
attr_reader :blob_name
|
||||
|
||||
def initialize(blob_name, blob_content, repository: nil)
|
||||
@formatter = Rouge::Formatters::HTMLGitlab
|
||||
@repository = repository
|
||||
|
@ -21,16 +23,9 @@ module Gitlab
|
|||
end
|
||||
|
||||
def highlight(text, continue: true, plain: false)
|
||||
if plain
|
||||
hl_lexer = Rouge::Lexers::PlainText
|
||||
continue = false
|
||||
else
|
||||
hl_lexer = self.lexer
|
||||
end
|
||||
|
||||
@formatter.format(hl_lexer.lex(text, continue: continue), tag: hl_lexer.tag).html_safe
|
||||
rescue
|
||||
@formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe
|
||||
highlighted_text = highlight_text(text, continue: continue, plain: plain)
|
||||
highlighted_text = link_dependencies(text, highlighted_text) if blob_name
|
||||
highlighted_text
|
||||
end
|
||||
|
||||
def lexer
|
||||
|
@ -50,5 +45,27 @@ module Gitlab
|
|||
|
||||
Rouge::Lexer.find_fancy(language_name)
|
||||
end
|
||||
|
||||
def highlight_text(text, continue: true, plain: false)
|
||||
if plain
|
||||
highlight_plain(text)
|
||||
else
|
||||
highlight_rich(text, continue: continue)
|
||||
end
|
||||
end
|
||||
|
||||
def highlight_plain(text)
|
||||
@formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe
|
||||
end
|
||||
|
||||
def highlight_rich(text, continue: true)
|
||||
@formatter.format(lexer.lex(text, continue: continue), tag: lexer.tag).html_safe
|
||||
rescue
|
||||
highlight_plain(text)
|
||||
end
|
||||
|
||||
def link_dependencies(text, highlighted_text)
|
||||
Gitlab::DependencyLinker.link(blob_name, text, highlighted_text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
102
lib/gitlab/string_range_marker.rb
Normal file
102
lib/gitlab/string_range_marker.rb
Normal file
|
@ -0,0 +1,102 @@
|
|||
module Gitlab
|
||||
class StringRangeMarker
|
||||
attr_accessor :raw_line, :rich_line
|
||||
|
||||
def initialize(raw_line, rich_line = raw_line)
|
||||
@raw_line = raw_line
|
||||
@rich_line = ERB::Util.html_escape(rich_line)
|
||||
end
|
||||
|
||||
def mark(marker_ranges)
|
||||
return rich_line unless marker_ranges
|
||||
|
||||
rich_marker_ranges = []
|
||||
marker_ranges.each do |range|
|
||||
# Map the inline-diff range based on the raw line to character positions in the rich line
|
||||
rich_positions = position_mapping[range].flatten
|
||||
# Turn the array of character positions into ranges
|
||||
rich_marker_ranges.concat(collapse_ranges(rich_positions))
|
||||
end
|
||||
|
||||
offset = 0
|
||||
# Mark each range
|
||||
rich_marker_ranges.each_with_index do |range, i|
|
||||
offset_range = (range.begin + offset)..(range.end + offset)
|
||||
original_text = rich_line[offset_range]
|
||||
|
||||
text = yield(original_text, left: i == 0, right: i == rich_marker_ranges.length - 1)
|
||||
|
||||
rich_line[offset_range] = text
|
||||
|
||||
offset += text.length - original_text.length
|
||||
end
|
||||
|
||||
rich_line.html_safe
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Mapping of character positions in the raw line, to the rich (highlighted) line
|
||||
def position_mapping
|
||||
@position_mapping ||= begin
|
||||
mapping = []
|
||||
rich_pos = 0
|
||||
(0..raw_line.length).each do |raw_pos|
|
||||
rich_char = rich_line[rich_pos]
|
||||
|
||||
# The raw and rich lines are the same except for HTML tags,
|
||||
# so skip over any `<...>` segment
|
||||
while rich_char == '<'
|
||||
until rich_char == '>'
|
||||
rich_pos += 1
|
||||
rich_char = rich_line[rich_pos]
|
||||
end
|
||||
|
||||
rich_pos += 1
|
||||
rich_char = rich_line[rich_pos]
|
||||
end
|
||||
|
||||
# multi-char HTML entities in the rich line correspond to a single character in the raw line
|
||||
if rich_char == '&'
|
||||
multichar_mapping = [rich_pos]
|
||||
until rich_char == ';'
|
||||
rich_pos += 1
|
||||
multichar_mapping << rich_pos
|
||||
rich_char = rich_line[rich_pos]
|
||||
end
|
||||
|
||||
mapping[raw_pos] = multichar_mapping
|
||||
else
|
||||
mapping[raw_pos] = rich_pos
|
||||
end
|
||||
|
||||
rich_pos += 1
|
||||
end
|
||||
|
||||
mapping
|
||||
end
|
||||
end
|
||||
|
||||
# Takes an array of integers, and returns an array of ranges covering the same integers
|
||||
def collapse_ranges(positions)
|
||||
return [] if positions.empty?
|
||||
ranges = []
|
||||
|
||||
start = prev = positions[0]
|
||||
range = start..prev
|
||||
positions[1..-1].each do |pos|
|
||||
if pos == prev + 1
|
||||
range = start..pos
|
||||
prev = pos
|
||||
else
|
||||
ranges << range
|
||||
start = prev = pos
|
||||
range = start..prev
|
||||
end
|
||||
end
|
||||
ranges << range
|
||||
|
||||
ranges
|
||||
end
|
||||
end
|
||||
end
|
13
lib/gitlab/string_regex_marker.rb
Normal file
13
lib/gitlab/string_regex_marker.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
module Gitlab
|
||||
class StringRegexMarker < StringRangeMarker
|
||||
def mark(regex, group: 0, &block)
|
||||
regex_match = raw_line.match(regex)
|
||||
return rich_line unless regex_match
|
||||
|
||||
begin_index, end_index = regex_match.offset(group)
|
||||
name_range = begin_index..(end_index - 1)
|
||||
|
||||
super([name_range], &block)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -122,9 +122,9 @@ describe DiffHelper do
|
|||
it "returns strings with marked inline diffs" do
|
||||
marked_old_line, marked_new_line = mark_inline_diffs(old_line, new_line)
|
||||
|
||||
expect(marked_old_line).to eq("abc <span class='idiff left right deletion'>'def'</span>")
|
||||
expect(marked_old_line).to eq(%q{abc <span class="idiff left right deletion">'def'</span>})
|
||||
expect(marked_old_line).to be_html_safe
|
||||
expect(marked_new_line).to eq("abc <span class='idiff left right addition'>"def"</span>")
|
||||
expect(marked_new_line).to eq(%q{abc <span class="idiff left right addition">"def"</span>})
|
||||
expect(marked_new_line).to be_html_safe
|
||||
end
|
||||
end
|
||||
|
|
60
spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb
Normal file
60
spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb
Normal file
|
@ -0,0 +1,60 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe Gitlab::DependencyLinker::GemfileLinker, lib: true do
|
||||
describe '.support?' do
|
||||
it 'supports Gemfile' do
|
||||
expect(described_class.support?('Gemfile')).to be_truthy
|
||||
end
|
||||
|
||||
it 'supports gems.rb' do
|
||||
expect(described_class.support?('gems.rb')).to be_truthy
|
||||
end
|
||||
|
||||
it 'does not support other files' do
|
||||
expect(described_class.support?('Gemfile.lock')).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
describe '#link' do
|
||||
let(:file_name) { 'Gemfile' }
|
||||
|
||||
let(:file_content) do
|
||||
<<-CONTENT.strip_heredoc
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem "rails", '4.2.6', github: "rails/rails"
|
||||
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
|
||||
gem 'responders', '~> 2.0', :github => 'rails/responders'
|
||||
gem 'sprockets', '~> 3.6.0', git: 'https://gitlab.example.com/gems/sprockets'
|
||||
gem 'default_value_for', '~> 3.0.0'
|
||||
CONTENT
|
||||
end
|
||||
|
||||
subject { Gitlab::Highlight.highlight(file_name, file_content) }
|
||||
|
||||
def link(name, url)
|
||||
%{<a href="#{url}" rel="noopener noreferrer" target="_blank">#{name}</a>}
|
||||
end
|
||||
|
||||
it 'links sources' do
|
||||
expect(subject).to include(link('https://rubygems.org', 'https://rubygems.org'))
|
||||
end
|
||||
|
||||
it 'links dependencies' do
|
||||
expect(subject).to include(link('rails', 'https://rubygems.org/gems/rails'))
|
||||
expect(subject).to include(link('rails-deprecated_sanitizer', 'https://rubygems.org/gems/rails-deprecated_sanitizer'))
|
||||
expect(subject).to include(link('responders', 'https://rubygems.org/gems/responders'))
|
||||
expect(subject).to include(link('sprockets', 'https://rubygems.org/gems/sprockets'))
|
||||
expect(subject).to include(link('default_value_for', 'https://rubygems.org/gems/default_value_for'))
|
||||
end
|
||||
|
||||
it 'links GitHub repos' do
|
||||
expect(subject).to include(link('rails/rails', 'https://github.com/rails/rails'))
|
||||
expect(subject).to include(link('rails/responders', 'https://github.com/rails/responders'))
|
||||
end
|
||||
|
||||
it 'links Git repos' do
|
||||
expect(subject).to include(link('https://gitlab.example.com/gems/sprockets', 'https://gitlab.example.com/gems/sprockets'))
|
||||
end
|
||||
end
|
||||
end
|
13
spec/lib/gitlab/dependency_linker_spec.rb
Normal file
13
spec/lib/gitlab/dependency_linker_spec.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe Gitlab::DependencyLinker, lib: true do
|
||||
describe '.link' do
|
||||
it 'links using GemfileLinker' do
|
||||
blob_name = 'Gemfile'
|
||||
|
||||
expect(described_class::GemfileLinker).to receive(:link)
|
||||
|
||||
described_class.link(blob_name, nil, nil)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -34,7 +34,7 @@ describe Gitlab::Diff::Highlight, lib: true do
|
|||
end
|
||||
|
||||
it 'highlights and marks added lines' do
|
||||
code = %Q{+<span id="LC9" class="line" lang="ruby"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">"System commands must be given as an array of strings"</span></span>\n}
|
||||
code = %Q{+<span id="LC9" class="line" lang="ruby"> <span class="k">raise</span> <span class="no"><span class="idiff left">RuntimeError</span></span><span class="p"><span class="idiff">,</span></span><span class="idiff right"> </span><span class="s2">"System commands must be given as an array of strings"</span></span>\n}
|
||||
|
||||
expect(subject[5].text).to eq(code)
|
||||
end
|
||||
|
@ -67,7 +67,7 @@ describe Gitlab::Diff::Highlight, lib: true do
|
|||
end
|
||||
|
||||
it 'marks added lines' do
|
||||
code = %q{+ raise <span class='idiff left right'>RuntimeError, </span>"System commands must be given as an array of strings"}
|
||||
code = %q{+ raise <span class="idiff left right">RuntimeError, </span>"System commands must be given as an array of strings"}
|
||||
|
||||
expect(subject[5].text).to eq(code)
|
||||
expect(subject[5].text).to be_html_safe
|
||||
|
|
14
spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb
Normal file
14
spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Diff::InlineDiffMarkdownMarker, lib: true do
|
||||
describe '#mark' do
|
||||
let(:raw) { "abc 'def'" }
|
||||
let(:inline_diffs) { [2..5] }
|
||||
let(:subject) { described_class.new(raw).mark(inline_diffs, mode: :deletion) }
|
||||
|
||||
it 'marks the range' do
|
||||
expect(subject).to eq("ab{-c 'd-}ef'")
|
||||
expect(subject).to be_html_safe
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,26 +1,26 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Diff::InlineDiffMarker, lib: true do
|
||||
describe '#inline_diffs' do
|
||||
describe '#mark' do
|
||||
context "when the rich text is html safe" do
|
||||
let(:raw) { "abc 'def'" }
|
||||
let(:raw) { "abc 'def'" }
|
||||
let(:rich) { %{<span class="abc">abc</span><span class="space"> </span><span class="def">'def'</span>}.html_safe }
|
||||
let(:inline_diffs) { [2..5] }
|
||||
let(:subject) { Gitlab::Diff::InlineDiffMarker.new(raw, rich).mark(inline_diffs) }
|
||||
let(:subject) { described_class.new(raw, rich).mark(inline_diffs) }
|
||||
|
||||
it 'marks the inline diffs' do
|
||||
expect(subject).to eq(%{<span class="abc">ab<span class='idiff left'>c</span></span><span class="space"><span class='idiff'> </span></span><span class="def"><span class='idiff right'>'d</span>ef'</span>})
|
||||
it 'marks the range' do
|
||||
expect(subject).to eq(%{<span class="abc">ab<span class="idiff left">c</span></span><span class="space"><span class="idiff"> </span></span><span class="def"><span class="idiff right">'d</span>ef'</span>})
|
||||
expect(subject).to be_html_safe
|
||||
end
|
||||
end
|
||||
|
||||
context "when the text text is not html safe" do
|
||||
let(:raw) { "abc 'def'" }
|
||||
let(:raw) { "abc 'def'" }
|
||||
let(:inline_diffs) { [2..5] }
|
||||
let(:subject) { Gitlab::Diff::InlineDiffMarker.new(raw).mark(inline_diffs) }
|
||||
let(:subject) { described_class.new(raw).mark(inline_diffs) }
|
||||
|
||||
it 'marks the inline diffs' do
|
||||
expect(subject).to eq(%{ab<span class='idiff left right'>c 'd</span>ef'})
|
||||
it 'marks the range' do
|
||||
expect(subject).to eq(%{ab<span class="idiff left right">c 'd</span>ef'})
|
||||
expect(subject).to be_html_safe
|
||||
end
|
||||
end
|
||||
|
|
|
@ -57,4 +57,15 @@ describe Gitlab::Highlight, lib: true do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#highlight' do
|
||||
subject { described_class.highlight(file_name, file_content, nowrap: false) }
|
||||
|
||||
it 'links dependencies via DependencyLinker' do
|
||||
expect(Gitlab::DependencyLinker).to receive(:link).
|
||||
with('file.name', 'Contents', anything).and_call_original
|
||||
|
||||
described_class.highlight('file.name', 'Contents')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
36
spec/lib/gitlab/string_range_marker_spec.rb
Normal file
36
spec/lib/gitlab/string_range_marker_spec.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::StringRangeMarker, lib: true do
|
||||
describe '#mark' do
|
||||
context "when the rich text is html safe" do
|
||||
let(:raw) { "abc <def>" }
|
||||
let(:rich) { %{<span class="abc">abc</span><span class="space"> </span><span class="def"><def></span>}.html_safe }
|
||||
let(:inline_diffs) { [2..5] }
|
||||
subject do
|
||||
described_class.new(raw, rich).mark(inline_diffs) do |text, left:, right:|
|
||||
"LEFT#{text}RIGHT"
|
||||
end
|
||||
end
|
||||
|
||||
it 'marks the inline diffs' do
|
||||
expect(subject).to eq(%{<span class="abc">abLEFTcRIGHT</span><span class="space">LEFT RIGHT</span><span class="def">LEFT<dRIGHTef></span>})
|
||||
expect(subject).to be_html_safe
|
||||
end
|
||||
end
|
||||
|
||||
context "when the rich text is not html safe" do
|
||||
let(:raw) { "abc <def>" }
|
||||
let(:inline_diffs) { [2..5] }
|
||||
subject do
|
||||
described_class.new(raw).mark(inline_diffs) do |text, left:, right:|
|
||||
"LEFT#{text}RIGHT"
|
||||
end
|
||||
end
|
||||
|
||||
it 'marks the inline diffs' do
|
||||
expect(subject).to eq(%{abLEFTc <dRIGHTef>})
|
||||
expect(subject).to be_html_safe
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
18
spec/lib/gitlab/string_regex_marker_spec.rb
Normal file
18
spec/lib/gitlab/string_regex_marker_spec.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::StringRegexMarker, lib: true do
|
||||
describe '#mark' do
|
||||
let(:raw) { %{"name": "AFNetworking"} }
|
||||
let(:rich) { %{<span class="key">"name"</span><span class="punctuation">: </span><span class="value">"AFNetworking"</span>}.html_safe }
|
||||
subject do
|
||||
described_class.new(raw, rich).mark(/"[^"]+":\s*"(?<name>[^"]+)"/, group: :name) do |text, left:, right:|
|
||||
%{<a href="#">#{text}</a>}
|
||||
end
|
||||
end
|
||||
|
||||
it 'marks the inline diffs' do
|
||||
expect(subject).to eq(%{<span class="key">"name"</span><span class="punctuation">: </span><span class="value">"<a href="#">AFNetworking</a>"</span>})
|
||||
expect(subject).to be_html_safe
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue