summaryrefslogtreecommitdiff
path: root/lib/gitlab/diff/rendered/notebook/diff_file_helper.rb
blob: f381792953e616468ed67ba7824c6b4413cb59fc (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
# frozen_string_literal: true
module Gitlab
  module Diff
    module Rendered
      module Notebook
        module DiffFileHelper
          require 'set'

          EMBEDDED_IMAGE_PATTERN = '    ![](data:image'

          def strip_diff_frontmatter(diff_content)
            diff_content.scan(/.*\n/)[2..]&.join('') if diff_content.present?
          end

          # line_positions_at_source_diff: given the transformed lines,
          # what are the correct values for old_pos and new_pos?
          #
          # Example:
          #
          # Original
          # from | to
          # A    | A
          # B    | D
          # C    | E
          # F    | F
          #
          # Original Diff
          #   A A
          # - B
          # - C
          # +   D
          # +   E
          #   F F
          #
          # Transformed
          # from | to
          # A    | A
          # C    | D
          # B    | J
          # L    | E
          # K    | K
          # F    | F
          #
          # Transformed diff | transf old, new | OG old_pos, new_pos |
          #  A A             | 1, 1            | 1, 1                |
          # -C               | 2, 2            | 3, 2                |
          # -B               | 3, 2            | 2, 2                |
          # -L               | 4, 2            | 0, 0                |
          # +  D             | 5, 2            | 4, 2                |
          # +  J             | 5, 3            | 0, 0                |
          # +  E             | 5, 4            | 4, 3                |
          #  K K             | 5, 5            | 0, 0                |
          #  F F             | 6, 6            | 4, 4                |
          def line_positions_at_source_diff(lines, blocks)
            last_mapped_old_pos = 0
            last_mapped_new_pos = 0

            lines.reverse_each.map do |line|
              old_pos = source_line_from_block(line.old_pos, blocks[:from])
              new_pos = source_line_from_block(line.new_pos, blocks[:to])

              old_has_no_mapping = old_pos == 0
              new_has_no_mapping = new_pos == 0

              next [0, 0] if old_has_no_mapping && (new_has_no_mapping || line.type == 'old')
              next [0, 0] if new_has_no_mapping && line.type == 'new'

              new_pos = last_mapped_new_pos if new_has_no_mapping && line.type == 'old'
              old_pos = last_mapped_old_pos if old_has_no_mapping && line.type == 'new'

              last_mapped_old_pos = old_pos
              last_mapped_new_pos = new_pos

              [old_pos, new_pos]
            end.reverse
          end

          def lines_in_source_diff(source_diff_lines, is_deleted_file, is_added_file)
            {
              from: is_added_file ? Set[] : source_diff_lines.map {|l| l.old_pos}.to_set,
              to: is_deleted_file ? Set[] : source_diff_lines.map {|l| l.new_pos}.to_set
            }
          end

          def source_line_from_block(transformed_line, transformed_blocks)
            # Blocks are the lines returned from the library and are a hash with {text:, source_line:}
            # Blocks source_line are 0 indexed
            return 0 if transformed_blocks.empty?

            line_in_source = transformed_blocks[transformed_line - 1][:source_line]

            return 0 unless line_in_source.present?

            line_in_source
          end

          def image_as_rich_text(line_text)
            return unless line_text[1..].starts_with?(EMBEDDED_IMAGE_PATTERN)

            image_body = line_text[1..].delete_prefix(EMBEDDED_IMAGE_PATTERN).delete_suffix(')')

            "<img src=\"data:image#{CGI.escapeHTML(image_body)}\">".html_safe
          end
        end
      end
    end
  end
end