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.
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ diff-display.gemspec
3
+ pkg/*
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2008-01-28
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -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.
@@ -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
@@ -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
@@ -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
File without changes
@@ -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,15 @@
1
+ module Diff
2
+ module Display
3
+ class Unified
4
+ def initialize(udiff)
5
+ @data = Diff::Display::Unified::Generator.run(udiff)
6
+ end
7
+ attr_reader :data
8
+
9
+ def render(renderer, out="")
10
+ out << renderer.render(data)
11
+ out
12
+ end
13
+ end
14
+ end
15
+ 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