2018-11-06 04:45:35 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-06-20 17:17:56 +00:00
|
|
|
# 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
|
|
|
|
|
2016-07-06 23:28:13 +00:00
|
|
|
# Find old/new line number based on its old/new counterpart line number.
|
2016-06-20 17:17:56 +00:00
|
|
|
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|
|
2017-08-10 16:39:26 +00:00
|
|
|
diff_from_line = diff_line.public_send(from) # rubocop:disable GitlabSecurity/PublicSend
|
2016-06-20 17:17:56 +00:00
|
|
|
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
|
|
|
|
|
2017-08-10 16:39:26 +00:00
|
|
|
diff_from_line = diff_line.public_send(from) # rubocop:disable GitlabSecurity/PublicSend
|
|
|
|
diff_to_line = diff_line.public_send(to) # rubocop:disable GitlabSecurity/PublicSend
|
2016-06-20 17:17:56 +00:00
|
|
|
|
|
|
|
# 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
|