summaryrefslogtreecommitdiff
path: root/lib/gitlab/conflict/file.rb
blob: 7f81c72431d328d3fb0c870c8f8f233327096fbc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
module Gitlab
  module Conflict
    class File
      CONTEXT_LINES = 3

      attr_reader :merge_file_result, :their_path, :their_ref, :our_path, :our_ref, :repository

      def initialize(merge_file_result, conflict, diff_refs:, repository:)
        @merge_file_result = merge_file_result
        @their_path = conflict[:theirs][:path]
        @our_path = conflict[:ours][:path]
        @their_ref = diff_refs.start_sha
        @our_ref = diff_refs.head_sha
        @repository = repository
      end

      # Array of Gitlab::Diff::Line objects
      def lines
        @lines ||= Gitlab::Conflict::Parser.new.parse(merge_file_result[:data],
                                                      our_path: our_path,
                                                      their_path: their_path)
      end

      def highlighted_lines
        return @highlighted_lines if @highlighted_lines

        their_highlight = Gitlab::Highlight.highlight_lines(repository, their_ref, their_path)
        our_highlight = Gitlab::Highlight.highlight_lines(repository, our_ref, our_path)

        @highlighted_lines = lines.map do |line|
          line = line.dup
          if line.type == 'old'
            line.rich_text = their_highlight[line.old_line - 1]
          else
            line.rich_text = our_highlight[line.new_line - 1]
          end
          line
        end
      end

      def sections
        return @sections if @sections

        chunked_lines = highlighted_lines.chunk { |line| line.type.nil? }
        match_line = nil

        @sections = chunked_lines.flat_map.with_index do |(no_conflict, lines), i|
          section = nil

          if no_conflict
            conflict_before = i > 0
            conflict_after = chunked_lines.peek

            if conflict_before && conflict_after
              if lines.length > CONTEXT_LINES * 2
                tail_lines = lines.last(CONTEXT_LINES)
                first_tail_line = tail_lines.first
                match_line = Gitlab::Diff::Line.new('',
                                                    'match',
                                                    first_tail_line.index,
                                                    first_tail_line.old_pos,
                                                    first_tail_line.new_pos)

                section = [
                  { conflict: false, lines: lines.first(CONTEXT_LINES) },
                  { conflict: false, lines: tail_lines.unshift(match_line) }
                ]
              end
            elsif conflict_after
              lines = lines.last(CONTEXT_LINES)
            elsif conflict_before
              lines = lines.first(CONTEXT_LINES)
            end
          end

          if match_line && !section
            match_line.text = "@@ -#{match_line.old_pos},#{lines.last.old_pos} +#{match_line.new_pos},#{lines.last.new_pos} @@"
          end

          section || { conflict: !no_conflict, lines: lines }
        end
      end

      def as_json(opts = nil)
        {
          old_path: their_path,
          new_path: our_path,
          sections: sections
        }
      end
    end
  end
end