summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--History.rdoc5
-rw-r--r--bin/htmldiff120
-rw-r--r--bin/ldiff41
-rw-r--r--lib/diff/lcs/htmldiff.rb151
4 files changed, 177 insertions, 140 deletions
diff --git a/History.rdoc b/History.rdoc
index feebcf0..0943f0f 100644
--- a/History.rdoc
+++ b/History.rdoc
@@ -1,5 +1,10 @@
== 1.1.3 / 2011-07-30
+* Converted to 'hoe' for release.
+* Converted tests to RSpec 2.
* Bugs fixed:
+ - Issue 4 (https://github.com/halostatue/diff-lcs/issues/4) fixed.
+ In the process, HTMLDiff was made easier to use outside of the htmldiff
+ command.
== 1.1.2 / 2004-10-20
* Fixed a problem reported by Mauricio Fernandez in htmldiff. Future versions
diff --git a/bin/htmldiff b/bin/htmldiff
index 509860c..056564c 100644
--- a/bin/htmldiff
+++ b/bin/htmldiff
@@ -1,112 +1,32 @@
#!/usr/bin/env ruby
-#--
-# Copyright 2004 Austin Ziegler <diff-lcs@halostatue.ca>
-# adapted from:
-# Algorithm::Diff (Perl) by Ned Konz <perl@bike-nomad.com>
-# Smalltalk by Mario I. Wolczko <mario@wolczko.com>
-# implements McIlroy-Hunt diff algorithm
-#
-# 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
-# Ruby licence.
-#
-# $Id$
-#++
+
+require 'diff/lcs'
+require 'diff/lcs/htmldiff'
begin
- require 'rubygems'
- require_gem 'diff-lcs', "1.1.1"
- require 'diff/lcs/string'
+ require 'text/format'
rescue LoadError
- require 'diff/lcs'
- require 'diff/lcs/string'
+ Diff::LCS::HTMLDiff.can_expand_tabs = false
end
-require 'text/format'
+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
-class HTMLDiff #:nodoc:
- attr_accessor :output
+left = IO.read(ARGV[0]).split($/)
+right = IO.read(ARGV[1]).split($/)
- def initialize(output)
- @output = output
- end
+options = { :title => "diff #{ARGV[0]} #{ARGV[1]}" }
- # This will be called with both lines are the same
- def match(event)
- @output << %Q|<pre class="match">#{event.old_element}</pre>\n|
- end
+htmldiff = Diff::LCS::HTMLDiff.new(left, right, options)
- # This will be called when there is a line in A that isn't in B
- def discard_a(event)
- @output << %Q|<pre class="only_a">#{event.old_element}</pre>\n|
+if ARGV[2]
+ File.open(ARGV[2], "w") do |f|
+ htmldiff.options[:output] = f
+ htmldiff.run
end
-
- # This will be called when there is a line in B that isn't in A
- def discard_b(event)
- @output << %Q|<pre class="only_b">#{event.new_element}</pre>\n|
- end
-end
-
-if ARGV.size != 2
- puts "usage: #{File.basename($0)} old new > output.html"
- exit 255
+else
+ htmldiff.run
end
-
-hd = HTMLDiff.new($stdout)
-tf = Text::Format.new
-tf.tabstop = 4
-
-preprocess = lambda { |line| tf.expand(line.chomp) }
-
-a = IO.readlines(ARGV[0]).map(&preprocess)
-b = IO.readlines(ARGV[1]).map(&preprocess)
-
-$stdout.write <<-START
-<html>
- <head>
- <title>diff #{ARGV[0]} #{ARGV[1]}</title>
- <style>
- body { margin: 0; }
- .diff
- {
- border: 1px solid black;
- margin: 1em 2em;
- }
- pre
- {
- padding-left: 1em;
- margin: 0;
- font-family: 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; }
- </style>
- </head>
- <body>
- <h1>diff&nbsp;
- <span class="only_a">#{ARGV[0]}</span>&nbsp;
- <span class="only_b">#{ARGV[1]}</span>
- </h1>
- <div class="diff">
-START
-
-Diff::LCS.traverse_sequences(a, b, hd)
-
-$stdout.write <<-END
- </div>
- </body>
-</html>
-END
diff --git a/bin/ldiff b/bin/ldiff
index ae2b245..21b4ed8 100644
--- a/bin/ldiff
+++ b/bin/ldiff
@@ -1,45 +1,6 @@
#!/usr/bin/env ruby
-#--
-# Copyright 2004 Austin Ziegler <diff-lcs@halostatue.ca>
-# adapted from:
-# Algorithm::Diff (Perl) by Ned Konz <perl@bike-nomad.com>
-# Smalltalk by Mario I. Wolczko <mario@wolczko.com>
-# implements McIlroy-Hunt diff algorithm
-#
-# 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
-# Ruby licence.
-#
-# $Id$
-#++
-
- # 1) Try to load Ruwiki from the gem.
- # 2) Try to load Ruwiki from $LOAD_PATH.
- # 3) Modify $LOAD_PATH and try to load it from the modified $LOAD_PATH.
- # 4) Fail hard.
-load_state = 1
- begin
- if 1 == load_state
- require 'rubygems'
- require_gem 'diff-lcs', '= 1.1.1'
- else
- require 'diff/lcs'
- end
- rescue LoadError
- load_state += 1
-
- case load_state
- when 3
- $LOAD_PATH.unshift "#{File.dirname($0)}/../lib"
- when 4
- $LOAD_PATH.shift
- $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
- when 5
- raise
- end
- retry
- end
+require 'diff/lcs'
require 'diff/lcs/ldiff'
exit Diff::LCS::Ldiff.run(ARGV)
diff --git a/lib/diff/lcs/htmldiff.rb b/lib/diff/lcs/htmldiff.rb
new file mode 100644
index 0000000..bf576a2
--- /dev/null
+++ b/lib/diff/lcs/htmldiff.rb
@@ -0,0 +1,151 @@
+# -*- 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 = "&nbsp;" if element.empty?
+ %Q|<pre class="#{__send__(css_class)}">#{element}</pre>\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 = left.map { |line| formatter.expand(line.chomp) }
+ @right = 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
+<html>
+ <head>
+ <title>#{@options[:title]}</title>
+ <style type="text/css">
+ #{@options[:css]}
+ </style>
+ </head>
+ <body>
+ <h1>#{@options[:title]}</h1>
+ <p>Legend: <span class="only_a">Only in Old</span>&nbsp;
+ <span class="only_b">Only in New</span></p>
+ <div class="diff">
+ OUTPUT
+
+ callbacks = Callbacks.new(@options[:output])
+ Diff::LCS.traverse_sequences(@left, @right, callbacks)
+
+ @options[:output] << <<-OUTPUT
+ </div>
+ </body>
+</html>
+ OUTPUT
+ end
+end
+
+# vim: ft=ruby