summaryrefslogtreecommitdiff
path: root/lib/gitlab/diff/line.rb
blob: c2b834c71b557089341f7299f6af141cac02fa54 (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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# frozen_string_literal: true

module Gitlab
  module Diff
    class Line
      # When SERIALIZE_KEYS is updated, to reset the redis cache entries you'll
      #   need to bump the VERSION constant on Gitlab::Diff::HighlightCache
      #
      SERIALIZE_KEYS = %i(line_code rich_text text type index old_pos new_pos).freeze

      attr_reader :marker_ranges
      attr_writer :text, :rich_text, :discussable
      attr_accessor :index, :type, :old_pos, :new_pos, :line_code

      def initialize(text, type, index, old_pos, new_pos, parent_file: nil, line_code: nil, rich_text: nil)
        @text = text
        @type = type
        @index = index
        @old_pos = old_pos
        @new_pos = new_pos
        @parent_file = parent_file
        @rich_text = rich_text

        # When line code is not provided from cache store we build it
        # using the parent_file(Diff::File or Conflict::File).
        @line_code = line_code || calculate_line_code

        @marker_ranges = []
        @discussable = true
      end

      def self.init_from_hash(hash)
        new(hash[:text],
            hash[:type],
            hash[:index],
            hash[:old_pos],
            hash[:new_pos],
            parent_file: hash[:parent_file],
            line_code: hash[:line_code],
            rich_text: hash[:rich_text])
      end

      def self.safe_init_from_hash(hash)
        line = hash.with_indifferent_access
        rich_text = line[:rich_text]
        line[:rich_text] = rich_text&.html_safe

        init_from_hash(line)
      end

      def to_hash
        hash = {}
        SERIALIZE_KEYS.each { |key| hash[key] = send(key) } # rubocop:disable GitlabSecurity/PublicSend
        hash
      end

      def set_marker_ranges(marker_ranges)
        @marker_ranges = marker_ranges
      end

      def text(prefix: true)
        return @text if prefix

        @text&.slice(1..).to_s
      end

      def old_line
        old_pos unless added? || meta?
      end

      def new_line
        new_pos unless removed? || meta?
      end

      def line
        new_line || old_line
      end

      def unchanged?
        type.nil?
      end

      def added?
        %w[new new-nonewline].include?(type)
      end

      def removed?
        %w[old old-nonewline].include?(type)
      end

      def meta?
        %w[match new-nonewline old-nonewline].include?(type)
      end

      def match?
        type == :match
      end

      def discussable?
        @discussable && !meta?
      end

      def suggestible?
        !removed?
      end

      def rich_text
        @parent_file.try(:highlight_lines!) if @parent_file && !@rich_text

        @rich_text
      end

      def meta_positions
        return unless meta?

        {
          old_pos: old_pos,
          new_pos: new_pos
        }
      end

      # We have to keep this here since it is still used for conflict resolution
      # Conflict::File#as_json renders json diff lines in sections
      def as_json(opts = nil)
        DiffLineSerializer.new.represent(self)
      end

      private

      def calculate_line_code
        @parent_file&.line_code(self)
      end
    end
  end
end