/
html_formatter.rb
139 lines (128 loc) · 4.2 KB
/
html_formatter.rb
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
module Diffy
class HtmlFormatter
def initialize(diff, options = {})
@diff = diff
@options = options
end
def to_s
if @options[:highlight_words]
wrap_lines(highlighted_words)
else
wrap_lines(@diff.map{|line| wrap_line(ERB::Util.h(line))})
end
end
private
def wrap_line(line)
cleaned = clean_line(line)
case line
when /^(---|\+\+\+|\\\\)/
' <li class="diff-comment"><span>' + line.chomp + '</span></li>'
when /^\+/
' <li class="ins"><ins>' + cleaned + '</ins></li>'
when /^-/
' <li class="del"><del>' + cleaned + '</del></li>'
when /^ /
' <li class="unchanged"><span>' + cleaned + '</span></li>'
when /^@@/
' <li class="diff-block-info"><span>' + line.chomp + '</span></li>'
end
end
# remove +/- or wrap in html
def clean_line(line)
if @options[:include_plus_and_minus_in_html]
line.sub(/^(.)/, '<span class="symbol">\1</span>')
else
line.sub(/^./, '')
end.chomp
end
def wrap_lines(lines)
if lines.empty?
%'<div class="diff"></div>'
else
%'<div class="diff">\n <ul>\n#{lines.join("\n")}\n </ul>\n</div>\n'
end
end
def highlighted_words
chunks = @diff.each_chunk.
reject{|c| c == '\ No newline at end of file'"\n"}
processed = []
lines = chunks.each_with_index.map do |chunk1, index|
next if processed.include? index
processed << index
chunk1 = chunk1
chunk2 = chunks[index + 1]
if not chunk2
next ERB::Util.h(chunk1)
end
dir1 = chunk1.each_char.first
dir2 = chunk2.each_char.first
case [dir1, dir2]
when ['-', '+']
if chunk1.each_char.take(3).join("") =~ /^(---|\+\+\+|\\\\)/ and
chunk2.each_char.take(3).join("") =~ /^(---|\+\+\+|\\\\)/
ERB::Util.h(chunk1)
else
line_diff = Diffy::Diff.new(
split_characters(chunk1),
split_characters(chunk2),
Diffy::Diff::ORIGINAL_DEFAULT_OPTIONS
)
hi1 = reconstruct_characters(line_diff, '-')
hi2 = reconstruct_characters(line_diff, '+')
processed << (index + 1)
[hi1, hi2]
end
else
ERB::Util.h(chunk1)
end
end.flatten
lines.map{|line| line.each_line.map(&:chomp).to_a if line }.flatten.compact.
map{|line|wrap_line(line) }.compact
end
def split_characters(chunk)
chunk.gsub(/^./, '').each_line.map do |line|
if @options[:ignore_crlf]
(line.chomp.split('') + ['\n']).map{|chr| ERB::Util.h(chr) }
else
chars = line.sub(/([\r\n]$)/, '').split('')
# add escaped newlines
chars << '\n'
chars.map{|chr| ERB::Util.h(chr) }
end
end.flatten.join("\n") + "\n"
end
def reconstruct_characters(line_diff, type)
enum = line_diff.each_chunk.to_a
enum.each_with_index.map do |l, i|
re = /(^|\\n)#{Regexp.escape(type)}/
case l
when re
highlight(l)
when /^ /
if i > 1 and enum[i+1] and l.each_line.to_a.size < 4
highlight(l)
else
l.gsub(/^./, '').gsub("\n", '').
gsub('\r', "\r").gsub('\n', "\n")
end
end
end.join('').split("\n").map do |l|
type + l.gsub('</strong><strong>' , '')
end
end
def highlight(lines)
"<strong>" +
lines.
# strip diff tokens (e.g. +,-,etc.)
gsub(/(^|\\n)./, '').
# mark line boundaries from higher level line diff
# html is all escaped so using brackets should make this safe.
gsub('\n', '<LINE_BOUNDARY>').
# join characters back by stripping out newlines
gsub("\n", '').
# close and reopen strong tags. we don't want inline elements
# spanning block elements which get added later.
gsub('<LINE_BOUNDARY>',"</strong>\n<strong>") + "</strong>"
end
end
end