pretty_diff 0.9.3 → 2.0.0
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.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +41 -6
- data/Rakefile +0 -1
- data/VERSION +1 -1
- data/lib/pretty_diff.rb +5 -6
- data/lib/pretty_diff/abstract_generator.rb +51 -0
- data/lib/pretty_diff/basic_generator.rb +72 -0
- data/lib/pretty_diff/chunk.rb +24 -41
- data/lib/pretty_diff/diff.rb +75 -41
- data/lib/pretty_diff/encoding.rb +30 -0
- data/lib/pretty_diff/line.rb +30 -51
- data/lib/pretty_diff/line_numbers.rb +47 -67
- data/pretty_diff.gemspec +33 -18
- data/test/abstract_generator_test.rb +65 -0
- data/test/basic_generator_test.rb +29 -0
- data/test/chunk_test.rb +13 -30
- data/test/diff_test.rb +50 -19
- data/test/encoding_test.rb +35 -0
- data/test/fixtures/blank.diff +0 -0
- data/test/fixtures/blank.diff.html +1 -0
- data/test/fixtures/chinese-gbk-crlf +5 -0
- data/test/fixtures/cp1251.diff +13 -0
- data/test/fixtures/csharp.diff +12 -0
- data/test/fixtures/csharp.diff.html +19 -0
- data/test/{data → fixtures}/first.diff +0 -0
- data/test/fixtures/first.diff.html +85 -0
- data/test/{data → fixtures}/second.diff +0 -0
- data/test/fixtures/single_line.diff +7 -0
- data/test/fixtures/single_line.diff.html +4 -0
- data/test/fixtures/text.diff +8 -0
- data/test/fixtures/text.diff.html +13 -0
- data/test/fixtures/windows-cp1251-lf +6 -0
- data/test/helper.rb +10 -8
- data/test/line_numbers_test.rb +25 -0
- data/test/line_test.rb +26 -37
- metadata +81 -75
- data/lib/pretty_diff/html_generators/chunk_generator.rb +0 -41
- data/lib/pretty_diff/html_generators/diff_generator.rb +0 -13
- data/lib/pretty_diff/html_generators/line_generator.rb +0 -45
- data/lib/pretty_diff/html_generators/line_numbers_generator.rb +0 -36
- data/test/html_generator_test.rb +0 -39
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2b6469b15bc93eef7d745c13cb407cf7ed005b34
|
4
|
+
data.tar.gz: 30836b4814f14cf92ea0032be12daf987259b311
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8d4df170603a0a29963cc321bd292110107374e0f37bd1ae89cb76254cadcf10d52f0f956cc20987d81eb83cf2687dcbc4761cd3ff16aed0bc6c43a93a071431
|
7
|
+
data.tar.gz: 92f7e156c4e5fbfcff6b23f749c656a4d9c29d03b4344d321df1addfc0a8e08239f7b71c56f0985a194d417667924bb1da6e603c916bf1b796aebe5cd944f455
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,16 +1,50 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
+
addressable (2.3.5)
|
4
5
|
builder (3.1.4)
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
charlock_holmes (0.6.9.1)
|
7
|
+
descendants_tracker (0.0.3)
|
8
|
+
faraday (0.9.0)
|
9
|
+
multipart-post (>= 1.2, < 3)
|
10
|
+
git (1.2.6)
|
11
|
+
github_api (0.11.2)
|
12
|
+
addressable (~> 2.3)
|
13
|
+
descendants_tracker (~> 0.0.1)
|
14
|
+
faraday (~> 0.8, < 0.10)
|
15
|
+
hashie (>= 1.2)
|
16
|
+
multi_json (>= 1.7.5, < 2.0)
|
17
|
+
nokogiri (~> 1.6.0)
|
18
|
+
oauth2
|
19
|
+
hashie (2.0.5)
|
20
|
+
highline (1.6.20)
|
21
|
+
jeweler (2.0.1)
|
22
|
+
builder
|
23
|
+
bundler (>= 1.0)
|
8
24
|
git (>= 1.2.5)
|
25
|
+
github_api
|
26
|
+
highline (>= 1.6.15)
|
27
|
+
nokogiri (>= 1.5.10)
|
9
28
|
rake
|
10
29
|
rdoc
|
11
|
-
json (1.
|
12
|
-
|
13
|
-
|
30
|
+
json (1.8.1)
|
31
|
+
jwt (0.1.11)
|
32
|
+
multi_json (>= 1.5)
|
33
|
+
mini_portile (0.5.2)
|
34
|
+
multi_json (1.8.4)
|
35
|
+
multi_xml (0.5.5)
|
36
|
+
multipart-post (2.0.0)
|
37
|
+
nokogiri (1.6.1)
|
38
|
+
mini_portile (~> 0.5.0)
|
39
|
+
oauth2 (0.9.3)
|
40
|
+
faraday (>= 0.8, < 0.10)
|
41
|
+
jwt (~> 0.1.8)
|
42
|
+
multi_json (~> 1.3)
|
43
|
+
multi_xml (~> 0.5)
|
44
|
+
rack (~> 1.2)
|
45
|
+
rack (1.5.2)
|
46
|
+
rake (10.1.1)
|
47
|
+
rdoc (4.1.1)
|
14
48
|
json (~> 1.4)
|
15
49
|
|
16
50
|
PLATFORMS
|
@@ -18,4 +52,5 @@ PLATFORMS
|
|
18
52
|
|
19
53
|
DEPENDENCIES
|
20
54
|
builder
|
55
|
+
charlock_holmes (~> 0.6)
|
21
56
|
jeweler
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
2.0.0
|
data/lib/pretty_diff.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
|
-
module PrettyDiff
|
1
|
+
module PrettyDiff
|
2
|
+
class InvalidGenerator < Exception; end
|
2
3
|
end
|
3
4
|
|
4
5
|
def require_local(suffix)
|
5
6
|
require(File.expand_path(File.join(File.dirname(__FILE__), suffix)))
|
6
7
|
end
|
7
8
|
|
9
|
+
require_local 'pretty_diff/encoding'
|
8
10
|
require_local 'pretty_diff/diff'
|
9
11
|
require_local 'pretty_diff/chunk'
|
10
12
|
require_local 'pretty_diff/line_numbers'
|
11
13
|
require_local 'pretty_diff/line'
|
12
|
-
|
13
|
-
require_local 'pretty_diff/
|
14
|
-
require_local 'pretty_diff/html_generators/chunk_generator'
|
15
|
-
require_local 'pretty_diff/html_generators/line_generator'
|
16
|
-
require_local 'pretty_diff/html_generators/line_numbers_generator'
|
14
|
+
require_local 'pretty_diff/abstract_generator'
|
15
|
+
require_local 'pretty_diff/basic_generator'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module PrettyDiff
|
4
|
+
class AbstractGenerator
|
5
|
+
|
6
|
+
def self.[](tgt)
|
7
|
+
new(tgt)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(tgt)
|
11
|
+
target_name = class_to_target_name(tgt.class)
|
12
|
+
instance_variable_set("@#{target_name}", tgt)
|
13
|
+
self.class.class_eval do
|
14
|
+
attr_accessor(target_name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate
|
19
|
+
raise 'Not implemented!'
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def tag(name, options=nil, open=false)
|
25
|
+
"<#{name}#{tag_options(options) if options}#{open ? ">" : " />"}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def content_tag(name, options=nil, &block)
|
29
|
+
"<#{name}#{tag_options(options) if options}>#{block.call}</#{name}>"
|
30
|
+
end
|
31
|
+
|
32
|
+
def tag_options(options)
|
33
|
+
return if options.empty?
|
34
|
+
attrs = []
|
35
|
+
options.each_pair do |key, value|
|
36
|
+
unless value.nil?
|
37
|
+
attrs << %(#{key}="#{h(value)}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
" #{attrs.sort * ' '}" unless attrs.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
def h(t)
|
44
|
+
CGI.escapeHTML(t)
|
45
|
+
end
|
46
|
+
|
47
|
+
def class_to_target_name(c)
|
48
|
+
c.to_s.split('::').last.gsub(/Gen(erator)?\z/, '').gsub(/(.)([A-Z])/,'\1_\2').downcase
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module PrettyDiff
|
2
|
+
class BasicGenerator < AbstractGenerator
|
3
|
+
|
4
|
+
def generate
|
5
|
+
content_tag :div, :class => 'diff' do
|
6
|
+
diff.chunks.map{|c| ChunkGen[c].generate}.join('')
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class ChunkGen < PrettyDiff::AbstractGenerator
|
11
|
+
def generate
|
12
|
+
content_tag :div, :class => 'diff-chunk' do
|
13
|
+
numbers + code
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def numbers
|
20
|
+
LineNumbersGen[chunk.line_numbers].generate
|
21
|
+
end
|
22
|
+
|
23
|
+
def code
|
24
|
+
content_tag :div, :class => 'diff-code' do
|
25
|
+
content_tag :pre do
|
26
|
+
chunk.lines.map{|l| LineGen[l].generate }.join("\n")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class LineNumbersGen < PrettyDiff::AbstractGenerator
|
33
|
+
def generate
|
34
|
+
column(line_numbers.left_column) + column(line_numbers.right_column)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def column(clmn)
|
40
|
+
content_tag :div, :class => 'diff-line-nums' do
|
41
|
+
content_tag :pre do
|
42
|
+
fill_whitespace(clmn).join("\n")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def fill_whitespace(numbers)
|
48
|
+
[].tap do |filled|
|
49
|
+
numbers.each do |v|
|
50
|
+
filled << (v.nil? ? ' ' : v)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class LineGen < PrettyDiff::AbstractGenerator
|
57
|
+
def generate
|
58
|
+
if line.added?
|
59
|
+
content_tag(:span, :class => 'diff-line-add') do
|
60
|
+
h(line.contents)
|
61
|
+
end
|
62
|
+
elsif line.deleted?
|
63
|
+
content_tag(:span, :class => 'diff-line-del') do
|
64
|
+
h(line.contents)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
h(line.contents)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/pretty_diff/chunk.rb
CHANGED
@@ -1,49 +1,32 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
class PrettyDiff::Chunk #:nodoc:
|
5
|
-
attr_reader :diff, :meta_info, :input, :lines
|
1
|
+
module PrettyDiff
|
2
|
+
class Chunk
|
3
|
+
attr_reader :diff, :meta_info, :lines, :contents
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# Generate HTML presentation for a Chunk. Return a string.
|
14
|
-
def to_html
|
15
|
-
# We have to find lines before we can call line numbers methods.
|
16
|
-
find_lines!
|
17
|
-
generator.generate
|
18
|
-
end
|
19
|
-
|
20
|
-
# Return LineNumbers object that represents two columns of numbers
|
21
|
-
# that will be displayed on the left of the HTML presentation.
|
22
|
-
#
|
23
|
-
# IMPORTANT! Before calling this method it's essential to call "find_lines!" first,
|
24
|
-
# otherwise the array will be empty.
|
25
|
-
def line_numbers
|
26
|
-
@_line_numbers ||= PrettyDiff::LineNumbers.new(diff, meta_info)
|
27
|
-
end
|
5
|
+
def initialize(diff, meta_info, contents)
|
6
|
+
@diff = diff
|
7
|
+
@meta_info = meta_info
|
8
|
+
@contents = contents
|
9
|
+
@lines = find_lines
|
10
|
+
end
|
28
11
|
|
29
|
-
|
12
|
+
def line_numbers
|
13
|
+
@_line_numbers ||= LineNumbers.new(diff, meta_info)
|
14
|
+
end
|
30
15
|
|
31
|
-
|
32
|
-
@_generator ||= PrettyDiff::ChunkGenerator.new(self)
|
33
|
-
end
|
16
|
+
private
|
34
17
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
18
|
+
def find_lines
|
19
|
+
[].tap do |lines|
|
20
|
+
contents.split(/\r?\n|\r/).each do |line_str|
|
21
|
+
line = Line.new(self, line_str)
|
22
|
+
next if line.ignored?
|
23
|
+
lines << line
|
24
|
+
line_numbers.act_on_line(line)
|
25
|
+
line.left_number = line_numbers.left_column.last
|
26
|
+
line.right_number = line_numbers.right_column.last
|
27
|
+
end
|
45
28
|
end
|
46
29
|
end
|
47
|
-
end
|
48
30
|
|
31
|
+
end
|
49
32
|
end
|
data/lib/pretty_diff/diff.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'cgi'
|
2
|
-
|
3
1
|
#
|
4
2
|
# Main class to interact with. In fact this is the only class you should interact with
|
5
3
|
# when using the library.
|
@@ -11,52 +9,88 @@ require 'cgi'
|
|
11
9
|
# Keep in mind that Diff will automatically escape all HTML tags from the intput string
|
12
10
|
# so that it doesn't interfere with the output.
|
13
11
|
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
attr_reader :input, :options
|
18
|
-
|
19
|
-
# Create new Diff object.
|
20
|
-
# Accept a String in unified diff format and options hash.
|
21
|
-
# Currrent options:
|
22
|
-
# * wrap_lines -- wrap each line in code block with <div> element.
|
23
|
-
def initialize(unified_diff, options={})
|
24
|
-
@input = escape_html(unified_diff)
|
25
|
-
@options = options
|
26
|
-
end
|
12
|
+
module PrettyDiff
|
13
|
+
class Diff
|
14
|
+
CHUNK_REGEXP = /^@@ .+? @@.*\n?$/
|
27
15
|
|
28
|
-
|
29
|
-
def to_html
|
30
|
-
generator.generate
|
31
|
-
end
|
16
|
+
attr_reader :unified_diff, :generator, :out_encoding
|
32
17
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
18
|
+
#
|
19
|
+
# Create new Diff object.
|
20
|
+
# Accept a String in unified diff format and options hash.
|
21
|
+
# Currrent options:
|
22
|
+
# * generator -- your own custom implementation of HTML generator. Will use BasicGenerator by default.
|
23
|
+
# * out_encoding -- convert encoding of diffs to the specififed encoding. utf-8 by default.
|
24
|
+
#
|
25
|
+
def initialize(unified_diff, options={})
|
26
|
+
@unified_diff = unified_diff
|
27
|
+
@options = options
|
28
|
+
@out_encoding =
|
29
|
+
@generator = validate_generator(options[:generator]) || BasicGenerator
|
30
|
+
@out_encoding = options[:out_encoding] || 'utf-8'
|
31
|
+
end
|
37
32
|
|
38
|
-
|
33
|
+
def metadata
|
34
|
+
@_metadata ||= begin
|
35
|
+
''.tap do |result|
|
36
|
+
unified_diff.each_line do |l|
|
37
|
+
result << l
|
38
|
+
break if l =~ /^\+\+\+ /
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
39
43
|
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
def contents
|
45
|
+
# We have to strip metadata from the rest of the diff
|
46
|
+
# to enforce encoding. It's not uncommon for metadata to be in Unicode
|
47
|
+
# while the diff itself is in some other encoding.
|
48
|
+
@_contents ||= enforce_encoding(unified_diff.lines[metadata.lines.size..-1].join(''))
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_html
|
52
|
+
generator.new(self).generate
|
53
|
+
end
|
54
|
+
|
55
|
+
def chunks
|
56
|
+
@_chunks ||= find_chunks
|
57
|
+
end
|
43
58
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
59
|
+
private
|
60
|
+
|
61
|
+
def find_chunks
|
62
|
+
chunks_meta = contents.scan(CHUNK_REGEXP)
|
63
|
+
[].tap do |chunks|
|
64
|
+
split = contents.split(CHUNK_REGEXP)
|
65
|
+
split.shift
|
66
|
+
split.each_with_index do |lines, index|
|
67
|
+
chunks << Chunk.new(self, chunks_meta[index], cleanup(lines))
|
68
|
+
end
|
54
69
|
end
|
55
70
|
end
|
56
|
-
end
|
57
71
|
|
58
|
-
|
59
|
-
|
60
|
-
|
72
|
+
def validate_generator(gen)
|
73
|
+
if valid_generator?(gen)
|
74
|
+
gen
|
75
|
+
else
|
76
|
+
raise InvalidGenerator, "#{gen.inspect} is not a valid PrettyDiff generator"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def valid_generator?(generator)
|
81
|
+
!generator.nil? &&
|
82
|
+
generator.kind_of?(Class) &&
|
83
|
+
generator.ancestors.include?(PrettyDiff::AbstractGenerator) &&
|
84
|
+
generator.instance_methods.include?(:generate)
|
85
|
+
end
|
86
|
+
|
87
|
+
def enforce_encoding(text)
|
88
|
+
Encoding.enforce(out_encoding, text)
|
89
|
+
end
|
61
90
|
|
91
|
+
def cleanup(line)
|
92
|
+
line[1..-1]
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
62
96
|
end
|