mirror of
https://github.com/discourse/discourse.git
synced 2025-05-30 15:28:37 +08:00
better revision history
This commit is contained in:
153
lib/onpdiff.rb
Normal file
153
lib/onpdiff.rb
Normal file
@ -0,0 +1,153 @@
|
||||
# Use "An O(NP) Sequence Comparison Algorithm" as described by Sun Wu, Udi Manber and Gene Myers
|
||||
# in http://www.itu.dk/stud/speciale/bepjea/xwebtex/litt/an-onp-sequence-comparison-algorithm.pdf
|
||||
class ONPDiff
|
||||
|
||||
def initialize(a, b)
|
||||
@a, @b = a, b
|
||||
@m, @n = a.length, b.length
|
||||
@backtrack = []
|
||||
if @reverse = @m > @n
|
||||
@a, @b = @b, @a
|
||||
@m, @n = @n, @m
|
||||
end
|
||||
@offset = @m + 1
|
||||
@delta = @n - @m
|
||||
end
|
||||
|
||||
def diff
|
||||
@diff ||= build_diff_script(compose)
|
||||
end
|
||||
|
||||
def short_diff
|
||||
@short_diff ||= build_short_diff_script(compose)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def compose
|
||||
return @shortest_path if @shortest_path
|
||||
|
||||
size = @m + @n + 3
|
||||
fp = Array.new(size) { |i| -1 }
|
||||
@path = Array.new(size) { |i| -1 }
|
||||
p = -1
|
||||
|
||||
begin
|
||||
p += 1
|
||||
|
||||
k = -p
|
||||
while k <= @delta - 1
|
||||
fp[k + @offset] = snake(k, fp[k - 1 + @offset] + 1, fp[k + 1 + @offset])
|
||||
k += 1
|
||||
end
|
||||
|
||||
k = @delta + p
|
||||
while k >= @delta + 1
|
||||
fp[k + @offset] = snake(k, fp[k - 1 + @offset] + 1, fp[k + 1 + @offset])
|
||||
k -= 1
|
||||
end
|
||||
|
||||
fp[@delta + @offset] = snake(@delta, fp[@delta - 1 + @offset] + 1, fp[@delta + 1 + @offset])
|
||||
|
||||
end until fp[@delta + @offset] == @n
|
||||
|
||||
r = @path[@delta + @offset]
|
||||
|
||||
@shortest_path = []
|
||||
while r != -1
|
||||
@shortest_path << [@backtrack[r][0], @backtrack[r][1]]
|
||||
r = @backtrack[r][2]
|
||||
end
|
||||
|
||||
@shortest_path
|
||||
end
|
||||
|
||||
def snake(k, p, pp)
|
||||
r = p > pp ? @path[k - 1 + @offset] : @path[k + 1 + @offset]
|
||||
y = [p, pp].max
|
||||
x = y - k
|
||||
|
||||
while x < @m && y < @n && @a[x] == @b[y]
|
||||
x += 1
|
||||
y += 1
|
||||
end
|
||||
|
||||
@path[k + @offset] = @backtrack.length
|
||||
@backtrack << [x, y, r]
|
||||
|
||||
y
|
||||
end
|
||||
|
||||
def build_diff_script(shortest_path)
|
||||
ses = []
|
||||
x, y = 1, 1
|
||||
px, py = 0, 0
|
||||
i = shortest_path.length - 1
|
||||
while i >= 0
|
||||
while px < shortest_path[i][0] || py < shortest_path[i][1]
|
||||
if shortest_path[i][1] - shortest_path[i][0] > py - px
|
||||
t = @reverse ? :delete : :add
|
||||
ses << [@b[py], t]
|
||||
y += 1
|
||||
py += 1
|
||||
elsif shortest_path[i][1] - shortest_path[i][0] < py - px
|
||||
t = @reverse ? :add : :delete
|
||||
ses << [@a[px], t]
|
||||
x += 1
|
||||
px += 1
|
||||
else
|
||||
ses << [@a[px], :common]
|
||||
x += 1
|
||||
y += 1
|
||||
px += 1
|
||||
py += 1
|
||||
end
|
||||
end
|
||||
i -= 1
|
||||
end
|
||||
ses
|
||||
end
|
||||
|
||||
def build_short_diff_script(shortest_path)
|
||||
ses = []
|
||||
x, y = 1, 1
|
||||
px, py = 0, 0
|
||||
i = shortest_path.length - 1
|
||||
while i >= 0
|
||||
while px < shortest_path[i][0] || py < shortest_path[i][1]
|
||||
if shortest_path[i][1] - shortest_path[i][0] > py - px
|
||||
t = @reverse ? :delete : :add
|
||||
if ses.length > 0 && ses[-1][1] == t
|
||||
ses[-1][0] << @b[py]
|
||||
else
|
||||
ses << [@b[py], t]
|
||||
end
|
||||
y += 1
|
||||
py += 1
|
||||
elsif shortest_path[i][1] - shortest_path[i][0] < py - px
|
||||
t = @reverse ? :add : :delete
|
||||
if ses.length > 0 && ses[-1][1] == t
|
||||
ses[-1][0] << @a[px]
|
||||
else
|
||||
ses << [@a[px], t]
|
||||
end
|
||||
x += 1
|
||||
px += 1
|
||||
else
|
||||
if ses.length > 0 && ses[-1][1] == :common
|
||||
ses[-1][0] << @a[px]
|
||||
else
|
||||
ses << [@a[px], :common]
|
||||
end
|
||||
x += 1
|
||||
y += 1
|
||||
px += 1
|
||||
py += 1
|
||||
end
|
||||
end
|
||||
i -= 1
|
||||
end
|
||||
ses
|
||||
end
|
||||
|
||||
end
|
Reference in New Issue
Block a user