Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2016-01-15 18:02:48 +0300
committerDouwe Maan <douwe@gitlab.com>2016-01-15 18:02:48 +0300
commit5de8971ddfa10cbc6a082316eee7377038670f75 (patch)
treef69cb6eed018d7e2bdcae59aebb84a471655a7be /lib/gitlab/diff
parent13f10efcf120f2d5244bf6abe934dc7c026834ef (diff)
Whoops, forgot to add files
Diffstat (limited to 'lib/gitlab/diff')
-rw-r--r--lib/gitlab/diff/inline_diff.rb75
-rw-r--r--lib/gitlab/diff/inline_diff_marker.rb85
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