gitlab-org--gitlab-foss/lib/gitlab/diff/line_mapper.rb

67 lines
2.4 KiB
Ruby

# frozen_string_literal: true
# When provided a diff for a specific file, maps old line numbers to new line
# numbers and back, to find out where a specific line in a file was moved by the
# changes.
module Gitlab
module Diff
class LineMapper
attr_accessor :diff_file
def initialize(diff_file)
@diff_file = diff_file
end
# Find new line number for old line number.
def old_to_new(old_line)
map_line_number(old_line, from: :old_line, to: :new_line)
end
# Find old line number for new line number.
def new_to_old(new_line)
map_line_number(new_line, from: :new_line, to: :old_line)
end
private
def diff_lines
@diff_lines ||= @diff_file.diff_lines
end
# Find old/new line number based on its old/new counterpart line number.
def map_line_number(from_line, from:, to:)
# If no diff file could be found, the file wasn't changed, and the
# mapped line number is the same as the specified line number.
return from_line unless diff_file
# To find the mapped line number for the specified line number,
# we need to find:
# - The diff line with that exact line number, if it is in the diff context
# - The first diff line with a higher line number, if it falls between diff contexts
# - The last known diff line, if it falls after the last diff context
diff_line = diff_lines.find do |diff_line|
diff_from_line = diff_line.public_send(from) # rubocop:disable GitlabSecurity/PublicSend
diff_from_line && diff_from_line >= from_line
end
diff_line ||= diff_lines.last
# If no diff line could be found, the file wasn't changed, and the
# mapped line number is the same as the specified line number.
return from_line unless diff_line
diff_from_line = diff_line.public_send(from) # rubocop:disable GitlabSecurity/PublicSend
diff_to_line = diff_line.public_send(to) # rubocop:disable GitlabSecurity/PublicSend
# If the line was removed, there is no mapped line number.
return unless diff_to_line
# Because we may not have the diff line with the exact line number
# we were looking for, we need to adjust the mapped line number.
distance = diff_from_line - from_line
diff_to_line - distance
end
end
end
end