-
Notifications
You must be signed in to change notification settings - Fork 69
Expand file tree
/
Copy pathresult_processor.rb
More file actions
122 lines (102 loc) · 3.13 KB
/
result_processor.rb
File metadata and controls
122 lines (102 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
require 'cgi'
class ResultProcessor
# input (loaded in @diff) is an array having Hash elements:
# { :action => action, :token => string }
# action can be :discard_a, :discard_b or :match
# output: two formatted html strings, one for the removals and one for the additions
def results
close_tags # close last tag
[array_of_lines(@result[:removal]), array_of_lines(@result[:addition])]
end
private
def initialize(diff)
@diff = diff
init
filter_replaced_lines
process
end
def init
@result = { :addition => [], :removal => [] }
@tag_open = { :addition => false, :removal => false}
end
def process
@highlight = !@diff.select { |d| d[:action] == :match}.empty? # highlight only if block contains both matches and differences
@diff.each do |diff|
case diff[:action]
when :match
match(diff)
when :discard_a
discard_a(diff)
when :discard_b
discard_b(diff)
end
end
end
def discard_match(position, token)
# replace it with 2 changes
@diff[position][:action] = :discard_a
@diff.insert(position, { :action => :discard_b, :token => token} )
end
def length_in_chars(diff)
diff.inject(0) { |length, s| length + s[:token].size}
end
def filter_replaced_lines
# if a block is replaced by an other one, lcs-diff will find even the single common word between the old and the new content
# no need for intelligent diff in this case, simply show the removed and the added block with no highlighting
# rule: if less than 33% of a block is not a match, we don't need intelligent diff for that block
match_length = length_in_chars(@diff.select { |d| d[:action] == :match})
total_length = length_in_chars(@diff)
if total_length.to_f / match_length > 3.3
@diff.each_with_index do |d, i|
next if d[:action] != :match
discard_match(i, d[:token])
end
end
end
def match(diff)
close_tags
all_actions do |action|
close_last_tag(diff)
@result[action] << escape_content(diff[:token])
end
end
def discard_a(diff)
open_tag(:removal, diff[:token])
close_last_tag(diff)
@result[:removal] << escape_content(diff[:token])
end
def discard_b(diff)
open_tag(:addition, diff[:token])
close_last_tag(diff)
@result[:addition] << escape_content(diff[:token])
end
def all_actions
[:addition, :removal].each do |action|
yield(action)
end
end
def open_tag(action, next_token)
return if !@highlight || next_token.strip.empty? # don't open span tag if no highlighting is needed or the first token is empty
unless @tag_open[action]
klass = action == :addition ? 'aa' : 'rr'
@result[action] << "<span class=\"#{klass}\">"
@tag_open[action] = true
end
end
def close_tags
return unless @highlight
all_actions do |action|
if @tag_open[action]
@result[action] << "</span>"
@tag_open[action] = false
end
end
end
def close_last_tag(diff)
return unless @highlight
close_tags if diff[:token] == "\n"
end
def array_of_lines(tokens)
tokens.join('').split("\n")
end
end