Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Austin Ziegler committed Jul 31, 2011
1 parent 2276593 commit 8b51589
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 140 deletions.
5 changes: 5 additions & 0 deletions History.rdoc
Original file line number Diff line number Diff line change
@@ -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
Expand Down
120 changes: 20 additions & 100 deletions bin/htmldiff
Original file line number Diff line number Diff line change
@@ -1,112 +1,32 @@
#!/usr/bin/env ruby
#--
# Copyright 2004 Austin Ziegler <[email protected]>
# adapted from:
# Algorithm::Diff (Perl) by Ned Konz <[email protected]>
# Smalltalk by Mario I. Wolczko <[email protected]>
# 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
41 changes: 1 addition & 40 deletions bin/ldiff
Original file line number Diff line number Diff line change
@@ -1,45 +1,6 @@
#!/usr/bin/env ruby
#--
# Copyright 2004 Austin Ziegler <[email protected]>
# adapted from:
# Algorithm::Diff (Perl) by Ned Konz <[email protected]>
# Smalltalk by Mario I. Wolczko <[email protected]>
# 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)
151 changes: 151 additions & 0 deletions lib/diff/lcs/htmldiff.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 8b51589

Please sign in to comment.