Module: DiffHelper

Instance Method Summary collapse

Instance Method Details

#apply_diff_view_cookie!Object



223
224
225
226
227
# File 'app/helpers/diff_helper.rb', line 223

def apply_diff_view_cookie!
  return unless params[:view].present?

  set_secure_cookie(:diff_view, params.delete(:view), type: CookiesHelper::COOKIE_TYPE_PERMANENT)
end

#collapsed_diff_url(diff_file) ⇒ Object



229
230
231
232
233
234
235
236
237
238
# File 'app/helpers/diff_helper.rb', line 229

def collapsed_diff_url(diff_file)
  url_for(
    safe_params.merge(
      action: :diff_for_path,
      old_path: diff_file.old_path,
      new_path: diff_file.new_path,
      file_identifier: diff_file.file_identifier
    )
  )
end

#conflicts(allow_tree_conflicts: false) ⇒ Object



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'app/helpers/diff_helper.rb', line 249

def conflicts(allow_tree_conflicts: false)
  unless merge_request.cannot_be_merged? && merge_request.source_branch_exists? && merge_request.target_branch_exists?
    return
  end

  conflicts_service = MergeRequests::Conflicts::ListService.new(merge_request,
    allow_tree_conflicts: allow_tree_conflicts)

  return unless allow_tree_conflicts || conflicts_service.can_be_resolved_in_ui?

  conflicts_service.conflicts.files.index_by(&:path)
rescue Gitlab::Git::Conflict::Resolver::ConflictSideMissing
  # This exception is raised when changes on a fork isn't present on canonical repo yet.
  # We can't list conflicts until the canonical repo gets the references from the fork
  # which happens asynchronously when updating MR.
  #
  # Return empty hash to indicate that there are no conflicts.
  {}
end

#conflicts_with_typesObject



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'app/helpers/diff_helper.rb', line 269

def conflicts_with_types
  unless merge_request.cannot_be_merged? && merge_request.source_branch_exists? && merge_request.target_branch_exists?
    return
  end

  cached_conflicts_with_types do
    # We set skip_content to true since we don't really need the content to list the conflicts and their types
    conflicts_service = MergeRequests::Conflicts::ListService.new(
      merge_request,
      allow_tree_conflicts: true,
      skip_content: true
    )

    {}.tap do |h|
      conflicts_service.conflicts.files.each do |file|
        h[file.path] = {
          conflict_type: file.conflict_type,
          conflict_type_when_renamed: file.conflict_type(when_renamed: true)
        }
      end
    end
  end
rescue Gitlab::Git::Conflict::Resolver::ConflictSideMissing
  # This exception is raised when changes on a fork isn't present on canonical repo yet.
  # We can't list conflicts until the canonical repo gets the references from the fork
  # which happens asynchronously when updating MR.
  #
  # Return empty hash to indicate that there are no conflicts.
  {}
end

#diff_file_blob_raw_path(diff_file) ⇒ Object



186
187
188
# File 'app/helpers/diff_helper.rb', line 186

def diff_file_blob_raw_path(diff_file)
  diff_file_blob_raw_url(diff_file, only_path: true)
end

#diff_file_blob_raw_url(diff_file, only_path: false) ⇒ Object



175
176
177
# File 'app/helpers/diff_helper.rb', line 175

def diff_file_blob_raw_url(diff_file, only_path: false)
  project_raw_url(@project, tree_join(diff_file.content_sha, diff_file.file_path), only_path: only_path)
end

#diff_file_html_data(project, diff_file_path, diff_commit_id) ⇒ Object



194
195
196
197
198
199
# File 'app/helpers/diff_helper.rb', line 194

def diff_file_html_data(project, diff_file_path, diff_commit_id)
  {
    blob_diff_path: project_blob_diff_path(project, tree_join(diff_commit_id, diff_file_path)),
    view: diff_view
  }
end

#diff_file_old_blob_raw_path(diff_file) ⇒ Object



190
191
192
# File 'app/helpers/diff_helper.rb', line 190

def diff_file_old_blob_raw_path(diff_file)
  diff_file_old_blob_raw_url(diff_file, only_path: true)
end

#diff_file_old_blob_raw_url(diff_file, only_path: false) ⇒ Object



179
180
181
182
183
184
# File 'app/helpers/diff_helper.rb', line 179

def diff_file_old_blob_raw_url(diff_file, only_path: false)
  sha = diff_file.old_content_sha
  return unless sha

  project_raw_url(@project, tree_join(diff_file.old_content_sha, diff_file.old_path), only_path: only_path)
end

#diff_file_stats_data(diff_file) ⇒ Object



201
202
203
204
205
206
207
208
209
210
211
# File 'app/helpers/diff_helper.rb', line 201

