diff_to_html 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,40 @@
1
+ = diff_to_html
2
+
3
+ Ruby library to convert unified diffs like you get from SVN and git to HTML
4
+
5
+ It'a fork of https://github.com/artemv/diff_to_html.rb to use as a dependency in gem specs of other gems through Rubygems.org hosting.
6
+
7
+ It's furthermore based on code from http://gurge.com/blog/2006/10/03/subversion-diff-viewer-cgi-in-ruby (thanks Adam
8
+ Doppelt!), adopted lightly to support multifile diffs and to have more familiar output. It definitely have
9
+ things to improve, so contribution/patches are very welcome.
10
+
11
+ * install the gem:
12
+
13
+ gem install diff_to_html
14
+
15
+ * go to gem's 'examples' dir and run 'ruby test.rb >out.html' -
16
+ this will get diff from sample 'svn diff' and generate out.html. Resulting html is linked to diff.css in 'examples' directory - you'll need
17
+ to copy it to your project's dir to use it.
18
+
19
+ * To use in Rails project:
20
+
21
+ require 'diff_to_html'
22
+ diff = `cat #{File.join(File.dirname(__FILE__), 'diff.git')}`
23
+ converter = DiffToHtml::GitConverter.new #there's also DiffToHtml::SvnConverter
24
+ puts converter.composite_to_html(diff)
25
+
26
+ * to use in any Ruby program:
27
+
28
+ require 'rubygems'
29
+ require 'diff_to_html'
30
+ ...
31
+
32
+ == License
33
+
34
+ diff_to_html is released under the MIT license.
35
+
36
+ == Authors and credits
37
+
38
+ Authors:: Artem Vasiliev
39
+ Original code:: Adam Doppelt, http://gurge.com/blog/2006/10/03/subversion-diff-viewer-cgi-in-ruby
40
+
@@ -0,0 +1,6 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module DiffToHtml
3
+ autoload :Converter, File.expand_path('diff_to_html/converter', File.dirname(__FILE__))
4
+ autoload :SvnConverter, File.expand_path('diff_to_html/svn_converter', File.dirname(__FILE__))
5
+ autoload :GitConverter, File.expand_path('diff_to_html/git_converter', File.dirname(__FILE__))
6
+ end
@@ -0,0 +1,230 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'cgi'
3
+
4
+ module DiffToHtml
5
+ class Converter
6
+ attr_accessor :file_prefix
7
+
8
+ def composite_to_html(composite_diff)
9
+ diffs_to_html get_diffs(composite_diff)
10
+ end
11
+
12
+ def diffs_to_html(diffs)
13
+ result = '<ul class="diff">'
14
+ @filenum = 0
15
+
16
+ diffs.each do |file_map|
17
+ result << get_single_file_diff(file_map[:filename], file_map[:file])
18
+ @filenum += 1
19
+ end
20
+
21
+ result << '</ul>'
22
+ result
23
+ end
24
+
25
+ def get_single_file_diff(file_name, diff_file)
26
+ result = ""
27
+ diff = diff_file.split("\n")
28
+ diff, line = shift_until_first_line(diff)
29
+
30
+ if line =~ /^---/
31
+ result << begin_file(file_name)
32
+ result << get_single_file_diff_body(diff)
33
+ result << "</li>"
34
+ else
35
+ #"<div class='error'>#{line}</div>"
36
+ result =%Q{<li><h2><a name="F#{@filenum}" href="#F#{@filenum}">#{file_name}</a></h2>#{line}</li>}
37
+ end
38
+
39
+ result
40
+ end
41
+
42
+ def get_single_file_diff_body(diff)
43
+ @last_op, @left, @right = ' ', [], []
44
+
45
+ if diff.is_a? String
46
+ diff = diff.split("\n")
47
+ diff, line = shift_until_first_line(diff)
48
+ end
49
+
50
+ diff.shift #+++
51
+
52
+ result = %Q{
53
+ <table class='diff'>
54
+ <colgroup>
55
+ <col class="lineno"/>
56
+ <col class="lineno"/>
57
+ <col class="content"/>
58
+ </colgroup>
59
+ }
60
+
61
+ range = diff.shift
62
+ left_ln, right_ln = range_info(range)
63
+ result << range_row(range)
64
+
65
+ diff.each do |line|
66
+ op = line[0,1]
67
+ line = line[1..-1] || ''
68
+
69
+ if op == '\\'
70
+ line = op + line
71
+ op = ' '
72
+ end
73
+
74
+ if ((@last_op != ' ' and op == ' ') or (@last_op == ' ' and op != ' '))
75
+ left_ln, right_ln = flush_changes(result, left_ln, right_ln)
76
+ end
77
+
78
+ # truncate and escape
79
+ line = CGI.escapeHTML(line)
80
+
81
+ case op
82
+ when ' '
83
+ @left.push(line)
84
+ @right.push(line)
85
+ when '-' then @left.push(line)
86
+ when '+' then @right.push(line)
87
+ when '@'
88
+ range = '@' + line
89
+ flush_changes(result, left_ln, right_ln)
90
+ left_ln, right_ln = range_info(range)
91
+ result << range_row(range)
92
+ else
93
+ flush_changes(result, left_ln, right_ln)
94
+ result << "</table></li>"
95
+ break
96
+ end
97
+ @last_op = op
98
+ end
99
+
100
+ flush_changes(result, left_ln, right_ln)
101
+ result << "</table>"
102
+
103
+ result
104
+ end
105
+
106
+ def file_header_pattern
107
+ raise "Method to be implemented in VCS-specific class"
108
+ end
109
+
110
+ private
111
+
112
+ def get_diffs(composite_diff)
113
+ pattern = file_header_pattern
114
+ files = composite_diff.split(pattern)
115
+ headers = composite_diff.scan(pattern) #huh can't find a way to get both at once
116
+ files.shift if files[0] == '' #first one is junk usually
117
+ result = []
118
+ i = 0
119
+
120
+ files.each do |file|
121
+ result << {:filename => "#{file_prefix}#{get_filename(headers[i])}", :file => file}
122
+ i += 1
123
+ end
124
+
125
+ result
126
+ end
127
+
128
+ def shift_until_first_line(diff)
129
+ diff.shift if diff.first.match(/#index/)
130
+
131
+ line = nil
132
+
133
+ while line !~ /^---/ && !diff.empty?
134
+ line = diff.shift
135
+ end
136
+
137
+ [diff, line]
138
+ end
139
+
140
+ def begin_file(file)
141
+ result = %Q{
142
+ <li>
143
+ <h2><a name="F#{@filenum}" href="#F#{@filenum}">#{file}</a></h2>
144
+ }
145
+
146
+ result
147
+ end
148
+
149
+ def range_info(range)
150
+ left_ln, right_ln = range.gsub(/(@|-|\+)+/, '').strip.split(' ').map{|ln| ln.split(',')[0]}
151
+
152
+ begin
153
+ return Integer(left_ln), Integer(right_ln)
154
+ rescue Exception => e
155
+ raise NotImplementedError.new(
156
+ e.class.name + " (#{e.message}): " + range.inspect + " => [#{left_ln.inspect}, #{right_ln.inspect}]"
157
+ )
158
+ end
159
+ end
160
+
161
+ def range_row(range)
162
+ "<tr class='range'><td>...</td><td>...</td><td>#{range}</td></tr>"
163
+ end
164
+
165
+ def flush_changes(result, left_ln, right_ln)
166
+ x, left_ln, right_ln = get_diff_row(left_ln, right_ln)
167
+ result << x
168
+ @left.clear
169
+ @right.clear
170
+ return left_ln, right_ln
171
+ end
172
+
173
+ #
174
+ # helper for building the next row in the diff
175
+ #
176
+ def get_diff_row(left_ln, right_ln)
177
+ result = []
178
+ if @left.length > 0 or @right.length > 0
179
+ modified = (@last_op != ' ')
180
+
181
+ if modified
182
+ left_class = " class='r'"
183
+ right_class = " class='a'"
184
+ result << "<tbody class='mod'>"
185
+ else
186
+ left_class = right_class = ''
187
+ end
188
+
189
+ result << @left.map do |line|
190
+ x = "<tr#{left_class}>#{ln_cell(left_ln, 'l')}"
191
+
192
+ if modified
193
+ x += ln_cell(nil)
194
+ else
195
+ x += ln_cell(right_ln, 'r')
196
+ right_ln += 1
197
+ end
198
+
199
+ x += "<td>#{line}</td></tr>"
200
+
201
+ left_ln += 1
202
+
203
+ x
204
+ end
205
+
206
+ if modified
207
+ result << @right.map do |line|
208
+ x = "<tr#{right_class}>#{ln_cell(nil)}#{ln_cell(right_ln, 'r')}<td>#{line}</td></tr>"
209
+ right_ln += 1
210
+ x
211
+ end
212
+
213
+ result << "</tbody>"
214
+ end
215
+ end
216
+
217
+ return result.join("\n"), left_ln, right_ln
218
+ end
219
+
220
+ def ln_cell(ln, side = nil)
221
+ anchor = "f#{@filenum}#{side}#{ln}"
222
+ result = "<td class = 'ln'>"
223
+ result += "<a name='#{anchor}' href='##{anchor}'>" if ln
224
+ result += "#{ln}"
225
+ result += "</a>" if ln
226
+ result += "</td>"
227
+ result
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,14 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module DiffToHtml
3
+ class GitConverter < Converter
4
+ def file_header_pattern
5
+ /^diff --git.+/
6
+ end
7
+
8
+ def get_filename(file_diff)
9
+ match = (file_diff =~ / b\/(.+)/)
10
+ raise "not matched!" if !match
11
+ $1
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module DiffToHtml
3
+ class SvnConverter < Converter
4
+ def file_header_pattern
5
+ /^Index: .+/
6
+ end
7
+
8
+ def get_filename(header)
9
+ match = (header =~ /^Index: (.+)/) #if we use this pattern file_header_pattern files split doesn't work
10
+ raise "header '#{header}' not matched!" if !match
11
+ $1
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module DiffToHtml
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: diff_to_html
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Adam Doppelt
9
+ - Artem Vasiliev
10
+ - Mathias Gawlista
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2013-05-13 00:00:00.000000000 Z
15
+ dependencies: []
16
+ description: Generates HTML view of given unified diff
17
+ email: gawlista@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files:
21
+ - README.rdoc
22
+ files:
23
+ - README.rdoc
24
+ - lib/diff_to_html/converter.rb
25
+ - lib/diff_to_html/git_converter.rb
26
+ - lib/diff_to_html/svn_converter.rb
27
+ - lib/diff_to_html/version.rb
28
+ - lib/diff_to_html.rb
29
+ homepage: http://github.com/applicat/diff_to_html
30
+ licenses:
31
+ - MIT
32
+ post_install_message:
33
+ rdoc_options:
34
+ - --main
35
+ - README.rdoc
36
+ - --inline-source
37
+ - --charset=UTF-8
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.24
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Unified diff to HTML converter
58
+ test_files: []