class PatienceDiff::Differ

Attributes

all_context[RW]
ignore_whitespace[RW]
line_ending[RW]
matcher[R]

Public Class Methods

new(opts = {}) click to toggle source

Options:

* :all_context: Output the entirety of each file. This overrides the sequence matcher's context setting.
* :line_ending: Delimiter to use when joining diff output. Defaults to $RS.
* :ignore_whitespace: Before comparing lines, strip trailing whitespace, and treat leading whitespace 
  as either present or not. Does not affect output.

Any additional options (e.g. :context) are passed on to the sequence matcher.

# File lib/patience_diff/differ.rb, line 16
def initialize(opts = {})
  @all_context = opts.delete(:all_context)
  @line_ending = opts.delete(:line_ending) || $RS
  @ignore_whitespace = opts.delete(:ignore_whitespace)
  @matcher = SequenceMatcher.new(opts)
end

Public Instance Methods

diff_files(left_file, right_file, formatter=Formatter.new) click to toggle source

Generates a unified diff from the contents of the files at the paths specified.

# File lib/patience_diff/differ.rb, line 24
def diff_files(left_file, right_file, formatter=Formatter.new)
  (left_data, left_timestamp), (right_data, right_timestamp) = [left_file, right_file].map do |filename|
    # Read in binary encoding, so that we can diff any encoding and split() won't complain
    File.open(filename, :external_encoding => Encoding::BINARY) do |file|
      [file.read.split($RS), file.mtime]
    end
  end
  diff_sequences(left_data, right_data, left_file, right_file, left_timestamp, right_timestamp, formatter)
end
diff_sequences(left, right, left_name=nil, right_name=nil, left_timestamp=nil, right_timestamp=nil, formatter=Formatter.new) click to toggle source

Generate a unified diff of the data specified. The left and right values should be strings, or any other indexable, sortable data. File names and timestamps do not affect the diff algorithm, but are used in the header text.

# File lib/patience_diff/differ.rb, line 36
def diff_sequences(left, right, left_name=nil, right_name=nil, left_timestamp=nil, right_timestamp=nil, formatter=Formatter.new)
  if @ignore_whitespace
    a = left.map  { |line| line.rstrip.gsub(/^\s+/, ' ') }
    b = right.map { |line| line.rstrip.gsub(/^\s+/, ' ') }
  else
    a = left
    b = right
  end
  
  if @all_context
    hunks = [@matcher.diff_opcodes(a, b)]
  else
    hunks = @matcher.grouped_opcodes(a, b)
  end
  
  return nil unless hunks.any?
  
  lines = []
  lines << formatter.render_header(left_name, right_name, left_timestamp, right_timestamp)
  last_hunk_end = -1
  hunks.each do |opcodes|
    lines << formatter.render_hunk(a, b, opcodes, last_hunk_end)
    last_hunk_end = opcodes.last[4]
  end
  lines.flatten.compact.join(@line_ending) + @line_ending
end