2021-03-02 16:11:07 -05:00
|
|
|
# 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
|
2021-03-22 23:09:04 -04:00
|
|
|
yielder << build_line(@chunks.content, nil, parent_file: diff_file).tap { |line| line.set_marker_ranges(@chunks.marker_ranges) }
|
2021-03-02 16:11:07 -05:00
|
|
|
|
|
|
|
@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
|