diffed 0.0.3 → 0.0.4
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.
- data/diffed.gemspec +4 -2
- data/lib/diffed.rb +38 -137
- data/lib/diffed/version.rb +1 -1
- data/lib/models/line.rb +22 -0
- data/lib/models/section.rb +9 -0
- data/lib/parsers/unified.rb +120 -0
- metadata +9 -4
data/diffed.gemspec
CHANGED
@@ -8,8 +8,10 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.version = Diffed::VERSION
|
9
9
|
gem.authors = ["Jun-Dai Bates-Kobashigawa"]
|
10
10
|
gem.email = ["jundai@kurutta.net"]
|
11
|
-
gem.description = %q{This is a library for creating HTML from a unified diff string, } +
|
12
|
-
%q{
|
11
|
+
gem.description = %q{This is a library for creating HTML from a unified diff string, built specifically for the diff section } +
|
12
|
+
%q{output by "perforce describe -du" or "git show [commit SHA]", but with an eye towards solving a more general problem. } +
|
13
|
+
%q{It supports two modes: with inline styles or with CSS classes(which you can style yourself). Either mode outputs an HTML table }
|
14
|
+
%q{that you may want to include in a Web page or an HTML e-mail.}
|
13
15
|
gem.summary = %q{This is a library for creating HTML from a unified diff string}
|
14
16
|
gem.homepage = "http://github.com/Jun-Dai/diffed"
|
15
17
|
|
data/lib/diffed.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "diffed/version"
|
2
|
+
require "parsers/unified"
|
2
3
|
|
3
4
|
module Diffed
|
4
5
|
class Diff
|
@@ -6,19 +7,13 @@ module Diffed
|
|
6
7
|
parse(raw_diff.split(/\n/))
|
7
8
|
end
|
8
9
|
|
9
|
-
def as_html_table(
|
10
|
-
html = make_table_tag(
|
10
|
+
def as_html_table(use_inline_styles = true)
|
11
|
+
html = make_table_tag(use_inline_styles)
|
11
12
|
|
12
13
|
@sections.each do |section|
|
13
|
-
|
14
|
-
right_line_num = section.header.line_nums[:right][:from]
|
15
|
-
|
16
|
-
html << make_section_header_row(section.header, inline_styles)
|
14
|
+
html << format_section_header_row(section.header, use_inline_styles)
|
17
15
|
section.lines.each_with_index do |line, i|
|
18
|
-
html <<
|
19
|
-
|
20
|
-
left_line_num += 1 unless line.type == :right
|
21
|
-
right_line_num += 1 unless line.type == :left
|
16
|
+
html << format_code_line(line, use_inline_styles)
|
22
17
|
end
|
23
18
|
end
|
24
19
|
|
@@ -26,41 +21,34 @@ module Diffed
|
|
26
21
|
end
|
27
22
|
|
28
23
|
private
|
29
|
-
def
|
30
|
-
|
31
|
-
|
24
|
+
def format_code_line(line, use_inline_styles)
|
25
|
+
row = OutputRow.new(:code_line =>line)
|
26
|
+
|
27
|
+
if use_inline_styles
|
28
|
+
format_styled_row code_line_style(line), '#000', row
|
32
29
|
else
|
33
|
-
|
30
|
+
format_classed_row line.type.to_s, row
|
34
31
|
end
|
35
32
|
end
|
36
33
|
|
37
|
-
def
|
38
|
-
case line.type
|
39
|
-
when :left
|
40
|
-
format_styled_tr_line '#FDD', nil, left_line_num, ".", line.text
|
41
|
-
when :right
|
42
|
-
format_styled_tr_line '#DFD', nil, ".", right_line_num, line.text
|
43
|
-
when :both
|
44
|
-
format_styled_tr_line nil, nil, left_line_num, right_line_num, line.text
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def make_classed_tr_line(line, left_line_num, right_line_num)
|
34
|
+
def code_line_style(line)
|
49
35
|
case line.type
|
50
36
|
when :left
|
51
|
-
|
37
|
+
'#FDD'
|
52
38
|
when :right
|
53
|
-
|
39
|
+
'#DFD'
|
54
40
|
when :both
|
55
|
-
|
56
|
-
end
|
41
|
+
nil
|
42
|
+
end
|
57
43
|
end
|
58
|
-
|
59
|
-
def
|
60
|
-
|
61
|
-
|
44
|
+
|
45
|
+
def format_section_header_row(header, use_inline_styles)
|
46
|
+
row = OutputRow.new(:left => "...", :right => "...", :text => header)
|
47
|
+
|
48
|
+
if use_inline_styles
|
49
|
+
format_styled_row '#F0F0FF', '#888', row
|
62
50
|
else
|
63
|
-
|
51
|
+
format_classed_row 'section', row
|
64
52
|
end
|
65
53
|
end
|
66
54
|
|
@@ -74,119 +62,32 @@ module Diffed
|
|
74
62
|
%Q{<table #{table_attributes}>\n}
|
75
63
|
end
|
76
64
|
|
77
|
-
def
|
65
|
+
def format_styled_row(bg_color, text_color, row)
|
78
66
|
row_style = bg_color.nil? ? "" : %Q{ style="background-color: #{bg_color}"}
|
79
|
-
text_color = text_color
|
67
|
+
text_color = text_color
|
80
68
|
|
81
|
-
%Q{<tr#{row_style}><td style="border-left: 1px solid \#CCC">#{
|
69
|
+
%Q{<tr#{row_style}><td style="border-left: 1px solid \#CCC">#{row.left}</td><td style="border-left: 1px solid \#CCC">#{row.right}</td><td style="border-left: 1px solid \#CCC; border-right: 1px solid \#CCC; color: #{text_color}"><pre>#{row.text}</pre></td></tr>\n}
|
82
70
|
end
|
83
71
|
|
84
|
-
def
|
85
|
-
%Q{<tr class="#{css_class}"><td>#{
|
72
|
+
def format_classed_row(css_class, row)
|
73
|
+
%Q{<tr class="#{css_class}"><td>#{row.left}</td><td>#{row.left}</td><td><pre>#{row.text}</pre></td></tr>\n}
|
86
74
|
end
|
87
75
|
|
88
76
|
def parse(lines)
|
89
|
-
@sections =
|
90
|
-
curr_header, curr_lines, left_counter, right_counter = nil, [], 0, 0
|
91
|
-
|
92
|
-
lines.each do |line|
|
93
|
-
if DiffHeaderLine.is_header?(line)
|
94
|
-
unless curr_header.nil?
|
95
|
-
raise "Found a header while still processing a section! #{line}"
|
96
|
-
end
|
97
|
-
|
98
|
-
curr_header, curr_lines = DiffHeaderLine.new(line), []
|
99
|
-
elsif curr_header.nil?
|
100
|
-
# Do nothing. We haven't started yet.
|
101
|
-
# puts "Ignoring line: #{line}"
|
102
|
-
elsif line =~ /\/
|
103
|
-
if curr_lines.empty?
|
104
|
-
@sections.last.no_new_line = true
|
105
|
-
else
|
106
|
-
curr_lines.last.no_new_line = true
|
107
|
-
end
|
108
|
-
else
|
109
|
-
diff_line = DiffLine.new(line)
|
110
|
-
curr_lines << diff_line
|
111
|
-
left_counter += 1 if diff_line.left?
|
112
|
-
right_counter += 1 if diff_line.right?
|
113
|
-
|
114
|
-
if curr_header.section_complete? left_counter, right_counter
|
115
|
-
@sections << DiffSection.new(curr_header, curr_lines)
|
116
|
-
curr_header, curr_lines, left_counter, right_counter = nil, [], 0, 0
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
class DiffSection
|
124
|
-
attr_reader :header, :lines
|
125
|
-
|
126
|
-
def initialize(header_line, lines)
|
127
|
-
@header, @lines = header_line, lines
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
class DiffLine
|
132
|
-
attr_reader :type, :text, :no_new_line
|
133
|
-
|
134
|
-
def initialize(line)
|
135
|
-
if line.start_with? "-"
|
136
|
-
@type = :left
|
137
|
-
elsif line.start_with? "+"
|
138
|
-
@type = :right
|
139
|
-
elsif line.start_with? " "
|
140
|
-
@type = :both
|
141
|
-
else
|
142
|
-
raise "Unparseable line: #{line}"
|
143
|
-
end
|
144
|
-
|
145
|
-
@text = line
|
146
|
-
@no_new_line = false
|
147
|
-
end
|
148
|
-
|
149
|
-
def left?
|
150
|
-
@type == :left || @type == :both
|
77
|
+
@sections = UnifiedDiffParser.new(lines).parse!.sections
|
151
78
|
end
|
152
79
|
|
153
|
-
|
154
|
-
|
155
|
-
end
|
156
|
-
|
157
|
-
def no_new_line= bool
|
158
|
-
# mutability like this kind of sucks, but this one's a pain to avoid.
|
159
|
-
@no_new_line = true
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
class DiffHeaderLine
|
164
|
-
attr_reader :line_nums, :text
|
165
|
-
|
166
|
-
def initialize(line)
|
167
|
-
@text = line
|
80
|
+
class OutputRow
|
81
|
+
attr_reader :left, :right, :text
|
168
82
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
83
|
+
def initialize(params = {})
|
84
|
+
if params[:code_line].nil?
|
85
|
+
@left, @right, @text = params[:left], params[:right], params[:text]
|
86
|
+
else
|
87
|
+
line = params[:code_line]
|
88
|
+
@left, @right, @text = line.left_line_num, line.right_line_num, line.text
|
89
|
+
end
|
175
90
|
end
|
176
|
-
|
177
|
-
if @line_nums[:right][:lines] > @line_nums[:left][:lines]
|
178
|
-
@side_to_count = :right
|
179
|
-
else
|
180
|
-
@side_to_count = :left
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def self.is_header?(line)
|
185
|
-
line.start_with? "@@ "
|
186
|
-
end
|
187
|
-
|
188
|
-
def section_complete?(left_count, right_count)
|
189
|
-
left_count >= @line_nums[:left][:lines] && right_count >= @line_nums[:right][:lines]
|
190
91
|
end
|
191
92
|
end
|
192
93
|
end
|
data/lib/diffed/version.rb
CHANGED
data/lib/models/line.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Diffed
|
2
|
+
class Line
|
3
|
+
attr_reader :type, :text, :left_line_num, :right_line_num, :no_newline
|
4
|
+
|
5
|
+
def initialize(type, text, left_line_num, right_line_num)
|
6
|
+
@type, @text, @left_line_num, @right_line_num, @no_newline = type, text, left_line_num, right_line_num, false
|
7
|
+
end
|
8
|
+
|
9
|
+
def left?
|
10
|
+
@type == :left || @type == :both
|
11
|
+
end
|
12
|
+
|
13
|
+
def right?
|
14
|
+
@type == :right || @type == :both
|
15
|
+
end
|
16
|
+
|
17
|
+
def no_newline= bool
|
18
|
+
# mutability like this kind of sucks, but this one's a pain to avoid.
|
19
|
+
@no_newline = bool
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'models/line'
|
2
|
+
require 'models/section'
|
3
|
+
|
4
|
+
module Diffed
|
5
|
+
class UnifiedDiffParser
|
6
|
+
def initialize(lines)
|
7
|
+
@lines, @sections = lines, []
|
8
|
+
reset_section!
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse!
|
12
|
+
raise "Already parsed into #{@sections.length} sections." unless @sections.empty?
|
13
|
+
|
14
|
+
@lines.each do |line|
|
15
|
+
if HeaderLineParser.is_header?(line)
|
16
|
+
unless @curr_header.nil?
|
17
|
+
raise "Found a header while still processing a section! #{line}"
|
18
|
+
end
|
19
|
+
|
20
|
+
@curr_header, @curr_lines = HeaderLineParser.new(line), []
|
21
|
+
elsif @curr_header.nil?
|
22
|
+
# Do nothing. We haven't started yet.
|
23
|
+
# puts "Ignoring line: #{line}"
|
24
|
+
elsif line =~ /\/
|
25
|
+
if @curr_lines.empty?
|
26
|
+
@sections.last.lines.last.no_newline = true
|
27
|
+
else
|
28
|
+
@curr_lines.last.no_newline = true
|
29
|
+
end
|
30
|
+
else
|
31
|
+
line_parser = LineParser.new(line)
|
32
|
+
@left_counter, @right_counter = line_parser.increment(@left_counter, @right_counter)
|
33
|
+
@curr_lines << line_parser.line(@left_counter, @right_counter)
|
34
|
+
|
35
|
+
if @curr_header.section_complete? @left_counter, @right_counter
|
36
|
+
@sections << Section.new(@curr_header.line, @curr_lines)
|
37
|
+
reset_section!
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def sections
|
46
|
+
raise "Not parsed. Call parse! before attempting to read sections from the parser." if @sections.empty?
|
47
|
+
@sections
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def reset_section!
|
52
|
+
@curr_header, @curr_lines, @left_counter, @right_counter = nil, [], 0, 0
|
53
|
+
end
|
54
|
+
|
55
|
+
class LineParser
|
56
|
+
def initialize(line_text)
|
57
|
+
@line_text = line_text
|
58
|
+
|
59
|
+
if line_text.start_with? "-"
|
60
|
+
@type = :left
|
61
|
+
elsif line_text.start_with? "+"
|
62
|
+
@type = :right
|
63
|
+
elsif line_text.start_with? " "
|
64
|
+
@type = :both
|
65
|
+
else
|
66
|
+
raise "Unparseable line: #{line}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def increment(left_counter, right_counter)
|
71
|
+
case @type
|
72
|
+
when :left
|
73
|
+
[left_counter + 1, right_counter]
|
74
|
+
when :right
|
75
|
+
[left_counter, right_counter + 1]
|
76
|
+
when :both
|
77
|
+
[left_counter + 1, right_counter + 1]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def line(left_counter, right_counter)
|
82
|
+
Line.new(@type, @line, left_counter, right_counter)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class HeaderLineParser
|
87
|
+
attr_reader :line_nums, :text
|
88
|
+
|
89
|
+
def initialize(line)
|
90
|
+
@text = line
|
91
|
+
|
92
|
+
if line =~ /^@@ -(\d+),(\d+) \+(\d+),(\d+) @@/
|
93
|
+
@line_nums = {left: {from: $1.to_i, lines: $2.to_i}, right: {from: $3.to_i, lines: $4.to_i}}
|
94
|
+
elsif line =~ /^@@ -(\d+) \+(\d+) @@/
|
95
|
+
@line_nums = {left: {from: $1.to_i, lines: $1.to_i}, right: {from: $2.to_i, lines: $2.to_i}}
|
96
|
+
else
|
97
|
+
raise "Unparseable header line: #{line}"
|
98
|
+
end
|
99
|
+
|
100
|
+
if @line_nums[:right][:lines] > @line_nums[:left][:lines]
|
101
|
+
@side_to_count = :right
|
102
|
+
else
|
103
|
+
@side_to_count = :left
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.is_header?(line)
|
108
|
+
line.start_with? "@@ "
|
109
|
+
end
|
110
|
+
|
111
|
+
def section_complete?(left_count, right_count)
|
112
|
+
left_count >= @line_nums[:left][:lines] && right_count >= @line_nums[:right][:lines]
|
113
|
+
end
|
114
|
+
|
115
|
+
def line
|
116
|
+
@text
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: diffed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -43,9 +43,11 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
-
description: This is a library for creating HTML from a unified diff string, built
|
47
|
-
specifically for the diff section output by "perforce describe -du"
|
48
|
-
eye towards solving a more general problem.
|
46
|
+
description: ! 'This is a library for creating HTML from a unified diff string, built
|
47
|
+
specifically for the diff section output by "perforce describe -du" or "git show
|
48
|
+
[commit SHA]", but with an eye towards solving a more general problem. It supports
|
49
|
+
two modes: with inline styles or with CSS classes(which you can style yourself). Either
|
50
|
+
mode outputs an HTML table '
|
49
51
|
email:
|
50
52
|
- jundai@kurutta.net
|
51
53
|
executables: []
|
@@ -61,6 +63,9 @@ files:
|
|
61
63
|
- diffed.gemspec
|
62
64
|
- lib/diffed.rb
|
63
65
|
- lib/diffed/version.rb
|
66
|
+
- lib/models/line.rb
|
67
|
+
- lib/models/section.rb
|
68
|
+
- lib/parsers/unified.rb
|
64
69
|
- spec/diffed_spec.rb
|
65
70
|
- spec/spec_helper.rb
|
66
71
|
- testdata/diff1.classed.html
|