Class: UDiff

Inherits:
Object
  • Object
show all
Defined in:
lib/misc/udiff.rb

Constant Summary collapse

SAFE_LINE =
/\A[\t -~]*\n\z/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path1, path2, out, header) ⇒ UDiff

Returns a new instance of UDiff.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/misc/udiff.rb', line 33

def initialize(path1, path2, out, header)
  @path1 = path1
  @path2 = path2
  @out = out
  @header = header
  @context = 3
  @beginning = true
  @l1 = 0
  @l2 = 0
  @hunk_beg = [@l1, @l2]
  @hunk = []
  @lines_hash = {}
  @lines_ary = []
end

Class Method Details

.diff(path1, path2, out, header = "--- #{path1}\n+++ #{path2}\n") ⇒ Object



29
30
31
# File 'lib/misc/udiff.rb', line 29

def UDiff.diff(path1, path2, out, header="--- #{path1}\n+++ #{path2}\n")
  UDiff.new(path1, path2, out, header).diff
end

Instance Method Details

#copy_common_part(f1, f2, n) ⇒ Object



93
94
95
96
97
98
99
# File 'lib/misc/udiff.rb', line 93

def copy_common_part(f1, f2, n)
  n.times {|i|
    v = gets_common_line(f1, f2)
    raise "[bug] diff error: unexpected EOF" unless v
    puts_common_line(v)
  }
end

#diffObject



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/misc/udiff.rb', line 209

def diff
  t1 = Tempfile.new("udiff")
  File.foreach(@path1) {|l|
    if SAFE_LINE =~ l
      l = "_" + l
    else
      if !@lines_hash[l]
        @lines_ary[@lines_hash.size] = l
        @lines_hash[l] = @lines_hash.size 
      end
      l = @lines_hash[l].to_s + "\n"
    end
    t1.puts l
  }
  t2 = Tempfile.new("udiff")
  File.foreach(@path2) {|l|
    if SAFE_LINE =~ l
      l = "_" + l
    else
      if !@lines_hash[l]
        @lines_ary[@lines_hash.size] = l
        @lines_hash[l] = @lines_hash.size 
      end
      l = @lines_hash[l].to_s + "\n"
    end
    t2.puts l
  }
  t1.close
  t2.close
  run_diff(t1.path, t2.path)
end

#encdump(str) ⇒ Object



71
72
73
74
75
76
77
78
# File 'lib/misc/udiff.rb', line 71

def encdump(str)
  d = str.dump
  if str.respond_to? :encoding
    "#{d}.force_encoding(#{str.encoding.name.dump})"
  else
    d
  end
end

#gets_common_line(f1, f2) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/misc/udiff.rb', line 80

def gets_common_line(f1, f2)
  v1 = f1.gets
  v2 = f2.gets
  if v1 != v2
    raise "[bug] diff error: #{encdump v1} != #{encdump v2}"
  end
  if v1
    @l1 += 1
    @l2 += 1
  end
  return v1
end

#output_common_part(f1, f2, common_num) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/misc/udiff.rb', line 120

def output_common_part(f1, f2, common_num)
  if @beginning
    if common_num <= @context
      copy_common_part(f1, f2, common_num)
    else
      skip_common_part(f1, f2, common_num-@context)
      copy_common_part(f1, f2, @context)
    end
  elsif common_num <= @context * 2
    copy_common_part(f1, f2, common_num)
  else
    copy_common_part(f1, f2, @context)
    output_hunk
    skip_common_part(f1, f2, common_num-@context*2)
    @hunk_beg = [@l1, @l2]
    @hunk = []
    copy_common_part(f1, f2, @context)
  end
end

#output_common_tail(f1, f2) ⇒ Object



140
141
142
143
144
145
146
147
148
# File 'lib/misc/udiff.rb', line 140

def output_common_tail(f1, f2)
  return if @beginning
  @context.times {
    v = gets_common_line(f1, f2)
    break unless v
    puts_common_line(v)
  }
  output_hunk
end

#output_hunkObject



108
109
110
111
112
113
114
115
116
117
118
# File 'lib/misc/udiff.rb', line 108

def output_hunk
  if @header
    @out.print @header
    @header = nil
  end
  l1_beg, l2_beg = @hunk_beg
  @out.print "@@ -#{l1_beg+1},#{@l1-l1_beg} +#{l2_beg+1},#{@l2-l2_beg} @@\n"
  @hunk.each {|s|
    @out.print s
  }
end

#process_commands(f1, f2, d) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/misc/udiff.rb', line 150

def process_commands(f1, f2, d)
  has_diff = false
  l1 = 0
  while com = d.gets
    case com
    when /\Ad(\d+) (\d+)/
      line = $1.to_i
      num = $2.to_i
      output_common_part(f1, f2, line-l1-1)
      num.times {
        v = f1.gets
        @l1 += 1
        puts_del_line(v)
        has_diff = true
      }
      l1 = line + num - 1
    when /\Aa(\d+) (\d+)/
      line = $1.to_i
      num = $2.to_i
      common_num = line-l1
      output_common_part(f1, f2, line-l1)
      l1 = line
      num.times {
        v1 = d.gets
        v2 = f2.gets
        if v1 != v2
          raise "[bug] diff error: #{encdump v1} != #{encdump v2}"
        end
        @l2 += 1
        v = v1
        puts_add_line(v)
        has_diff = true
      }
    else
      raise "[bug] unexpected diff line: #{com.inspect}"
    end
  end
  has_diff
end

#puts_add_line(line) ⇒ Object



61
62
63
64
# File 'lib/misc/udiff.rb', line 61

def puts_add_line(line)
  line = /\A_/ =~ line ? $' : @lines_ary[line.to_i]
  puts_line "+#{line}"
end

#puts_common_line(line) ⇒ Object



66
67
68
69
# File 'lib/misc/udiff.rb', line 66

def puts_common_line(line)
  line = /\A_/ =~ line ? $' : @lines_ary[line.to_i]
  puts_line " #{line}"
end

#puts_del_line(line) ⇒ Object



56
57
58
59
# File 'lib/misc/udiff.rb', line 56

def puts_del_line(line)
  line = /\A_/ =~ line ? $' : @lines_ary[line.to_i]
  puts_line "-#{line}"
end

#puts_line(line) ⇒ Object



48
49
50
51
52
53
54
# File 'lib/misc/udiff.rb', line 48

def puts_line(line)
  @hunk << line
  if /\n\z/ !~ line
    @hunk << "\n\\ No newline at end of file\n"
  end
  @beginning = false
end

#run_diff(path1, path2) ⇒ Object



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/misc/udiff.rb', line 190

def run_diff(path1, path2)
  has_diff = false
  open(path1) {|f1|
    f1.set_encoding "ascii-8bit" if f1.respond_to? :set_encoding
    open(path2) {|f2|
      f2.set_encoding "ascii-8bit" if f2.respond_to? :set_encoding
      command = Escape.shell_command(%W[diff -n #{path1} #{path2}]).to_s
  command = "LC_ALL='C' LANG='C' #{command}"
      IO.popen(command) {|d|
        d.set_encoding "ascii-8bit" if d.respond_to? :set_encoding
        has_diff = process_commands(f1, f2, d)
      }
      output_common_tail(f1, f2)
    }
  }
  has_diff
end

#skip_common_part(f1, f2, n) ⇒ Object



101
102
103
104
105
106
# File 'lib/misc/udiff.rb', line 101

def skip_common_part(f1, f2, n)
  n.times {|i|
    v = gets_common_line(f1, f2)
    raise "[bug] diff error: unexpected EOF" unless v
  }
end