diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 21:18:33 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 21:18:33 +0300 |
commit | f64a639bcfa1fc2bc89ca7db268f594306edfd7c (patch) | |
tree | a2c3c2ebcc3b45e596949db485d6ed18ffaacfa1 /lib/gitlab/word_diff | |
parent | bfbc3e0d6583ea1a91f627528bedc3d65ba4b10f (diff) |
Add latest changes from gitlab-org/gitlab@13-10-stable-eev13.10.0-rc40
Diffstat (limited to 'lib/gitlab/word_diff')
-rw-r--r-- | lib/gitlab/word_diff/chunk_collection.rb | 23 | ||||
-rw-r--r-- | lib/gitlab/word_diff/line_processor.rb | 45 | ||||
-rw-r--r-- | lib/gitlab/word_diff/parser.rb | 57 | ||||
-rw-r--r-- | lib/gitlab/word_diff/positions_counter.rb | 30 | ||||
-rw-r--r-- | lib/gitlab/word_diff/segments/chunk.rb | 36 | ||||
-rw-r--r-- | lib/gitlab/word_diff/segments/diff_hunk.rb | 40 | ||||
-rw-r--r-- | lib/gitlab/word_diff/segments/newline.rb | 13 |
7 files changed, 244 insertions, 0 deletions
diff --git a/lib/gitlab/word_diff/chunk_collection.rb b/lib/gitlab/word_diff/chunk_collection.rb new file mode 100644 index 00000000000..dd388f75302 --- /dev/null +++ b/lib/gitlab/word_diff/chunk_collection.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module WordDiff + class ChunkCollection + def initialize + @chunks = [] + end + + def add(chunk) + @chunks << chunk + end + + def content + @chunks.join('') + end + + def reset + @chunks = [] + end + end + end +end diff --git a/lib/gitlab/word_diff/line_processor.rb b/lib/gitlab/word_diff/line_processor.rb new file mode 100644 index 00000000000..49263962dd6 --- /dev/null +++ b/lib/gitlab/word_diff/line_processor.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +# Converts a line from `git diff --word-diff=porcelain` output into a segment +# +# Possible options: +# 1. Diff hunk +# 2. Chunk +# 3. Newline +module Gitlab + module WordDiff + class LineProcessor + def initialize(line) + @line = line + end + + def extract + return if empty_line? + return Segments::DiffHunk.new(full_line) if diff_hunk? + return Segments::Newline.new if newline_delimiter? + + Segments::Chunk.new(full_line) + end + + private + + attr_reader :line + + def diff_hunk? + line =~ /^@@ -/ + end + + def empty_line? + full_line == ' ' + end + + def newline_delimiter? + full_line == '~' + end + + def full_line + @full_line ||= line.delete("\n") + end + end + end +end diff --git a/lib/gitlab/word_diff/parser.rb b/lib/gitlab/word_diff/parser.rb new file mode 100644 index 00000000000..3b6d4d4d384 --- /dev/null +++ b/lib/gitlab/word_diff/parser.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +# Converts git diff --word-diff=porcelain output to Gitlab::Diff::Line objects +# see: https://git-scm.com/docs/git-diff#Documentation/git-diff.txt-porcelain +module Gitlab + module WordDiff + class Parser + include Enumerable + + def parse(lines, diff_file: nil) + return [] if lines.blank? + + # By returning an Enumerator we make it possible to search for a single line (with #find) + # without having to instantiate all the others that come after it. + Enumerator.new do |yielder| + @chunks = ChunkCollection.new + @counter = PositionsCounter.new + + lines.each do |line| + segment = LineProcessor.new(line).extract + + case segment + when Segments::DiffHunk + next if segment.first_line? + + counter.set_pos_num(old: segment.pos_old, new: segment.pos_new) + + yielder << build_line(segment.to_s, 'match', parent_file: diff_file) + + when Segments::Chunk + @chunks.add(segment) + + when Segments::Newline + yielder << build_line(@chunks.content, nil, parent_file: diff_file) + + @chunks.reset + counter.increase_pos_num + end + end + end + end + + private + + attr_reader :counter + + def build_line(content, type, options = {}) + Gitlab::Diff::Line.new( + content, type, + counter.line_obj_index, counter.pos_old, counter.pos_new, + **options).tap do + counter.increase_obj_index + end + end + end + end +end diff --git a/lib/gitlab/word_diff/positions_counter.rb b/lib/gitlab/word_diff/positions_counter.rb new file mode 100644 index 00000000000..ca66b43755f --- /dev/null +++ b/lib/gitlab/word_diff/positions_counter.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Responsible for keeping track of line numbers and created Gitlab::Diff::Line objects +module Gitlab + module WordDiff + class PositionsCounter + def initialize + @pos_old = 1 + @pos_new = 1 + @line_obj_index = 0 + end + + attr_reader :pos_old, :pos_new, :line_obj_index + + def increase_pos_num + @pos_old += 1 + @pos_new += 1 + end + + def increase_obj_index + @line_obj_index += 1 + end + + def set_pos_num(old:, new:) + @pos_old = old + @pos_new = new + end + end + end +end diff --git a/lib/gitlab/word_diff/segments/chunk.rb b/lib/gitlab/word_diff/segments/chunk.rb new file mode 100644 index 00000000000..7c5850666f9 --- /dev/null +++ b/lib/gitlab/word_diff/segments/chunk.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# Chunk is a part of the line that starts with ` `, `-`, `+` +# Consecutive chunks build a line. Line that starts with `~` is an identifier of +# end of the line. +module Gitlab + module WordDiff + module Segments + class Chunk + def initialize(line) + @line = line + end + + def removed? + line[0] == '-' + end + + def added? + line[0] == '+' + end + + def to_s + line[1..] || '' + end + + def length + to_s.length + end + + private + + attr_reader :line + end + end + end +end diff --git a/lib/gitlab/word_diff/segments/diff_hunk.rb b/lib/gitlab/word_diff/segments/diff_hunk.rb new file mode 100644 index 00000000000..88b6817676f --- /dev/null +++ b/lib/gitlab/word_diff/segments/diff_hunk.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# Diff hunk is line that starts with @@ +# It contains information about start line numbers +# +# Example: +# @@ -1,4 +1,5 @@ +# +# See more: https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html +module Gitlab + module WordDiff + module Segments + class DiffHunk + def initialize(line) + @line = line + end + + def pos_old + line.match(/\-[0-9]*/)[0].to_i.abs rescue 0 + end + + def pos_new + line.match(/\+[0-9]*/)[0].to_i.abs rescue 0 + end + + def first_line? + pos_old <= 1 && pos_new <= 1 + end + + def to_s + line + end + + private + + attr_reader :line + end + end + end +end diff --git a/lib/gitlab/word_diff/segments/newline.rb b/lib/gitlab/word_diff/segments/newline.rb new file mode 100644 index 00000000000..de8bbf252ff --- /dev/null +++ b/lib/gitlab/word_diff/segments/newline.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Gitlab + module WordDiff + module Segments + class Newline + def to_s + '' + end + end + end + end +end |