diff options
author | Douwe Maan <douwe@gitlab.com> | 2016-01-15 16:02:48 +0100 |
---|---|---|
committer | Douwe Maan <douwe@gitlab.com> | 2016-01-15 16:02:48 +0100 |
commit | 5de8971ddfa10cbc6a082316eee7377038670f75 (patch) | |
tree | f69cb6eed018d7e2bdcae59aebb84a471655a7be /lib | |
parent | 13f10efcf120f2d5244bf6abe934dc7c026834ef (diff) | |
download | gitlab-ce-5de8971ddfa10cbc6a082316eee7377038670f75.tar.gz |
Whoops, forgot to add files
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab/diff/inline_diff.rb | 75 | ||||
-rw-r--r-- | lib/gitlab/diff/inline_diff_marker.rb | 85 |
2 files changed, 160 insertions, 0 deletions
diff --git a/lib/gitlab/diff/inline_diff.rb b/lib/gitlab/diff/inline_diff.rb new file mode 100644 index 00000000000..bae297ab00f --- /dev/null +++ b/lib/gitlab/diff/inline_diff.rb @@ -0,0 +1,75 @@ +module Gitlab + module Diff + class InlineDiff + attr_accessor :lines + + def initialize(lines) + @lines = lines + end + + def inline_diffs + inline_diffs = [] + + local_edit_indexes.each do |index| + old_index = index + new_index = index + 1 + old_line = @lines[old_index] + new_line = @lines[new_index] + + suffixless_old_line = old_line[1..-1] + suffixless_new_line = new_line[1..-1] + + # Skip inline diff if empty line was replaced with content + next if suffixless_old_line == "" + + # Add one, because this is based on the suffixless version + lcp = longest_common_prefix(suffixless_old_line, suffixless_new_line) + 1 + lcs = longest_common_suffix(suffixless_old_line, suffixless_new_line) + + old_diff_range = lcp..(old_line.length - lcs - 1) + new_diff_range = lcp..(new_line.length - lcs - 1) + + inline_diffs[old_index] = [old_diff_range] if old_diff_range.begin <= old_diff_range.end + inline_diffs[new_index] = [new_diff_range] if new_diff_range.begin <= new_diff_range.end + end + + inline_diffs + end + + private + + def local_edit_indexes + line_prefixes = @lines.map { |line| line.match(/\A([+-])/) ? $1 : ' ' } + joined_line_prefixes = " #{line_prefixes.join} " + + offset = 0 + local_edit_indexes = [] + while index = joined_line_prefixes.index(" -+ ", offset) + local_edit_indexes << index + offset = index + 1 + end + + local_edit_indexes + end + + def longest_common_prefix(a, b) + max_length = [a.length, b.length].max + + length = 0 + (0..max_length - 1).each do |pos| + old_char = a[pos] + new_char = b[pos] + + break if old_char != new_char + length += 1 + end + + length + end + + def longest_common_suffix(a, b) + longest_common_prefix(a.reverse, b.reverse) + end + end + end +end diff --git a/lib/gitlab/diff/inline_diff_marker.rb b/lib/gitlab/diff/inline_diff_marker.rb new file mode 100644 index 00000000000..4bb755e3d3d --- /dev/null +++ b/lib/gitlab/diff/inline_diff_marker.rb @@ -0,0 +1,85 @@ +module Gitlab + module Diff + class InlineDiffMarker + attr_accessor :raw_line, :rich_line + + def initialize(raw_line, rich_line = raw_line) + @raw_line = raw_line + @rich_line = rich_line + end + + def mark(line_inline_diffs) + offset = 0 + line_inline_diffs.each do |inline_diff_range| + inline_diff_positions = position_mapping[inline_diff_range] + marker_ranges = collapse_ranges(inline_diff_positions) + + marker_ranges.each do |range| + offset = insert_around_range(rich_line, range, "<span class='idiff'>", "</span>", offset) + end + end + + rich_line + end + + def position_mapping + @position_mapping ||= begin + mapping = [] + raw_pos = 0 + rich_pos = 0 + (0..raw_line.length).each do |raw_pos| + raw_char = raw_line[raw_pos] + rich_char = rich_line[rich_pos] + + while rich_char == '<' + until rich_char == '>' + rich_pos += 1 + rich_char = rich_line[rich_pos] + end + + rich_pos += 1 + rich_char = rich_line[rich_pos] + end + + mapping[raw_pos] = rich_pos + + rich_pos += 1 + end + + mapping + end + end + + def collapse_ranges(positions) + return [] if positions.empty? + ranges = [] + + start = prev = positions[0] + range = start..prev + positions[1..-1].each do |pos| + if pos == prev + 1 + range = start..pos + prev = pos + else + ranges << range + start = prev = pos + range = start..prev + end + end + ranges << range + + ranges + end + + def insert_around_range(text, range, before, after, offset = 0) + text.insert(offset + range.begin, before) + offset += before.length + + text.insert(offset + range.end + 1, after) + offset += after.length + + offset + end + end + end +end |