def diff_file_stats_data(diff_file)
  old_blob = diff_file.old_blob
  new_blob = diff_file.new_blob
  {
    old_size: old_blob&.size,
    new_size: new_blob&.size,
    added_lines: diff_file.added_lines,
    removed_lines: diff_file.removed_lines,
    viewer_name: diff_file.viewer.partial_name
  }
end

#diff_line_content(line) ⇒ Object



94
95
96
97
98
99
100
101
102
103
# File 'app/helpers/diff_helper.rb', line 94

def diff_line_content(line)
  if line.blank?
    " ".html_safe
  elsif line.start_with?('+', '-', ' ')
    # `sub` and substring-ing would destroy HTML-safeness of `line`
    line[1, line.length]
  else
    line
  end
end


105
106
107
# File 'app/helpers/diff_helper.rb', line 105

def diff_link_number(line_type, match, text)
  line_type == match ? " " : text
end

#diff_match_line(old_pos, new_pos, text: '', view: :inline, bottom: false) ⇒ Object



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
# File 'app/helpers/diff_helper.rb', line 52

def diff_match_line(old_pos, new_pos, text: '', view: :inline, bottom: false)
  content_line_class = %w[line_content match]
  content_line_class << 'parallel' if view == :parallel

  line_num_class = %w[diff-line-num unfold js-unfold]
  line_num_class << 'js-unfold-bottom' if bottom

  html = []

  expand_data = {}
  if bottom
    expand_data[:expand_next_line] = true
  else
    expand_data[:expand_prev_line] = true
  end

  if old_pos
    html << (:td, '...', class: [*line_num_class, 'old_line'], data: { linenumber: old_pos })
    html << (:td, text, class: [*content_line_class, 'left-side']) if view == :parallel
  end

  if new_pos
    html << (:td, '...', class: [*line_num_class, 'new_line'], data: { linenumber: new_pos })
    html << (:td, text, class: [*content_line_class, ('right-side' if view == :parallel)])
  end

  html.join.html_safe
end

#diff_nomappinginraw_line(line, first_line_num_class, second_line_num_class, content_line_class) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
# File 'app/helpers/diff_helper.rb', line 81

def diff_nomappinginraw_line(line, first_line_num_class, second_line_num_class, content_line_class)
  css_class = ''
  css_class = 'old' if line.type == 'old-nomappinginraw'
  css_class = 'new' if line.type == 'new-nomappinginraw'

  html = [(:td, '', class: [*first_line_num_class, css_class])]
  html << (:td, '', class: [*second_line_num_class, css_class]) if second_line_num_class
  html << (:td, diff_line_content(line.rich_text),
    class: [*content_line_class, 'nomappinginraw', css_class])

  html.join.html_safe
end

#diff_optionsObject



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'app/helpers/diff_helper.rb', line 30

def diff_options
  options = { ignore_whitespace_change: hide_whitespace?, expanded: diffs_expanded?, use_extra_viewer_as_main: true }

  if action_name == 'diff_for_path' || action_name == 'diff_by_file_hash'
    options[:expanded] = true
    options[:paths] = params.values_at(:old_path, :new_path)
    options[:use_extra_viewer_as_main] = false

    if params[:file_identifier]&.include?('.ipynb')
      options[:max_patch_bytes_for_file_extension] = {
        '.ipynb' => 1.megabyte
      }
    end
  end

  options
end

#diff_viewObject



17
18
19
20
21
22
23
24
# File 'app/helpers/diff_helper.rb', line 17

def diff_view
  @diff_view ||= begin
    diff_views = %w[inline parallel]
    diff_view = params[:view] || cookies[:diff_view]
    diff_view = diff_views.first unless diff_views.include?(diff_view)
    diff_view.to_sym
  end
end

#diffs_expanded?Boolean

Returns:

  • (Boolean)


13
14
15
# File 'app/helpers/diff_helper.rb', line 13

def diffs_expanded?
  params[:expanded].present?
end

#editable_diff?(diff_file) ⇒ Boolean

Returns:

  • (Boolean)


213
214
215
# File 'app/helpers/diff_helper.rb', line 213

def editable_diff?(diff_file)
  !diff_file.deleted_file? && @merge_request && @merge_request.source_project
end

#file_heading_id(diff_file) ⇒ Object



306
307
308
# File 'app/helpers/diff_helper.rb', line 306

def file_heading_id(diff_file)
  "#{diff_file.file_hash[0..8]}-heading"
end

#hide_whitespace?Boolean

Returns:

  • (Boolean)


310
311
312
313
314
# File 'app/helpers/diff_helper.rb', line 310

def hide_whitespace?
  return params[:w] == '1' if params.key?(:w)

  current_user.nil? || !current_user.show_whitespace_in_diffs
end

