diff-lcs-1.3/ 0000755 0000041 0000041 00000000000 13041652267 013102 5 ustar www-data www-data diff-lcs-1.3/Rakefile 0000644 0000041 0000041 00000002723 13041652267 014553 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'rubygems'
require 'rspec'
require 'hoe'
Hoe.plugin :bundler
Hoe.plugin :doofus
Hoe.plugin :email unless ENV['CI'] or ENV['TRAVIS']
Hoe.plugin :gemspec2
Hoe.plugin :git
Hoe.plugin :travis
spec = Hoe.spec 'diff-lcs' do
developer('Austin Ziegler', 'halostatue@gmail.com')
require_ruby_version '>= 1.8'
self.history_file = 'History.md'
self.readme_file = 'README.rdoc'
self.licenses = [ 'MIT', 'Artistic-2.0', 'GPL-2.0+' ]
extra_dev_deps << ['hoe-doofus', '~> 1.0']
extra_dev_deps << ['hoe-gemspec2', '~> 1.1']
extra_dev_deps << ['hoe-git', '~> 1.6']
extra_dev_deps << ['hoe-rubygems', '~> 1.0']
extra_dev_deps << ['hoe-travis', '~> 1.2']
extra_dev_deps << ['rspec', '>= 2.0', '< 4']
extra_dev_deps << ['rake', '>= 10.0', '< 12']
extra_dev_deps << ['rdoc', '>= 0']
end
unless Rake::Task.task_defined? :test
task :test => :spec
Rake::Task['travis'].prerequisites.replace(%w(spec))
end
if RUBY_VERSION >= '2.0' && RUBY_ENGINE == 'ruby'
namespace :spec do
task :coveralls do
if ENV['CI'] or ENV['TRAVIS']
ENV['COVERALLS'] = 'yes'
Rake::Task['spec'].execute
else
Rake::Task['spec:coverage'].execute
end
end
desc "Runs test coverage. Only works Ruby 2.0+ and assumes 'simplecov' is installed."
task :coverage do
ENV['COVERAGE'] = 'yes'
Rake::Task['spec'].execute
end
end
# Rake::Task['travis'].prerequisites.replace(%w(spec:coveralls))
end
diff-lcs-1.3/bin/ 0000755 0000041 0000041 00000000000 13041652267 013652 5 ustar www-data www-data diff-lcs-1.3/bin/ldiff 0000755 0000041 0000041 00000000130 13041652267 014656 0 ustar www-data www-data #!ruby -w
require 'diff/lcs'
require 'diff/lcs/ldiff'
exit Diff::LCS::Ldiff.run(ARGV)
diff-lcs-1.3/bin/htmldiff 0000755 0000041 0000041 00000001210 13041652267 015367 0 ustar www-data www-data #!ruby -w
require 'diff/lcs'
require 'diff/lcs/htmldiff'
begin
require 'text/format'
rescue LoadError
Diff::LCS::HTMLDiff.can_expand_tabs = false
end
if ARGV.size < 2 or ARGV.size > 3
$stderr.puts "usage: #{File.basename($0)} old new [output.html]"
$stderr.puts " #{File.basename($0)} old new > output.html"
exit 127
end
left = IO.read(ARGV[0]).split($/)
right = IO.read(ARGV[1]).split($/)
options = { :title => "diff #{ARGV[0]} #{ARGV[1]}" }
htmldiff = Diff::LCS::HTMLDiff.new(left, right, options)
if ARGV[2]
File.open(ARGV[2], "w") do |f|
htmldiff.options[:output] = f
htmldiff.run
end
else
htmldiff.run
end
diff-lcs-1.3/Manifest.txt 0000644 0000041 0000041 00000001270 13041652267 015411 0 ustar www-data www-data .rspec
Code-of-Conduct.md
Contributing.md
History.md
License.md
Manifest.txt
README.rdoc
Rakefile
autotest/discover.rb
bin/htmldiff
bin/ldiff
docs/COPYING.txt
docs/artistic.txt
lib/diff-lcs.rb
lib/diff/lcs.rb
lib/diff/lcs/array.rb
lib/diff/lcs/block.rb
lib/diff/lcs/callbacks.rb
lib/diff/lcs/change.rb
lib/diff/lcs/htmldiff.rb
lib/diff/lcs/hunk.rb
lib/diff/lcs/internals.rb
lib/diff/lcs/ldiff.rb
lib/diff/lcs/string.rb
spec/change_spec.rb
spec/diff_spec.rb
spec/fixtures/ds1.csv
spec/fixtures/ds2.csv
spec/hunk_spec.rb
spec/issues_spec.rb
spec/lcs_spec.rb
spec/ldiff_spec.rb
spec/patch_spec.rb
spec/sdiff_spec.rb
spec/spec_helper.rb
spec/traverse_balanced_spec.rb
spec/traverse_sequences_spec.rb
diff-lcs-1.3/.rspec 0000644 0000041 0000041 00000000011 13041652267 014207 0 ustar www-data www-data --colour
diff-lcs-1.3/spec/ 0000755 0000041 0000041 00000000000 13041652267 014034 5 ustar www-data www-data diff-lcs-1.3/spec/sdiff_spec.rb 0000644 0000041 0000041 00000012505 13041652267 016471 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe "Diff::LCS.sdiff" do
include Diff::LCS::SpecHelper::Matchers
shared_examples "compare sequences correctly" do
it "compares s1 -> s2 correctly" do
expect(Diff::LCS.sdiff(s1, s2)).to eq(context_diff(result))
end
it "compares s2 -> s1 correctly" do
expect(Diff::LCS.sdiff(s2, s1)).to eq(context_diff(reverse_sdiff(result)))
end
end
describe "using seq1 & seq2" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:result) { correct_forward_sdiff }
it_has_behavior "compare sequences correctly"
end
describe "using %w(abc def yyy xxx ghi jkl) & %w(abc dxf xxx ghi jkl)" do
let(:s1) { %w(abc def yyy xxx ghi jkl) }
let(:s2) { %w(abc dxf xxx ghi jkl) }
let(:result) {
[
[ '=', [ 0, 'abc' ], [ 0, 'abc' ] ],
[ '!', [ 1, 'def' ], [ 1, 'dxf' ] ],
[ '-', [ 2, 'yyy' ], [ 2, nil ] ],
[ '=', [ 3, 'xxx' ], [ 2, 'xxx' ] ],
[ '=', [ 4, 'ghi' ], [ 3, 'ghi' ] ],
[ '=', [ 5, 'jkl' ], [ 4, 'jkl' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(a b c d e) & %w(a e)" do
let(:s1) { %w(a b c d e) }
let(:s2) { %w(a e) }
let(:result) {
[
[ '=', [ 0, 'a' ], [ 0, 'a' ] ],
[ '-', [ 1, 'b' ], [ 1, nil ] ],
[ '-', [ 2, 'c' ], [ 1, nil ] ],
[ '-', [ 3, 'd' ], [ 1, nil ] ],
[ '=', [ 4, 'e' ], [ 1, 'e' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(a e) & %w(a b c d e)" do
let(:s1) { %w(a e) }
let(:s2) { %w(a b c d e) }
let(:result) {
[
[ '=', [ 0, 'a' ], [ 0, 'a' ] ],
[ '+', [ 1, nil ], [ 1, 'b' ] ],
[ '+', [ 1, nil ], [ 2, 'c' ] ],
[ '+', [ 1, nil ], [ 3, 'd' ] ],
[ '=', [ 1, 'e' ], [ 4, 'e' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(v x a e) & %w(w y a b c d e)" do
let(:s1) { %w(v x a e) }
let(:s2) { %w(w y a b c d e) }
let(:result) {
[
[ '!', [ 0, 'v' ], [ 0, 'w' ] ],
[ '!', [ 1, 'x' ], [ 1, 'y' ] ],
[ '=', [ 2, 'a' ], [ 2, 'a' ] ],
[ '+', [ 3, nil ], [ 3, 'b' ] ],
[ '+', [ 3, nil ], [ 4, 'c' ] ],
[ '+', [ 3, nil ], [ 5, 'd' ] ],
[ '=', [ 3, 'e' ], [ 6, 'e' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(x a e) & %w(a b c d e)" do
let(:s1) { %w(x a e) }
let(:s2) { %w(a b c d e) }
let(:result) {
[
[ '-', [ 0, 'x' ], [ 0, nil ] ],
[ '=', [ 1, 'a' ], [ 0, 'a' ] ],
[ '+', [ 2, nil ], [ 1, 'b' ] ],
[ '+', [ 2, nil ], [ 2, 'c' ] ],
[ '+', [ 2, nil ], [ 3, 'd' ] ],
[ '=', [ 2, 'e' ], [ 4, 'e' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(a e) & %w(x a b c d e)" do
let(:s1) { %w(a e) }
let(:s2) { %w(x a b c d e) }
let(:result) {
[
[ '+', [ 0, nil ], [ 0, 'x' ] ],
[ '=', [ 0, 'a' ], [ 1, 'a' ] ],
[ '+', [ 1, nil ], [ 2, 'b' ] ],
[ '+', [ 1, nil ], [ 3, 'c' ] ],
[ '+', [ 1, nil ], [ 4, 'd' ] ],
[ '=', [ 1, 'e' ], [ 5, 'e' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(a e v) & %w(x a b c d e w x)" do
let(:s1) { %w(a e v) }
let(:s2) { %w(x a b c d e w x) }
let(:result) {
[
[ '+', [ 0, nil ], [ 0, 'x' ] ],
[ '=', [ 0, 'a' ], [ 1, 'a' ] ],
[ '+', [ 1, nil ], [ 2, 'b' ] ],
[ '+', [ 1, nil ], [ 3, 'c' ] ],
[ '+', [ 1, nil ], [ 4, 'd' ] ],
[ '=', [ 1, 'e' ], [ 5, 'e' ] ],
[ '!', [ 2, 'v' ], [ 6, 'w' ] ],
[ '+', [ 3, nil ], [ 7, 'x' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w() & %w(a b c)" do
let(:s1) { %w() }
let(:s2) { %w(a b c) }
let(:result) {
[
[ '+', [ 0, nil ], [ 0, 'a' ] ],
[ '+', [ 0, nil ], [ 1, 'b' ] ],
[ '+', [ 0, nil ], [ 2, 'c' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(a b c) & %w(1)" do
let(:s1) { %w(a b c) }
let(:s2) { %w(1) }
let(:result) {
[
[ '!', [ 0, 'a' ], [ 0, '1' ] ],
[ '-', [ 1, 'b' ], [ 1, nil ] ],
[ '-', [ 2, 'c' ], [ 1, nil ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(a b c) & %w(c)" do
let(:s1) { %w(a b c) }
let(:s2) { %w(c) }
let(:result) {
[
[ '-', [ 0, 'a' ], [ 0, nil ] ],
[ '-', [ 1, 'b' ], [ 0, nil ] ],
[ '=', [ 2, 'c' ], [ 0, 'c' ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using %w(abcd efgh ijkl mnop) & []" do
let(:s1) { %w(abcd efgh ijkl mnop) }
let(:s2) { [] }
let(:result) {
[
[ '-', [ 0, 'abcd' ], [ 0, nil ] ],
[ '-', [ 1, 'efgh' ], [ 0, nil ] ],
[ '-', [ 2, 'ijkl' ], [ 0, nil ] ],
[ '-', [ 3, 'mnop' ], [ 0, nil ] ]
]
}
it_has_behavior "compare sequences correctly"
end
describe "using [[1,2]] & []" do
let(:s1) { [ [ 1, 2 ] ] }
let(:s2) { [] }
let(:result) {
[
[ '-', [ 0, [ 1, 2 ] ], [ 0, nil ] ]
]
}
it_has_behavior "compare sequences correctly"
end
end
diff-lcs-1.3/spec/traverse_balanced_spec.rb 0000644 0000041 0000041 00000016341 13041652267 021044 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe "Diff::LCS.traverse_balanced" do
include Diff::LCS::SpecHelper::Matchers
shared_examples "with a #change callback" do |s1, s2, result|
it "traverses s1 -> s2 correctly" do
traversal = balanced_traversal(s1, s2, :balanced_callback)
expect(traversal.result).to eq(result)
end
it "traverses s2 -> s1 correctly" do
traversal = balanced_traversal(s2, s1, :balanced_callback)
expect(traversal.result).to eq(balanced_reverse(result))
end
end
shared_examples "without a #change callback" do |s1, s2, result|
it "traverses s1 -> s2 correctly" do
traversal = balanced_traversal(s1, s2, :balanced_callback_no_change)
expect(traversal.result).to eq(map_to_no_change(result))
end
it "traverses s2 -> s1 correctly" do
traversal = balanced_traversal(s2, s1, :balanced_callback_no_change)
expect(traversal.result).to eq(map_to_no_change(balanced_reverse(result)))
end
end
describe "identical string sequences ('abc')" do
s1 = s2 = "abc"
result = [
[ '=', 0, 0 ],
[ '=', 1, 1 ],
[ '=', 2, 2 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "identical array sequences %w(a b c)" do
s1 = s2 = %w(a b c)
result = [
[ '=', 0, 0 ],
[ '=', 1, 1 ],
[ '=', 2, 2 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(a b c) & %w(a x c)" do
s1 = %w(a b c)
s2 = %w(a x c)
result = [
[ '=', 0, 0 ],
[ '!', 1, 1 ],
[ '=', 2, 2 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(a x y c) & %w(a v w c)" do
s1 = %w(a x y c)
s2 = %w(a v w c)
result = [
[ '=', 0, 0 ],
[ '!', 1, 1 ],
[ '!', 2, 2 ],
[ '=', 3, 3 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(x y c) & %w(v w c)" do
s1 = %w(x y c)
s2 = %w(v w c)
result = [
[ '!', 0, 0 ],
[ '!', 1, 1 ],
[ '=', 2, 2 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(a x y z) & %w(b v w)" do
s1 = %w(a x y z)
s2 = %w(b v w)
result = [
[ '!', 0, 0 ],
[ '!', 1, 1 ],
[ '!', 2, 2 ],
[ '<', 3, 3 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(a z) & %w(a)" do
s1 = %w(a z)
s2 = %w(a)
result = [
[ '=', 0, 0 ],
[ '<', 1, 1 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(z a) & %w(a)" do
s1 = %w(z a)
s2 = %w(a)
result = [
[ '<', 0, 0 ],
[ '=', 1, 0 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(a b c) & %w(x y z)" do
s1 = %w(a b c)
s2 = %w(x y z)
result = [
[ '!', 0, 0 ],
[ '!', 1, 1 ],
[ '!', 2, 2 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "sequences %w(abcd efgh ijkl mnoopqrstuvwxyz) & []" do
s1 = %w(abcd efgh ijkl mnopqrstuvwxyz)
s2 = []
result = [
[ '<', 0, 0 ],
[ '<', 1, 0 ],
[ '<', 2, 0 ],
[ '<', 3, 0 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(a b c) & %Q(a x c)" do
s1 = %Q(a b c)
s2 = %Q(a x c)
result = [
[ '=', 0, 0 ],
[ '=', 1, 1 ],
[ '!', 2, 2 ],
[ '=', 3, 3 ],
[ '=', 4, 4 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(a x y c) & %Q(a v w c)" do
s1 = %Q(a x y c)
s2 = %Q(a v w c)
result = [
[ '=', 0, 0 ],
[ '=', 1, 1 ],
[ '!', 2, 2 ],
[ '=', 3, 3 ],
[ '!', 4, 4 ],
[ '=', 5, 5 ],
[ '=', 6, 6 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(x y c) & %Q(v w c)" do
s1 = %Q(x y c)
s2 = %Q(v w c)
result = [
[ '!', 0, 0 ],
[ '=', 1, 1 ],
[ '!', 2, 2 ],
[ '=', 3, 3 ],
[ '=', 4, 4 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(a x y z) & %Q(b v w)" do
s1 = %Q(a x y z)
s2 = %Q(b v w)
result = [
[ '!', 0, 0 ],
[ '=', 1, 1 ],
[ '!', 2, 2 ],
[ '=', 3, 3 ],
[ '!', 4, 4 ],
[ '<', 5, 5 ],
[ '<', 6, 5 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(a z) & %Q(a)" do
s1 = %Q(a z)
s2 = %Q(a)
result = [
[ '=', 0, 0 ],
[ '<', 1, 1 ],
[ '<', 2, 1 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(z a) & %Q(a)" do
s1 = %Q(z a)
s2 = %Q(a)
result = [
[ '<', 0, 0 ],
[ '<', 1, 0 ],
[ '=', 2, 0 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(a b c) & %Q(x y z)" do
s1 = %Q(a b c)
s2 = %Q(x y z)
result = [
[ '!', 0, 0 ],
[ '=', 1, 1 ],
[ '!', 2, 2 ],
[ '=', 3, 3 ],
[ '!', 4, 4 ]
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
describe "strings %Q(abcd efgh ijkl mnopqrstuvwxyz) & %Q()" do
s1 = %Q(abcd efgh ijkl mnopqrstuvwxyz)
s2 = ""
result = [
[ '<', 0, 0 ],
[ '<', 1, 0 ],
[ '<', 2, 0 ],
[ '<', 3, 0 ],
[ '<', 4, 0 ],
[ '<', 5, 0 ],
[ '<', 6, 0 ],
[ '<', 7, 0 ],
[ '<', 8, 0 ],
[ '<', 9, 0 ],
[ '<', 10, 0 ],
[ '<', 11, 0 ],
[ '<', 12, 0 ],
[ '<', 13, 0 ],
[ '<', 14, 0 ],
[ '<', 15, 0 ],
[ '<', 16, 0 ],
[ '<', 17, 0 ],
[ '<', 18, 0 ],
[ '<', 19, 0 ],
[ '<', 20, 0 ],
[ '<', 21, 0 ],
[ '<', 22, 0 ],
[ '<', 23, 0 ],
[ '<', 24, 0 ],
[ '<', 25, 0 ],
[ '<', 26, 0 ],
[ '<', 27, 0 ],
[ '<', 28, 0 ],
]
it_has_behavior "with a #change callback", s1, s2, result
it_has_behavior "without a #change callback", s1, s2, result
end
end
diff-lcs-1.3/spec/hunk_spec.rb 0000644 0000041 0000041 00000003674 13041652267 016352 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'spec_helper'
if String.method_defined?(:encoding)
require 'diff/lcs/hunk'
describe Diff::LCS::Hunk do
let(:old_data) { ["Tu avec carté {count} itém has".encode('UTF-16LE')] }
let(:new_data) { ["Tu avec carte {count} item has".encode('UTF-16LE')] }
let(:pieces) { Diff::LCS.diff old_data, new_data }
let(:hunk) { Diff::LCS::Hunk.new(old_data, new_data, pieces[0], 3, 0) }
it 'produces a unified diff from the two pieces' do
expected = (<<-EOD.gsub(/^\s+/,'').encode('UTF-16LE').chomp)
@@ -1,2 +1,2 @@
-Tu avec carté {count} itém has
+Tu avec carte {count} item has
EOD
expect(hunk.diff(:unified)).to eq(expected)
end
it 'produces a context diff from the two pieces' do
expected = (<<-EOD.gsub(/^\s+/,'').encode('UTF-16LE').chomp)
***************
*** 1,2 ****
!Tu avec carté {count} itém has
--- 1,2 ----
!Tu avec carte {count} item has
EOD
expect(hunk.diff(:context)).to eq(expected)
end
it 'produces an old diff from the two pieces' do
expected = (<<-EOD.gsub(/^ +/,'').encode('UTF-16LE').chomp)
1,2c1,2
< Tu avec carté {count} itém has
---
> Tu avec carte {count} item has
EOD
expect(hunk.diff(:old)).to eq(expected)
end
it 'produces a reverse ed diff from the two pieces' do
expected = (<<-EOD.gsub(/^ +/,'').encode('UTF-16LE').chomp)
c1,2
Tu avec carte {count} item has
.
EOD
expect(hunk.diff(:reverse_ed)).to eq(expected)
end
context 'with empty first data set' do
let(:old_data) { [] }
it 'produces a unified diff' do
expected = (<<-EOD.gsub(/^\s+/,'').encode('UTF-16LE').chomp)
@@ -1 +1,2 @@
+Tu avec carte {count} item has
EOD
expect(hunk.diff(:unified)).to eq(expected)
end
end
end
end
diff-lcs-1.3/spec/ldiff_spec.rb 0000644 0000041 0000041 00000002472 13041652267 016464 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe "Diff::LCS.diff" do
include Diff::LCS::SpecHelper::Matchers
it 'correctly diffs seq1 to seq2' do
diff_s1_s2 = Diff::LCS.diff(seq1, seq2)
expect(change_diff(correct_forward_diff)).to eq(diff_s1_s2)
end
it 'correctly diffs seq2 to seq1' do
diff_s2_s1 = Diff::LCS.diff(seq2, seq1)
expect(change_diff(correct_backward_diff)).to eq(diff_s2_s1)
end
it 'correctly diffs against an empty sequence' do
diff = Diff::LCS.diff(word_sequence, [])
correct_diff = [
[ [ '-', 0, 'abcd' ],
[ '-', 1, 'efgh' ],
[ '-', 2, 'ijkl' ],
[ '-', 3, 'mnopqrstuvwxyz' ] ]
]
expect(change_diff(correct_diff)).to eq(diff)
diff = Diff::LCS.diff([], word_sequence)
correct_diff.each { |hunk| hunk.each { |change| change[0] = '+' } }
expect(change_diff(correct_diff)).to eq(diff)
end
it "correctly diffs 'xx' and 'xaxb'" do
left = 'xx'
right = 'xaxb'
expect(Diff::LCS.patch(left, Diff::LCS.diff(left, right))).to eq(right)
end
it "returns an empty diff with (hello, hello)" do
expect(Diff::LCS.diff(hello, hello)).to eq([])
end
it "returns an empty diff with (hello_ary, hello_ary)" do
expect(Diff::LCS.diff(hello_ary, hello_ary)).to eq([])
end
end
diff-lcs-1.3/spec/change_spec.rb 0000644 0000041 0000041 00000003575 13041652267 016632 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe Diff::LCS::Change do
describe "an add" do
subject { described_class.new('+', 0, 'element') }
it { should_not be_deleting }
it { should be_adding }
it { should_not be_unchanged }
it { should_not be_changed }
it { should_not be_finished_a }
it { should_not be_finished_b }
end
describe "a delete" do
subject { described_class.new('-', 0, 'element') }
it { should be_deleting }
it { should_not be_adding }
it { should_not be_unchanged }
it { should_not be_changed }
it { should_not be_finished_a }
it { should_not be_finished_b }
end
describe "an unchanged" do
subject { described_class.new('=', 0, 'element') }
it { should_not be_deleting }
it { should_not be_adding }
it { should be_unchanged }
it { should_not be_changed }
it { should_not be_finished_a }
it { should_not be_finished_b }
end
describe "a changed" do
subject { described_class.new('!', 0, 'element') }
it { should_not be_deleting }
it { should_not be_adding }
it { should_not be_unchanged }
it { should be_changed }
it { should_not be_finished_a }
it { should_not be_finished_b }
end
describe "a finished_a" do
subject { described_class.new('>', 0, 'element') }
it { should_not be_deleting }
it { should_not be_adding }
it { should_not be_unchanged }
it { should_not be_changed }
it { should be_finished_a }
it { should_not be_finished_b }
end
describe "a finished_b" do
subject { described_class.new('<', 0, 'element') }
it { should_not be_deleting }
it { should_not be_adding }
it { should_not be_unchanged }
it { should_not be_changed }
it { should_not be_finished_a }
it { should be_finished_b }
end
end
diff-lcs-1.3/spec/spec_helper.rb 0000644 0000041 0000041 00000015070 13041652267 016655 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'rubygems'
require 'pathname'
require 'psych'
if ENV['COVERALLS']
require 'coveralls'
Coveralls.wear!
elsif ENV['COVERAGE']
require 'simplecov'
def require_do(resource, &block)
require resource
block.call
rescue LoadError
nil
end
formatters = [ SimpleCov::Formatter::HTMLFormatter ]
require_do('simplecov-rcov') {
formatters << SimpleCov::Formatter::RcovFormatter
}
require_do('simplecov-vim/formatter') {
formatters << SimpleCov::Formatter::VimFormatter
}
require_do('simplecov-sublime-ruby-coverage') {
formatters << SimpleCov::Formatter::SublimeRubyCoverageFormatter
}
SimpleCov.start do
formatter SimpleCov::Formatter::MultiFormatter[*formatters]
end
end
file = Pathname.new(__FILE__).expand_path
path = file.parent
parent = path.parent
$:.unshift parent.join('lib')
require 'diff-lcs'
module Diff::LCS::SpecHelper
def hello
"hello"
end
def hello_ary
%W(h e l l o)
end
def seq1
%w(a b c e h j l m n p)
end
def skipped_seq1
%w(a h n p)
end
def seq2
%w(b c d e f j k l m r s t)
end
def skipped_seq2
%w(d f k r s t)
end
def word_sequence
%w(abcd efgh ijkl mnopqrstuvwxyz)
end
def correct_lcs
%w(b c e j l m)
end
def correct_forward_diff
[
[ [ '-', 0, 'a' ] ],
[ [ '+', 2, 'd' ] ],
[ [ '-', 4, 'h' ],
[ '+', 4, 'f' ] ],
[ [ '+', 6, 'k' ] ],
[ [ '-', 8, 'n' ],
[ '-', 9, 'p' ],
[ '+', 9, 'r' ],
[ '+', 10, 's' ],
[ '+', 11, 't' ] ]
]
end
def correct_backward_diff
[
[ [ '+', 0, 'a' ] ],
[ [ '-', 2, 'd' ] ],
[ [ '-', 4, 'f' ],
[ '+', 4, 'h' ] ],
[ [ '-', 6, 'k' ] ],
[
[ '-', 9, 'r' ],
[ '-', 10, 's' ],
[ '+', 8, 'n' ],
[ '-', 11, 't' ],
[ '+', 9, 'p' ] ]
]
end
def correct_forward_sdiff
[
[ '-', [ 0, 'a' ], [ 0, nil ] ],
[ '=', [ 1, 'b' ], [ 0, 'b' ] ],
[ '=', [ 2, 'c' ], [ 1, 'c' ] ],
[ '+', [ 3, nil ], [ 2, 'd' ] ],
[ '=', [ 3, 'e' ], [ 3, 'e' ] ],
[ '!', [ 4, 'h' ], [ 4, 'f' ] ],
[ '=', [ 5, 'j' ], [ 5, 'j' ] ],
[ '+', [ 6, nil ], [ 6, 'k' ] ],
[ '=', [ 6, 'l' ], [ 7, 'l' ] ],
[ '=', [ 7, 'm' ], [ 8, 'm' ] ],
[ '!', [ 8, 'n' ], [ 9, 'r' ] ],
[ '!', [ 9, 'p' ], [ 10, 's' ] ],
[ '+', [ 10, nil ], [ 11, 't' ] ]
]
end
def reverse_sdiff(forward_sdiff)
forward_sdiff.map { |line|
line[1], line[2] = line[2], line[1]
case line[0]
when '-' then line[0] = '+'
when '+' then line[0] = '-'
end
line
}
end
def change_diff(diff)
map_diffs(diff, Diff::LCS::Change)
end
def context_diff(diff)
map_diffs(diff, Diff::LCS::ContextChange)
end
def format_diffs(diffs)
diffs.map do |e|
if e.kind_of?(Array)
e.map { |f| f.to_a.join }.join(", ")
else
e.to_a.join
end
end.join("\n")
end
def map_diffs(diffs, klass = Diff::LCS::ContextChange)
diffs.map do |chunks|
if klass == Diff::LCS::ContextChange
klass.from_a(chunks)
else
chunks.map { |changes| klass.from_a(changes) }
end
end
end
def balanced_traversal(s1, s2, callback_type)
callback = __send__(callback_type)
Diff::LCS.traverse_balanced(s1, s2, callback)
callback
end
def balanced_reverse(change_result)
new_result = []
change_result.each { |line|
line = [ line[0], line[2], line[1] ]
case line[0]
when '<'
line[0] = '>'
when '>'
line[0] = '<'
end
new_result << line
}
new_result.sort_by { |line| [ line[1], line[2] ] }
end
def map_to_no_change(change_result)
new_result = []
change_result.each { |line|
case line[0]
when '!'
new_result << [ '<', line[1], line[2] ]
new_result << [ '>', line[1] + 1, line[2] ]
else
new_result << line
end
}
new_result
end
def simple_callback
callbacks = Object.new
class << callbacks
attr_reader :matched_a
attr_reader :matched_b
attr_reader :discards_a
attr_reader :discards_b
attr_reader :done_a
attr_reader :done_b
def reset
@matched_a = []
@matched_b = []
@discards_a = []
@discards_b = []
@done_a = []
@done_b = []
end
def match(event)
@matched_a << event.old_element
@matched_b << event.new_element
end
def discard_b(event)
@discards_b << event.new_element
end
def discard_a(event)
@discards_a << event.old_element
end
def finished_a(event)
@done_a << [event.old_element, event.old_position,
event.new_element, event.new_position]
end
def finished_b(event)
p "called #finished_b"
@done_b << [event.old_element, event.old_position,
event.new_element, event.new_position]
end
end
callbacks.reset
callbacks
end
def simple_callback_no_finishers
simple = simple_callback
class << simple
undef :finished_a
undef :finished_b
end
simple
end
def balanced_callback
cb = Object.new
class << cb
attr_reader :result
def reset
@result = []
end
def match(event)
@result << [ "=", event.old_position, event.new_position ]
end
def discard_a(event)
@result << [ "<", event.old_position, event.new_position ]
end
def discard_b(event)
@result << [ ">", event.old_position, event.new_position ]
end
def change(event)
@result << [ "!", event.old_position, event.new_position ]
end
end
cb.reset
cb
end
def balanced_callback_no_change
balanced = balanced_callback
class << balanced
undef :change
end
balanced
end
module Matchers
extend RSpec::Matchers::DSL
matcher :be_nil_or_match_values do |ii, s1, s2|
match do |ee|
expect(ee).to(satisfy { |vee| vee.nil? || s1[ii] == s2[ee] })
end
end
matcher :correctly_map_sequence do |s1|
match do |actual|
actual.each_with_index { |ee, ii|
expect(ee).to be_nil_or_match_values(ii, s1, @s2)
}
end
chain :to_other_sequence do |s2|
@s2 = s2
end
end
end
end
RSpec.configure do |conf|
conf.include Diff::LCS::SpecHelper
conf.alias_it_should_behave_like_to :it_has_behavior, 'has behavior:'
conf.filter_run_excluding :broken => true
end
diff-lcs-1.3/spec/patch_spec.rb 0000644 0000041 0000041 00000035424 13041652267 016502 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe "Diff::LCS.patch" do
include Diff::LCS::SpecHelper::Matchers
shared_examples "patch sequences correctly" do
it "correctly patches left-to-right (patch autodiscovery)" do
expect(Diff::LCS.patch(s1, patch_set)).to eq(s2)
end
it "correctly patches left-to-right (explicit patch)" do
expect(Diff::LCS.patch(s1, patch_set, :patch)).to eq(s2)
expect(Diff::LCS.patch!(s1, patch_set)).to eq(s2)
end
it "correctly patches right-to-left (unpatch autodiscovery)" do
expect(Diff::LCS.patch(s2, patch_set)).to eq(s1)
end
it "correctly patches right-to-left (explicit unpatch)" do
expect(Diff::LCS.patch(s2, patch_set, :unpatch)).to eq(s1)
expect(Diff::LCS.unpatch!(s2, patch_set)).to eq(s1)
end
end
describe "using a Diff::LCS.diff patchset" do
describe "an empty patchset returns the source" do
it "works on a string (hello)" do
diff = Diff::LCS.diff(hello, hello)
expect(Diff::LCS::patch(hello, diff)).to eq(hello)
end
it "works on an array %W(h e l l o)" do
diff = Diff::LCS.diff(hello_ary, hello_ary)
expect(Diff::LCS::patch(hello_ary, diff)).to eq(hello_ary)
end
end
describe "with default diff callbacks (DiffCallbacks)" do
describe "forward (s1 -> s2)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:patch_set) { Diff::LCS.diff(seq1, seq2) }
end
end
describe "reverse (s2 -> s1)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq2 }
let(:s2) { seq1 }
let(:patch_set) { Diff::LCS.diff(seq2, seq1) }
end
end
end
describe "with context diff callbacks (ContextDiffCallbacks)" do
describe "forward (s1 -> s2)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:patch_set) {
Diff::LCS.diff(seq1, seq2, Diff::LCS::ContextDiffCallbacks)
}
end
end
describe "reverse (s2 -> s1)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq2 }
let(:s2) { seq1 }
let(:patch_set) {
Diff::LCS.diff(seq2, seq1, Diff::LCS::ContextDiffCallbacks)
}
end
end
end
describe "with sdiff callbacks (SDiffCallbacks)" do
describe "forward (s1 -> s2)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:patch_set) {
Diff::LCS.diff(seq1, seq2, Diff::LCS::SDiffCallbacks)
}
end
end
describe "reverse (s2 -> s1)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq2 }
let(:s2) { seq1 }
let(:patch_set) {
Diff::LCS.diff(seq2, seq1, Diff::LCS::SDiffCallbacks)
}
end
end
end
end
describe "using a Diff::LCS.sdiff patchset" do
describe "an empty patchset returns the source" do
it "works on a string (hello)" do
expect(Diff::LCS::patch(hello, Diff::LCS.sdiff(hello, hello))).to eq(hello)
end
it "works on an array %W(h e l l o)" do
expect(Diff::LCS::patch(hello_ary, Diff::LCS.sdiff(hello_ary, hello_ary))).to eq(hello_ary)
end
end
describe "with default diff callbacks (DiffCallbacks)" do
describe "forward (s1 -> s2)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:patch_set) {
Diff::LCS.sdiff(seq1, seq2, Diff::LCS::DiffCallbacks)
}
end
end
describe "reverse (s2 -> s1)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq2 }
let(:s2) { seq1 }
let(:patch_set) {
Diff::LCS.sdiff(seq2, seq1, Diff::LCS::DiffCallbacks)
}
end
end
end
describe "with context diff callbacks (DiffCallbacks)" do
describe "forward (s1 -> s2)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:patch_set) {
Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks)
}
end
end
describe "reverse (s2 -> s1)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq2 }
let(:s2) { seq1 }
let(:patch_set) {
Diff::LCS.sdiff(seq2, seq1, Diff::LCS::ContextDiffCallbacks)
}
end
end
end
describe "with sdiff callbacks (SDiffCallbacks)" do
describe "forward (s1 -> s2)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq1 }
let(:s2) { seq2 }
let(:patch_set) { Diff::LCS.sdiff(seq1, seq2) }
end
end
describe "reverse (s2 -> s1)" do
it_has_behavior "patch sequences correctly" do
let(:s1) { seq2 }
let(:s2) { seq1 }
let(:patch_set) { Diff::LCS.sdiff(seq2, seq1) }
end
end
end
end
# Note: because of the error in autodiscovery ("does not autodiscover s1
# to s2 patches"), this cannot use the "patch sequences correctly" shared
# set. Once the bug in autodiscovery is fixed, this can be converted as
# above.
describe "fix bug 891: patchsets do not contain the last equal part" do
before :each do
@s1 = %w(a b c d e f g h i j k)
@s2 = %w(a b c d D e f g h i j k)
end
describe "using Diff::LCS.diff with default diff callbacks" do
before :each do
@patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2)
@patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1)
end
it "autodiscovers s1 to s2 patches" do
expect do
expect(Diff::LCS.patch(@s1, @patch_set_s1_s2)).to eq(@s2)
end.to_not raise_error
end
it "autodiscovers s2 to s1 patches" do
expect do
expect(Diff::LCS.patch(@s1, @patch_set_s2_s1)).to eq(@s2)
end.to_not raise_error
end
it "autodiscovers s2 to s1 the left-to-right patches" do
expect(Diff::LCS.patch(@s2, @patch_set_s2_s1)).to eq(@s1)
expect(Diff::LCS.patch(@s2, @patch_set_s1_s2)).to eq(@s1)
end
it "correctly patches left-to-right (explicit patch)" do
expect(Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch)).to eq(@s2)
expect(Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch)).to eq(@s1)
expect(Diff::LCS.patch!(@s1, @patch_set_s1_s2)).to eq(@s2)
expect(Diff::LCS.patch!(@s2, @patch_set_s2_s1)).to eq(@s1)
end
it "correctly patches right-to-left (explicit unpatch)" do
expect(Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch)).to eq(@s1)
expect(Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch)).to eq(@s2)
expect(Diff::LCS.unpatch!(@s2, @patch_set_s1_s2)).to eq(@s1)
expect(Diff::LCS.unpatch!(@s1, @patch_set_s2_s1)).to eq(@s2)
end
end
describe "using Diff::LCS.diff with context diff callbacks" do
before :each do
@patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2,
Diff::LCS::ContextDiffCallbacks)
@patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1,
Diff::LCS::ContextDiffCallbacks)
end
it "autodiscovers s1 to s2 patches" do
expect do
expect(Diff::LCS.patch(@s1, @patch_set_s1_s2)).to eq(@s2)
end.to_not raise_error
end
it "autodiscovers s2 to s1 patches" do
expect do
expect(Diff::LCS.patch(@s1, @patch_set_s2_s1)).to eq(@s2)
end.to_not raise_error
end
it "autodiscovers s2 to s1 the left-to-right patches" do
expect(Diff::LCS.patch(@s2, @patch_set_s2_s1)).to eq(@s1)
expect(Diff::LCS.patch(@s2, @patch_set_s1_s2)).to eq(@s1)
end
it "correctly patches left-to-right (explicit patch)" do
expect(Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch)).to eq(@s2)
expect(Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch)).to eq(@s1)
expect(Diff::LCS.patch!(@s1, @patch_set_s1_s2)).to eq(@s2)
expect(Diff::LCS.patch!(@s2, @patch_set_s2_s1)).to eq(@s1)
end
it "correctly patches right-to-left (explicit unpatch)" do
expect(Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch)).to eq(@s1)
expect(Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch)).to eq(@s2)
expect(Diff::LCS.unpatch!(@s2, @patch_set_s1_s2)).to eq(@s1)
expect(Diff::LCS.unpatch!(@s1, @patch_set_s2_s1)).to eq(@s2)
end
end
describe "using Diff::LCS.diff with sdiff callbacks" do
before(:each) do
@patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2,
Diff::LCS::SDiffCallbacks)
@patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1,
Diff::LCS::SDiffCallbacks)
end
it "autodiscovers s1 to s2 patches" do
expect do
expect(Diff::LCS.patch(@s1, @patch_set_s1_s2)).to eq(@s2)
end.to_not raise_error
end
it "autodiscovers s2 to s1 patches" do
expect do
expect(Diff::LCS.patch(@s1, @patch_set_s2_s1)).to eq(@s2)
end.to_not raise_error
end
it "autodiscovers s2 to s1 the left-to-right patches" do
expect(Diff::LCS.patch(@s2, @patch_set_s2_s1)).to eq(@s1)
expect(Diff::LCS.patch(@s2, @patch_set_s1_s2)).to eq(@s1)
end
it "correctly patches left-to-right (explicit patch)" do
expect(Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch)).to eq(@s2)
expect(Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch)).to eq(@s1)
expect(Diff::LCS.patch!(@s1, @patch_set_s1_s2)).to eq(@s2)
expect(Diff::LCS.patch!(@s2, @patch_set_s2_s1)).to eq(@s1)
end
it "correctly patches right-to-left (explicit unpatch)" do
expect(Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch)).to eq(@s1)
expect(Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch)).to eq(@s2)
expect(Diff::LCS.unpatch!(@s2, @patch_set_s1_s2)).to eq(@s1)
expect(Diff::LCS.unpatch!(@s1, @patch_set_s2_s1)).to eq(@s2)
end
end
describe "using Diff::LCS.sdiff with default sdiff callbacks" do
before(:each) do
@patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2)
@patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1)
end
it "autodiscovers s1 to s2 patches" do
expect do
expect(Diff::LCS.patch(@s1, @patch_set_s1_s2)).to eq(@s2)
end.to_not raise_error
end
it "autodiscovers s2 to s1 patches" do
expect do
expect(Diff::LCS.patch(@s1, @patch_set_s2_s1)).to eq(@s2)
end.to_not raise_error
end
it "autodiscovers s2 to s1 the left-to-right patches" do
expect(Diff::LCS.patch(@s2, @patch_set_s2_s1)).to eq(@s1)
expect(Diff::LCS.patch(@s2, @patch_set_s1_s2)).to eq(@s1)
end
it "correctly patches left-to-right (explicit patch)" do
expect(Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch)).to eq(@s2)
expect(Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch)).to eq(@s1)
expect(Diff::LCS.patch!(@s1, @patch_set_s1_s2)).to eq(@s2)
expect(Diff::LCS.patch!(@s2, @patch_set_s2_s1)).to eq(@s1)
end
it "correctly patches right-to-left (explicit unpatch)" do
expect(Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch)).to eq(@s1)
expect(Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch)).to eq(@s2)
expect(Diff::LCS.unpatch!(@s2, @patch_set_s1_s2)).to eq(@s1)
expect(Diff::LCS.unpatch!(@s1, @patch_set_s2_s1)).to eq(@s2)
end
end
describe "using Diff::LCS.sdiff with context diff callbacks" do
before(:each) do
@patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2,
Diff::LCS::ContextDiffCallbacks)
@patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1,
Diff::LCS::ContextDiffCallbacks)
end
it "autodiscovers s1 to s2 patches" do
expect do
expect(Diff::LCS.patch(@s1, @patch_set_s1_s2)).to eq(@s2)
end.to_not raise_error
end
it "autodiscovers s2 to s1 patches" do
expect do
expect(Diff::LCS.patch(@s1, @patch_set_s2_s1)).to eq(@s2)
end.to_not raise_error
end
it "autodiscovers s2 to s1 the left-to-right patches" do
expect(Diff::LCS.patch(@s2, @patch_set_s2_s1)).to eq(@s1)
expect(Diff::LCS.patch(@s2, @patch_set_s1_s2)).to eq(@s1)
end
it "correctly patches left-to-right (explicit patch)" do
expect(Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch)).to eq(@s2)
expect(Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch)).to eq(@s1)
expect(Diff::LCS.patch!(@s1, @patch_set_s1_s2)).to eq(@s2)
expect(Diff::LCS.patch!(@s2, @patch_set_s2_s1)).to eq(@s1)
end
it "correctly patches right-to-left (explicit unpatch)" do
expect(Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch)).to eq(@s1)
expect(Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch)).to eq(@s2)
expect(Diff::LCS.unpatch!(@s2, @patch_set_s1_s2)).to eq(@s1)
expect(Diff::LCS.unpatch!(@s1, @patch_set_s2_s1)).to eq(@s2)
end
end
describe "using Diff::LCS.sdiff with default diff callbacks" do
before(:each) do
@patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2, Diff::LCS::DiffCallbacks)
@patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1, Diff::LCS::DiffCallbacks)
end
it "autodiscovers s1 to s2 patches" do
expect do
expect(Diff::LCS.patch(@s1, @patch_set_s1_s2)).to eq(@s2)
end.to_not raise_error
end
it "autodiscovers s2 to s1 patches" do
expect do
expect(Diff::LCS.patch(@s1, @patch_set_s2_s1)).to eq(@s2)
end.to_not raise_error
end
it "autodiscovers s2 to s1 the left-to-right patches" do
expect(Diff::LCS.patch(@s2, @patch_set_s2_s1)).to eq(@s1)
expect(Diff::LCS.patch(@s2, @patch_set_s1_s2)).to eq(@s1)
end
it "correctly patches left-to-right (explicit patch)" do
expect(Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch)).to eq(@s2)
expect(Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch)).to eq(@s1)
expect(Diff::LCS.patch!(@s1, @patch_set_s1_s2)).to eq(@s2)
expect(Diff::LCS.patch!(@s2, @patch_set_s2_s1)).to eq(@s1)
end
it "correctly patches right-to-left (explicit unpatch)" do
expect(Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch)).to eq(@s1)
expect(Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch)).to eq(@s2)
expect(Diff::LCS.unpatch!(@s2, @patch_set_s1_s2)).to eq(@s1)
expect(Diff::LCS.unpatch!(@s1, @patch_set_s2_s1)).to eq(@s2)
end
end
end
end
diff-lcs-1.3/spec/diff_spec.rb 0000644 0000041 0000041 00000002500 13041652267 016300 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe Diff::LCS, ".diff" do
include Diff::LCS::SpecHelper::Matchers
it "correctly diffs seq1 to seq2" do
diff_s1_s2 = Diff::LCS.diff(seq1, seq2)
expect(change_diff(correct_forward_diff)).to eq(diff_s1_s2)
end
it "correctly diffs seq2 to seq1" do
diff_s2_s1 = Diff::LCS.diff(seq2, seq1)
expect(change_diff(correct_backward_diff)).to eq(diff_s2_s1)
end
it "correctly diffs against an empty sequence" do
diff = Diff::LCS.diff(word_sequence, [])
correct_diff = [
[ [ '-', 0, 'abcd' ],
[ '-', 1, 'efgh' ],
[ '-', 2, 'ijkl' ],
[ '-', 3, 'mnopqrstuvwxyz' ] ]
]
expect(change_diff(correct_diff)).to eq(diff)
diff = Diff::LCS.diff([], word_sequence)
correct_diff.each { |hunk| hunk.each { |change| change[0] = '+' } }
expect(change_diff(correct_diff)).to eq(diff)
end
it "correctly diffs 'xx' and 'xaxb'" do
left = 'xx'
right = 'xaxb'
expect(Diff::LCS.patch(left, Diff::LCS.diff(left, right))).to eq(right)
end
it "returns an empty diff with (hello, hello)" do
expect(Diff::LCS.diff(hello, hello)).to be_empty
end
it "returns an empty diff with (hello_ary, hello_ary)" do
expect(Diff::LCS.diff(hello_ary, hello_ary)).to be_empty
end
end
diff-lcs-1.3/spec/fixtures/ 0000755 0000041 0000041 00000000000 13041652267 015705 5 ustar www-data www-data diff-lcs-1.3/spec/fixtures/ds1.csv 0000644 0000041 0000041 00000000534 13041652267 017113 0 ustar www-data www-data 1,3
2,7
3,13
4,21
5,31
6,43
7,57
8,73
9,91
10,111
11,133
12,157
13,183
14,211
15,241
16,273
17,307
18,343
19,381
20,421
21,463
22,507
23,553
24,601
25,651
26,703
27,757
28,813
29,871
30,931
31,993
32,1057
33,1123
34,1191
35,1261
36,1333
37,1407
38,1483
39,1561
40,1641
41,1723
42,1807
43,1893
44,1981
45,2071
46,2163
47,2257
48,2353
49,2451
50,2500 diff-lcs-1.3/spec/fixtures/ds2.csv 0000644 0000041 0000041 00000000547 13041652267 017120 0 ustar www-data www-data 1,3
2,7
3,13
4,21
5,31
6,42
7,57
8,73
9,91
10,111
11,133
12,157
13,183
14,211
15,241
16,273
17,307
18,343
19,200
20,421
21,463
22,507
23,553
24,601
25,651
26,703
27,757
28,813
29,871
30,931
31,123
32,1057
33,1123
34,1000
35,1261
36,1333
37,1407
38,1483
39,1561
40,1641
41,1723
42,1807
43,1893
44,1981
45,2071
46,2163
47,1524
48,2353
49,2451
50,2500
51,2520
diff-lcs-1.3/spec/issues_spec.rb 0000644 0000041 0000041 00000002334 13041652267 016710 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe "Diff::LCS Issues" do
include Diff::LCS::SpecHelper::Matchers
describe 'issue #1' do
shared_examples 'handles simple diffs' do |s1, s2, forward_diff|
before do
@diff_s1_s2 = Diff::LCS.diff(s1, s2)
end
it 'creates the correct diff' do
expect(change_diff(forward_diff)).to eq(@diff_s1_s2)
end
it 'creates the correct patch s1->s2' do
expect(Diff::LCS.patch(s1, @diff_s1_s2)).to eq(s2)
end
it 'creates the correct patch s2->s1' do
expect(Diff::LCS.patch(s2, @diff_s1_s2)).to eq(s1)
end
end
describe 'string' do
it_has_behavior 'handles simple diffs', 'aX', 'bXaX', [
[ [ '+', 0, 'b' ],
[ '+', 1, 'X' ] ],
]
it_has_behavior 'handles simple diffs', 'bXaX', 'aX', [
[ [ '-', 0, 'b' ],
[ '-', 1, 'X' ] ],
]
end
describe 'array' do
it_has_behavior 'handles simple diffs', %w(a X), %w(b X a X), [
[ [ '+', 0, 'b' ],
[ '+', 1, 'X' ] ],
]
it_has_behavior 'handles simple diffs', %w(b X a X), %w(a X), [
[ [ '-', 0, 'b' ],
[ '-', 1, 'X' ] ],
]
end
end
end
diff-lcs-1.3/spec/traverse_sequences_spec.rb 0000644 0000041 0000041 00000011273 13041652267 021305 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe "Diff::LCS.traverse_sequences" do
describe "callback with no finishers" do
describe "over (seq1, seq2)" do
before(:each) do
@callback_s1_s2 = simple_callback_no_finishers
Diff::LCS.traverse_sequences(seq1, seq2, @callback_s1_s2)
@callback_s2_s1 = simple_callback_no_finishers
Diff::LCS.traverse_sequences(seq2, seq1, @callback_s2_s1)
end
it "has the correct LCS result on left-matches" do
expect(@callback_s1_s2.matched_a).to eq(correct_lcs)
expect(@callback_s2_s1.matched_a).to eq(correct_lcs)
end
it "has the correct LCS result on right-matches" do
expect(@callback_s1_s2.matched_b).to eq(correct_lcs)
expect(@callback_s2_s1.matched_b).to eq(correct_lcs)
end
it "has the correct skipped sequences with the left sequence" do
expect(@callback_s1_s2.discards_a).to eq(skipped_seq1)
expect(@callback_s2_s1.discards_a).to eq(skipped_seq2)
end
it "has the correct skipped sequences with the right sequence" do
expect(@callback_s1_s2.discards_b).to eq(skipped_seq2)
expect(@callback_s2_s1.discards_b).to eq(skipped_seq1)
end
it "does not have anything done markers from the left or right sequences" do
expect(@callback_s1_s2.done_a).to be_empty
expect(@callback_s1_s2.done_b).to be_empty
expect(@callback_s2_s1.done_a).to be_empty
expect(@callback_s2_s1.done_b).to be_empty
end
end
describe "over (hello, hello)" do
before(:each) do
@callback = simple_callback_no_finishers
Diff::LCS.traverse_sequences(hello, hello, @callback)
end
it "has the correct LCS result on left-matches" do
expect(@callback.matched_a).to eq(hello.split(//))
end
it "has the correct LCS result on right-matches" do
expect(@callback.matched_b).to eq(hello.split(//))
end
it "has the correct skipped sequences with the left sequence", :only => true do
expect(@callback.discards_a).to be_empty
end
it "has the correct skipped sequences with the right sequence" do
expect(@callback.discards_b).to be_empty
end
it "does not have anything done markers from the left or right sequences" do
expect(@callback.done_a).to be_empty
expect(@callback.done_b).to be_empty
end
end
describe "over (hello_ary, hello_ary)" do
before(:each) do
@callback = simple_callback_no_finishers
Diff::LCS.traverse_sequences(hello_ary, hello_ary, @callback)
end
it "has the correct LCS result on left-matches" do
expect(@callback.matched_a).to eq(hello_ary)
end
it "has the correct LCS result on right-matches" do
expect(@callback.matched_b).to eq(hello_ary)
end
it "has the correct skipped sequences with the left sequence" do
expect(@callback.discards_a).to be_empty
end
it "has the correct skipped sequences with the right sequence" do
expect(@callback.discards_b).to be_empty
end
it "does not have anything done markers from the left or right sequences" do
expect(@callback.done_a).to be_empty
expect(@callback.done_b).to be_empty
end
end
end
describe "callback with finisher" do
before(:each) do
@callback_s1_s2 = simple_callback
Diff::LCS.traverse_sequences(seq1, seq2, @callback_s1_s2)
@callback_s2_s1 = simple_callback
Diff::LCS.traverse_sequences(seq2, seq1, @callback_s2_s1)
end
it "has the correct LCS result on left-matches" do
expect(@callback_s1_s2.matched_a).to eq(correct_lcs)
expect(@callback_s2_s1.matched_a).to eq(correct_lcs)
end
it "has the correct LCS result on right-matches" do
expect(@callback_s1_s2.matched_b).to eq(correct_lcs)
expect(@callback_s2_s1.matched_b).to eq(correct_lcs)
end
it "has the correct skipped sequences for the left sequence" do
expect(@callback_s1_s2.discards_a).to eq(skipped_seq1)
expect(@callback_s2_s1.discards_a).to eq(skipped_seq2)
end
it "has the correct skipped sequences for the right sequence" do
expect(@callback_s1_s2.discards_b).to eq(skipped_seq2)
expect(@callback_s2_s1.discards_b).to eq(skipped_seq1)
end
it "has done markers differently-sized sequences" do
expect(@callback_s1_s2.done_a).to eq([[ "p", 9, "s", 10 ]])
expect(@callback_s1_s2.done_b).to be_empty
# 20110731 I don't yet understand why this particular behaviour
# isn't transitive.
expect(@callback_s2_s1.done_a).to be_empty
expect(@callback_s2_s1.done_b).to be_empty
end
end
end
diff-lcs-1.3/spec/lcs_spec.rb 0000644 0000041 0000041 00000003305 13041652267 016155 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'spec_helper'
describe Diff::LCS::Internals, ".lcs" do
include Diff::LCS::SpecHelper::Matchers
it "returns a meaningful LCS array with (seq1, seq2)" do
res = Diff::LCS::Internals.lcs(seq1, seq2)
# The result of the LCS (less the +nil+ values) must be as long as the
# correct result.
expect(res.compact.size).to eq(correct_lcs.size)
expect(res).to correctly_map_sequence(seq1).to_other_sequence(seq2)
# Compact these transformations and they should be the correct LCS.
x_seq1 = (0...res.size).map { |ix| res[ix] ? seq1[ix] : nil }.compact
x_seq2 = (0...res.size).map { |ix| res[ix] ? seq2[res[ix]] : nil }.compact
expect(x_seq1).to eq(correct_lcs)
expect(x_seq2).to eq(correct_lcs)
end
it "returns all indexes with (hello, hello)" do
expect(Diff::LCS::Internals.lcs(hello, hello)).to \
eq((0...hello.size).to_a)
end
it "returns all indexes with (hello_ary, hello_ary)" do
expect(Diff::LCS::Internals.lcs(hello_ary, hello_ary)).to \
eq((0...hello_ary.size).to_a)
end
end
describe Diff::LCS, ".LCS" do
include Diff::LCS::SpecHelper::Matchers
it "returns the correct compacted values from Diff::LCS.LCS" do
res = Diff::LCS.LCS(seq1, seq2)
expect(res).to eq(correct_lcs)
expect(res.compact).to eq(res)
end
it "is transitive" do
res = Diff::LCS.LCS(seq2, seq1)
expect(res).to eq(correct_lcs)
expect(res.compact).to eq(res)
end
it "returns %W(h e l l o) with (hello, hello)" do
expect(Diff::LCS.LCS(hello, hello)).to eq(hello.split(//))
end
it "returns hello_ary with (hello_ary, hello_ary)" do
expect(Diff::LCS.LCS(hello_ary, hello_ary)).to eq(hello_ary)
end
end
diff-lcs-1.3/README.rdoc 0000644 0000041 0000041 00000006636 13041652267 014723 0 ustar www-data www-data = Diff::LCS
home :: https://github.com/halostatue/diff-lcs
code :: https://github.com/halostatue/diff-lcs
bugs :: https://github.com/halostatue/diff-lcs/issues
rdoc :: http://rubydoc.info/github/halostatue/diff-lcs
continuous integration :: {}[https://travis-ci.org/halostatue/diff-lcs]
test coverage :: {
}[https://coveralls.io/r/halostatue/diff-lcs]
== Description
Diff::LCS computes the difference between two Enumerable sequences using the
McIlroy-Hunt longest common subsequence (LCS) algorithm. It includes utilities
to create a simple HTML diff output format and a standard diff-like tool.
This is release 1.3, providing a tentative fix to a long-standing issue related
to incorrect detection of a patch direction. Also modernizes the gem
infrastructure, testing infrastructure, and provides a warning-free experience
to Ruby 2.4 users.
== Synopsis
Using this module is quite simple. By default, Diff::LCS does not extend
objects with the Diff::LCS interface, but will be called as if it were a
function:
require 'diff/lcs'
seq1 = %w(a b c e h j l m n p)
seq2 = %w(b c d e f j k l m r s t)
lcs = Diff::LCS.LCS(seq1, seq2)
diffs = Diff::LCS.diff(seq1, seq2)
sdiff = Diff::LCS.sdiff(seq1, seq2)
seq = Diff::LCS.traverse_sequences(seq1, seq2, callback_obj)
bal = Diff::LCS.traverse_balanced(seq1, seq2, callback_obj)
seq2 == Diff::LCS.patch!(seq1, diffs)
seq1 == Diff::LCS.unpatch!(seq2, diffs)
seq2 == Diff::LCS.patch!(seq1, sdiff)
seq1 == Diff::LCS.unpatch!(seq2, sdiff)
Objects can be extended with Diff::LCS:
seq1.extend(Diff::LCS)
lcs = seq1.lcs(seq2)
diffs = seq1.diff(seq2)
sdiff = seq1.sdiff(seq2)
seq = seq1.traverse_sequences(seq2, callback_obj)
bal = seq1.traverse_balanced(seq2, callback_obj)
seq2 == seq1.patch!(diffs)
seq1 == seq2.unpatch!(diffs)
seq2 == seq1.patch!(sdiff)
seq1 == seq2.unpatch!(sdiff)
By requiring 'diff/lcs/array' or 'diff/lcs/string', Array or String will be
extended for use this way.
Note that Diff::LCS requires a sequenced enumerable container, which means that
the order of enumeration is both predictable and consistent for the same set of
data. While it is theoretically possible to generate a diff for an unordered
hash, it will only be meaningful if the enumeration of the hashes is
consistent. In general, this will mean that containers that behave like String
or Array will perform best.
== History
Diff::LCS is a port of Perl's Algorithm::Diff that uses the McIlroy-Hunt
longest common subsequence (LCS) algorithm to compute intelligent differences
between two sequenced enumerable containers. The implementation is based on
Mario I. Wolczko's
{Smalltalk version 1.2}[ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st]
(1993) and Ned Konz's Perl version
{Algorithm::Diff 1.15}[http://search.cpan.org/~nedkonz/Algorithm-Diff-1.15/].
Diff::LCS#sdiff and Diff::LCS#traverse_balanced were originally written for the
Perl version by Mike Schilli.
The algorithm is described in A Fast Algorithm for Computing Longest Common
Subsequences, CACM, vol.20, no.5, pp.350-353, May 1977, with a few minor
improvements to improve the speed. A simplified description of the algorithm,
originally written for the Perl version, was written by Mark-Jason Dominus.
:include: Contributing.rdoc
:include: License.rdoc
diff-lcs-1.3/lib/ 0000755 0000041 0000041 00000000000 13041652267 013650 5 ustar www-data www-data diff-lcs-1.3/lib/diff/ 0000755 0000041 0000041 00000000000 13041652267 014560 5 ustar www-data www-data diff-lcs-1.3/lib/diff/lcs/ 0000755 0000041 0000041 00000000000 13041652267 015341 5 ustar www-data www-data diff-lcs-1.3/lib/diff/lcs/string.rb 0000644 0000041 0000041 00000000105 13041652267 017170 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
class String
include Diff::LCS
end
diff-lcs-1.3/lib/diff/lcs/ldiff.rb 0000644 0000041 0000041 00000011507 13041652267 016756 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'optparse'
require 'ostruct'
require 'diff/lcs/hunk'
module Diff::LCS::Ldiff #:nodoc:
BANNER = <<-COPYRIGHT
ldiff #{Diff::LCS::VERSION}
Copyright 2004-2014 Austin Ziegler
Part of Diff::LCS.
https://github.com/halostatue/diff-lcs
This program is free software. It may be redistributed and/or modified under
the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
MIT licence.
COPYRIGHT
end
class << Diff::LCS::Ldiff
attr_reader :format, :lines #:nodoc:
attr_reader :file_old, :file_new #:nodoc:
attr_reader :data_old, :data_new #:nodoc:
def run(args, input = $stdin, output = $stdout, error = $stderr) #:nodoc:
@binary = nil
args.options do |o|
o.banner = "Usage: #{File.basename($0)} [options] oldfile newfile"
o.separator ""
o.on('-c', '-C', '--context [LINES]', Numeric, 'Displays a context diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
@format = :context
@lines = ctx || 3
end
o.on('-u', '-U', '--unified [LINES]', Numeric, 'Displays a unified diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
@format = :unified
@lines = ctx || 3
end
o.on('-e', 'Creates an \'ed\' script to change', 'oldfile to newfile.') do |ctx|
@format = :ed
end
o.on('-f', 'Creates an \'ed\' script to change', 'oldfile to newfile in reverse order.') do |ctx|
@format = :reverse_ed
end
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|
@binary = false
end
o.on('--binary', 'Treats the files as binary.') do |bin|
@binary = true
end
o.on('-q', '--brief', 'Report only whether or not the files', 'differ, not the details.') do |ctx|
@format = :report
end
o.on_tail('--help', 'Shows this text.') do
error << o
return 0
end
o.on_tail('--version', 'Shows the version of Diff::LCS.') do
error << Diff::LCS::Ldiff::BANNER
return 0
end
o.on_tail ""
o.on_tail 'By default, runs produces an "old-style" diff, with output like UNIX diff.'
o.parse!
end
unless args.size == 2
error << args.options
return 127
end
# Defaults are for old-style diff
@format ||= :old
@lines ||= 0
file_old, file_new = *ARGV
case @format
when :context
char_old = '*' * 3
char_new = '-' * 3
when :unified
char_old = '-' * 3
char_new = '+' * 3
end
# After we've read up to a certain point in each file, the number of
# items we've read from each file will differ by FLD (could be 0).
file_length_difference = 0
if @binary.nil? or @binary
data_old = IO.read(file_old)
data_new = IO.read(file_new)
# Test binary status
if @binary.nil?
old_txt = data_old[0, 4096].scan(/\0/).empty?
new_txt = data_new[0, 4096].scan(/\0/).empty?
@binary = (not old_txt) or (not new_txt)
old_txt = new_txt = nil
end
unless @binary
data_old = data_old.split($/).map { |e| e.chomp }
data_new = data_new.split($/).map { |e| e.chomp }
end
else
data_old = IO.readlines(file_old).map { |e| e.chomp }
data_new = IO.readlines(file_new).map { |e| e.chomp }
end
# diff yields lots of pieces, each of which is basically a Block object
if @binary
diffs = (data_old == data_new)
else
diffs = Diff::LCS.diff(data_old, data_new)
diffs = nil if diffs.empty?
end
return 0 unless diffs
if @format == :report
output << "Files #{file_old} and #{file_new} differ\n"
return 1
end
if (@format == :unified) or (@format == :context)
ft = File.stat(file_old).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z')
output << "#{char_old} #{file_old}\t#{ft}\n"
ft = File.stat(file_new).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z')
output << "#{char_new} #{file_new}\t#{ft}\n"
end
# Loop over hunks. If a hunk overlaps with the last hunk, join them.
# Otherwise, print out the old one.
oldhunk = hunk = nil
if @format == :ed
real_output = output
output = []
end
diffs.each do |piece|
begin
hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, @lines,
file_length_difference)
file_length_difference = hunk.file_length_difference
next unless oldhunk
next if (@lines > 0) and hunk.merge(oldhunk)
output << oldhunk.diff(@format) << "\n"
ensure
oldhunk = hunk
end
end
output << oldhunk.diff(@format) << "\n"
if @format == :ed
output.reverse_each { |e| real_output << e.diff(:ed_finish) }
end
return 1
end
end
diff-lcs-1.3/lib/diff/lcs/change.rb 0000644 0000041 0000041 00000010330 13041652267 017110 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
# Represents a simplistic (non-contextual) change. Represents the removal or
# addition of an element from either the old or the new sequenced
# enumerable.
class Diff::LCS::Change
IntClass = 1.class # Fixnum is deprecated in Ruby 2.4
# The only actions valid for changes are '+' (add), '-' (delete), '='
# (no change), '!' (changed), '<' (tail changes from first sequence), or
# '>' (tail changes from second sequence). The last two ('<>') are only
# found with Diff::LCS::diff and Diff::LCS::sdiff.
VALID_ACTIONS = %W(+ - = ! > <)
def self.valid_action?(action)
VALID_ACTIONS.include? action
end
# Returns the action this Change represents.
attr_reader :action
# Returns the position of the Change.
attr_reader :position
# Returns the sequence element of the Change.
attr_reader :element
def initialize(*args)
@action, @position, @element = *args
unless Diff::LCS::Change.valid_action?(@action)
raise "Invalid Change Action '#{@action}'"
end
raise "Invalid Position Type" unless @position.kind_of? IntClass
end
def inspect
to_a.inspect
end
def to_a
[ @action, @position, @element ]
end
def self.from_a(arr)
arr = arr.flatten(1)
case arr.size
when 5
Diff::LCS::ContextChange.new(*(arr[0...5]))
when 3
Diff::LCS::Change.new(*(arr[0...3]))
else
raise "Invalid change array format provided."
end
end
include Comparable
def ==(other)
(self.class == other.class) and
(self.action == other.action) and
(self.position == other.position) and
(self.element == other.element)
end
def <=>(other)
r = self.action <=> other.action
r = self.position <=> other.position if r.zero?
r = self.element <=> other.element if r.zero?
r
end
def adding?
@action == '+'
end
def deleting?
@action == '-'
end
def unchanged?
@action == '='
end
def changed?
@action == '!'
end
def finished_a?
@action == '>'
end
def finished_b?
@action == '<'
end
end
# Represents a contextual change. Contains the position and values of the
# elements in the old and the new sequenced enumerables as well as the action
# taken.
class Diff::LCS::ContextChange < Diff::LCS::Change
# We don't need these two values.
undef :position
undef :element
# Returns the old position being changed.
attr_reader :old_position
# Returns the new position being changed.
attr_reader :new_position
# Returns the old element being changed.
attr_reader :old_element
# Returns the new element being changed.
attr_reader :new_element
def initialize(*args)
@action, @old_position, @old_element, @new_position, @new_element = *args
unless Diff::LCS::Change.valid_action?(@action)
raise "Invalid Change Action '#{@action}'"
end
unless @old_position.nil? or @old_position.kind_of? IntClass
raise "Invalid (Old) Position Type"
end
unless @new_position.nil? or @new_position.kind_of? IntClass
raise "Invalid (New) Position Type"
end
end
def to_a
[ @action,
[ @old_position, @old_element ],
[ @new_position, @new_element ]
]
end
def inspect(*args)
to_a.inspect
end
def self.from_a(arr)
Diff::LCS::Change.from_a(arr)
end
# Simplifies a context change for use in some diff callbacks. '<' actions
# are converted to '-' and '>' actions are converted to '+'.
def self.simplify(event)
ea = event.to_a
case ea[0]
when '-'
ea[2][1] = nil
when '<'
ea[0] = '-'
ea[2][1] = nil
when '+'
ea[1][1] = nil
when '>'
ea[0] = '+'
ea[1][1] = nil
end
Diff::LCS::ContextChange.from_a(ea)
end
def ==(other)
(self.class == other.class) and
(@action == other.action) and
(@old_position == other.old_position) and
(@new_position == other.new_position) and
(@old_element == other.old_element) and
(@new_element == other.new_element)
end
def <=>(other)
r = @action <=> other.action
r = @old_position <=> other.old_position if r.zero?
r = @new_position <=> other.new_position if r.zero?
r = @old_element <=> other.old_element if r.zero?
r = @new_element <=> other.new_element if r.zero?
r
end
end
diff-lcs-1.3/lib/diff/lcs/internals.rb 0000644 0000041 0000041 00000020445 13041652267 017672 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
class << Diff::LCS
def diff_traversal(method, seq1, seq2, callbacks, &block)
callbacks = callbacks_for(callbacks)
case method
when :diff
traverse_sequences(seq1, seq2, callbacks)
when :sdiff
traverse_balanced(seq1, seq2, callbacks)
end
callbacks.finish if callbacks.respond_to? :finish
if block
callbacks.diffs.map do |hunk|
if hunk.kind_of? Array
hunk.map { |hunk_block| block[hunk_block] }
else
block[hunk]
end
end
else
callbacks.diffs
end
end
private :diff_traversal
end
module Diff::LCS::Internals # :nodoc:
end
class << Diff::LCS::Internals
# Compute the longest common subsequence between the sequenced
# Enumerables +a+ and +b+. The result is an array whose contents is such
# that
#
# result = Diff::LCS::Internals.lcs(a, b)
# result.each_with_index do |e, i|
# assert_equal(a[i], b[e]) unless e.nil?
# end
def lcs(a, b)
a_start = b_start = 0
a_finish = a.size - 1
b_finish = b.size - 1
vector = []
# Prune off any common elements at the beginning...
while ((a_start <= a_finish) and (b_start <= b_finish) and
(a[a_start] == b[b_start]))
vector[a_start] = b_start
a_start += 1
b_start += 1
end
b_start = a_start
# Now the end...
while ((a_start <= a_finish) and (b_start <= b_finish) and
(a[a_finish] == b[b_finish]))
vector[a_finish] = b_finish
a_finish -= 1
b_finish -= 1
end
# Now, compute the equivalence classes of positions of elements.
b_matches = position_hash(b, b_start..b_finish)
thresh = []
links = []
string = a.kind_of?(String)
(a_start .. a_finish).each do |i|
ai = string ? a[i, 1] : a[i]
bm = b_matches[ai]
k = nil
bm.reverse_each do |j|
if k and (thresh[k] > j) and (thresh[k - 1] < j)
thresh[k] = j
else
k = replace_next_larger(thresh, j, k)
end
links[k] = [ (k > 0) ? links[k - 1] : nil, i, j ] unless k.nil?
end
end
unless thresh.empty?
link = links[thresh.size - 1]
while not link.nil?
vector[link[1]] = link[2]
link = link[0]
end
end
vector
end
# This method will analyze the provided patchset to provide a
# single-pass normalization (conversion of the array form of
# Diff::LCS::Change objects to the object form of same) and detection of
# whether the patchset represents changes to be made.
def analyze_patchset(patchset, depth = 0)
raise "Patchset too complex" if depth > 1
has_changes = false
# Format:
# [ # patchset
# # hunk (change)
# [ # hunk
# # change
# ]
# ]
patchset = patchset.map do |hunk|
case hunk
when Diff::LCS::Change
has_changes ||= !hunk.unchanged?
hunk
when Array
# Detect if the 'hunk' is actually an array-format
# Change object.
if Diff::LCS::Change.valid_action? hunk[0]
hunk = Diff::LCS::Change.from_a(hunk)
has_changes ||= !hunk.unchanged?
hunk
else
with_changes, hunk = analyze_patchset(hunk, depth + 1)
has_changes ||= with_changes
hunk.flatten
end
else
raise ArgumentError, "Cannot normalise a hunk of class #{hunk.class}."
end
end
[ has_changes, patchset.flatten(1) ]
end
# Examine the patchset and the source to see in which direction the
# patch should be applied.
#
# WARNING: By default, this examines the whole patch, so this could take
# some time. This also works better with Diff::LCS::ContextChange or
# Diff::LCS::Change as its source, as an array will cause the creation
# of one of the above.
def intuit_diff_direction(src, patchset, limit = nil)
string = src.kind_of?(String)
count = left_match = left_miss = right_match = right_miss = 0
patchset.each do |change|
count += 1
case change
when Diff::LCS::ContextChange
le = string ? src[change.old_position, 1] : src[change.old_position]
re = string ? src[change.new_position, 1] : src[change.new_position]
case change.action
when '-' # Remove details from the old string
if le == change.old_element
left_match += 1
else
left_miss += 1
end
when '+'
if re == change.new_element
right_match += 1
else
right_miss += 1
end
when '='
left_miss += 1 if le != change.old_element
right_miss += 1 if re != change.new_element
when '!'
if le == change.old_element
left_match += 1
else
if re == change.new_element
right_match += 1
else
left_miss += 1
right_miss += 1
end
end
end
when Diff::LCS::Change
# With a simplistic change, we can't tell the difference between
# the left and right on '!' actions, so we ignore those. On '='
# actions, if there's a miss, we miss both left and right.
element = string ? src[change.position, 1] : src[change.position]
case change.action
when '-'
if element == change.element
left_match += 1
else
left_miss += 1
end
when '+'
if element == change.element
right_match += 1
else
right_miss += 1
end
when '='
if element != change.element
left_miss += 1
right_miss += 1
end
end
end
break if (not limit.nil?) && (count > limit)
end
no_left = (left_match == 0) && (left_miss > 0)
no_right = (right_match == 0) && (right_miss > 0)
case [ no_left, no_right ]
when [ false, true ]
:patch
when [ true, false ]
:unpatch
else
case left_match <=> right_match
when 1
if left_miss.zero?
:patch
else
:unpatch
end
when -1
if right_miss.zero?
:unpatch
else
:patch
end
else
raise "The provided patchset does not appear to apply to the provided enumerable as either source or destination value."
end
end
end
# Find the place at which +value+ would normally be inserted into the
# Enumerable. If that place is already occupied by +value+, do nothing
# and return +nil+. If the place does not exist (i.e., it is off the end
# of the Enumerable), add it to the end. Otherwise, replace the element
# at that point with +value+. It is assumed that the Enumerable's values
# are numeric.
#
# This operation preserves the sort order.
def replace_next_larger(enum, value, last_index = nil)
# Off the end?
if enum.empty? or (value > enum[-1])
enum << value
return enum.size - 1
end
# Binary search for the insertion point
last_index ||= enum.size
first_index = 0
while (first_index <= last_index)
i = (first_index + last_index) >> 1
found = enum[i]
if value == found
return nil
elsif value > found
first_index = i + 1
else
last_index = i - 1
end
end
# The insertion point is in first_index; overwrite the next larger
# value.
enum[first_index] = value
return first_index
end
private :replace_next_larger
# If +vector+ maps the matching elements of another collection onto this
# Enumerable, compute the inverse of +vector+ that maps this Enumerable
# onto the collection. (Currently unused.)
def inverse_vector(a, vector)
inverse = a.dup
(0...vector.size).each do |i|
inverse[vector[i]] = i unless vector[i].nil?
end
inverse
end
private :inverse_vector
# Returns a hash mapping each element of an Enumerable to the set of
# positions it occupies in the Enumerable, optionally restricted to the
# elements specified in the range of indexes specified by +interval+.
def position_hash(enum, interval)
string = enum.kind_of?(String)
hash = Hash.new { |h, k| h[k] = [] }
interval.each do |i|
k = string ? enum[i, 1] : enum[i]
hash[k] << i
end
hash
end
private :position_hash
end
diff-lcs-1.3/lib/diff/lcs/block.rb 0000644 0000041 0000041 00000001372 13041652267 016763 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
# A block is an operation removing, adding, or changing a group of items.
# Basically, this is just a list of changes, where each change adds or
# deletes a single item. Used by bin/ldiff.
class Diff::LCS::Block
attr_reader :changes, :insert, :remove
def initialize(chunk)
@changes = []
@insert = []
@remove = []
chunk.each do |item|
@changes << item
@remove << item if item.deleting?
@insert << item if item.adding?
end
end
def diff_size
@insert.size - @remove.size
end
def op
case [@remove.empty?, @insert.empty?]
when [false, false]
'!'
when [false, true]
'-'
when [true, false]
'+'
else # [true, true]
'^'
end
end
end
diff-lcs-1.3/lib/diff/lcs/array.rb 0000644 0000041 0000041 00000000130 13041652267 016776 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'diff/lcs'
class Array
include Diff::LCS
end
diff-lcs-1.3/lib/diff/lcs/callbacks.rb 0000644 0000041 0000041 00000024467 13041652267 017622 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'diff/lcs/change'
module Diff::LCS
# This callback object implements the default set of callback events,
# which only returns the event itself. Note that #finished_a and
# #finished_b are not implemented -- I haven't yet figured out where they
# would be useful.
#
# Note that this is intended to be called as is, e.g.,
#
# Diff::LCS.LCS(seq1, seq2, Diff::LCS::DefaultCallbacks)
class DefaultCallbacks
class << self
# Called when two items match.
def match(event)
event
end
# Called when the old value is discarded in favour of the new value.
def discard_a(event)
event
end
# Called when the new value is discarded in favour of the old value.
def discard_b(event)
event
end
# Called when both the old and new values have changed.
def change(event)
event
end
private :new
end
end
# An alias for DefaultCallbacks that is used in
# Diff::LCS#traverse_sequences.
#
# Diff::LCS.LCS(seq1, seq2, Diff::LCS::SequenceCallbacks)
SequenceCallbacks = DefaultCallbacks
# An alias for DefaultCallbacks that is used in
# Diff::LCS#traverse_balanced.
#
# Diff::LCS.LCS(seq1, seq2, Diff::LCS::BalancedCallbacks)
BalancedCallbacks = DefaultCallbacks
def self.callbacks_for(callbacks)
callbacks.new rescue callbacks
end
end
# This will produce a compound array of simple diff change objects. Each
# element in the #diffs array is a +hunk+ or +hunk+ array, where each
# element in each +hunk+ array is a single Change object representing the
# addition or removal of a single element from one of the two tested
# sequences. The +hunk+ provides the full context for the changes.
#
# diffs = Diff::LCS.diff(seq1, seq2)
# # This example shows a simplified array format.
# # [ [ [ '-', 0, 'a' ] ], # 1
# # [ [ '+', 2, 'd' ] ], # 2
# # [ [ '-', 4, 'h' ], # 3
# # [ '+', 4, 'f' ] ],
# # [ [ '+', 6, 'k' ] ], # 4
# # [ [ '-', 8, 'n' ], # 5
# # [ '-', 9, 'p' ],
# # [ '+', 9, 'r' ],
# # [ '+', 10, 's' ],
# # [ '+', 11, 't' ] ] ]
#
# There are five hunks here. The first hunk says that the +a+ at position 0
# of the first sequence should be deleted ('-'). The second hunk
# says that the +d+ at position 2 of the second sequence should be inserted
# ('+'). The third hunk says that the +h+ at position 4 of the
# first sequence should be removed and replaced with the +f+ from position 4
# of the second sequence. The other two hunks are described similarly.
#
# === Use
#
# This callback object must be initialised and is used by the Diff::LCS#diff
# method.
#
# cbo = Diff::LCS::DiffCallbacks.new
# Diff::LCS.LCS(seq1, seq2, cbo)
# cbo.finish
#
# Note that the call to #finish is absolutely necessary, or the last set of
# changes will not be visible. Alternatively, can be used as:
#
# cbo = Diff::LCS::DiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
#
# The necessary #finish call will be made.
#
# === Simplified Array Format
#
# The simplified array format used in the example above can be obtained
# with:
#
# require 'pp'
# pp diffs.map { |e| e.map { |f| f.to_a } }
class Diff::LCS::DiffCallbacks
# Returns the difference set collected during the diff process.
attr_reader :diffs
def initialize # :yields self:
@hunk = []
@diffs = []
if block_given?
begin
yield self
ensure
self.finish
end
end
end
# Finalizes the diff process. If an unprocessed hunk still exists, then it
# is appended to the diff list.
def finish
finish_hunk
end
def match(event)
finish_hunk
end
def discard_a(event)
@hunk << Diff::LCS::Change.new('-', event.old_position, event.old_element)
end
def discard_b(event)
@hunk << Diff::LCS::Change.new('+', event.new_position, event.new_element)
end
def finish_hunk
@diffs << @hunk unless @hunk.empty?
@hunk = []
end
private :finish_hunk
end
# This will produce a compound array of contextual diff change objects. Each
# element in the #diffs array is a "hunk" array, where each element in each
# "hunk" array is a single change. Each change is a Diff::LCS::ContextChange
# that contains both the old index and new index values for the change. The
# "hunk" provides the full context for the changes. Both old and new objects
# will be presented for changed objects. +nil+ will be substituted for a
# discarded object.
#
# seq1 = %w(a b c e h j l m n p)
# seq2 = %w(b c d e f j k l m r s t)
#
# diffs = Diff::LCS.diff(seq1, seq2, Diff::LCS::ContextDiffCallbacks)
# # This example shows a simplified array format.
# # [ [ [ '-', [ 0, 'a' ], [ 0, nil ] ] ], # 1
# # [ [ '+', [ 3, nil ], [ 2, 'd' ] ] ], # 2
# # [ [ '-', [ 4, 'h' ], [ 4, nil ] ], # 3
# # [ '+', [ 5, nil ], [ 4, 'f' ] ] ],
# # [ [ '+', [ 6, nil ], [ 6, 'k' ] ] ], # 4
# # [ [ '-', [ 8, 'n' ], [ 9, nil ] ], # 5
# # [ '+', [ 9, nil ], [ 9, 'r' ] ],
# # [ '-', [ 9, 'p' ], [ 10, nil ] ],
# # [ '+', [ 10, nil ], [ 10, 's' ] ],
# # [ '+', [ 10, nil ], [ 11, 't' ] ] ] ]
#
# The five hunks shown are comprised of individual changes; if there is a
# related set of changes, they are still shown individually.
#
# This callback can also be used with Diff::LCS#sdiff, which will produce
# results like:
#
# diffs = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextCallbacks)
# # This example shows a simplified array format.
# # [ [ [ "-", [ 0, "a" ], [ 0, nil ] ] ], # 1
# # [ [ "+", [ 3, nil ], [ 2, "d" ] ] ], # 2
# # [ [ "!", [ 4, "h" ], [ 4, "f" ] ] ], # 3
# # [ [ "+", [ 6, nil ], [ 6, "k" ] ] ], # 4
# # [ [ "!", [ 8, "n" ], [ 9, "r" ] ], # 5
# # [ "!", [ 9, "p" ], [ 10, "s" ] ],
# # [ "+", [ 10, nil ], [ 11, "t" ] ] ] ]
#
# The five hunks are still present, but are significantly shorter in total
# presentation, because changed items are shown as changes ("!") instead of
# potentially "mismatched" pairs of additions and deletions.
#
# The result of this operation is similar to that of
# Diff::LCS::SDiffCallbacks. They may be compared as:
#
# s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" }
# c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten
#
# s == c # -> true
#
# === Use
#
# This callback object must be initialised and can be used by the
# Diff::LCS#diff or Diff::LCS#sdiff methods.
#
# cbo = Diff::LCS::ContextDiffCallbacks.new
# Diff::LCS.LCS(seq1, seq2, cbo)
# cbo.finish
#
# Note that the call to #finish is absolutely necessary, or the last set of
# changes will not be visible. Alternatively, can be used as:
#
# cbo = Diff::LCS::ContextDiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
#
# The necessary #finish call will be made.
#
# === Simplified Array Format
#
# The simplified array format used in the example above can be obtained
# with:
#
# require 'pp'
# pp diffs.map { |e| e.map { |f| f.to_a } }
class Diff::LCS::ContextDiffCallbacks < Diff::LCS::DiffCallbacks
def discard_a(event)
@hunk << Diff::LCS::ContextChange.simplify(event)
end
def discard_b(event)
@hunk << Diff::LCS::ContextChange.simplify(event)
end
def change(event)
@hunk << Diff::LCS::ContextChange.simplify(event)
end
end
# This will produce a simple array of diff change objects. Each element in
# the #diffs array is a single ContextChange. In the set of #diffs provided
# by SDiffCallbacks, both old and new objects will be presented for both
# changed and unchanged objects. +nil+ will be substituted
# for a discarded object.
#
# The diffset produced by this callback, when provided to Diff::LCS#sdiff,
# will compute and display the necessary components to show two sequences
# and their minimized differences side by side, just like the Unix utility
# +sdiff+.
#
# same same
# before | after
# old < -
# - > new
#
# seq1 = %w(a b c e h j l m n p)
# seq2 = %w(b c d e f j k l m r s t)
#
# diffs = Diff::LCS.sdiff(seq1, seq2)
# # This example shows a simplified array format.
# # [ [ "-", [ 0, "a"], [ 0, nil ] ],
# # [ "=", [ 1, "b"], [ 0, "b" ] ],
# # [ "=", [ 2, "c"], [ 1, "c" ] ],
# # [ "+", [ 3, nil], [ 2, "d" ] ],
# # [ "=", [ 3, "e"], [ 3, "e" ] ],
# # [ "!", [ 4, "h"], [ 4, "f" ] ],
# # [ "=", [ 5, "j"], [ 5, "j" ] ],
# # [ "+", [ 6, nil], [ 6, "k" ] ],
# # [ "=", [ 6, "l"], [ 7, "l" ] ],
# # [ "=", [ 7, "m"], [ 8, "m" ] ],
# # [ "!", [ 8, "n"], [ 9, "r" ] ],
# # [ "!", [ 9, "p"], [ 10, "s" ] ],
# # [ "+", [ 10, nil], [ 11, "t" ] ] ]
#
# The result of this operation is similar to that of
# Diff::LCS::ContextDiffCallbacks. They may be compared as:
#
# s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" }
# c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten
#
# s == c # -> true
#
# === Use
#
# This callback object must be initialised and is used by the Diff::LCS#sdiff
# method.
#
# cbo = Diff::LCS::SDiffCallbacks.new
# Diff::LCS.LCS(seq1, seq2, cbo)
#
# As with the other initialisable callback objects,
# Diff::LCS::SDiffCallbacks can be initialised with a block. As there is no
# "fininishing" to be done, this has no effect on the state of the object.
#
# cbo = Diff::LCS::SDiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
#
# === Simplified Array Format
#
# The simplified array format used in the example above can be obtained
# with:
#
# require 'pp'
# pp diffs.map { |e| e.to_a }
class Diff::LCS::SDiffCallbacks
# Returns the difference set collected during the diff process.
attr_reader :diffs
def initialize #:yields self:
@diffs = []
yield self if block_given?
end
def match(event)
@diffs << Diff::LCS::ContextChange.simplify(event)
end
def discard_a(event)
@diffs << Diff::LCS::ContextChange.simplify(event)
end
def discard_b(event)
@diffs << Diff::LCS::ContextChange.simplify(event)
end
def change(event)
@diffs << Diff::LCS::ContextChange.simplify(event)
end
end
diff-lcs-1.3/lib/diff/lcs/hunk.rb 0000644 0000041 0000041 00000020616 13041652267 016640 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'diff/lcs/block'
# A Hunk is a group of Blocks which overlap because of the context
# surrounding each block. (So if we're not using context, every hunk will
# contain one block.) Used in the diff program (bin/diff).
class Diff::LCS::Hunk
# Create a hunk using references to both the old and new data, as well as
# the piece of data.
def initialize(data_old, data_new, piece, flag_context, file_length_difference)
# At first, a hunk will have just one Block in it
@blocks = [ Diff::LCS::Block.new(piece) ]
if String.method_defined?(:encoding)
@preferred_data_encoding = data_old.fetch(0, data_new.fetch(0,'') ).encoding
end
@data_old = data_old
@data_new = data_new
before = after = file_length_difference
after += @blocks[0].diff_size
@file_length_difference = after # The caller must get this manually
# Save the start & end of each array. If the array doesn't exist (e.g.,
# we're only adding items in this block), then figure out the line
# number based on the line number of the other file and the current
# difference in file lengths.
if @blocks[0].remove.empty?
a1 = a2 = nil
else
a1 = @blocks[0].remove[0].position
a2 = @blocks[0].remove[-1].position
end
if @blocks[0].insert.empty?
b1 = b2 = nil
else
b1 = @blocks[0].insert[0].position
b2 = @blocks[0].insert[-1].position
end
@start_old = a1 || (b1 - before)
@start_new = b1 || (a1 + before)
@end_old = a2 || (b2 - after)
@end_new = b2 || (a2 + after)
self.flag_context = flag_context
end
attr_reader :blocks
attr_reader :start_old, :start_new
attr_reader :end_old, :end_new
attr_reader :file_length_difference
# Change the "start" and "end" fields to note that context should be added
# to this hunk.
attr_accessor :flag_context
undef :flag_context=;
def flag_context=(context) #:nodoc:
return if context.nil? or context.zero?
add_start = (context > @start_old) ? @start_old : context
@start_old -= add_start
@start_new -= add_start
if (@end_old + context) > @data_old.size
add_end = @data_old.size - @end_old
else
add_end = context
end
@end_old += add_end
@end_new += add_end
end
# Merges this hunk and the provided hunk together if they overlap. Returns
# a truthy value so that if there is no overlap, you can know the merge
# was skipped.
def merge(hunk)
if overlaps?(hunk)
@start_old = hunk.start_old
@start_new = hunk.start_new
blocks.unshift(*hunk.blocks)
else
nil
end
end
alias_method :unshift, :merge
# Determines whether there is an overlap between this hunk and the
# provided hunk. This will be true if the difference between the two hunks
# start or end positions is within one position of each other.
def overlaps?(hunk)
hunk and (((@start_old - hunk.end_old) <= 1) or
((@start_new - hunk.end_new) <= 1))
end
# Returns a diff string based on a format.
def diff(format)
case format
when :old
old_diff
when :unified
unified_diff
when :context
context_diff
when :ed
self
when :reverse_ed, :ed_finish
ed_diff(format)
else
raise "Unknown diff format #{format}."
end
end
# Note that an old diff can't have any context. Therefore, we know that
# there's only one block in the hunk.
def old_diff
warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1
op_act = { "+" => 'a', "-" => 'd', "!" => "c" }
block = @blocks[0]
# Calculate item number range. Old diff range is just like a context
# diff range, except the ranges are on one line with the action between
# them.
s = encode("#{context_range(:old)}#{op_act[block.op]}#{context_range(:new)}\n")
# If removing anything, just print out all the remove lines in the hunk
# which is just all the remove lines in the block.
@data_old[@start_old .. @end_old].each { |e| s << encode("< ") + e + encode("\n") } unless block.remove.empty?
s << encode("---\n") if block.op == "!"
@data_new[@start_new .. @end_new].each { |e| s << encode("> ") + e + encode("\n") } unless block.insert.empty?
s
end
private :old_diff
def unified_diff
# Calculate item number range.
s = encode("@@ -#{unified_range(:old)} +#{unified_range(:new)} @@\n")
# Outlist starts containing the hunk of the old file. Removing an item
# just means putting a '-' in front of it. Inserting an item requires
# getting it from the new file and splicing it in. We splice in
# +num_added+ items. Remove blocks use +num_added+ because splicing
# changed the length of outlist.
#
# We remove +num_removed+ items. Insert blocks use +num_removed+
# because their item numbers -- corresponding to positions in the NEW
# file -- don't take removed items into account.
lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0
outlist = @data_old[lo .. hi].map { |e| e.insert(0, encode(' ')) }
@blocks.each do |block|
block.remove.each do |item|
op = item.action.to_s # -
offset = item.position - lo + num_added
outlist[offset][0, 1] = encode(op)
num_removed += 1
end
block.insert.each do |item|
op = item.action.to_s # +
offset = item.position - @start_new + num_removed
outlist[offset, 0] = encode(op) + @data_new[item.position]
num_added += 1
end
end
s << outlist.join(encode("\n"))
end
private :unified_diff
def context_diff
s = encode("***************\n")
s << encode("*** #{context_range(:old)} ****\n")
r = context_range(:new)
# Print out file 1 part for each block in context diff format if there
# are any blocks that remove items
lo, hi = @start_old, @end_old
removes = @blocks.select { |e| not e.remove.empty? }
if removes
outlist = @data_old[lo .. hi].map { |e| e.insert(0, encode(' ')) }
removes.each do |block|
block.remove.each do |item|
outlist[item.position - lo][0, 1] = encode(block.op) # - or !
end
end
s << outlist.join("\n")
end
s << encode("\n--- #{r} ----\n")
lo, hi = @start_new, @end_new
inserts = @blocks.select { |e| not e.insert.empty? }
if inserts
outlist = @data_new[lo .. hi].collect { |e| e.insert(0, encode(' ')) }
inserts.each do |block|
block.insert.each do |item|
outlist[item.position - lo][0, 1] = encode(block.op) # + or !
end
end
s << outlist.join("\n")
end
s
end
private :context_diff
def ed_diff(format)
op_act = { "+" => 'a', "-" => 'd', "!" => "c" }
warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1
if format == :reverse_ed
s = encode("#{op_act[@blocks[0].op]}#{context_range(:old)}\n")
else
s = encode("#{context_range(:old, ' ')}#{op_act[@blocks[0].op]}\n")
end
unless @blocks[0].insert.empty?
@data_new[@start_new .. @end_new].each { |e| s << e + encode("\n") }
s << encode(".\n")
end
s
end
private :ed_diff
# Generate a range of item numbers to print. Only print 1 number if the
# range has only one item in it. Otherwise, it's 'start,end'
def context_range(mode, op = ',')
case mode
when :old
s, e = (@start_old + 1), (@end_old + 1)
when :new
s, e = (@start_new + 1), (@end_new + 1)
end
(s < e) ? "#{s}#{op}#{e}" : "#{e}"
end
private :context_range
# Generate a range of item numbers to print for unified diff. Print number
# where block starts, followed by number of lines in the block
# (don't print number of lines if it's 1)
def unified_range(mode)
case mode
when :old
s, e = (@start_old + 1), (@end_old + 1)
when :new
s, e = (@start_new + 1), (@end_new + 1)
end
length = e - s + 1
first = (length < 2) ? e : s # "strange, but correct"
(length == 1) ? "#{first}" : "#{first},#{length}"
end
private :unified_range
if String.method_defined?(:encoding)
def encode(literal, target_encoding = @preferred_data_encoding)
literal.encode target_encoding
end
def encode_as(string, *args)
args.map { |arg| arg.encode(string.encoding) }
end
else
def encode(literal, target_encoding = nil)
literal
end
def encode_as(string, *args)
args
end
end
private :encode
private :encode_as
end
diff-lcs-1.3/lib/diff/lcs/htmldiff.rb 0000644 0000041 0000041 00000006174 13041652267 017473 0 ustar www-data www-data # -*- ruby encoding: utf-8 -*-
require 'cgi'
class Diff::LCS::HTMLDiff
class << self
attr_accessor :can_expand_tabs #:nodoc:
end
self.can_expand_tabs = true
class Callbacks
attr_accessor :output
attr_accessor :match_class
attr_accessor :only_a_class
attr_accessor :only_b_class
def initialize(output, options = {})
@output = output
options ||= {}
@match_class = options[:match_class] || "match"
@only_a_class = options[:only_a_class] || "only_a"
@only_b_class = options[:only_b_class] || "only_b"
end
def htmlize(element, css_class)
element = " " if element.empty?
%Q|
#{element}\n| end private :htmlize # This will be called with both lines are the same def match(event) @output << htmlize(event.old_element, :match_class) end # This will be called when there is a line in A that isn't in B def discard_a(event) @output << htmlize(event.old_element, :only_a_class) end # This will be called when there is a line in B that isn't in A def discard_b(event) @output << htmlize(event.new_element, :only_b_class) end end DEFAULT_OPTIONS = { :expand_tabs => nil, :output => nil, :css => nil, :title => nil, } DEFAULT_CSS = <<-CSS body { margin: 0; } .diff { border: 1px solid black; margin: 1em 2em; } p { margin-left: 2em; } pre { padding-left: 1em; margin: 0; font-family: Inconsolata, Consolas, Lucida, Courier, monospaced; white-space: pre; } .match { } .only_a { background-color: #fdd; color: red; text-decoration: line-through; } .only_b { background-color: #ddf; color: blue; border-left: 3px solid blue } h1 { margin-left: 2em; } CSS def initialize(left, right, options = nil) @left = left @right = right @options = options @options = DEFAULT_OPTIONS.dup if @options.nil? end def verify_options @options[:expand_tabs] ||= 4 @options[:expand_tabs] = 4 if @options[:expand_tabs] < 0 @options[:output] ||= $stdout @options[:css] ||= DEFAULT_CSS.dup @options[:title] ||= "diff" end private :verify_options attr_reader :options def run verify_options if @options[:expand_tabs] > 0 && self.class.can_expand_tabs formatter = Text::Format.new formatter.tabstop = @options[:expand_tabs] @left.map! { |line| formatter.expand(line.chomp) } @right.map! { |line| formatter.expand(line.chomp) } end @left.map! { |line| CGI.escapeHTML(line.chomp) } @right.map! { |line| CGI.escapeHTML(line.chomp) } @options[:output] << <<-OUTPUT
Legend: Only in Old Only in New