diff-display 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/History.txt +4 -0
- data/License.txt +21 -0
- data/Manifest.txt +26 -0
- data/README.txt +58 -0
- data/Rakefile +48 -0
- data/doc/.gitignore +0 -0
- data/lib/diff-display.rb +14 -0
- data/lib/diff/display/data_structure.rb +193 -0
- data/lib/diff/display/unified.rb +15 -0
- data/lib/diff/display/unified/generator.rb +210 -0
- data/lib/diff/display/version.rb +11 -0
- data/lib/diff/renderer/base.rb +90 -0
- data/lib/diff/renderer/diff.rb +29 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/test/fixtures/big.diff +590 -0
- data/test/fixtures/multiple_adds_after_rem.diff +11 -0
- data/test/fixtures/multiple_rems_then_add.diff +14 -0
- data/test/fixtures/only_add.diff +4 -0
- data/test/fixtures/only_rem.diff +4 -0
- data/test/fixtures/pseudo_recursive.diff +21 -0
- data/test/fixtures/simple.diff +12 -0
- data/test/fixtures/simple_oneliner.diff +7 -0
- data/test/fixtures/simple_rewrite.diff +8 -0
- data/test/test_api.rb +12 -0
- data/test/test_base_renderer.rb +101 -0
- data/test/test_datastructure.rb +100 -0
- data/test/test_diff_renderer.rb +14 -0
- data/test/test_generator.rb +148 -0
- data/test/test_helper.rb +25 -0
- data/test/test_unified.rb +22 -0
- metadata +123 -0
@@ -0,0 +1,590 @@
|
|
1
|
+
--- unified.rb (revision 620)
|
2
|
+
+++ unified.rb (revision 644)
|
3
|
+
@@ -1,298 +1,390 @@
|
4
|
+
module Diff
|
5
|
+
module Display
|
6
|
+
module Unified
|
7
|
+
-
|
8
|
+
- LINE_RE = /@@ [+-]([0-9]+),([0-9]+) [+-]([0-9]+),([0-9]+) @@/
|
9
|
+
- TABWIDTH = 4
|
10
|
+
- SPACE = ' ' #' '
|
11
|
+
- # By defaul don't wrap inline diffs in anything
|
12
|
+
- INLINE_REM_OPEN = "\e[4;33m"
|
13
|
+
- INLINE_REM_CLOSE = "\e[m"
|
14
|
+
- INLINE_ADD_OPEN = "\e[4;35m"
|
15
|
+
- INLINE_ADD_CLOSE = "\e[m"
|
16
|
+
- ESCAPE_HTML = false
|
17
|
+
-
|
18
|
+
class Line < String
|
19
|
+
- attr_reader :add_lineno, :rem_lineno
|
20
|
+
- def initialize(line, type, add_lineno, rem_lineno = add_lineno)
|
21
|
+
+ def initialize(line, line_number)
|
22
|
+
super(line)
|
23
|
+
- @type = type
|
24
|
+
- @add_lineno = add_lineno
|
25
|
+
- @rem_lineno = rem_lineno
|
26
|
+
+ @line_number = line_number
|
27
|
+
+ self
|
28
|
+
end
|
29
|
+
|
30
|
+
+ def contains_inline_change?
|
31
|
+
+ @inline
|
32
|
+
+ end
|
33
|
+
+
|
34
|
+
def number
|
35
|
+
- add_lineno ? add_lineno : rem_lineno
|
36
|
+
+ @line_number
|
37
|
+
end
|
38
|
+
|
39
|
+
- def type
|
40
|
+
- @type
|
41
|
+
+ def decorate(&block)
|
42
|
+
+ yield self
|
43
|
+
end
|
44
|
+
|
45
|
+
- class << self
|
46
|
+
- def add(line, add_lineno)
|
47
|
+
- AddLine.new(line, add_lineno)
|
48
|
+
+ def inline_add_open; '' end
|
49
|
+
+ def inline_add_close; '' end
|
50
|
+
+ def inline_rem_open; '' end
|
51
|
+
+ def inline_rem_close; '' end
|
52
|
+
+
|
53
|
+
+ protected
|
54
|
+
+
|
55
|
+
+ def escape
|
56
|
+
+ self
|
57
|
+
end
|
58
|
+
|
59
|
+
- def rem(line, rem_lineno)
|
60
|
+
- RemLine.new(line, rem_lineno)
|
61
|
+
+ def expand
|
62
|
+
+ escape.gsub("\t", ' ' * tabwidth).gsub(/ ( +)|^ /) do |match|
|
63
|
+
+ (space + ' ') * (match.size / 2) +
|
64
|
+
+ space * (match.size % 2)
|
65
|
+
+ end
|
66
|
+
end
|
67
|
+
|
68
|
+
- def unmod(line, lineno)
|
69
|
+
- UnModLine.new(line, lineno)
|
70
|
+
+ def tabwidth
|
71
|
+
+ 4
|
72
|
+
end
|
73
|
+
|
74
|
+
- def mod(line, lineno)
|
75
|
+
- ModLine.new(line, lineno)
|
76
|
+
+
|
77
|
+
+ def space
|
78
|
+
+ ' '
|
79
|
+
end
|
80
|
+
+
|
81
|
+
+ class << self
|
82
|
+
+ def add(line, line_number, inline = false)
|
83
|
+
+ AddLine.new(line, line_number, inline)
|
84
|
+
+ end
|
85
|
+
+
|
86
|
+
+ def rem(line, line_number, inline = false)
|
87
|
+
+ RemLine.new(line, line_number, inline)
|
88
|
+
+ end
|
89
|
+
+
|
90
|
+
+ def unmod(line, line_number)
|
91
|
+
+ UnModLine.new(line, line_number)
|
92
|
+
+ end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class AddLine < Line
|
97
|
+
- def initialize(line, add_lineno)
|
98
|
+
- super(line, 'add', add_lineno, nil)
|
99
|
+
+ def initialize(line, line_number, inline = false)
|
100
|
+
+ line = inline ? line % [inline_add_open, inline_add_close] : line
|
101
|
+
+ super(line, line_number)
|
102
|
+
+ @inline = inline
|
103
|
+
+ self
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class RemLine < Line
|
108
|
+
- def initialize(line, rem_lineno)
|
109
|
+
- super(line, 'rem', nil, rem_lineno)
|
110
|
+
+ def initialize(line, line_number, inline = false)
|
111
|
+
+ line = inline ? line % [inline_rem_open, inline_rem_close] : line
|
112
|
+
+ super(line, line_number)
|
113
|
+
+ @inline = inline
|
114
|
+
+ self
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class UnModLine < Line
|
119
|
+
- def initialize(line, lineno)
|
120
|
+
- super(line, 'unmod', lineno)
|
121
|
+
+ def initialize(line, line_number)
|
122
|
+
+ super(line, line_number)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
- class ModLine < Line
|
127
|
+
- def initialize(line, lineno)
|
128
|
+
- super(line, 'mod', lineno)
|
129
|
+
+ class SepLine < Line
|
130
|
+
+ def initialize(line = '...')
|
131
|
+
+ super(line, nil)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
+ # This class is an array which contains Line objects. Just like Line
|
136
|
+
+ # classes, several Block classes inherit from Block. If all the lines
|
137
|
+
+ # in the block are added lines then it is an AddBlock. If all lines
|
138
|
+
+ # in the block are removed lines then it is a RemBlock. If the lines
|
139
|
+
+ # in the block are all unmodified then it is an UnMod block. If the
|
140
|
+
+ # lines in the block are a mixture of added and removed lines then
|
141
|
+
+ # it is a ModBlock. There are no blocks that contain a mixture of
|
142
|
+
+ # modified and unmodified lines.
|
143
|
+
class Block < Array
|
144
|
+
- def initialize(type)
|
145
|
+
- super(0)
|
146
|
+
- @type = type
|
147
|
+
+ def initialize
|
148
|
+
+ super
|
149
|
+
+ @line_types = []
|
150
|
+
end
|
151
|
+
|
152
|
+
def <<(line_object)
|
153
|
+
super(line_object)
|
154
|
+
- (@line_types ||= []).push(line_object.type)
|
155
|
+
- @line_types.uniq!
|
156
|
+
+ line_class = line_object.class.name[/\w+$/]
|
157
|
+
+ @line_types.push(line_class) unless @line_types.include?(line_class)
|
158
|
+
self
|
159
|
+
end
|
160
|
+
|
161
|
+
+ def decorate(&block)
|
162
|
+
+ yield self
|
163
|
+
+ end
|
164
|
+
+
|
165
|
+
def line_types
|
166
|
+
@line_types
|
167
|
+
end
|
168
|
+
|
169
|
+
- def type
|
170
|
+
- @type
|
171
|
+
+ class << self
|
172
|
+
+ def add; AddBlock.new end
|
173
|
+
+ def rem; RemBlock.new end
|
174
|
+
+ def mod; ModBlock.new end
|
175
|
+
+ def unmod; UnModBlock.new end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
- class Generator < Array
|
180
|
+
+ class AddBlock < Block; end
|
181
|
+
+ class RemBlock < Block; end
|
182
|
+
+ class ModBlock < Block; end
|
183
|
+
+ class UnModBlock < Block; end
|
184
|
+
+ class SepBlock < Block; end
|
185
|
+
|
186
|
+
+ # This data object contains the generated diff data structure. It is an
|
187
|
+
+ # array of Block objects which are themselves arrays of Line objects. The
|
188
|
+
+ # Generator class returns a Data instance object after it is done
|
189
|
+
+ # processing the diff.
|
190
|
+
+ class Data < Array
|
191
|
+
+ def initialize
|
192
|
+
+ super
|
193
|
+
+ end
|
194
|
+
+
|
195
|
+
+ def debug
|
196
|
+
+ demodularize = Proc.new {|obj| obj.class.name[/\w+$/]}
|
197
|
+
+ each do |diff_block|
|
198
|
+
+ print "*" * 40, ' ', demodularize.call(diff_block)
|
199
|
+
+ puts
|
200
|
+
+ puts diff_block.map {|line|
|
201
|
+
+ "%5d" % line.number +
|
202
|
+
+ " [#{demodularize.call(line)}]" +
|
203
|
+
+ line
|
204
|
+
+ }.join("\n")
|
205
|
+
+ puts "*" * 40, ' '
|
206
|
+
+ end
|
207
|
+
+ end
|
208
|
+
+
|
209
|
+
+ end
|
210
|
+
+
|
211
|
+
+ # Processes the diff and generates a Data object which contains the
|
212
|
+
+ # resulting data structure.
|
213
|
+
+ class Generator
|
214
|
+
+
|
215
|
+
+ # Extracts the line number info for a given diff section
|
216
|
+
+ LINE_NUM_RE = /@@ [+-]([0-9]+),([0-9]+) [+-]([0-9]+),([0-9]+) @@/
|
217
|
+
+ LINE_TYPES = {'+' => :add, '-' => :rem, ' ' => :unmod}
|
218
|
+
+
|
219
|
+
class << self
|
220
|
+
- def run(udiff, options = {})
|
221
|
+
- generator = new(options)
|
222
|
+
- udiff.split("\n").each {|line| generator.build(line) }
|
223
|
+
- generator.close
|
224
|
+
- generator
|
225
|
+
+
|
226
|
+
+ # Runs the generator on a diff and returns a Data object without
|
227
|
+
+ # instantiating a Generator object
|
228
|
+
+ def run(udiff)
|
229
|
+
+ raise ArgumentError, "Object must be enumerable" unless udiff.respond_to?(:each)
|
230
|
+
+ generator = new
|
231
|
+
+ udiff.each {|line| generator.process(line.chomp)}
|
232
|
+
+ generator.render
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
- def initialize(options = {})
|
237
|
+
- super(0)
|
238
|
+
- default_options = {:inline_add_open => INLINE_ADD_OPEN,
|
239
|
+
- :inline_add_close => INLINE_ADD_CLOSE,
|
240
|
+
- :inline_rem_open => INLINE_REM_OPEN,
|
241
|
+
- :inline_rem_close => INLINE_REM_CLOSE,
|
242
|
+
- :escape_html => ESCAPE_HTML,
|
243
|
+
- :tabwidth => TABWIDTH,
|
244
|
+
- :space => SPACE}
|
245
|
+
-
|
246
|
+
- @options = default_options.merge(options)
|
247
|
+
- @block = []
|
248
|
+
- @ttype = nil
|
249
|
+
- @p_block = []
|
250
|
+
- @p_type = nil
|
251
|
+
- @changeno = -1
|
252
|
+
- @blockno = 0
|
253
|
+
+ def initialize
|
254
|
+
+ @buffer = []
|
255
|
+
+ @prev_buffer = []
|
256
|
+
+ @line_type = nil
|
257
|
+
+ @prev_line_type = nil
|
258
|
+
@offset_base = 0
|
259
|
+
@offset_changed = 0
|
260
|
+
+ @data = Diff::Display::Unified::Data.new
|
261
|
+
+ self
|
262
|
+
end
|
263
|
+
|
264
|
+
- def current_block
|
265
|
+
- last
|
266
|
+
+ # Operates on a single line from the diff and passes along the
|
267
|
+
+ # collected data to the appropriate method for further processing. The
|
268
|
+
+ # cycle of processing is in general:
|
269
|
+
+ #
|
270
|
+
+ # process --> identify_block --> process_block --> process_line
|
271
|
+
+ #
|
272
|
+
+ def process(line)
|
273
|
+
+ return if ['++', '--'].include?(line[0,2])
|
274
|
+
+
|
275
|
+
+ if match = LINE_NUM_RE.match(line)
|
276
|
+
+ identify_block
|
277
|
+
+ push SepBlock.new and current_block << SepLine.new unless @offset_changed.zero?
|
278
|
+
+ @line_type = nil
|
279
|
+
+ @offset_base = match[1].to_i - 1
|
280
|
+
+ @offset_changed = match[3].to_i - 1
|
281
|
+
+ return
|
282
|
+
+ end
|
283
|
+
+
|
284
|
+
+ new_line_type, line = LINE_TYPES[car(line)], cdr(line)
|
285
|
+
+
|
286
|
+
+ # Add line to the buffer if it's the same diff line type
|
287
|
+
+ # as the previous line
|
288
|
+
+ #
|
289
|
+
+ # e.g.
|
290
|
+
+ #
|
291
|
+
+ # + This is a new line
|
292
|
+
+ # + As is this one
|
293
|
+
+ # + And yet another one...
|
294
|
+
+ #
|
295
|
+
+ if new_line_type.eql?(@line_type)
|
296
|
+
+ @buffer.push(line)
|
297
|
+
+ else
|
298
|
+
+ # Side by side inline diff
|
299
|
+
+ #
|
300
|
+
+ # e.g.
|
301
|
+
+ #
|
302
|
+
+ # - This line just had to go
|
303
|
+
+ # + This line is on the way in
|
304
|
+
+ #
|
305
|
+
+ if new_line_type.eql?(LINE_TYPES['+']) and @line_type.eql?(LINE_TYPES['-'])
|
306
|
+
+ @prev_buffer = @buffer
|
307
|
+
+ @prev_line_type = @line_type
|
308
|
+
+ else
|
309
|
+
+ identify_block
|
310
|
+
+ end
|
311
|
+
+ @buffer = [line]
|
312
|
+
+ @line_type = new_line_type
|
313
|
+
+ end
|
314
|
+
end
|
315
|
+
|
316
|
+
+ # Finishes up with the generation and returns the Data object (could
|
317
|
+
+ # probably use a better name...maybe just #data?)
|
318
|
+
def render
|
319
|
+
close
|
320
|
+
- self
|
321
|
+
+ @data
|
322
|
+
end
|
323
|
+
-
|
324
|
+
- def escape(text)
|
325
|
+
- return '' unless text
|
326
|
+
- return text unless @options[:escape_html]
|
327
|
+
- text.gsub('&', '&').
|
328
|
+
- gsub('<', '<' ).
|
329
|
+
- gsub('>', '>' ).
|
330
|
+
- gsub('"', '"')
|
331
|
+
- end
|
332
|
+
|
333
|
+
- def expand(text)
|
334
|
+
- escape(text).gsub(/ ( +)|^ /) do |match|
|
335
|
+
- (@options[:space] + ' ') * (match.size / 2) +
|
336
|
+
- @options[:space] * (match.size % 2)
|
337
|
+
- end
|
338
|
+
- end
|
339
|
+
+ protected
|
340
|
+
|
341
|
+
- def inline_diff(line, start, ending, change)
|
342
|
+
- expand(line[0, start]) +
|
343
|
+
- change +
|
344
|
+
- expand(line[ending, ending.abs])
|
345
|
+
- end
|
346
|
+
+ def identify_block
|
347
|
+
+ if @prev_line_type.eql?(LINE_TYPES['-']) and @line_type.eql?(LINE_TYPES['+'])
|
348
|
+
+ process_block(:mod, {:old => @prev_buffer, :new => @buffer})
|
349
|
+
+ else
|
350
|
+
+ if LINE_TYPES.values.include?(@line_type)
|
351
|
+
+ process_block(@line_type, {:new => @buffer})
|
352
|
+
+ end
|
353
|
+
+ end
|
354
|
+
|
355
|
+
- def write_line(oldline, newline)
|
356
|
+
- start, ending = get_change_extent(oldline, newline)
|
357
|
+
- change = ''
|
358
|
+
- if oldline.size > start - ending
|
359
|
+
- change = @options[:inline_rem_open] +
|
360
|
+
- expand(oldline[start...ending]) +
|
361
|
+
- @options[:inline_rem_close]
|
362
|
+
+ @prev_line_type = nil
|
363
|
+
end
|
364
|
+
|
365
|
+
- line = inline_diff(oldline, start, ending, change)
|
366
|
+
- current_block << Line.rem(line, @offset_base)
|
367
|
+
+ def process_block(diff_line_type, blocks = {:old => nil, :new => nil})
|
368
|
+
+ push Block.send(diff_line_type)
|
369
|
+
+ old, new = blocks[:old], blocks[:new]
|
370
|
+
|
371
|
+
- change = ''
|
372
|
+
- if newline.size > start - ending
|
373
|
+
- change = @options[:inline_add_open] +
|
374
|
+
- expand(newline[start...ending]) +
|
375
|
+
- @options[:inline_add_close]
|
376
|
+
+ # Mod block
|
377
|
+
+ if diff_line_type.eql?(:mod) and old.size & new.size == 1
|
378
|
+
+ process_line(old.first, new.first)
|
379
|
+
+ return
|
380
|
+
+ end
|
381
|
+
+
|
382
|
+
+ if old and not old.empty?
|
383
|
+
+ old.each do |line|
|
384
|
+
+ @offset_base += 1
|
385
|
+
+ current_block << Line.send(@prev_line_type, line, @offset_base)
|
386
|
+
+ end
|
387
|
+
+ end
|
388
|
+
+
|
389
|
+
+ if new and not new.empty?
|
390
|
+
+ new.each do |line|
|
391
|
+
+ @offset_changed += 1
|
392
|
+
+ current_block << Line.send(@line_type, line, @offset_changed)
|
393
|
+
+ end
|
394
|
+
+ end
|
395
|
+
end
|
396
|
+
|
397
|
+
- line = inline_diff(newline, start, ending, change)
|
398
|
+
- current_block << Line.add(line, @offset_changed)
|
399
|
+
- end
|
400
|
+
+ # TODO Needs a better name...it does process a line (two in fact) but
|
401
|
+
+ # its primary function is to add a Rem and an Add pair which
|
402
|
+
+ # potentially have inline changes
|
403
|
+
+ def process_line(oldline, newline)
|
404
|
+
+ start, ending = get_change_extent(oldline, newline)
|
405
|
+
|
406
|
+
- def write_block(dtype, old = nil, new = nil)
|
407
|
+
- push Block.new(dtype)
|
408
|
+
+ # -
|
409
|
+
+ line = inline_diff(oldline, start, ending)
|
410
|
+
+ current_block << Line.rem(line, @offset_base += 1, true)
|
411
|
+
|
412
|
+
- if dtype == 'mod' and old.size == 1 and new.size == 1
|
413
|
+
- write_line(old.first, new.first)
|
414
|
+
- return
|
415
|
+
+ # +
|
416
|
+
+ line = inline_diff(newline, start, ending)
|
417
|
+
+ current_block << Line.add(line, @offset_changed += 1, true)
|
418
|
+
end
|
419
|
+
|
420
|
+
- if old and not old.empty?
|
421
|
+
- old.each do |e|
|
422
|
+
- current_block << Line.send(dtype, expand(e), @offset_base)
|
423
|
+
- @offset_base += 1
|
424
|
+
- end
|
425
|
+
+ # Inserts string formating characters around the section of a string
|
426
|
+
+ # that differs internally from another line so that the Line class
|
427
|
+
+ # can insert the desired formating
|
428
|
+
+ def inline_diff(line, start, ending)
|
429
|
+
+ line[0, start] +
|
430
|
+
+ '%s' + extract_change(line, start, ending) + '%s' +
|
431
|
+
+ line[ending, ending.abs]
|
432
|
+
end
|
433
|
+
|
434
|
+
- if new and not new.empty?
|
435
|
+
- new.each do |e|
|
436
|
+
- current_block << Line.send(dtype, expand(e), @offset_changed)
|
437
|
+
- @offset_changed += 1
|
438
|
+
- end
|
439
|
+
+ def extract_change(line, start, ending)
|
440
|
+
+ line.size > (start - ending) ? line[start...ending] : ''
|
441
|
+
end
|
442
|
+
- end
|
443
|
+
|
444
|
+
- def print_block
|
445
|
+
- if @p_type.eql?('-') and @ttype.eql?('+')
|
446
|
+
- write_block('mod', @p_block, @block)
|
447
|
+
- else
|
448
|
+
- case @ttype
|
449
|
+
- when '+'
|
450
|
+
- write_block('add', @block)
|
451
|
+
- when '-'
|
452
|
+
- write_block('rem', @block)
|
453
|
+
- when ' '
|
454
|
+
- write_block('unmod', @block)
|
455
|
+
- end
|
456
|
+
+ def car(line)
|
457
|
+
+ line[0,1]
|
458
|
+
end
|
459
|
+
|
460
|
+
- @block = @p_block = []
|
461
|
+
- @p_type = ' '
|
462
|
+
- @blockno += 1
|
463
|
+
- end
|
464
|
+
+ def cdr(line)
|
465
|
+
+ line[1..-1]
|
466
|
+
+ end
|
467
|
+
|
468
|
+
- def build(text)
|
469
|
+
- # TODO Names of the files and their versions go here perhaps
|
470
|
+
+ # Returns the current Block object
|
471
|
+
+ def current_block
|
472
|
+
+ @data.last
|
473
|
+
+ end
|
474
|
+
|
475
|
+
- return if ['++', '--'].include?(text[0,2])
|
476
|
+
+ # Adds a Line object onto the current Block object
|
477
|
+
+ def push(line)
|
478
|
+
+ @data.push line
|
479
|
+
+ end
|
480
|
+
|
481
|
+
- if match = LINE_RE.match(text)
|
482
|
+
- print_block
|
483
|
+
- @changeno += 1
|
484
|
+
- @blockno = 0
|
485
|
+
- @offset_base = match[1].to_i - 1
|
486
|
+
- @offset_changed = match[3].to_i - 1
|
487
|
+
- return
|
488
|
+
+ # This method is called once the generator is done with the unified
|
489
|
+
+ # diff. It is a finalizer of sorts. By the time it is called all data
|
490
|
+
+ # has been collected and processed.
|
491
|
+
+ def close
|
492
|
+
+ # certain things could be set now that processing is done
|
493
|
+
+ identify_block
|
494
|
+
end
|
495
|
+
|
496
|
+
- # Set ttype to first character of line
|
497
|
+
- ttype = text[0, 1]
|
498
|
+
- text = text[1..-1]
|
499
|
+
- text = text.gsub("\t", ' ' * @options[:tabwidth]) if text
|
500
|
+
- # If it's the same type of mod as the last line push this line onto the
|
501
|
+
- # block stack
|
502
|
+
- if ttype.eql?(@ttype)
|
503
|
+
- @block.push(text)
|
504
|
+
- else
|
505
|
+
- # If we have a side by side subtraction/addition
|
506
|
+
- if ttype == '+' and @ttype == '-'
|
507
|
+
- @p_block = @block
|
508
|
+
- @p_type = @ttype
|
509
|
+
- else
|
510
|
+
- print_block
|
511
|
+
+ # Determines the extent of differences between two string. Returns
|
512
|
+
+ # an array containing the offset at which changes start, and then
|
513
|
+
+ # negative offset at which the chnages end. If the two strings have
|
514
|
+
+ # neither a common prefix nor a common suffic, [0, 0] is returned.
|
515
|
+
+ def get_change_extent(str1, str2)
|
516
|
+
+ start = 0
|
517
|
+
+ limit = [str1.size, str2.size].sort.first
|
518
|
+
+ while start < limit and str1[start, 1] == str2[start, 1]
|
519
|
+
+ start += 1
|
520
|
+
end
|
521
|
+
- @block = [text]
|
522
|
+
- @ttype = ttype
|
523
|
+
+ ending = -1
|
524
|
+
+ limit -= start
|
525
|
+
+ while -ending <= limit and str1[ending, 1] == str2[ending, 1]
|
526
|
+
+ ending -= 1
|
527
|
+
+ end
|
528
|
+
+
|
529
|
+
+ return [start, ending + 1]
|
530
|
+
end
|
531
|
+
- end
|
532
|
+
+ end
|
533
|
+
|
534
|
+
- def debug
|
535
|
+
- each do |diff_block|
|
536
|
+
- print "*" * (40 - diff_block.type.size / 2), ' ', diff_block.type
|
537
|
+
- puts
|
538
|
+
- puts diff_block.map {|line| "#{line.number}" << line << " [#{line.type}]"}.join("\n")
|
539
|
+
- print "Line types:"
|
540
|
+
- puts diff_block.line_types.join(", ")
|
541
|
+
- puts
|
542
|
+
- end
|
543
|
+
+ # Mostly a convinience class at this point that just overwrites various
|
544
|
+
+ # customization methods
|
545
|
+
+ class HTMLGenerator < Generator
|
546
|
+
+
|
547
|
+
+ # This and the space method now don't work/make sense now that those
|
548
|
+
+ # methods are part of the Line class and there certainly won't be an
|
549
|
+
+ # HTMLLine class
|
550
|
+
+ def escape(text)
|
551
|
+
+ text.gsub('&', '&').
|
552
|
+
+ gsub('<', '<' ).
|
553
|
+
+ gsub('>', '>' ).
|
554
|
+
+ gsub('"', '"')
|
555
|
+
end
|
556
|
+
|
557
|
+
- def close
|
558
|
+
- # certain things could be set now that processing is done
|
559
|
+
- print_block
|
560
|
+
+ def space
|
561
|
+
+ ' '
|
562
|
+
end
|
563
|
+
|
564
|
+
- # Determines the extent of differences between two string. Returns
|
565
|
+
- # an array containing the offset at which changes start, and then
|
566
|
+
- # negative offset at which the chnages end. If the two strings have
|
567
|
+
- # neither a common prefix nor a common suffic, [0, 0] is returned.
|
568
|
+
- def get_change_extent(str1, str2)
|
569
|
+
- start = 0
|
570
|
+
- limit = [str1.size, str2.size].sort.first
|
571
|
+
- while start < limit and str1[start, 1] == str2[start, 1]
|
572
|
+
- start += 1
|
573
|
+
- end
|
574
|
+
- ending = -1
|
575
|
+
- limit -= start
|
576
|
+
- while -ending <= limit and str1[ending, 1] == str2[ending, 1]
|
577
|
+
- ending -= 1
|
578
|
+
- end
|
579
|
+
+ end
|
580
|
+
|
581
|
+
- return [start, ending + 1]
|
582
|
+
- end
|
583
|
+
+ # See doc string for HTMLGenerator
|
584
|
+
+ class ASCIIGenerator < Generator
|
585
|
+
end
|
586
|
+
+
|
587
|
+
end
|
588
|
+
end
|
589
|
+
end
|
590
|
+
-
|