#inline_diff_btnObject



127
128
129
# File 'app/helpers/diff_helper.rb', line 127

def inline_diff_btn
  diff_btn(s_('Diffs|Inline'), 'inline', diff_view == :inline)
end

#mark_inline_diffs(old_line, new_line) ⇒ Object



4
5
6
7
8
9
10
11
# File 'app/helpers/diff_helper.rb', line 4

def mark_inline_diffs(old_line, new_line)
  old_diffs, new_diffs = Gitlab::Diff::InlineDiff.new(old_line, new_line).inline_diffs

  marked_old_line = Gitlab::Diff::InlineDiffMarker.new(old_line).mark(old_diffs)
  marked_new_line = Gitlab::Diff::InlineDiffMarker.new(new_line).mark(new_diffs)

  [marked_old_line, marked_new_line]
end

#parallel_diff_btnObject



131
132
133
# File 'app/helpers/diff_helper.rb', line 131

def parallel_diff_btn
  diff_btn(s_('Diffs|Side-by-side'), 'parallel', diff_view == :parallel)
end

#parallel_diff_discussions(left, right, diff_file) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'app/helpers/diff_helper.rb', line 109

def parallel_diff_discussions(left, right, diff_file)
  return unless @grouped_diff_discussions

  discussions_left = discussions_right = nil

  if left && left.discussable? && (left.unchanged? || left.removed?)
    line_code = diff_file.line_code(left)
    discussions_left = @grouped_diff_discussions[line_code]
  end

  if right && right.discussable? && right.added?
    line_code = diff_file.line_code(right)
    discussions_right = @grouped_diff_discussions[line_code]
  end

  [discussions_left, discussions_right]
end

#params_with_whitespaceObject



300
301
302
303
304
# File 'app/helpers/diff_helper.rb', line 300

def params_with_whitespace
  # w - ignore whitespace
  w = hide_whitespace? ? 0 : 1
  safe_params.merge(w: w)
end

#render_fork_suggestionObject

As the fork suggestion button is identical every time, we cache it for a full page load



241
242
243
244
245
246
247
# File 'app/helpers/diff_helper.rb', line 241

def render_fork_suggestion
  return unless current_user

  strong_memoize(:fork_suggestion) do
    render partial: "projects/fork_suggestion"
  end
end

#render_overflow_warning?(diffs_collection) ⇒ Boolean

Returns:

  • (Boolean)


217
218
219
220
221
# File 'app/helpers/diff_helper.rb', line 217

def render_overflow_warning?(diffs_collection)
  diffs_collection.overflow?.tap do |overflown|
    log_overflow_limits(diff_files: diffs_collection.raw_diff_files, collection_overflow: overflown)
  end
end

#show_only_context_commits?Boolean

Returns:

  • (Boolean)


26
27
28
# File 'app/helpers/diff_helper.rb', line 26

def show_only_context_commits?
  !!params[:only_context_commits] || @merge_request.has_no_commits?
end

#submodule_diff_compare_data(diff_file) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'app/helpers/diff_helper.rb', line 151

def submodule_diff_compare_data(diff_file)
  compare_url = submodule_links(diff_file.blob, diff_file.content_sha, diff_file.repository, diff_file)&.compare
  return unless compare_url

  title = safe_format(
    _('Compare %{old_commit} to %{new_commit}'),
    old_commit: (:span, Commit.truncate_sha(diff_file.old_blob.id), class: 'commit-sha'),
    new_commit: (:span, Commit.truncate_sha(diff_file.blob.id), class: 'commit-sha')
  )

  {
    title: title,
    href: compare_url,
    tooltip: _('Compare submodule commit revisions')
  }
end


168
169
170
171
172
173
# File 'app/helpers/diff_helper.rb', line 168

def submodule_diff_compare_link(diff_file)
  data = submodule_diff_compare_data(diff_file)
  return '' unless data

  link_button_to data[:title], data[:href], class: 'has-tooltip submodule-compare', title: data[:tooltip]
end


135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'app/helpers/diff_helper.rb', line 135

def submodule_link(blob, ref, repository = @repository)
  urls = submodule_links(blob, ref, repository)

  folder_name = truncate(blob.name, length: 40)
  folder_name = link_to(folder_name, urls.web) if urls&.web

  commit_id = Commit.truncate_sha(blob.id)
  commit_id = link_to(commit_id, urls.tree) if urls&.tree

  [
    (:span, folder_name),
    '@',
    (:span, commit_id, class: 'commit-sha')
  ].join(' ').html_safe
end

#with_custom_diff_options {|diff_options.dup| ... } ⇒ Object

Yields:



48
49
50
# File 'app/helpers/diff_helper.rb', line 48

def with_custom_diff_options
  yield diff_options.dup
end