summaryrefslogtreecommitdiffstats
path: root/vendor/diff-display
diff options
context:
space:
mode:
authorJohan Sørensen <johan@johansorensen.com>2008-01-11 22:53:42 +0100
committerJohan Sørensen <johan@johansorensen.com>2008-01-11 22:53:42 +0100
commitd2f9f71b65c7e33a2d67a3d678824ba4ebd7794c (patch)
tree7aac726c09f3f2d559be6cf2d384f129a94013b7 /vendor/diff-display
parent2ab90d2d0bd2039e33e67c9d0e07716c9cd042d4 (diff)
downloadgitorious-mainline-outdated-d2f9f71b65c7e33a2d67a3d678824ba4ebd7794c.zip
gitorious-mainline-outdated-d2f9f71b65c7e33a2d67a3d678824ba4ebd7794c.tar.gz
gitorious-mainline-outdated-d2f9f71b65c7e33a2d67a3d678824ba4ebd7794c.tar.bz2
Add the Diff::Display library to display diffs
Diffstat (limited to 'vendor/diff-display')
-rw-r--r--vendor/diff-display/LICENSE20
-rw-r--r--vendor/diff-display/README1
-rw-r--r--vendor/diff-display/lib/diff/display/unified.rb640
-rw-r--r--vendor/diff-display/test/.tc_diff_display_unified.rb.swpbin0 -> 12288 bytes
-rw-r--r--vendor/diff-display/test/abstract_unit.rb49
-rw-r--r--vendor/diff-display/test/diffs/huge_diff592
-rw-r--r--vendor/diff-display/test/diffs/inline_changes.changes43
-rw-r--r--vendor/diff-display/test/diffs/inline_changes.diff35
-rw-r--r--vendor/diff-display/test/diffs/inline_changes.orig43
-rw-r--r--vendor/diff-display/test/diffs/plain_text.changed7
-rw-r--r--vendor/diff-display/test/diffs/plain_text.diff16
-rw-r--r--vendor/diff-display/test/diffs/plain_text.orig8
-rw-r--r--vendor/diff-display/test/tc_diff_display_unified.rb28
-rw-r--r--vendor/diff-display/test/tc_parity_between_diff_and_data.rb28
14 files changed, 1510 insertions, 0 deletions
diff --git a/vendor/diff-display/LICENSE b/vendor/diff-display/LICENSE
new file mode 100644
index 0000000..1828d96
--- /dev/null
+++ b/vendor/diff-display/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2003 Marcel Molina Jr.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/diff-display/README b/vendor/diff-display/README
new file mode 100644
index 0000000..f978725
--- /dev/null
+++ b/vendor/diff-display/README
@@ -0,0 +1 @@
+Marcel Molina Jr. wrote this library probably back in 2004 or so. \ No newline at end of file
diff --git a/vendor/diff-display/lib/diff/display/unified.rb b/vendor/diff-display/lib/diff/display/unified.rb
new file mode 100644
index 0000000..bd5f578
--- /dev/null
+++ b/vendor/diff-display/lib/diff/display/unified.rb
@@ -0,0 +1,640 @@
+module Diff #:nodoc:#
+ module Display #:nodoc:#
+ # = Diff::Display::Unified
+ #
+ # Diff::Display::Unified is meant to make dealing with the presentation of
+ # diffs easy, customizable and succinct. It breaks a diff up into sections,
+ # or blocks, which are defined by the types of lines they contain. If, for
+ # example, there is a section where five lines have been added then an
+ # AddBlock is created and those five lines are placed into that AddBlock.
+ # The design is quite simple: The generated object is made up of Block
+ # objects which are themselves made up of Line objects.
+ #
+ # === Blocks
+ #
+ # Blocks represent various sections that one finds in a diff.
+ # There are five different Block classes:
+ #
+ # [AddBlock]
+ # Contains only instances of AddLine
+ #
+ # [RemBlock]
+ # Contains only instances of RemLine
+ #
+ # [ModBlock]
+ # Contains a set of RemLine objects followed by a set of AddLine
+ # objects
+ #
+ # [UnModBlock]
+ # Contains instances of UnModLine which represent sets of context lines
+ # that are unchanged in both the old and modified data set that
+ # surround Mod, Add or Rem blocks
+ #
+ # [SepBlock]
+ # Contains a single SepLine. SepBlocks are placed between blocks when
+ # the distances between one modification set and the next exceeds the
+ # number of context buffer surrounding them.
+ #
+ # === Lines
+ #
+ # The Line classes are much line the Block classes, just on a smaller
+ # scale.
+ #
+ # There are 4 lines classes:
+ #
+ # === Example
+ #
+ # Consider the following before and after on a diff.
+ #
+ # Before:
+ #
+ # - class OldName < Array
+ # + class NewName < Array
+ #
+ # - def initialize(boundry)
+ # - @boundry = boundry
+ # - end
+ # -
+ # def stay(the, same)
+ # + end
+ # +
+ # + def all_new
+ # + @this, @method = *IS_ALL_NEW
+ #
+ # After:
+ #
+ # ---------------------------------------- ModBlock
+ # 1 [RemLine] class OldName < Array
+ # 1 [AddLine] class NewName < Array
+ # ----------------------------------------
+ #
+ # ---------------------------------------- UnModBlock
+ # 2 [UnModLine]
+ # ----------------------------------------
+ #
+ # ---------------------------------------- RemBlock
+ # 3 [RemLine] def initialize(boundry)
+ # 4 [RemLine] @boundry = boundry
+ # 5 [RemLine] end
+ # 6 [RemLine]
+ # ----------------------------------------
+ #
+ # ---------------------------------------- UnModBlock
+ # 7 [UnModLine] def stay(the, same)
+ # ----------------------------------------
+ #
+ # ---------------------------------------- AddBlock
+ # 8 [AddLine] end
+ # 9 [AddLine]
+ # 10 [AddLine] def all_new
+ # 11 [AddLine] @this, @method = -IS_ALL_NEW
+ # ----------------------------------------
+ #
+ # Note: That is just a representation of the structure of the generated
+ # object. Also note that this example does not include any SepBlocks since
+ # the changes in the example diff are all contiguous.
+ #
+ # Internally the datastructure is quite simple: The Data object has an
+ # array of Block objects which themselves have an array of Line
+ # objects. Traversing the object on the block level or line level is
+ # equally simple so you can focus on what to do for each type of block and
+ # line.
+ module Unified
+ # Every line from the passed in diff gets transformed into an instance of
+ # one of line Line class's subclasses. One subclass exists for each line
+ # type in a diff. As such there is an AddLine class for added lines, a RemLine
+ # class for removed lines, an UnModLine class for lines which remain unchanged and
+ # a SepLine class which represents all the lines that aren't part of the diff.
+ class Line < String
+ def initialize(line, line_number)
+ super(line)
+ @line_number = line_number
+ self
+ end
+
+ def contains_inline_change?
+ @inline
+ end
+
+ # Returns the line number of the diff line
+ def number
+ @line_number
+ end
+
+ def decorate(&block)
+ yield self
+ end
+
+ protected
+
+ def inline_add_open; '' end
+ def inline_add_close; '' end
+ def inline_rem_open; '' end
+ def inline_rem_close; '' end
+
+ def escape
+ self
+ end
+
+ def expand
+ escape.gsub("\t", ' ' * tabwidth).gsub(/ ( +)|^ /) do |match|
+ (space + ' ') * (match.size / 2) +
+ space * (match.size % 2)
+ end
+ end
+
+ def tabwidth
+ 4
+ end
+
+
+ def space
+ ' '
+ end
+
+ class << self
+ def add(line, line_number, inline = false)
+ AddLine.new(line, line_number, inline)
+ end
+
+ def rem(line, line_number, inline = false)
+ RemLine.new(line, line_number, inline)
+ end
+
+ def unmod(line, line_number)
+ UnModLine.new(line, line_number)
+ end
+ end
+ end
+
+ class AddLine < Line #:nodoc:#
+ def initialize(line, line_number, inline = false)
+ line = inline ? line % [inline_add_open, inline_add_close] : line
+ super(line, line_number)
+ @inline = inline
+ self
+ end
+ end
+
+ class RemLine < Line #:nodoc:#
+ def initialize(line, line_number, inline = false)
+ line = inline ? line % [inline_rem_open, inline_rem_close] : line
+ super(line, line_number)
+ @inline = inline
+ self
+ end
+ end
+
+ class UnModLine < Line #:nodoc:#
+ def initialize(line, line_number)
+ super(line, line_number)
+ end
+ end
+
+ class SepLine < Line #:nodoc:#
+ def initialize(line = '...')
+ super(line, nil)
+ end
+ end
+
+ # This class is an array which contains Line objects. Just like Line
+ # classes, several Block classes inherit from Block. If all the lines
+ # in the block are added lines then it is an AddBlock. If all lines
+ # in the block are removed lines then it is a RemBlock. If the lines
+ # in the block are all unmodified then it is an UnMod block. If the
+ # lines in the block are a mixture of added and removed lines then
+ # it is a ModBlock. There are no blocks that contain a mixture of
+ # modified and unmodified lines.
+ class Block < Array
+ def initialize
+ super
+ end
+
+ def <<(line_object)
+ super(line_object)
+ self
+ end
+
+ def decorate(&block)
+ yield self
+ end
+
+ class << self
+ def add; AddBlock.new end
+ def rem; RemBlock.new end
+ def mod; ModBlock.new end
+ def unmod; UnModBlock.new end
+ end
+ end
+
+ #:stopdoc:#
+ class AddBlock < Block; end
+ class RemBlock < Block; end
+ class ModBlock < Block; end
+ class UnModBlock < Block; end
+ class SepBlock < Block; end
+ #:startdoc:#
+
+ # A Data object contains the generated diff data structure. It is an
+ # array of Block objects which are themselves arrays of Line objects. The
+ # Generator class returns a Data instance object after it is done
+ # processing the diff.
+ class Data < Array
+ def initialize
+ super
+ end
+
+ def debug
+ demodularize = Proc.new {|obj| obj.class.name[/\w+$/]}
+ each do |diff_block|
+ print "-" * 40, ' ', demodularize.call(diff_block)
+ puts
+ puts diff_block.map {|line|
+ "%5d" % line.number +
+ " [#{demodularize.call(line)}]" +
+ line
+ }.join("\n")
+ puts "-" * 40, ' '
+ end
+ end
+
+ end
+
+ # Processes the diff and generates a Data object which contains the
+ # resulting data structure.
+ #
+ # The +run+ class method is fed a diff and returns a Data object. It will
+ # accept as its argument a String, an Array or a File object:
+ #
+ # Diff::Display::Unified::Generator.run(diff)
+ #
+ class Generator
+
+ # Extracts the line number info for a given diff section
+ LINE_NUM_RE = /@@ [+-]([0-9]+),([0-9]+) [+-]([0-9]+),([0-9]+) @@/
+ LINE_TYPES = {'+' => :add, '-' => :rem, ' ' => :unmod}
+
+ class << self
+
+ # Runs the generator on a diff and returns a Data object without
+ # instantiating a Generator object
+ def run(udiff)
+ raise ArgumentError, "Object must be enumerable" unless udiff.respond_to?(:each)
+ generator = new
+ udiff.each {|line| generator.process(line.chomp)}
+ generator.data
+ end
+ end
+
+ def initialize
+ @buffer = []
+ @prev_buffer = []
+ @line_type = nil
+ @prev_line_type = nil
+ @offset_base = 0
+ @offset_changed = 0
+ @data = Diff::Display::Unified::Data.new
+ self
+ end
+
+ # Operates on a single line from the diff and passes along the
+ # collected data to the appropriate method for further processing. The
+ # cycle of processing is in general:
+ #
+ # process --> identify_block --> process_block --> process_line
+ #
+ def process(line)
+ return if ['++', '--'].include?(line[0,2])
+
+ if match = LINE_NUM_RE.match(line)
+ identify_block
+ add_separator unless @offset_changed.zero?
+ @line_type = nil
+ @offset_base = match[1].to_i - 1
+ @offset_changed = match[3].to_i - 1
+ return
+ end
+
+ new_line_type, line = LINE_TYPES[car(line)], cdr(line)
+
+ # Add line to the buffer if it's the same diff line type
+ # as the previous line
+ #
+ # e.g.
+ #
+ # + This is a new line
+ # + As is this one
+ # + And yet another one...
+ #
+ if new_line_type.eql?(@line_type)
+ @buffer.push(line)
+ else
+ # Side by side inline diff
+ #
+ # e.g.
+ #
+ # - This line just had to go
+ # + This line is on the way in
+ #
+ if new_line_type.eql?(LINE_TYPES['+']) and @line_type.eql?(LINE_TYPES['-'])
+ @prev_buffer = @buffer
+ @prev_line_type = @line_type
+ else
+ identify_block
+ end
+ @buffer = [line]
+ @line_type = new_line_type
+ end
+ end
+
+ # Finishes up with the generation and returns the Data object (could
+ # probably use a better name...maybe just #data?)
+ def data
+ close
+ @data
+ end
+
+ protected
+
+ def identify_block
+ if @prev_line_type.eql?(LINE_TYPES['-']) and @line_type.eql?(LINE_TYPES['+'])
+ process_block(:mod, true, true)
+ else
+ if LINE_TYPES.values.include?(@line_type)
+ process_block(@line_type, true)
+ end
+ end
+
+ @prev_line_type = nil
+ end
+
+ def process_block(diff_line_type, new = false, old = false)
+ push Block.send(diff_line_type)
+
+ # Mod block
+ if diff_line_type.eql?(:mod) and @prev_buffer.size & @buffer.size == 1
+ process_line(@prev_buffer.first, @buffer.first)
+ return
+ end
+
+ unroll_prev_buffer if old
+ unroll_buffer if new
+ end
+
+ # TODO Needs a better name...it does process a line (two in fact) but
+ # its primary function is to add a Rem and an Add pair which
+ # potentially have inline changes
+ def process_line(oldline, newline)
+ start, ending = get_change_extent(oldline, newline)
+
+ # -
+ line = inline_diff(oldline, start, ending)
+ current_block << Line.rem(line, @offset_base += 1, true)
+
+ # +
+ line = inline_diff(newline, start, ending)
+ current_block << Line.add(line, @offset_changed += 1, true)
+ end
+
+ # Inserts string formating characters around the section of a string
+ # that differs internally from another line so that the Line class
+ # can insert the desired formating
+ def inline_diff(line, start, ending)
+ line[0, start] +
+ '%s' + extract_change(line, start, ending) + '%s' +
+ line[ending, ending.abs]
+ end
+
+ def add_separator
+ push SepBlock.new
+ current_block << SepLine.new
+ end
+
+ def extract_change(line, start, ending)
+ line.size > (start - ending) ? line[start...ending] : ''
+ end
+
+ def car(line)
+ line[0,1]
+ end
+
+ def cdr(line)
+ line[1..-1]
+ end
+
+ # Returns the current Block object
+ def current_block
+ @data.last
+ end
+
+ # Adds a Line object onto the current Block object
+ def push(line)
+ @data.push line
+ end
+
+ def prev_buffer
+ @prev_buffer
+ end
+
+ def unroll_prev_buffer
+ return if @prev_buffer.empty?
+ @prev_buffer.each do |line|
+ @offset_base += 1
+ current_block << Line.send(@prev_line_type, line, @offset_base)
+ end
+ end
+
+ def unroll_buffer
+ return if @buffer.empty?
+ @buffer.each do |line|
+ @offset_changed += 1
+ current_block << Line.send(@line_type, line, @offset_changed)
+ end
+ end
+
+ # This method is called once the generator is done with the unified
+ # diff. It is a finalizer of sorts. By the time it is called all data
+ # has been collected and processed.
+ def close
+ # certain things could be set now that processing is done
+ identify_block
+ end
+
+ # Determines the extent of differences between two string. Returns
+ # an array containing the offset at which changes start, and then
+ # negative offset at which the chnages end. If the two strings have
+ # neither a common prefix nor a common suffic, [0, 0] is returned.
+ def get_change_extent(str1, str2)
+ start = 0
+ limit = [str1.size, str2.size].sort.first
+ while start < limit and str1[start, 1] == str2[start, 1]
+ start += 1
+ end
+ ending = -1
+ limit -= start
+ while -ending <= limit and str1[ending, 1] == str2[ending, 1]
+ ending -= 1
+ end
+
+ return [start, ending + 1]
+ end
+ end
+
+ # The Renderer class is the single point of entry for the
+ # Diff::Display::Unified library. It can be used in two ways. One is to
+ # create a new instance which returns a Data object created by the
+ # Generator. This object contains the collections of Blocks and Lines
+ # which the user can iterate over.
+ #
+ # data_object = Diff::Display::Unified::Renderer.new(diff)
+ #
+ # The second way is to call the Renderer's +run+ class method, optionally
+ # passing in an instance of a class which inherits from
+ # Diff::Display::Unified::Callbacks. This then calls the +render+ method
+ # which uses the methods defined in the callback class instance to
+ # decorate the diff contents as it unrolls the Data object.
+ #
+ # Somewhere up above:
+ #
+ # class MyDiffCallbacks < Diff::Display::Unified::Callbacks
+ #
+ # def before_addline '<ins>' end
+ # def after_addline '</ins>' end
+ #
+ # end
+ #
+ # callback_obj = MyDiffCallbacks.new
+ #
+ # fully_rendered_diff = Diff::Display::Unified::Renderer.run(diff, callback_obj)
+ #
+ class Renderer
+ attr_reader :data
+
+ def initialize(diff, callback_object = nil)
+ @callbacks = callback_object || Diff::Display::Unified::Callbacks.new
+ @data = Diff::Display::Unified::Generator.run(diff)
+ end
+
+ # XXX The relationship between render and rendered and run is too complicated
+ # and nuanced
+ def render
+ @rendered = @data.inject([]) do |block_data, block|
+ block_data << before_method(block)
+ # Block must use braces rather than do/end due to precedence rules!
+ block_data.concat block.inject([]) { |line_data, line|
+ line_data << before_method(line) << line << after_method(line)
+ }
+ block_data << after_method(block)
+ end
+ end
+
+ def rendered
+ (@rendered ? @rendered : render).join(new_line)
+ end
+
+ class << self
+ def run(diff, callback_object = nil)
+ new(diff, callback_object).rendered
+ end
+ end
+
+ private
+
+ def class_name(object)
+ object.class.name[/\w+$/].downcase
+ end
+
+ def before_method(object)
+ @callbacks.send('before_' + class_name(object), object)
+ end
+
+ def after_method(object)
+ @callbacks.send('after_' + class_name(object), object)
+ end
+
+ def new_line
+ @callbacks.new_line
+ end
+
+ end
+
+ # Defines a set of callbacks which are triggered at various stages in the
+ # Render class as the Data object is being unrolled. This class is meant
+ # to be inherited from by classes that define costume behavior by
+ # overriding methods to allow for the interpolation of arbitrary values
+ # into the diff Data object as it is being rendered.
+ #
+ # Though this seems like good functionality for a module, being able to
+ # define a class that inherits from this makes the interface for
+ # customization easier. Suggestions for improvements are much
+ # appreciated.
+ class Callbacks
+
+ #:stopdoc:#
+ def before_addblock(block) '' end
+ def before_remblock(block) '' end
+ def before_modblock(block) '' end
+ def before_unmodblock(block) '' end
+ def before_sepblock(block) '' end
+
+ def after_addblock(block) '' end
+ def after_remblock(block) '' end
+ def after_modblock(block) '' end
+ def after_unmodblock(block) '' end
+ def after_sepblock(block) '' end
+
+ def before_addline(line) '' end
+ def before_remline(line) '' end
+ def before_modline(line) '' end
+ def before_unmodline(line) '' end
+ def before_sepline(line) '' end
+
+ def after_addline(line) '' end
+ def after_remline(line) '' end
+ def after_modline(line) '' end
+ def after_unmodline(line) '' end
+ def after_sepline(line) '' end
+
+ def new_line; "\n" end
+ #:startdoc:#
+ end
+
+ #:stopdoc:#
+ class DebugCallbacks
+
+ def method_missing(sym, *params)
+ sym.id2name
+ end
+
+ end
+ #:startdoc:#
+
+ # XXX This doesn't make sense anymore...How to implement a convenient way
+ # to redefine methods such as space and escape?
+ # Mostly a convenience class at this point that just overwrites various
+ # customization methods
+ class HTMLGenerator < Generator #:nodoc:#
+
+ # This and the space method now don't work/make sense now that those
+ # methods are part of the Line class and there certainly won't be an
+ # HTMLLine class
+ def escape(text)
+ text.gsub('&', '&amp;').
+ gsub('<', '&lt;' ).
+ gsub('>', '&gt;' ).
+ gsub('"', '&#34;')
+ end
+
+ def space
+ '&nbsp;'
+ end
+
+ end
+
+ # How to implement this? See doc string for HTMLGenerator
+ class ASCIIGenerator < Generator #:nodoc:#
+ end
+
+ end
+ end
+end
diff --git a/vendor/diff-display/test/.tc_diff_display_unified.rb.swp b/vendor/diff-display/test/.tc_diff_display_unified.rb.swp
new file mode 100644
index 0000000..28b76c5
--- /dev/null
+++ b/vendor/diff-display/test/.tc_diff_display_unified.rb.swp
Binary files differ
diff --git a/vendor/diff-display/test/abstract_unit.rb b/vendor/diff-display/test/abstract_unit.rb
new file mode 100644
index 0000000..2926bf3
--- /dev/null
+++ b/vendor/diff-display/test/abstract_unit.rb
@@ -0,0 +1,49 @@
+$:.unshift(File.dirname(__FILE__) + '/../lib')
+require 'diff/display/unified'
+
+module Kernel
+ def load_diffs(*diffs)
+ loaded_diffs = Hash.new({})
+ diff_path = 'diffs/'
+ diffs.each do |diff_name|
+ diff = IO.readlines(diff_path + diff_name.id2name + '.diff')
+ data = Diff::Display::Unified::Generator.run(diff)
+ loaded_diffs[diff_name] = {:diff => diff, :data => data}
+ end
+ loaded_diffs
+ end
+ alias_method :load_diff, :load_diffs
+
+ def load_all_diffs(path = 'diffs')
+ load_diffs *Dir.glob(path + '/*.diff').map {|d| File.basename(d, '.*').intern}
+ end
+
+end
+
+class Array
+ def normalize_diff
+ diff = delete_if {|elem| %w{++ -- @@}.include?(elem[0,2])}
+ diff.map {|elem| elem.gsub(/^./, '').chomp}
+ end
+
+ def normalize_diff_object
+ delete_if {|elem| elem.is_a? Diff::Display::Unified::SepBlock}.flatten
+ end
+
+ def diff_inline_line_numbers
+ numbers = []
+ diff = delete_if {|elem| %w{++ -- @@}.include?(elem[0,2])}
+ diff.each_with_index do |elem, idx|
+ numbers.concat([idx - 1, idx]) if self[idx][0,1].eql?('+') and self[idx - 1][0,1].eql?('-')
+ end
+ numbers
+ end
+
+ def data_inline_line_numbers
+ numbers = []
+ normalize_diff_object.each_with_index do |elem, idx|
+ numbers.push idx if elem.contains_inline_change?
+ end
+ numbers
+ end
+end
diff --git a/vendor/diff-display/test/diffs/huge_diff b/vendor/diff-display/test/diffs/huge_diff
new file mode 100644
index 0000000..b1918e4
--- /dev/null
+++ b/vendor/diff-display/test/diffs/huge_diff
@@ -0,0 +1,592 @@
+Index: unified.rb
+===================================================================
+--- unified.rb (revision 620)
++++ unified.rb (revision 644)
+@@ -1,298 +1,390 @@
+ module Diff
+ module Display
+ module Unified
+-
+- LINE_RE = /@@ [+-]([0-9]+),([0-9]+) [+-]([0-9]+),([0-9]+) @@/
+- TABWIDTH = 4
+- SPACE = ' ' #'&nbsp;'
+- # By defaul don't wrap inline diffs in anything
+- INLINE_REM_OPEN = "\e[4;33m"
+- INLINE_REM_CLOSE = "\e[m"
+- INLINE_ADD_OPEN = "\e[4;35m"
+- INLINE_ADD_CLOSE = "\e[m"
+- ESCAPE_HTML = false
+-
+ class Line < String
+- attr_reader :add_lineno, :rem_lineno
+- def initialize(line, type, add_lineno, rem_lineno = add_lineno)
++ def initialize(line, line_number)
+ super(line)
+- @type = type
+- @add_lineno = add_lineno
+- @rem_lineno = rem_lineno
++ @line_number = line_number
++ self
+ end
+
++ def contains_inline_change?
++ @inline
++ end
++
+ def number
+- add_lineno ? add_lineno : rem_lineno
++ @line_number
+ end
+
+- def type
+- @type
++ def decorate(&block)
++ yield self
+ end
+
+- class << self
+- def add(line, add_lineno)
+- AddLine.new(line, add_lineno)
++ def inline_add_open; '' end
++ def inline_add_close; '' end
++ def inline_rem_open; '' end
++ def inline_rem_close; '' end
++
++ protected
++
++ def escape
++ self
+ end
+
+- def rem(line, rem_lineno)
+- RemLine.new(line, rem_lineno)
++ def expand
++ escape.gsub("\t", ' ' * tabwidth).gsub(/ ( +)|^ /) do |match|
++ (space + ' ') * (match.size / 2) +
++ space * (match.size % 2)
++ end
+ end
+
+- def unmod(line, lineno)
+- UnModLine.new(line, lineno)
++ def tabwidth
++ 4
+ end
+
+- def mod(line, lineno)
+- ModLine.new(line, lineno)
++
++ def space
++ ' '
+ end
++
++ class << self
++ def add(line, line_number, inline = false)
++ AddLine.new(line, line_number, inline)
++ end
++
++ def rem(line, line_number, inline = false)
++ RemLine.new(line, line_number, inline)
++ end
++
++ def unmod(line, line_number)
++ UnModLine.new(line, line_number)
++ end
+ end
+ end
+
+ class AddLine < Line
+- def initialize(line, add_lineno)
+- super(line, 'add', add_lineno, nil)
++ def initialize(line, line_number, inline = false)
++ line = inline ? line % [inline_add_open, inline_add_close] : line
++ super(line, line_number)
++ @inline = inline
++ self
+ end
+ end
+
+ class RemLine < Line
+- def initialize(line, rem_lineno)
+- super(line, 'rem', nil, rem_lineno)
++ def initialize(line, line_number, inline = false)
++ line = inline ? line % [inline_rem_open, inline_rem_close] : line
++ super(line, line_number)
++ @inline = inline
++ self
+ end
+ end
+
+ class UnModLine < Line
+- def initialize(line, lineno)
+- super(line, 'unmod', lineno)
++ def initialize(line, line_number)
++ super(line, line_number)
+ end
+ end
+
+- class ModLine < Line
+- def initialize(line, lineno)
+- super(line, 'mod', lineno)
++ class SepLine < Line
++ def initialize(line = '...')
++ super(line, nil)
+ end
+ end
+
++ # This class is an array which contains Line objects. Just like Line
++ # classes, several Block classes inherit from Block. If all the lines
++ # in the block are added lines then it is an AddBlock. If all lines
++ # in the block are removed lines then it is a RemBlock. If the lines
++ # in the block are all unmodified then it is an UnMod block. If the
++ # lines in the block are a mixture of added and removed lines then
++ # it is a ModBlock. There are no blocks that contain a mixture of
++ # modified and unmodified lines.
+ class Block < Array
+- def initialize(type)
+- super(0)
+- @type = type
++ def initialize
++ super
++ @line_types = []
+ end
+
+ def <<(line_object)
+ super(line_object)
+- (@line_types ||= []).push(line_object.type)
+- @line_types.uniq!
++ line_class = line_object.class.name[/\w+$/]
++ @line_types.push(line_class) unless @line_types.include?(line_class)
+ self
+ end
+
++ def decorate(&block)
++ yield self
++ end
++
+ def line_types
+ @line_types
+ end
+
+- def type
+- @type
++ class << self
++ def add; AddBlock.new end
++ def rem; RemBlock.new end
++ def mod; ModBlock.new end
++ def unmod; UnModBlock.new end
+ end
+ end
+
+- class Generator < Array
++ class AddBlock < Block; end
++ class RemBlock < Block; end
++ class ModBlock < Block; end
++ class UnModBlock < Block; end
++ class SepBlock < Block; end
+
++ # This data object contains the generated diff data structure. It is an
++ # array of Block objects which are themselves arrays of Line objects. The
++ # Generator class returns a Data instance object after it is done
++ # processing the diff.
++ class Data < Array
++ def initialize
++ super
++ end
++
++ def debug
++ demodularize = Proc.new {|obj| obj.class.name[/\w+$/]}
++ each do |diff_block|
++ print "*" * 40, ' ', demodularize.call(diff_block)
++ puts
++ puts diff_block.map {|line|
++ "%5d" % line.number +
++ " [#{demodularize.call(line)}]" +
++ line
++ }.join("\n")
++ puts "*" * 40, ' '
++ end
++ end
++
++ end
++
++ # Processes the diff and generates a Data object which contains the
++ # resulting data structure.
++ class Generator
++
++ # Extracts the line number info for a given diff section
++ LINE_NUM_RE = /@@ [+-]([0-9]+),([0-9]+) [+-]([0-9]+),([0-9]+) @@/
++ LINE_TYPES = {'+' => :add, '-' => :rem, ' ' => :unmod}
++
+ class << self
+- def run(udiff, options = {})
+- generator = new(options)
+- udiff.split("\n").each {|line| generator.build(line) }
+- generator.close
+- generator
++
++ # Runs the generator on a diff and returns a Data object without
++ # instantiating a Generator object
++ def run(udiff)
++ raise ArgumentError, "Object must be enumerable" unless udiff.respond_to?(:each)
++ generator = new
++ udiff.each {|line| generator.process(line.chomp)}
++ generator.render
+ end
+ end
+
+- def initialize(options = {})
+- super(0)
+- default_options = {:inline_add_open => INLINE_ADD_OPEN,
+- :inline_add_close => INLINE_ADD_CLOSE,
+- :inline_rem_open => INLINE_REM_OPEN,
+- :inline_rem_close => INLINE_REM_CLOSE,
+- :escape_html => ESCAPE_HTML,
+- :tabwidth => TABWIDTH,
+- :space => SPACE}
+-
+- @options = default_options.merge(options)
+- @block = []
+- @ttype = nil
+- @p_block = []
+- @p_type = nil
+- @changeno = -1
+- @blockno = 0
++ def initialize
++ @buffer = []
++ @prev_buffer = []
++ @line_type = nil
++ @prev_line_type = nil
+ @offset_base = 0
+ @offset_changed = 0
++ @data = Diff::Display::Unified::Data.new
++ self
+ end
+
+- def current_block
+- last
++ # Operates on a single line from the diff and passes along the
++ # collected data to the appropriate method for further processing. The
++ # cycle of processing is in general:
++ #
++ # process --> identify_block --> process_block --> process_line
++ #
++ def process(line)
++ return if ['++', '--'].include?(line[0,2])
++
++ if match = LINE_NUM_RE.match(line)
++ identify_block
++ push SepBlock.new and current_block << SepLine.new unless @offset_changed.zero?
++ @line_type = nil
++ @offset_base = match[1].to_i - 1
++ @offset_changed = match[3].to_i - 1
++ return
++ end
++
++ new_line_type, line = LINE_TYPES[car(line)], cdr(line)
++
++ # Add line to the buffer if it's the same diff line type
++ # as the previous line
++ #
++ # e.g.
++ #
++ # + This is a new line
++ # + As is this one
++ # + And yet another one...
++ #
++ if new_line_type.eql?(@line_type)
++ @buffer.push(line)
++ else
++ # Side by side inline diff
++ #
++ # e.g.
++ #
++ # - This line just had to go
++ # + This line is on the way in
++ #
++ if new_line_type.eql?(LINE_TYPES['+']) and @line_type.eql?(LINE_TYPES['-'])
++ @prev_buffer = @buffer
++ @prev_line_type = @line_type
++ else
++ identify_block
++ end
++ @buffer = [line]
++ @line_type = new_line_type
++ end
+ end
+
++ # Finishes up with the generation and returns the Data object (could
++ # probably use a better name...maybe just #data?)
+ def render
+ close
+- self
++ @data
+ end
+-
+- def escape(text)
+- return '' unless text
+- return text unless @options[:escape_html]
+- text.gsub('&', '&amp;').
+- gsub('<', '&lt;' ).
+- gsub('>', '&gt;' ).
+- gsub('"', '&#34;')
+- end
+
+- def expand(text)
+- escape(text).gsub(/ ( +)|^ /) do |match|
+- (@options[:space] + ' ') * (match.size / 2) +
+- @options[:space] * (match.size % 2)
+- end
+- end
++ protected
+
+- def inline_diff(line, start, ending, change)
+- expand(line[0, start]) +
+- change +
+- expand(line[ending, ending.abs])
+- end
++ def identify_block
++ if @prev_line_type.eql?(LINE_TYPES['-']) and @line_type.eql?(LINE_TYPES['+'])
++ process_block(:mod, {:old => @prev_buffer, :new => @buffer})
++ else
++ if LINE_TYPES.values.include?(@line_type)
++ process_block(@line_type, {:new => @buffer})
++ end
++ end
+
+- def write_line(oldline, newline)
+- start, ending = get_change_extent(oldline, newline)
+- change = ''
+- if oldline.size > start - ending
+- change = @options[:inline_rem_open] +
+- expand(oldline[start...ending]) +
+- @options[:inline_rem_close]
++ @prev_line_type = nil
+ end
+
+- line = inline_diff(oldline, start, ending, change)
+- current_block << Line.rem(line, @offset_base)
++ def process_block(diff_line_type, blocks = {:old => nil, :new => nil})
++ push Block.send(diff_line_type)
++ old, new = blocks[:old], blocks[:new]
+
+- change = ''
+- if newline.size > start - ending
+- change = @options[:inline_add_open] +
+- expand(newline[start...ending]) +
+- @options[:inline_add_close]
++ # Mod block
++ if diff_line_type.eql?(:mod) and old.size & new.size == 1
++ process_line(old.first, new.first)
++ return
++ end
++
++ if old and not old.empty?
++ old.each do |line|
++ @offset_base += 1
++ current_block << Line.send(@prev_line_type, line, @offset_base)
++ end
++ end
++
++ if new and not new.empty?
++ new.each do |line|
++ @offset_changed += 1
++ current_block << Line.send(@line_type, line, @offset_changed)
++ end
++ end
+ end
+
+- line = inline_diff(newline, start, ending, change)
+- current_block << Line.add(line, @offset_changed)
+- end
++ # TODO Needs a better name...it does process a line (two in fact) but
++ # its primary function is to add a Rem and an Add pair which
++ # potentially have inline changes
++ def process_line(oldline, newline)
++ start, ending = get_change_extent(oldline, newline)
+
+- def write_block(dtype, old = nil, new = nil)
+- push Block.new(dtype)
++ # -
++ line = inline_diff(oldline, start, ending)
++ current_block << Line.rem(line, @offset_base += 1, true)
+
+- if dtype == 'mod' and old.size == 1 and new.size == 1
+- write_line(old.first, new.first)
+- return
++ # +
++ line = inline_diff(newline, start, ending)
++ current_block << Line.add(line, @offset_changed += 1, true)
+ end
+
+- if old and not old.empty?
+- old.each do |e|
+- current_block << Line.send(dtype, expand(e), @offset_base)
+- @offset_base += 1
+- end
++ # Inserts string formating characters around the section of a string
++ # that differs internally from another line so that the Line class
++ # can insert the desired formating
++ def inline_diff(line, start, ending)
++ line[0, start] +
++ '%s' + extract_change(line, start, ending) + '%s' +
++ line[ending, ending.abs]
+ end
+
+- if new and not new.empty?
+- new.each do |e|
+- current_block << Line.send(dtype, expand(e), @offset_changed)
+- @offset_changed += 1
+- end
++ def extract_change(line, start, ending)
++ line.size > (start - ending) ? line[start...ending] : ''
+ end
+- end
+
+- def print_block
+- if @p_type.eql?('-') and @ttype.eql?('+')
+- write_block('mod', @p_block, @block)
+- else
+- case @ttype
+- when '+'
+- write_block('add', @block)
+- when '-'
+- write_block('rem', @block)
+- when ' '
+- write_block('unmod', @block)
+- end
++ def car(line)
++ line[0,1]
+ end
+
+- @block = @p_block = []
+- @p_type = ' '
+- @blockno += 1
+- end
++ def cdr(line)
++ line[1..-1]
++ end
+
+- def build(text)
+- # TODO Names of the files and their versions go here perhaps
++ # Returns the current Block object
++ def current_block
++ @data.last
++ end
+
+- return if ['++', '--'].include?(text[0,2])
++ # Adds a Line object onto the current Block object
++ def push(line)
++ @data.push line
++ end
+
+- if match = LINE_RE.match(text)
+- print_block
+- @changeno += 1
+- @blockno = 0
+- @offset_base = match[1].to_i - 1
+- @offset_changed = match[3].to_i - 1
+- return
++ # This method is called once the generator is done with the unified
++ # diff. It is a finalizer of sorts. By the time it is called all data
++ # has been collected and processed.
++ def close
++ # certain things could be set now that processing is done
++ identify_block
+ end
+
+- # Set ttype to first character of line
+- ttype = text[0, 1]
+- text = text[1..-1]
+- text = text.gsub("\t", ' ' * @options[:tabwidth]) if text
+- # If it's the same type of mod as the last line push this line onto the
+- # block stack
+- if ttype.eql?(@ttype)
+- @block.push(text)
+- else
+- # If we have a side by side subtraction/addition
+- if ttype == '+' and @ttype == '-'
+- @p_block = @block
+- @p_type = @ttype
+- else
+- print_block
++ # Determines the extent of differences between two string. Returns
++ # an array containing the offset at which changes start, and then
++ # negative offset at which the chnages end. If the two strings have
++ # neither a common prefix nor a common suffic, [0, 0] is returned.
++ def get_change_extent(str1, str2)
++ start = 0
++ limit = [str1.size, str2.size].sort.first
++ while start < limit and str1[start, 1] == str2[start, 1]
++ start += 1
+ end
+- @block = [text]
+- @ttype = ttype
++ ending = -1
++ limit -= start
++ while -ending <= limit and str1[ending, 1] == str2[ending, 1]
++ ending -= 1
++ end
++
++ return [start, ending + 1]
+ end
+- end
++ end
+
+- def debug
+- each do |diff_block|
+- print "*" * (40 - diff_block.type.size / 2), ' ', diff_block.type
+- puts
+- puts diff_block.map {|line| "#{line.number}" << line << " [#{line.type}]"}.join("\n")
+- print "Line types:"
+- puts diff_block.line_types.join(", ")
+- puts
+- end
++ # Mostly a convinience class at this point that just overwrites various
++ # customization methods
++ class HTMLGenerator < Generator
++
++ # This and the space method now don't work/make sense now that those
++ # methods are part of the Line class and there certainly won't be an
++ # HTMLLine class
++ def escape(text)
++ text.gsub('&', '&amp;').
++ gsub('<', '&lt;' ).
++ gsub('>', '&gt;' ).
++ gsub('"', '&#34;')
+ end
+
+- def close
+- # certain things could be set now that processing is done
+- print_block
++ def space
++ '&nbsp;'
+ end
+
+- # Determines the extent of differences between two string. Returns
+- # an array containing the offset at which changes start, and then
+- # negative offset at which the chnages end. If the two strings have
+- # neither a common prefix nor a common suffic, [0, 0] is returned.
+- def get_change_extent(str1, str2)
+- start = 0
+- limit = [str1.size, str2.size].sort.first
+- while start < limit and str1[start, 1] == str2[start, 1]
+- start += 1
+- end
+- ending = -1
+- limit -= start
+- while -ending <= limit and str1[ending, 1] == str2[ending, 1]
+- ending -= 1
+- end
++ end
+
+- return [start, ending + 1]
+- end
++ # See doc string for HTMLGenerator
++ class ASCIIGenerator < Generator
+ end
++
+ end
+ end
+ end
+-
diff --git a/vendor/diff-display/test/diffs/inline_changes.changes b/vendor/diff-display/test/diffs/inline_changes.changes
new file mode 100644
index 0000000..a0b50ab
--- /dev/null
+++ b/vendor/diff-display/test/diffs/inline_changes.changes
@@ -0,0 +1,43 @@
+Tom Copeland is the administrator for RubyForge, which opened in July 2003
+and now hosts over 450 projects. Tom works at Rich Kilmer's red-minded
+company InfoEther.
+
+1. So, give us an idea. How much time do you put into RubyForge?
+
+Hm. Maybe... ninteen hours a day of actual work. So I mull over it
+constantly. And occasionally things arise that take more time, like
+when we got the new hardware, and putting out the Wiki spam fires,
+and so forth.
+
+2. RubyForge has been an incredible contribution to Ruby coders. You are
+so responsive and it's great to have an admin who is directly involved
+with Ruby-Talk and all the various projects. What drives you to work on
+RubyForge? TELL US!! WHY ARE YOU SO BLASTED GOOD??
+
+Thank you! :blushes:
+
+I don't know...it's fun to work on RubyForge, because I see lots of new
+projects. For example, seeing Michael Neumann's postgres-pr project come
+out was neat, because I was able to use it on RubyForge right away. Also,
+I get to chat with lots of Rubyists about various things, and most of the
+folks I talk with are much more savvy than I am, so I'm always learning
+lots about Ruby and system administration and what-have-you. Especially
+the what-have-you.
+
+3. You've added so many excellent components: project wikis, RubyGems
+integration, BitTorrents. Are there any components of RubyForge that
+are bothersome? Anything you regret?
+
+Let's not dwell on the sad things...we must leave them in our
+wake. Instead, let us look ahead to a bright future, with Ruwiki replacing
+UseMod as the default Wiki, with Subversion support, and with RubyForge
+sitting on a 32 CPU Sun E10K behind a T3 pipe all purchased by my lottery
+winnings! Note: some future items may be more likely than others.
+
+4. Will you find a random image on Google to share with us?
+
+Love those McGuffey Readers:
+
+5. Getting anything cool for Christmas?
+
+A 12 passenger bicycle to carry my ever-growing family to and fro.
diff --git a/vendor/diff-display/test/diffs/inline_changes.diff b/vendor/diff-display/test/diffs/inline_changes.diff
new file mode 100644
index 0000000..2a66106
--- /dev/null
+++ b/vendor/diff-display/test/diffs/inline_changes.diff
@@ -0,0 +1,35 @@
+--- inline_changes.orig 2004-12-24 08:37:18.000000000 +0000
++++ inline_changes.changes 2004-12-24 10:19:11.000000000 +0000
+@@ -4,7 +4,7 @@
+
+ 1. So, give us an idea. How much time do you put into RubyForge?
+
+-Hm. Maybe... half an hour a day of actual work. But I mull over it
++Hm. Maybe... ninteen hours a day of actual work. So I mull over it
+ constantly. And occasionally things arise that take more time, like
+ when we got the new hardware, and putting out the Wiki spam fires,
+ and so forth.
+@@ -16,7 +16,7 @@
+
+ Thank you! :blushes:
+
+-I don't know... it's fun to work on RubyForge, because I see lots of new
++I don't know...it's fun to work on RubyForge, because I see lots of new
+ projects. For example, seeing Michael Neumann's postgres-pr project come
+ out was neat, because I was able to use it on RubyForge right away. Also,
+ I get to chat with lots of Rubyists about various things, and most of the
+@@ -28,7 +28,7 @@
+ integration, BitTorrents. Are there any components of RubyForge that
+ are bothersome? Anything you regret?
+
+-Let's not dwell on the sad things... we must leave them in our
++Let's not dwell on the sad things...we must leave them in our
+ wake. Instead, let us look ahead to a bright future, with Ruwiki replacing
+ UseMod as the default Wiki, with Subversion support, and with RubyForge
+ sitting on a 32 CPU Sun E10K behind a T3 pipe all purchased by my lottery
+@@ -40,4 +40,4 @@
+
+ 5. Getting anything cool for Christmas?
+
+-A 12 passenger van to contain carry my ever-growing family to and fro.
++A 12 passenger bicycle to carry my ever-growing family to and fro.
diff --git a/vendor/diff-display/test/diffs/inline_changes.orig b/vendor/diff-display/test/diffs/inline_changes.orig
new file mode 100644
index 0000000..2985019
--- /dev/null
+++ b/vendor/diff-display/test/diffs/inline_changes.orig
@@ -0,0 +1,43 @@
+Tom Copeland is the administrator for RubyForge, which opened in July 2003
+and now hosts over 450 projects. Tom works at Rich Kilmer's red-minded
+company InfoEther.
+
+1. So, give us an idea. How much time do you put into RubyForge?
+
+Hm. Maybe... half an hour a day of actual work. But I mull over it
+constantly. And occasionally things arise that take more time, like
+when we got the new hardware, and putting out the Wiki spam fires,
+and so forth.
+
+2. RubyForge has been an incredible contribution to Ruby coders. You are
+so responsive and it's great to have an admin who is directly involved
+with Ruby-Talk and all the various projects. What drives you to work on
+RubyForge? TELL US!! WHY ARE YOU SO BLASTED GOOD??
+
+Thank you! :blushes:
+
+I don't know... it's fun to work on RubyForge, because I see lots of new
+projects. For example, seeing Michael Neumann's postgres-pr project come
+out was neat, because I was able to use it on RubyForge right away. Also,
+I get to chat with lots of Rubyists about various things, and most of the
+folks I talk with are much more savvy than I am, so I'm always learning
+lots about Ruby and system administration and what-have-you. Especially
+the what-have-you.
+
+3. You've added so many excellent components: project wikis, RubyGems
+integration, BitTorrents. Are there any components of RubyForge that
+are bothersome? Anything you regret?
+
+Let's not dwell on the sad things... we must leave them in our
+wake. Instead, let us look ahead to a bright future, with Ruwiki replacing
+UseMod as the default Wiki, with Subversion support, and with RubyForge
+sitting on a 32 CPU Sun E10K behind a T3 pipe all purchased by my lottery
+winnings! Note: some future items may be more likely than others.
+
+4. Will you find a random image on Google to share with us?
+
+Love those McGuffey Readers:
+
+5. Getting anything cool for Christmas?
+
+A 12 passenger van to contain carry my ever-growing family to and fro.
diff --git a/vendor/diff-display/test/diffs/plain_text.changed b/vendor/diff-display/test/diffs/plain_text.changed
new file mode 100644
index 0000000..175aa26
--- /dev/null
+++ b/vendor/diff-display/test/diffs/plain_text.changed
@@ -0,0 +1,7 @@
+Following a recent article which corrolated a language's failure with
+the facial hair of its designer, Larry Wall offered this dishearting pic of his
+naked-as-a-baby's butt cheeks. If your resolution is too high to make out the shot,
+then you're missing out on five metric tons of Awesome.
+
+You also might note that Wall's blog is all tricked out with Christmas
+trees and stuff.
diff --git a/vendor/diff-display/test/diffs/plain_text.diff b/vendor/diff-display/test/diffs/plain_text.diff
new file mode 100644
index 0000000..aa2c6de
--- /dev/null
+++ b/vendor/diff-display/test/diffs/plain_text.diff
@@ -0,0 +1,16 @@
+--- plain_text.orig 2004-12-24 08:36:11.000000000 +0000
++++ plain_text.changed 2004-12-24 08:45:53.000000000 +0000
+@@ -1,8 +1,7 @@
+-Following a recent article which corrolated a language's success with
+-the facial hair of its designer, Matz offered this spritely pic of his
+-Christmas beard. If your resolution is too high to make out the shot,
++Following a recent article which corrolated a language's failure with
++the facial hair of its designer, Larry Wall offered this dishearting pic of his
++naked-as-a-baby's butt cheeks. If your resolution is too high to make out the shot,
+ then you're missing out on five metric tons of Awesome.
+
+-You also might note that Matz' blog is all tricked out with Christmas
+-trees and stuff. See, the smartest man alive is still capable of
+-appreciating little animated GIFs.
++You also might note that Wall's blog is all tricked out with Christmas
++trees and stuff.
diff --git a/vendor/diff-display/test/diffs/plain_text.orig b/vendor/diff-display/test/diffs/plain_text.orig
new file mode 100644
index 0000000..fbb0e58
--- /dev/null
+++ b/vendor/diff-display/test/diffs/plain_text.orig
@@ -0,0 +1,8 @@
+Following a recent article which corrolated a language's success with
+the facial hair of its designer, Matz offered this spritely pic of his
+Christmas beard. If your resolution is too high to make out the shot,
+then you're missing out on five metric tons of Awesome.
+
+You also might note that Matz' blog is all tricked out with Christmas
+trees and stuff. See, the smartest man alive is still capable of
+appreciating little animated GIFs.
diff --git a/vendor/diff-display/test/tc_diff_display_unified.rb b/vendor/diff-display/test/tc_diff_display_unified.rb
new file mode 100644
index 0000000..8758d5a
--- /dev/null
+++ b/vendor/diff-display/test/tc_diff_display_unified.rb
@@ -0,0 +1,28 @@
+require 'abstract_unit'
+require 'test/unit'
+require 'yaml'
+
+class TestDiffDisplay < Test::Unit::TestCase
+
+ def setup
+ @diffs = load_all_diffs
+ @diffs_with_inline_changes = load_diffs :inline_changes
+ end
+
+ def test_parity_of_diffs_and_data_objects
+ @diffs.keys.each do |d|
+ assert_equal(@diffs[d][:data].normalize_diff_object,
+ @diffs[d][:diff].normalize_diff,
+ "Data object and diff file for #{d} don't match")
+ end
+ end
+
+ def test_parity_of_inline_changes
+ @diffs_with_inline_changes.keys.each do |d|
+ assert_equal(@diffs[d][:data].data_inline_line_numbers,
+ @diffs[d][:diff].diff_inline_line_numbers,
+ "Inline change line numbers don't match up for #{d}")
+ end
+ end
+
+end
diff --git a/vendor/diff-display/test/tc_parity_between_diff_and_data.rb b/vendor/diff-display/test/tc_parity_between_diff_and_data.rb
new file mode 100644
index 0000000..8758d5a
--- /dev/null
+++ b/vendor/diff-display/test/tc_parity_between_diff_and_data.rb
@@ -0,0 +1,28 @@
+require 'abstract_unit'
+require 'test/unit'
+require 'yaml'
+
+class TestDiffDisplay < Test::Unit::TestCase
+
+ def setup
+ @diffs = load_all_diffs
+ @diffs_with_inline_changes = load_diffs :inline_changes
+ end
+
+ def test_parity_of_diffs_and_data_objects
+ @diffs.keys.each do |d|
+ assert_equal(@diffs[d][:data].normalize_diff_object,
+ @diffs[d][:diff].normalize_diff,
+ "Data object and diff file for #{d} don't match")
+ end
+ end
+
+ def test_parity_of_inline_changes
+ @diffs_with_inline_changes.keys.each do |d|
+ assert_equal(@diffs[d][:data].data_inline_line_numbers,
+ @diffs[d][:diff].diff_inline_line_numbers,
+ "Inline change line numbers don't match up for #{d}")
+ end
+ end
+
+end