diff-display 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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