diff-lcs 1.1.2 → 1.1.3
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/.gemtest +0 -0
- data/{ChangeLog → History.rdoc} +23 -15
- data/License.rdoc +38 -0
- data/Manifest.txt +27 -0
- data/README.rdoc +72 -0
- data/Rakefile +16 -106
- data/bin/htmldiff +21 -101
- data/bin/ldiff +2 -41
- data/diff-lcs.gemspec +51 -0
- data/docs/COPYING.txt +340 -0
- data/docs/artistic.html +289 -0
- data/lib/diff-lcs.rb +5 -0
- data/lib/diff/lcs.rb +471 -471
- data/lib/diff/lcs/array.rb +1 -1
- data/lib/diff/lcs/block.rb +1 -1
- data/lib/diff/lcs/callbacks.rb +1 -1
- data/lib/diff/lcs/change.rb +1 -1
- data/lib/diff/lcs/htmldiff.rb +151 -0
- data/lib/diff/lcs/hunk.rb +1 -16
- data/lib/diff/lcs/ldiff.rb +37 -53
- data/lib/diff/lcs/string.rb +1 -1
- data/spec/diff_spec.rb +35 -0
- data/spec/lcs_spec.rb +36 -0
- data/spec/patch_spec.rb +390 -0
- data/spec/sdiff_spec.rb +204 -0
- data/spec/spec_helper.rb +284 -0
- data/spec/traverse_balanced_spec.rb +286 -0
- data/spec/traverse_sequences_spec.rb +83 -0
- metadata +136 -58
- data/Install +0 -6
- data/README +0 -76
- data/tests/00test.rb +0 -626
data/lib/diff/lcs/array.rb
CHANGED
data/lib/diff/lcs/block.rb
CHANGED
data/lib/diff/lcs/callbacks.rb
CHANGED
data/lib/diff/lcs/change.rb
CHANGED
@@ -0,0 +1,151 @@
|
|
1
|
+
# -*- ruby encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
class Diff::LCS::HTMLDiff
|
6
|
+
class << self
|
7
|
+
attr_accessor :can_expand_tabs #:nodoc:
|
8
|
+
end
|
9
|
+
self.can_expand_tabs = true
|
10
|
+
|
11
|
+
class Callbacks
|
12
|
+
attr_accessor :output
|
13
|
+
attr_accessor :match_class
|
14
|
+
attr_accessor :only_a_class
|
15
|
+
attr_accessor :only_b_class
|
16
|
+
|
17
|
+
def initialize(output, options = {})
|
18
|
+
@output = output
|
19
|
+
options ||= {}
|
20
|
+
|
21
|
+
@match_class = options[:match_class] || "match"
|
22
|
+
@only_a_class = options[:only_a_class] || "only_a"
|
23
|
+
@only_b_class = options[:only_b_class] || "only_b"
|
24
|
+
end
|
25
|
+
|
26
|
+
def htmlize(element, css_class)
|
27
|
+
element = " " if element.empty?
|
28
|
+
%Q|<pre class="#{__send__(css_class)}">#{element}</pre>\n|
|
29
|
+
end
|
30
|
+
private :htmlize
|
31
|
+
|
32
|
+
# This will be called with both lines are the same
|
33
|
+
def match(event)
|
34
|
+
@output << htmlize(event.old_element, :match_class)
|
35
|
+
end
|
36
|
+
|
37
|
+
# This will be called when there is a line in A that isn't in B
|
38
|
+
def discard_a(event)
|
39
|
+
@output << htmlize(event.old_element, :only_a_class)
|
40
|
+
end
|
41
|
+
|
42
|
+
# This will be called when there is a line in B that isn't in A
|
43
|
+
def discard_b(event)
|
44
|
+
@output << htmlize(event.new_element, :only_b_class)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
DEFAULT_OPTIONS = {
|
49
|
+
:expand_tabs => nil,
|
50
|
+
:output => nil,
|
51
|
+
:css => nil,
|
52
|
+
:title => nil,
|
53
|
+
}
|
54
|
+
|
55
|
+
DEFAULT_CSS = <<-CSS
|
56
|
+
body { margin: 0; }
|
57
|
+
.diff
|
58
|
+
{
|
59
|
+
border: 1px solid black;
|
60
|
+
margin: 1em 2em;
|
61
|
+
}
|
62
|
+
p
|
63
|
+
{
|
64
|
+
margin-left: 2em;
|
65
|
+
}
|
66
|
+
pre
|
67
|
+
{
|
68
|
+
padding-left: 1em;
|
69
|
+
margin: 0;
|
70
|
+
font-family: Inconsolata, Consolas, Lucida, Courier, monospaced;
|
71
|
+
white-space: pre;
|
72
|
+
}
|
73
|
+
.match { }
|
74
|
+
.only_a
|
75
|
+
{
|
76
|
+
background-color: #fdd;
|
77
|
+
color: red;
|
78
|
+
text-decoration: line-through;
|
79
|
+
}
|
80
|
+
.only_b
|
81
|
+
{
|
82
|
+
background-color: #ddf;
|
83
|
+
color: blue;
|
84
|
+
border-left: 3px solid blue
|
85
|
+
}
|
86
|
+
h1 { margin-left: 2em; }
|
87
|
+
CSS
|
88
|
+
|
89
|
+
def initialize(left, right, options = nil)
|
90
|
+
@left = left
|
91
|
+
@right = right
|
92
|
+
@options = options
|
93
|
+
|
94
|
+
@options = DEFAULT_OPTIONS.dup if @options.nil?
|
95
|
+
end
|
96
|
+
|
97
|
+
def verify_options
|
98
|
+
@options[:expand_tabs] ||= 4
|
99
|
+
@options[:expand_tabs] = 4 if @options[:expand_tabs] < 0
|
100
|
+
|
101
|
+
@options[:output] ||= $stdout
|
102
|
+
|
103
|
+
@options[:css] ||= DEFAULT_CSS.dup
|
104
|
+
|
105
|
+
@options[:title] ||= "diff"
|
106
|
+
end
|
107
|
+
private :verify_options
|
108
|
+
|
109
|
+
attr_reader :options
|
110
|
+
|
111
|
+
def run
|
112
|
+
verify_options
|
113
|
+
|
114
|
+
if @options[:expand_tabs] > 0 && self.class.can_expand_tabs
|
115
|
+
formatter = Text::Format.new
|
116
|
+
formatter.tabstop = @options[:expand_tabs]
|
117
|
+
|
118
|
+
@left = left.map { |line| formatter.expand(line.chomp) }
|
119
|
+
@right = right.map { |line| formatter.expand(line.chomp) }
|
120
|
+
end
|
121
|
+
|
122
|
+
@left.map! { |line| CGI.escapeHTML(line.chomp) }
|
123
|
+
@right.map! { |line| CGI.escapeHTML(line.chomp) }
|
124
|
+
|
125
|
+
@options[:output] << <<-OUTPUT
|
126
|
+
<html>
|
127
|
+
<head>
|
128
|
+
<title>#{@options[:title]}</title>
|
129
|
+
<style type="text/css">
|
130
|
+
#{@options[:css]}
|
131
|
+
</style>
|
132
|
+
</head>
|
133
|
+
<body>
|
134
|
+
<h1>#{@options[:title]}</h1>
|
135
|
+
<p>Legend: <span class="only_a">Only in Old</span>
|
136
|
+
<span class="only_b">Only in New</span></p>
|
137
|
+
<div class="diff">
|
138
|
+
OUTPUT
|
139
|
+
|
140
|
+
callbacks = Callbacks.new(@options[:output])
|
141
|
+
Diff::LCS.traverse_sequences(@left, @right, callbacks)
|
142
|
+
|
143
|
+
@options[:output] << <<-OUTPUT
|
144
|
+
</div>
|
145
|
+
</body>
|
146
|
+
</html>
|
147
|
+
OUTPUT
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# vim: ft=ruby
|
data/lib/diff/lcs/hunk.rb
CHANGED
@@ -1,19 +1,3 @@
|
|
1
|
-
#! /usr/env/bin ruby
|
2
|
-
#--
|
3
|
-
# Copyright 2004 Austin Ziegler <diff-lcs@halostatue.ca>
|
4
|
-
# adapted from:
|
5
|
-
# Algorithm::Diff (Perl) by Ned Konz <perl@bike-nomad.com>
|
6
|
-
# Smalltalk by Mario I. Wolczko <mario@wolczko.com>
|
7
|
-
# implements McIlroy-Hunt diff algorithm
|
8
|
-
#
|
9
|
-
# This program is free software. It may be redistributed and/or modified under
|
10
|
-
# the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
|
11
|
-
# Ruby licence.
|
12
|
-
#
|
13
|
-
# $Id: hunk.rb,v 1.2 2004/08/08 20:33:09 austin Exp $
|
14
|
-
#++
|
15
|
-
# Contains Diff::LCS::Hunk for bin/ldiff.
|
16
|
-
|
17
1
|
require 'diff/lcs/block'
|
18
2
|
|
19
3
|
# A Hunk is a group of Blocks which overlap because of the context
|
@@ -66,6 +50,7 @@ class Diff::LCS::Hunk
|
|
66
50
|
# Change the "start" and "end" fields to note that context should be added
|
67
51
|
# to this hunk
|
68
52
|
attr_accessor :flag_context
|
53
|
+
undef :flag_context=
|
69
54
|
def flag_context=(context) #:nodoc:
|
70
55
|
return if context.nil? or context.zero?
|
71
56
|
|
data/lib/diff/lcs/ldiff.rb
CHANGED
@@ -33,7 +33,7 @@ require 'diff/lcs/hunk'
|
|
33
33
|
module Diff::LCS::Ldiff
|
34
34
|
BANNER = <<-COPYRIGHT
|
35
35
|
ldiff #{Diff::LCS::VERSION}
|
36
|
-
Copyright
|
36
|
+
Copyright 2004-2011 Austin Ziegler
|
37
37
|
|
38
38
|
Part of Diff::LCS.
|
39
39
|
http://rubyforge.org/projects/ruwiki/
|
@@ -42,67 +42,51 @@ ldiff #{Diff::LCS::VERSION}
|
|
42
42
|
|
43
43
|
This program is free software. It may be redistributed and/or modified under
|
44
44
|
the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
|
45
|
-
|
45
|
+
MIT licence.
|
46
46
|
|
47
|
-
$Id
|
47
|
+
$Id$
|
48
48
|
COPYRIGHT
|
49
|
-
|
49
|
+
|
50
50
|
class << self
|
51
51
|
attr_reader :format, :lines #:nodoc:
|
52
52
|
attr_reader :file_old, :file_new #:nodoc:
|
53
53
|
attr_reader :data_old, :data_new #:nodoc:
|
54
54
|
|
55
55
|
def run(args, input = $stdin, output = $stdout, error = $stderr) #:nodoc:
|
56
|
+
@binary = nil
|
57
|
+
|
56
58
|
args.options do |o|
|
57
59
|
o.banner = "Usage: #{File.basename($0)} [options] oldfile newfile"
|
58
60
|
o.separator ""
|
59
|
-
o.on('-c',
|
60
|
-
'Displays a context diff with 3 lines of',
|
61
|
-
'context.') do |ctx|
|
61
|
+
o.on('-c', 'Displays a context diff with 3 lines of', 'context.') do |ctx|
|
62
62
|
@format = :context
|
63
63
|
@lines = 3
|
64
64
|
end
|
65
|
-
o.on('-C', '--context [LINES]', Numeric,
|
66
|
-
'Displays a context diff with LINES lines',
|
67
|
-
'of context. Default 3 lines.') do |ctx|
|
65
|
+
o.on('-C', '--context [LINES]', Numeric, 'Displays a context diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
|
68
66
|
@format = :context
|
69
67
|
@lines = ctx || 3
|
70
68
|
end
|
71
|
-
o.on('-u',
|
72
|
-
'Displays a unified diff with 3 lines of',
|
73
|
-
'context.') do |ctx|
|
69
|
+
o.on('-u', 'Displays a unified diff with 3 lines of', 'context.') do |ctx|
|
74
70
|
@format = :unified
|
75
71
|
@lines = 3
|
76
72
|
end
|
77
|
-
o.on('-U', '--unified [LINES]', Numeric,
|
78
|
-
'Displays a unified diff with LINES lines',
|
79
|
-
'of context. Default 3 lines.') do |ctx|
|
73
|
+
o.on('-U', '--unified [LINES]', Numeric, 'Displays a unified diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
|
80
74
|
@format = :unified
|
81
75
|
@lines = ctx || 3
|
82
76
|
end
|
83
|
-
o.on('-e',
|
84
|
-
'Creates an \'ed\' script to change',
|
85
|
-
'oldfile to newfile.') do |ctx|
|
77
|
+
o.on('-e', 'Creates an \'ed\' script to change', 'oldfile to newfile.') do |ctx|
|
86
78
|
@format = :ed
|
87
79
|
end
|
88
|
-
o.on('-f',
|
89
|
-
'Creates an \'ed\' script to change',
|
90
|
-
'oldfile to newfile in reverse order.') do |ctx|
|
80
|
+
o.on('-f', 'Creates an \'ed\' script to change', 'oldfile to newfile in reverse order.') do |ctx|
|
91
81
|
@format = :reverse_ed
|
92
82
|
end
|
93
|
-
o.on('-a', '--text',
|
94
|
-
'Treat the files as text and compare them',
|
95
|
-
'line-by-line, even if they do not seem',
|
96
|
-
'to be text.') do |txt|
|
83
|
+
o.on('-a', '--text', 'Treat the files as text and compare them', 'line-by-line, even if they do not seem', 'to be text.') do |txt|
|
97
84
|
@binary = false
|
98
85
|
end
|
99
|
-
o.on('--binary',
|
100
|
-
'Treats the files as binary.') do |bin|
|
86
|
+
o.on('--binary', 'Treats the files as binary.') do |bin|
|
101
87
|
@binary = true
|
102
88
|
end
|
103
|
-
o.on('-q', '--brief',
|
104
|
-
'Report only whether or not the files',
|
105
|
-
'differ, not the details.') do |ctx|
|
89
|
+
o.on('-q', '--brief', 'Report only whether or not the files', 'differ, not the details.') do |ctx|
|
106
90
|
@format = :report
|
107
91
|
end
|
108
92
|
o.on_tail('--help', 'Shows this text.') do
|
@@ -123,7 +107,7 @@ $Id: ldiff.rb,v 1.1 2004/09/26 01:37:49 austin Exp $
|
|
123
107
|
return 127
|
124
108
|
end
|
125
109
|
|
126
|
-
|
110
|
+
# Defaults are for old-style diff
|
127
111
|
@format ||= :old
|
128
112
|
@lines ||= 0
|
129
113
|
|
@@ -138,15 +122,15 @@ $Id: ldiff.rb,v 1.1 2004/09/26 01:37:49 austin Exp $
|
|
138
122
|
char_new = '+' * 3
|
139
123
|
end
|
140
124
|
|
141
|
-
|
142
|
-
|
125
|
+
# After we've read up to a certain point in each file, the number of
|
126
|
+
# items we've read from each file will differ by FLD (could be 0).
|
143
127
|
file_length_difference = 0
|
144
128
|
|
145
129
|
if @binary.nil? or @binary
|
146
130
|
data_old = IO::read(file_old)
|
147
131
|
data_new = IO::read(file_new)
|
148
132
|
|
149
|
-
|
133
|
+
# Test binary status
|
150
134
|
if @binary.nil?
|
151
135
|
old_txt = data_old[0...4096].grep(/\0/).empty?
|
152
136
|
new_txt = data_new[0...4096].grep(/\0/).empty?
|
@@ -163,7 +147,7 @@ $Id: ldiff.rb,v 1.1 2004/09/26 01:37:49 austin Exp $
|
|
163
147
|
data_new = IO::readlines(file_new).map! { |e| e.chomp }
|
164
148
|
end
|
165
149
|
|
166
|
-
|
150
|
+
# diff yields lots of pieces, each of which is basically a Block object
|
167
151
|
if @binary
|
168
152
|
diffs = (data_old == data_new)
|
169
153
|
else
|
@@ -185,8 +169,8 @@ $Id: ldiff.rb,v 1.1 2004/09/26 01:37:49 austin Exp $
|
|
185
169
|
puts "#{char_new} #{file_new}\t#{ft}"
|
186
170
|
end
|
187
171
|
|
188
|
-
|
189
|
-
|
172
|
+
# Loop over hunks. If a hunk overlaps with the last hunk, join them.
|
173
|
+
# Otherwise, print out the old one.
|
190
174
|
oldhunk = hunk = nil
|
191
175
|
|
192
176
|
if @format == :ed
|
@@ -195,22 +179,22 @@ $Id: ldiff.rb,v 1.1 2004/09/26 01:37:49 austin Exp $
|
|
195
179
|
end
|
196
180
|
|
197
181
|
diffs.each do |piece|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
182
|
+
begin
|
183
|
+
hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, @lines,
|
184
|
+
file_length_difference)
|
185
|
+
file_length_difference = hunk.file_length_difference
|
186
|
+
|
187
|
+
next unless oldhunk
|
188
|
+
|
189
|
+
if (@lines > 0) and hunk.overlaps?(oldhunk)
|
190
|
+
hunk.unshift(oldhunk)
|
191
|
+
else
|
192
|
+
output << oldhunk.diff(@format)
|
193
|
+
end
|
194
|
+
ensure
|
195
|
+
oldhunk = hunk
|
196
|
+
output << "\n"
|
209
197
|
end
|
210
|
-
ensure
|
211
|
-
oldhunk = hunk
|
212
|
-
output << "\n"
|
213
|
-
end
|
214
198
|
end
|
215
199
|
|
216
200
|
output << oldhunk.diff(@format)
|
data/lib/diff/lcs/string.rb
CHANGED
data/spec/diff_spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- ruby encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "Diff::LCS.diff" do
|
6
|
+
include Diff::LCS::SpecHelper::Matchers
|
7
|
+
|
8
|
+
it "should correctly diff seq1 to seq2" do
|
9
|
+
diff_s1_s2 = Diff::LCS.diff(seq1, seq2)
|
10
|
+
change_diff(correct_forward_diff).should == diff_s1_s2
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should correctly diff seq2 to seq1" do
|
14
|
+
diff_s2_s1 = Diff::LCS.diff(seq2, seq1)
|
15
|
+
change_diff(correct_backward_diff).should == diff_s2_s1
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should correctly diff against an empty sequence" do
|
19
|
+
diff = Diff::LCS.diff(word_sequence, [])
|
20
|
+
correct_diff = [
|
21
|
+
[ [ '-', 0, 'abcd' ],
|
22
|
+
[ '-', 1, 'efgh' ],
|
23
|
+
[ '-', 2, 'ijkl' ],
|
24
|
+
[ '-', 3, 'mnopqrstuvwxyz' ] ]
|
25
|
+
]
|
26
|
+
|
27
|
+
change_diff(correct_diff).should == diff
|
28
|
+
|
29
|
+
diff = Diff::LCS.diff([], word_sequence)
|
30
|
+
correct_diff.each { |hunk| hunk.each { |change| change[0] = '+' } }
|
31
|
+
change_diff(correct_diff).should == diff
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# vim: ft=ruby
|
data/spec/lcs_spec.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- ruby encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe "Diff::LCS.LCS and Diff::LCS.__lcs" do
|
6
|
+
include Diff::LCS::SpecHelper::Matchers
|
7
|
+
|
8
|
+
it "should return the correct raw values from Diff::LCS.__lcs" do
|
9
|
+
res = Diff::LCS.__lcs(seq1, seq2)
|
10
|
+
# The result of the LCS (less the +nil+ values) must be as long as the
|
11
|
+
# correct result.
|
12
|
+
res.compact.size.should == correct_lcs.size
|
13
|
+
res.should correctly_map_sequence(seq1).to_other_sequence(seq2)
|
14
|
+
|
15
|
+
# Compact these transformations and they should be the correct LCS.
|
16
|
+
x_seq1 = (0...res.size).map { |ix| res[ix] ? seq1[ix] : nil }.compact
|
17
|
+
x_seq2 = (0...res.size).map { |ix| res[ix] ? seq2[res[ix]] : nil }.compact
|
18
|
+
|
19
|
+
x_seq1.should == correct_lcs
|
20
|
+
x_seq2.should == correct_lcs
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return the correct compacted values from Diff::LCS.LCS" do
|
24
|
+
res = Diff::LCS.LCS(seq1, seq2)
|
25
|
+
res.should == correct_lcs
|
26
|
+
res.compact.should == res
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should be transitive" do
|
30
|
+
res = Diff::LCS.LCS(seq2, seq1)
|
31
|
+
res.should == correct_lcs
|
32
|
+
res.compact.should == res
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# vim: ft=ruby
|