Skip to content

Commit

Permalink
Extract ldiff display logic to reuse it as lib
Browse files Browse the repository at this point in the history
The ldiff "display" logic was too tightly coupled with the command line
program. Let's just extract it in a distinct class method accepting all
the necessary parameters.

One can now use
  Diff::LCS::Ldiff.diff?(
    Diff::LCS::Ldiff::InputInfo.new('file1'),
    Diff::LCS::Ldiff::InputInfo.new('file2'),
    :unified, # or :ed or :context or :report
    $stdout, # or a ref to [] or to a StringIO or a File
    binary: nil, # or true or false if you want to be explicit
    lines: 3) # to customize the number of context lines

The result will be `true` in case of diff and `false` otherwise.
The output if any, will be appended to the output parameter.
  • Loading branch information
Annih committed Jan 28, 2025
1 parent 2384789 commit 20b32e3
Showing 1 changed file with 33 additions and 30 deletions.
63 changes: 33 additions & 30 deletions lib/diff/lcs/ldiff.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ module Diff::LCS::Ldiff # :nodoc:
# standard:enable Layout/HeredocIndentation
end

InputInfo = Struct.new(:filename, :data, :mtime) do
def initialize(filename)
super(filename, ::File.read(filename), ::File.stat(filename).mtime)
end
end

class << Diff::LCS::Ldiff
attr_reader :format, :lines # :nodoc:
attr_reader :file_old, :file_new # :nodoc:
Expand Down Expand Up @@ -85,8 +91,11 @@ def run(args, _input = $stdin, output = $stdout, error = $stderr) # :nodoc:
@lines ||= 0

file_old, file_new = *ARGV
diff?(InputInfo.new(file_old), InputInfo.new(file_new), @format, output, binary: @binary, lines: @lines) ? 1 : 0
end

case @format
def diff?(info_old, info_new, format, output, binary: nil, lines: 0)
case format
when :context
char_old = "*" * 3
char_new = "-" * 3
Expand All @@ -99,40 +108,34 @@ def run(args, _input = $stdin, output = $stdout, error = $stderr) # :nodoc:
# items we've read from each file will differ by FLD (could be 0).
file_length_difference = 0

data_old = File.read(file_old)
data_new = File.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 = !old_txt || !new_txt
end

unless @binary
data_old = data_old.lines.to_a
data_new = data_new.lines.to_a
if binary.nil?
old_txt = info_old.data[0, 4096].scan(/\0/).empty?
new_txt = info_new.data[0, 4096].scan(/\0/).empty?
binary = !old_txt || !new_txt
end

# diff yields lots of pieces, each of which is basically a Block object
if @binary
diffs = (data_old == data_new)
if binary
diffs = (info_old.data == info_new.data)
else
data_old = info_old.data.lines.to_a
data_new = info_new.data.lines.to_a
diffs = Diff::LCS.diff(data_old, data_new)
diffs = nil if diffs.empty?
end

return 0 unless diffs
return false unless diffs

case @format
case format
when :report
output << "Files #{file_old} and #{file_new} differ\n"
return 1
output << "Files #{info_old.filename} and #{info_new.filename} differ\n"
return true
when :unified, :context
ft = File.stat(file_old).mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.000000000 %z")
output << "#{char_old} #{file_old}\t#{ft}\n"
ft = File.stat(file_new).mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.000000000 %z")
output << "#{char_new} #{file_new}\t#{ft}\n"
ft = info_old.mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.000000000 %z")
output << "#{char_old} #{info_old.filename}\t#{ft}\n"
ft = info_new.mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.000000000 %z")
output << "#{char_new} #{info_new.filename}\t#{ft}\n"
when :ed
real_output = output
output = []
Expand All @@ -143,26 +146,26 @@ def run(args, _input = $stdin, output = $stdout, error = $stderr) # :nodoc:
oldhunk = hunk = nil
diffs.each do |piece|
begin # rubocop:disable Style/RedundantBegin
hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, @lines, file_length_difference)
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.positive? && hunk.merge(oldhunk)
next if lines.positive? && hunk.merge(oldhunk)

output << oldhunk.diff(@format)
output << "\n" if @format == :unified
output << oldhunk.diff(format)
output << "\n" if format == :unified
ensure
oldhunk = hunk
end
end

last = oldhunk.diff(@format, true)
last = oldhunk.diff(format, true)
last << "\n" if last.respond_to?(:end_with?) && !last.end_with?("\n")

output << last

output.reverse_each { |e| real_output << e.diff(:ed_finish) } if @format == :ed
output.reverse_each { |e| real_output << e.diff(:ed_finish) } if format == :ed

1
true
end
end

0 comments on commit 20b32e3

Please sign in to comment.