diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-18 13:34:06 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-02-18 13:34:06 +0300 |
commit | 859a6fb938bb9ee2a317c46dfa4fcc1af49608f0 (patch) | |
tree | d7f2700abe6b4ffcb2dcfc80631b2d87d0609239 /lib/gitlab/diff | |
parent | 446d496a6d000c73a304be52587cd9bbc7493136 (diff) |
Add latest changes from gitlab-org/gitlab@13-9-stable-eev13.9.0-rc42
Diffstat (limited to 'lib/gitlab/diff')
-rw-r--r-- | lib/gitlab/diff/char_diff.rb | 74 | ||||
-rw-r--r-- | lib/gitlab/diff/file_collection/base.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/diff/file_collection_sorter.rb | 14 | ||||
-rw-r--r-- | lib/gitlab/diff/highlight.rb | 5 | ||||
-rw-r--r-- | lib/gitlab/diff/highlight_cache.rb | 11 | ||||
-rw-r--r-- | lib/gitlab/diff/inline_diff.rb | 43 |
6 files changed, 124 insertions, 25 deletions
diff --git a/lib/gitlab/diff/char_diff.rb b/lib/gitlab/diff/char_diff.rb new file mode 100644 index 00000000000..c8bb39e9f5d --- /dev/null +++ b/lib/gitlab/diff/char_diff.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +module Gitlab + module Diff + class CharDiff + include Gitlab::Utils::StrongMemoize + + def initialize(old_string, new_string) + @old_string = old_string.to_s + @new_string = new_string.to_s + @changes = [] + end + + def generate_diff + @changes = diff_match_patch.diff_main(@old_string, @new_string) + diff_match_patch.diff_cleanupSemantic(@changes) + + @changes + end + + def changed_ranges(offset: 0) + old_diffs = [] + new_diffs = [] + new_pointer = old_pointer = offset + + generate_diff.each do |(action, content)| + content_size = content.size + + if action == :equal + new_pointer += content_size + old_pointer += content_size + end + + if action == :delete + old_diffs << (old_pointer..(old_pointer + content_size - 1)) + old_pointer += content_size + end + + if action == :insert + new_diffs << (new_pointer..(new_pointer + content_size - 1)) + new_pointer += content_size + end + end + + [old_diffs, new_diffs] + end + + def to_html + @changes.map do |op, text| + %{<span class="#{html_class_names(op)}">#{ERB::Util.html_escape(text)}</span>} + end.join.html_safe + end + + private + + def diff_match_patch + strong_memoize(:diff_match_patch) { DiffMatchPatch.new } + end + + def html_class_names(operation) + class_names = ['idiff'] + + case operation + when :insert + class_names << 'addition' + when :delete + class_names << 'deletion' + end + + class_names.join(' ') + end + end + end +end diff --git a/lib/gitlab/diff/file_collection/base.rb b/lib/gitlab/diff/file_collection/base.rb index 8f4f8febec0..627abfbfe7e 100644 --- a/lib/gitlab/diff/file_collection/base.rb +++ b/lib/gitlab/diff/file_collection/base.rb @@ -117,7 +117,7 @@ module Gitlab end def sort_diffs(diffs) - return diffs unless Feature.enabled?(:sort_diffs, project, default_enabled: false) + return diffs unless Feature.enabled?(:sort_diffs, project, default_enabled: :yaml) Gitlab::Diff::FileCollectionSorter.new(diffs).sort end diff --git a/lib/gitlab/diff/file_collection_sorter.rb b/lib/gitlab/diff/file_collection_sorter.rb index 94626875580..7b099543c83 100644 --- a/lib/gitlab/diff/file_collection_sorter.rb +++ b/lib/gitlab/diff/file_collection_sorter.rb @@ -3,6 +3,10 @@ module Gitlab module Diff class FileCollectionSorter + B_FOLLOWS_A = 1 + A_FOLLOWS_B = -1 + EQUIVALENT = 0 + attr_reader :diffs def initialize(diffs) @@ -29,14 +33,16 @@ module Gitlab a_part = a_parts.shift b_part = b_parts.shift - return 1 if a_parts.size < b_parts.size && a_parts.empty? - return -1 if a_parts.size > b_parts.size && b_parts.empty? + return B_FOLLOWS_A if a_parts.size < b_parts.size && a_parts.empty? + return A_FOLLOWS_B if a_parts.size > b_parts.size && b_parts.empty? comparison = a_part <=> b_part - return comparison unless comparison == 0 + return comparison unless comparison == EQUIVALENT + return compare_path_parts(a_parts, b_parts) if a_parts.any? && b_parts.any? - compare_path_parts(a_parts, b_parts) + # If A and B have the same name (e.g. symlink change), they are identical so return 0 + EQUIVALENT end end end diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb index a5259079345..035084d4861 100644 --- a/lib/gitlab/diff/highlight.rb +++ b/lib/gitlab/diff/highlight.rb @@ -3,12 +3,13 @@ module Gitlab module Diff class Highlight - attr_reader :diff_file, :diff_lines, :raw_lines, :repository + attr_reader :diff_file, :diff_lines, :raw_lines, :repository, :project delegate :old_path, :new_path, :old_sha, :new_sha, to: :diff_file, prefix: :diff def initialize(diff_lines, repository: nil) @repository = repository + @project = repository&.project if diff_lines.is_a?(Gitlab::Diff::File) @diff_file = diff_lines @@ -66,7 +67,7 @@ module Gitlab end def inline_diffs - @inline_diffs ||= InlineDiff.for_lines(@raw_lines) + @inline_diffs ||= InlineDiff.for_lines(@raw_lines, project: project) end def old_lines diff --git a/lib/gitlab/diff/highlight_cache.rb b/lib/gitlab/diff/highlight_cache.rb index 90cb9c8638a..7932cd2a837 100644 --- a/lib/gitlab/diff/highlight_cache.rb +++ b/lib/gitlab/diff/highlight_cache.rb @@ -8,6 +8,7 @@ module Gitlab EXPIRATION = 1.week VERSION = 1 + NEXT_VERSION = 2 delegate :diffable, to: :@diff_collection delegate :diff_options, to: :@diff_collection @@ -69,12 +70,20 @@ module Gitlab def key strong_memoize(:redis_key) do - ['highlighted-diff-files', diffable.cache_key, VERSION, diff_options].join(":") + ['highlighted-diff-files', diffable.cache_key, version, diff_options].join(":") end end private + def version + if Feature.enabled?(:improved_merge_diff_highlighting, diffable.project, default_enabled: :yaml) + NEXT_VERSION + else + VERSION + end + end + def set_highlighted_diff_lines(diff_file, content) diff_file.highlighted_diff_lines = content.map do |line| Gitlab::Diff::Line.safe_init_from_hash(line) diff --git a/lib/gitlab/diff/inline_diff.rb b/lib/gitlab/diff/inline_diff.rb index 5815d1bae4a..cf769262958 100644 --- a/lib/gitlab/diff/inline_diff.rb +++ b/lib/gitlab/diff/inline_diff.rb @@ -27,28 +27,19 @@ module Gitlab @offset = offset end - def inline_diffs + def inline_diffs(project: nil) # Skip inline diff if empty line was replaced with content return if old_line == "" - lcp = longest_common_prefix(old_line, new_line) - lcs = longest_common_suffix(old_line[lcp..-1], new_line[lcp..-1]) - - lcp += offset - old_length = old_line.length + offset - new_length = new_line.length + offset - - old_diff_range = lcp..(old_length - lcs - 1) - new_diff_range = lcp..(new_length - lcs - 1) - - old_diffs = [old_diff_range] if old_diff_range.begin <= old_diff_range.end - new_diffs = [new_diff_range] if new_diff_range.begin <= new_diff_range.end - - [old_diffs, new_diffs] + if Feature.enabled?(:improved_merge_diff_highlighting, project, default_enabled: :yaml) + CharDiff.new(old_line, new_line).changed_ranges(offset: offset) + else + deprecated_diff + end end class << self - def for_lines(lines) + def for_lines(lines, project: nil) changed_line_pairs = find_changed_line_pairs(lines) inline_diffs = [] @@ -57,7 +48,7 @@ module Gitlab old_line = lines[old_index] new_line = lines[new_index] - old_diffs, new_diffs = new(old_line, new_line, offset: 1).inline_diffs + old_diffs, new_diffs = new(old_line, new_line, offset: 1).inline_diffs(project: project) inline_diffs[old_index] = old_diffs inline_diffs[new_index] = new_diffs @@ -97,6 +88,24 @@ module Gitlab private + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/299884 + def deprecated_diff + lcp = longest_common_prefix(old_line, new_line) + lcs = longest_common_suffix(old_line[lcp..-1], new_line[lcp..-1]) + + lcp += offset + old_length = old_line.length + offset + new_length = new_line.length + offset + + old_diff_range = lcp..(old_length - lcs - 1) + new_diff_range = lcp..(new_length - lcs - 1) + + old_diffs = [old_diff_range] if old_diff_range.begin <= old_diff_range.end + new_diffs = [new_diff_range] if new_diff_range.begin <= new_diff_range.end + + [old_diffs, new_diffs] + end + def longest_common_prefix(a, b) # rubocop:disable Naming/UncommunicativeMethodParamName max_length = [a.length, b.length].max |