diff-lcs 1.1.2 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|