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
data/.gitignore
ADDED
data/History.txt
ADDED
data/License.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2008 Johan Sørensen
|
2
|
+
Copyright (c) 2003 Marcel Molina Jr.
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
History.txt
|
2
|
+
License.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
config/hoe.rb
|
7
|
+
config/requirements.rb
|
8
|
+
lib/diff-display.rb
|
9
|
+
lib/diff-display/version.rb
|
10
|
+
log/debug.log
|
11
|
+
script/destroy
|
12
|
+
script/generate
|
13
|
+
script/txt2html
|
14
|
+
setup.rb
|
15
|
+
spec/diff-display_spec.rb
|
16
|
+
spec/spec.opts
|
17
|
+
spec/spec_helper.rb
|
18
|
+
tasks/deployment.rake
|
19
|
+
tasks/environment.rake
|
20
|
+
tasks/rspec.rake
|
21
|
+
tasks/website.rake
|
22
|
+
website/index.html
|
23
|
+
website/index.txt
|
24
|
+
website/javascripts/rounded_corners_lite.inc.js
|
25
|
+
website/stylesheets/screen.css
|
26
|
+
website/template.rhtml
|
data/README.txt
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
Diff::Display
|
2
|
+
=============
|
3
|
+
|
4
|
+
Diff::Display::Unified renders unified diffs into various forms. The output is
|
5
|
+
based on a callback object that's passed into the renderer
|
6
|
+
|
7
|
+
Rewrite of an (unreleased) library by Marcel Molina Jr., who wrote this it
|
8
|
+
probably back in 2004 or so.
|
9
|
+
|
10
|
+
Usage
|
11
|
+
======
|
12
|
+
|
13
|
+
irb(main):001:0> require 'diff-display'
|
14
|
+
=> true
|
15
|
+
irb(main):002:0> diff = <<EOS
|
16
|
+
irb(main):003:0" diff --git a/History.txt b/History.txt
|
17
|
+
irb(main):004:0" index 0ed7358..622c384 100644
|
18
|
+
irb(main):005:0" --- a/History.txt
|
19
|
+
irb(main):006:0" +++ b/History.txt
|
20
|
+
irb(main):007:0" @@ -1,4 +1,5 @@
|
21
|
+
irb(main):008:0" == 0.0.1 2008-01-28
|
22
|
+
irb(main):009:0"
|
23
|
+
irb(main):010:0" -* 1 major enhancement:
|
24
|
+
irb(main):011:0" - * Initial release
|
25
|
+
irb(main):012:0" +* 2 major enhancements:
|
26
|
+
irb(main):013:0" + * The Initial release
|
27
|
+
irb(main):014:0" + * stuff added
|
28
|
+
irb(main):015:0" EOS
|
29
|
+
...
|
30
|
+
irb(main):016:0> diff_display = Diff::Display::Unified.new(diff)
|
31
|
+
=> #<Diff::Display::Unified:0x331c9c @data=...
|
32
|
+
# Be boring and render it back out as a diff
|
33
|
+
irb(main):017:0> puts diff_display.render(Diff::Renderer::Diff.new)
|
34
|
+
diff --git a/History.txt b/History.txt
|
35
|
+
index 0ed7358..622c384 100644
|
36
|
+
--- a/History.txt
|
37
|
+
+++ b/History.txt
|
38
|
+
@@ -1,4 +1,5 @@
|
39
|
+
== 0.0.1 2008-01-28
|
40
|
+
|
41
|
+
-* 1 major enhancement:
|
42
|
+
- * Initial release
|
43
|
+
+* 2 major enhancements:
|
44
|
+
+ * The Initial release
|
45
|
+
+ * stuff added
|
46
|
+
|
47
|
+
See Diff::Renderer::Base for what methods your callback needs to implement
|
48
|
+
|
49
|
+
Git Repository
|
50
|
+
===============
|
51
|
+
|
52
|
+
http://gitorious.org/projects/diff-display/
|
53
|
+
|
54
|
+
|
55
|
+
License
|
56
|
+
======
|
57
|
+
|
58
|
+
Please see License.txt
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
require './lib/diff-display.rb'
|
7
|
+
|
8
|
+
task "gemspec" do
|
9
|
+
spec = Gem::Specification.new do |s|
|
10
|
+
s.name = "diff-display"
|
11
|
+
s.version = Diff::Display::VERSION::STRING
|
12
|
+
s.platform = Gem::Platform::RUBY
|
13
|
+
s.summary = "Diff::Display::Unified renders unified diffs into various forms."
|
14
|
+
|
15
|
+
s.description = %Q{Diff::Display::Unified renders unified diffs into various forms. The output is based on a callback object that's passed into the renderer}
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n") + %w(diff-display.gemspec)
|
18
|
+
s.require_path = 'lib'
|
19
|
+
s.has_rdoc = false
|
20
|
+
s.extra_rdoc_files = ['README.txt']
|
21
|
+
s.test_files = Dir['test/{test,spec}_*.rb']
|
22
|
+
|
23
|
+
s.authors = ['Johan Sørensen', 'Ketan Padegaonkar']
|
24
|
+
s.email = ['johan@johansorensen.com', 'KetanPadegaonkar@gmail.com']
|
25
|
+
s.homepage = 'http://github.com/ketan/diff-display'
|
26
|
+
|
27
|
+
s.add_development_dependency 'mocha'
|
28
|
+
end
|
29
|
+
|
30
|
+
File.open("diff-display.gemspec", "w") { |f| f << spec.to_ruby }
|
31
|
+
end
|
32
|
+
|
33
|
+
task :gem => ["gemspec"] do
|
34
|
+
rm_rf 'pkg'
|
35
|
+
sh "gem build diff-display.gemspec"
|
36
|
+
mkdir 'pkg'
|
37
|
+
mv "diff-display-#{Diff::Display::VERSION::STRING}.gem", "pkg"
|
38
|
+
end
|
39
|
+
|
40
|
+
task :test do
|
41
|
+
Rake::TestTask.new do |t|
|
42
|
+
t.libs << "test"
|
43
|
+
t.test_files = FileList['test/test_*.rb']
|
44
|
+
t.verbose = true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
task :default => :test
|
data/doc/.gitignore
ADDED
File without changes
|
data/lib/diff-display.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
module Diff
|
4
|
+
module Display
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require "diff/display/version"
|
9
|
+
require "diff/display/data_structure"
|
10
|
+
require "diff/display/unified"
|
11
|
+
require "diff/display/unified/generator"
|
12
|
+
|
13
|
+
require "diff/renderer/base"
|
14
|
+
require "diff/renderer/diff"
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module Diff
|
2
|
+
module Display
|
3
|
+
class Data < Array
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_diff
|
9
|
+
diff = ""
|
10
|
+
each do |block|
|
11
|
+
block.each do |line|
|
12
|
+
line_str = line.expand_inline_changes_with(nil, nil)
|
13
|
+
case line
|
14
|
+
when HeaderLine
|
15
|
+
diff << "#{line_str}\n"
|
16
|
+
when UnModLine
|
17
|
+
diff << " #{line_str}\n"
|
18
|
+
when SepLine
|
19
|
+
diff << "\n"
|
20
|
+
when AddLine
|
21
|
+
diff << "+#{line_str}\n"
|
22
|
+
when RemLine
|
23
|
+
diff << "-#{line_str}\n"
|
24
|
+
when NonewlineLine
|
25
|
+
diff << line_str
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
diff.chomp
|
30
|
+
end
|
31
|
+
|
32
|
+
def debug
|
33
|
+
demodularize = Proc.new {|obj| obj.class.name[/\w+$/]}
|
34
|
+
each do |diff_block|
|
35
|
+
print "-" * 40, ' ', demodularize.call(diff_block)
|
36
|
+
puts
|
37
|
+
puts diff_block.map {|line|
|
38
|
+
# "%5d" % line.old_number +
|
39
|
+
"%8s" % "[#{line.old_number || '.'} #{line.new_number || '.'}]" +
|
40
|
+
" [#{demodularize.call(line)}#{'(i)' if line.inline_changes?}]" +
|
41
|
+
line
|
42
|
+
}.join("\n")
|
43
|
+
puts "-" * 40, ' '
|
44
|
+
end
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Every line from the passed in diff gets transformed into an instance of
|
50
|
+
# one of line Line class's subclasses. One subclass exists for each line
|
51
|
+
# type in a diff. As such there is an AddLine class for added lines, a RemLine
|
52
|
+
# class for removed lines, an UnModLine class for lines which remain unchanged and
|
53
|
+
# a SepLine class which represents all the lines that aren't part of the diff.
|
54
|
+
class Line < String
|
55
|
+
class << self
|
56
|
+
def add(line, line_number, inline = false)
|
57
|
+
AddLine.new(line, line_number, inline)
|
58
|
+
end
|
59
|
+
|
60
|
+
def rem(line, line_number, inline = false)
|
61
|
+
RemLine.new(line, line_number, inline)
|
62
|
+
end
|
63
|
+
|
64
|
+
def unmod(line, old_number, new_number)
|
65
|
+
UnModLine.new(line, old_number, new_number)
|
66
|
+
end
|
67
|
+
|
68
|
+
def nonewline(line)
|
69
|
+
NonewlineLine.new(line)
|
70
|
+
end
|
71
|
+
|
72
|
+
def header(line)
|
73
|
+
HeaderLine.new(line)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def initialize(line, old_number = nil, new_number = nil)
|
78
|
+
super(line)
|
79
|
+
@old_number, @new_number = old_number, new_number
|
80
|
+
@inline = false
|
81
|
+
end
|
82
|
+
attr_reader :old_number, :new_number
|
83
|
+
|
84
|
+
def identifier
|
85
|
+
self.class.name[/\w+$/].gsub(/Line$/, "").downcase.to_sym
|
86
|
+
end
|
87
|
+
|
88
|
+
def inline_changes?
|
89
|
+
# Is set in the AddLine+RemLine subclasses
|
90
|
+
@inline
|
91
|
+
end
|
92
|
+
|
93
|
+
# returns the prefix, middle and postfix parts of a line with inline changes
|
94
|
+
def segments
|
95
|
+
return self.dup unless inline_changes?
|
96
|
+
prefix, changed = self.dup.split('\\0')
|
97
|
+
changed, postfix = changed.split('\\1')
|
98
|
+
[prefix, changed, postfix]
|
99
|
+
end
|
100
|
+
|
101
|
+
# Expand any inline changes with +prefix+ and +postfix+
|
102
|
+
def expand_inline_changes_with(prefix, postfix)
|
103
|
+
return self.dup unless inline_changes?
|
104
|
+
str = self.dup
|
105
|
+
str.sub!('\\0', prefix.to_s)
|
106
|
+
str.sub!('\\1', postfix.to_s)
|
107
|
+
str
|
108
|
+
end
|
109
|
+
|
110
|
+
def inspect
|
111
|
+
%Q{#<#{self.class.name} [#{old_number.inspect}-#{new_number.inspect}] "#{self}">}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# class AddLine < Line
|
116
|
+
# def initialize(line, line_number)
|
117
|
+
# super(line, nil, line_number)
|
118
|
+
# end
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# class RemLine < Line
|
122
|
+
# def initialize(line, line_number)
|
123
|
+
# super(line, line_number, nil)
|
124
|
+
# end
|
125
|
+
# end
|
126
|
+
class AddLine < Line
|
127
|
+
def initialize(line, line_number, inline = false)
|
128
|
+
super(line, nil, line_number)
|
129
|
+
@inline = inline
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class RemLine < Line
|
134
|
+
def initialize(line, line_number, inline = false)
|
135
|
+
super(line, line_number, nil)
|
136
|
+
@inline = inline
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class NonewlineLine < Line
|
141
|
+
def initialize(line = '\')
|
142
|
+
super(line)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class UnModLine < Line
|
147
|
+
def initialize(line, old_number, new_number)
|
148
|
+
super(line, old_number, new_number)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class SepLine < Line
|
153
|
+
def initialize(line = '...')
|
154
|
+
super(line)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class HeaderLine < Line
|
159
|
+
def initialize(line)
|
160
|
+
super(line)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# This class is an array which contains Line objects. Just like Line
|
165
|
+
# classes, several Block classes inherit from Block. If all the lines
|
166
|
+
# in the block are added lines then it is an AddBlock. If all lines
|
167
|
+
# in the block are removed lines then it is a RemBlock. If the lines
|
168
|
+
# in the block are all unmodified then it is an UnMod block. If the
|
169
|
+
# lines in the block are a mixture of added and removed lines then
|
170
|
+
# it is a ModBlock. There are no blocks that contain a mixture of
|
171
|
+
# modified and unmodified lines.
|
172
|
+
class Block < Array
|
173
|
+
class << self
|
174
|
+
def add; AddBlock.new end
|
175
|
+
def rem; RemBlock.new end
|
176
|
+
def mod; ModBlock.new end
|
177
|
+
def unmod; UnModBlock.new end
|
178
|
+
def header; HeaderBlock.new end
|
179
|
+
def nonewline; NonewlineBlock.new end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
#:stopdoc:#
|
184
|
+
class AddBlock < Block; end
|
185
|
+
class RemBlock < Block; end
|
186
|
+
class ModBlock < Block; end
|
187
|
+
class UnModBlock < Block; end
|
188
|
+
class SepBlock < Block; end
|
189
|
+
class HeaderBlock < Block; end
|
190
|
+
class NonewlineBlock < Block; end
|
191
|
+
#:startdoc:#
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
module Diff::Display
|
2
|
+
# Processes the diff and generates a Data object which contains the
|
3
|
+
# resulting data structure.
|
4
|
+
#
|
5
|
+
# The +run+ class method is fed a diff and returns a Data object. It will
|
6
|
+
# accept as its argument a String, an Array or a File object (or anything
|
7
|
+
# that responds to #each):
|
8
|
+
#
|
9
|
+
# Diff::Display::Unified::Generator.run(diff)
|
10
|
+
#
|
11
|
+
class Unified::Generator
|
12
|
+
|
13
|
+
# Extracts the line number info for a given diff section
|
14
|
+
LINE_NUM_RE = /^@@ [+-]([0-9]+)(?:,([0-9]+))? [+-]([0-9]+)(?:,([0-9]+))? @@/
|
15
|
+
LINE_TYPES = {'+' => :add, '-' => :rem, ' ' => :unmod, '\\' => :nonewline}
|
16
|
+
|
17
|
+
# Runs the generator on a diff and returns a Data object
|
18
|
+
def self.run(udiff)
|
19
|
+
raise ArgumentError, "Object must be enumerable" unless udiff.respond_to?(:each_line)
|
20
|
+
generator = new
|
21
|
+
udiff.each_line do |line|
|
22
|
+
begin
|
23
|
+
generator.process(line.chomp)
|
24
|
+
rescue ArgumentError => e
|
25
|
+
e.message =~ /^invalid byte sequence/ ? next : raise(e)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
generator.finish
|
29
|
+
generator.data
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@buffer = []
|
34
|
+
@line_type = nil
|
35
|
+
@prev_line_type = nil
|
36
|
+
@offset = [0, 0]
|
37
|
+
@data = Data.new
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
# Finishes up with the generation and returns the Data object (could
|
42
|
+
# probably use a better name...maybe just #data?)
|
43
|
+
def data
|
44
|
+
@data
|
45
|
+
end
|
46
|
+
|
47
|
+
# This method is called once the generator is done with the unified
|
48
|
+
# diff. It is a finalizer of sorts. By the time it is called all data
|
49
|
+
# has been collected and processed.
|
50
|
+
def finish
|
51
|
+
# certain things could be set now that processing is done
|
52
|
+
#identify_block
|
53
|
+
end
|
54
|
+
|
55
|
+
def process(line)
|
56
|
+
if is_header_line?(line)
|
57
|
+
push Block.header
|
58
|
+
current_block << Line.header(line)
|
59
|
+
return
|
60
|
+
end
|
61
|
+
|
62
|
+
if line =~ LINE_NUM_RE
|
63
|
+
push Block.header
|
64
|
+
current_block << Line.header(line)
|
65
|
+
add_separator unless @offset[0].zero?
|
66
|
+
@line_type = nil
|
67
|
+
@offset = Array.new(2) { $3.to_i - 1 }
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
@line_type, line = LINE_TYPES[car(line)], cdr(line)
|
72
|
+
|
73
|
+
if @line_type == :add && @prev_line_type == :rem
|
74
|
+
@offset[0] -= 1
|
75
|
+
@buffer.push current_block.pop
|
76
|
+
@buffer.push line
|
77
|
+
process_block(:mod, false)
|
78
|
+
return
|
79
|
+
end
|
80
|
+
|
81
|
+
if LINE_TYPES.values.include?(@line_type)
|
82
|
+
@buffer.push(line.to_s)
|
83
|
+
process_block(@line_type, true)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
def is_header_line?(line)
|
90
|
+
return true if ['+++ ', '--- '].include?(line[0,4])
|
91
|
+
return true if line =~ /^(new|delete) file mode [0-9]+$/
|
92
|
+
return true if line =~ /^diff \-\-git/
|
93
|
+
return true if line =~ /^index \w+\.\.\w+( [0-9]+)?$/i
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
97
|
+
def process_block(diff_line_type, isnew = false)
|
98
|
+
@data.pop unless isnew
|
99
|
+
push Block.send(diff_line_type)
|
100
|
+
|
101
|
+
current_line = @buffer.pop
|
102
|
+
return unless current_line
|
103
|
+
|
104
|
+
# \
|
105
|
+
if diff_line_type == :nonewline
|
106
|
+
current_block << Line.nonewline('\')
|
107
|
+
return
|
108
|
+
end
|
109
|
+
|
110
|
+
if isnew
|
111
|
+
process_line(current_line, diff_line_type)
|
112
|
+
else
|
113
|
+
process_lines_with_differences(@buffer.shift, current_line)
|
114
|
+
raise "buffer exceeded #{@buffer.inspect}" unless @buffer.empty?
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def process_line(line, type, inline = false)
|
119
|
+
case type
|
120
|
+
when :add
|
121
|
+
@offset[1] += 1
|
122
|
+
current_block << Line.send(type, line, @offset[1], inline)
|
123
|
+
when :rem
|
124
|
+
@offset[0] += 1
|
125
|
+
current_block << Line.send(type, line, @offset[0], inline)
|
126
|
+
# when :rmod
|
127
|
+
# @offset[0] += 1
|
128
|
+
# @offset[1] += 1 # TODO: is that really correct?
|
129
|
+
# current_block << Line.send(@prev_line_type, line, @offset[0])
|
130
|
+
when :unmod
|
131
|
+
@offset[0] += 1
|
132
|
+
@offset[1] += 1
|
133
|
+
current_block << Line.send(type, line, *@offset)
|
134
|
+
end
|
135
|
+
@prev_line_type = type
|
136
|
+
end
|
137
|
+
|
138
|
+
# TODO Needs a better name...it does process a line (two in fact) but
|
139
|
+
# its primary function is to add a Rem and an Add pair which
|
140
|
+
# potentially have inline changes
|
141
|
+
def process_lines_with_differences(oldline, newline)
|
142
|
+
start, ending = get_change_extent(oldline, newline)
|
143
|
+
|
144
|
+
if start.zero? && ending.zero?
|
145
|
+
process_line(oldline, :rem, false) # -
|
146
|
+
process_line(newline, :add, false) # +
|
147
|
+
else
|
148
|
+
# -
|
149
|
+
line = inline_diff(oldline, start, ending)
|
150
|
+
process_line(line, :rem, true)
|
151
|
+
# +
|
152
|
+
line = inline_diff(newline, start, ending)
|
153
|
+
process_line(line, :add, true)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Inserts string formating characters around the section of a string
|
158
|
+
# that differs internally from another line so that the Line class
|
159
|
+
# can insert the desired formating
|
160
|
+
def inline_diff(line, start, ending)
|
161
|
+
if start != 0 || ending != 0
|
162
|
+
last = ending + line.length
|
163
|
+
str = line[0...start] + '\0' + line[start...last] + '\1' + line[last...line.length]
|
164
|
+
end
|
165
|
+
str || line
|
166
|
+
end
|
167
|
+
|
168
|
+
def add_separator
|
169
|
+
push SepBlock.new
|
170
|
+
current_block << SepLine.new
|
171
|
+
end
|
172
|
+
|
173
|
+
def car(line)
|
174
|
+
line[0,1]
|
175
|
+
end
|
176
|
+
|
177
|
+
def cdr(line)
|
178
|
+
line[1..-1]
|
179
|
+
end
|
180
|
+
|
181
|
+
# Returns the current Block object
|
182
|
+
def current_block
|
183
|
+
@data.last
|
184
|
+
end
|
185
|
+
|
186
|
+
# Adds a Line object onto the current Block object
|
187
|
+
def push(line)
|
188
|
+
@data.push line
|
189
|
+
end
|
190
|
+
|
191
|
+
# Determines the extent of differences between two string. Returns
|
192
|
+
# an array containing the offset at which changes start, and then
|
193
|
+
# negative offset at which the chnages end. If the two strings have
|
194
|
+
# neither a common prefix nor a common suffic, [0, 0] is returned.
|
195
|
+
def get_change_extent(str1, str2)
|
196
|
+
start = 0
|
197
|
+
limit = [str1.size, str2.size].sort.first
|
198
|
+
while start < limit and str1[start, 1] == str2[start, 1]
|
199
|
+
start += 1
|
200
|
+
end
|
201
|
+
ending = -1
|
202
|
+
limit -= start
|
203
|
+
while -ending <= limit and str1[ending, 1] == str2[ending, 1]
|
204
|
+
ending -= 1
|
205
|
+
end
|
206
|
+
|
207
|
+
return [start, ending + 1]
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|