smart_diff 0.0.1 → 0.0.3

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.
@@ -1,21 +1,55 @@
1
1
  require "cgi"
2
2
  require_relative "utils.rb"
3
3
 
4
+
5
+ #
6
+ # A Tag wrapping an element in an HTML file.
7
+ # It contains information about the node wrapped inside
8
+ # it such as the start and end offset. The `tag` attr contains
9
+ # the actual HTML, and includes information used to style the nodes
10
+ # based on whether its an insertion, deletion or modification.
11
+ # Matching modifications are wrapped in anchor tags so they can be
12
+ # linked to their matches.
13
+ #
14
+ #
4
15
  class Tag
5
16
  attr_accessor :tag, :idx, :start
6
17
 
18
+
19
+ #
20
+ # Construct a new tag.
21
+ #
22
+ # @param tag [String] An HTML tag.
23
+ # @param idx [Fixnum] The start of the node's offset for an opening tag,
24
+ # The end offset of the node if it is a closing tag.
25
+ # @param start [Fixnum] Left -1 for an open tag, the node's start offset
26
+ # for a closing tag.
27
+ #
28
+ # @return [Tag] The Tag which was constructed.
7
29
  def initialize(tag, idx, start=-1)
8
30
  @tag = tag
9
31
  @idx = idx
10
32
  @start = start
11
33
  end
12
34
 
35
+
36
+ #
37
+ # String representation of the Tag object.
38
+ #
39
+ # @return [String] Tag as string.
13
40
  def to_s
14
41
  "tag: #{@tag}, idx: #{@idx} start: #{@start}"
15
42
  end
16
43
 
17
44
  end
18
45
 
46
+
47
+ #
48
+ # Given information about two Ruby files, and their
49
+ # semantic diff, creates an HTML file to represent the
50
+ # diff information in an intuitive and appealing visual
51
+ # format.
52
+ #
19
53
  class Htmlize
20
54
  include Utils
21
55
  attr_accessor :uid_count, :uid_hash
@@ -30,6 +64,15 @@ class Htmlize
30
64
  @uid_hash = {}
31
65
  end
32
66
 
67
+
68
+ #
69
+ # Give the node a uid, place it in the uid hash and
70
+ # up the count. If it already has one, fetch it from the hash
71
+ # and return it.
72
+ #
73
+ # @param node [org.jrubyparser.Node] A node in the AST.
74
+ #
75
+ # @return [Fixnum] The uid of the node passed in.
33
76
  def uid(node)
34
77
  if @uid_hash.has_key?(node)
35
78
  return @uid_hash[node]
@@ -39,6 +82,11 @@ class Htmlize
39
82
  @uid_hash[node] = @uid_count.to_s
40
83
  end
41
84
 
85
+
86
+ #
87
+ # Construct the HTML for the top of the file.
88
+ #
89
+ # @return [String] HTML header.
42
90
  def html_header
43
91
  install_path = get_install_path
44
92
 
@@ -61,11 +109,24 @@ class Htmlize
61
109
  <body>\n}
62
110
  end
63
111
 
112
+
113
+ #
114
+ # Create the html for the bottom of the page.
115
+ #
116
+ # @return [String] the html footer.
64
117
  def html_footer
65
118
  out = %Q{</body>\n
66
119
  </html>\n}
67
120
  end
68
121
 
122
+
123
+ #
124
+ # Takes in the text and outputs html.
125
+ #
126
+ # @param text [String] the text from either side of the diff, w/ html tags.
127
+ # @param side [String] left or right
128
+ #
129
+ # @return [String] All the html for one side of the diff.
69
130
  def write_html(text, side)
70
131
  out = ""
71
132
 
@@ -83,6 +144,17 @@ class Htmlize
83
144
  out << '</div>'
84
145
  end
85
146
 
147
+
148
+ #
149
+ # Takes in the information about the diff and writes out a file of HTML.
150
+ #
151
+ # @param changes [Array] An array of Changes, the diff
152
+ # @param file1 [String] path to the first file.
153
+ # @param file2 [String] path to the second file.
154
+ # @param text1 [String] the text from the first file
155
+ # @param text2 [String] the text from the second file.
156
+ #
157
+ # @return [String] The name of the HTML file that was written.
86
158
  def create_html(changes, file1, file2, text1, text2)
87
159
  tags1 = change_tags(changes, 'left')
88
160
  tags2 = change_tags(changes, 'right')
@@ -100,6 +172,15 @@ class Htmlize
100
172
 
101
173
  end
102
174
 
175
+
176
+ #
177
+ # Does HTML escaping on both tags and text, and places the tags around the
178
+ # appropriate text.
179
+ #
180
+ # @param s [String] The text from one of the files.
181
+ # @param tags [String] The tags belonging to one of the files.
182
+ #
183
+ # @return [String] The tagged text.
103
184
  def apply_tags(s, tags)
104
185
  tags = tags.sort_by { |x| [x.idx, -x.start] }
105
186
  curr = 0
@@ -118,6 +199,15 @@ class Htmlize
118
199
  return out
119
200
  end
120
201
 
202
+
203
+ #
204
+ # Works through the Change objects in the diff, creating the
205
+ # appropriate HTML tags for each.
206
+ #
207
+ # @param changes [Array] An array of Change objects.
208
+ # @param side [String] Tells us which side of the page to create tags for.
209
+ #
210
+ # @return [Array] The tags to place around the text.
121
211
  def change_tags(changes, side)
122
212
  tags = []
123
213
  changes.each do |c|
@@ -131,15 +221,19 @@ class Htmlize
131
221
  if inside_anchor?(tags, nd_start, nd_end)
132
222
  if change_class(c) =~ /c/
133
223
  # no op
224
+ # we don't nest anchors inside other anchors
134
225
  else
226
+ # In this case, we have an insertion or deletion
135
227
  tags << Tag.new(span_start(c), nd_start)
136
228
  tags << Tag.new('</span>', nd_end, nd_start)
137
229
  end
138
230
  else
231
+ # Link up the matching nodes with anchor tags
139
232
  tags << Tag.new(link_start(c, side), nd_start)
140
233
  tags << Tag.new('</a>', nd_end, nd_start)
141
234
  end
142
235
  else
236
+ # Wrap a span around the insertion or deletion.
143
237
  tags << Tag.new(span_start(c), nd_start)
144
238
  tags << Tag.new('</span>', nd_end, nd_start)
145
239
  end
@@ -148,6 +242,13 @@ class Htmlize
148
242
  return tags
149
243
  end
150
244
 
245
+
246
+ #
247
+ # Determines whether the change is an insertion, deletion or modification.
248
+ #
249
+ # @param change [Change] The Change object to be checked.
250
+ #
251
+ # @return [String] Either a 'c', 'd', or 'i'
151
252
  def change_class(change)
152
253
  if !change.old_node
153
254
  return 'd'
@@ -158,10 +259,26 @@ class Htmlize
158
259
  end
159
260
  end
160
261
 
262
+
263
+ #
264
+ # Takes a Change and creates a span tag.
265
+ #
266
+ # @param change [Change] A single change from the diff representing either
267
+ # an insertion or deletion.
268
+ #
269
+ # @return [String] A span tag based on the Change passed in.
161
270
  def span_start(change)
162
271
  "<span class=#{qs(change_class(change))}>"
163
272
  end
164
273
 
274
+
275
+ #
276
+ # Create anchor tags for a Change object.
277
+ #
278
+ # @param change [Change] The Change object to be wrapped.
279
+ # @param side [String] Which side of the page, in other words, which file?
280
+ #
281
+ # @return [String] An anchor tag.
165
282
  def link_start(change, side)
166
283
  cls = change_class(change)
167
284
 
@@ -174,6 +291,13 @@ class Htmlize
174
291
  "<a id=#{qs(uid(me))} tid=#{qs(uid(other))} class=#{qs(cls)}>"
175
292
  end
176
293
 
294
+
295
+ #
296
+ # Takes a string and returns it in quotes.
297
+ #
298
+ # @param s [String] A string to be quoted.
299
+ #
300
+ # @return [String] The quoted string.
177
301
  def qs(s)
178
302
  "'#{s}'"
179
303
  end
@@ -3,20 +3,60 @@ require "pathname"
3
3
  module Utils
4
4
  module_function
5
5
 
6
+
7
+ #
8
+ # Where in the file-system is smart_diff installed?
9
+ #
10
+ # @return [Pathname] the path to the installation of smart_diff
6
11
  def get_install_path
7
12
  utils_path = Pathname.new(__FILE__).dirname
8
13
  # There should be a more elegant way to do this
9
14
  install_path = utils_path.parent.parent
10
15
  end
11
16
 
17
+
18
+ #
19
+ # Get a node's start position,
20
+ #
21
+ # @param node [org.jrubyparser.Node] a node in an abstract syntax tree
22
+ #
23
+ # @return [Fixnum] Number representing the node's start offset.
12
24
  def node_start(node)
13
25
  node.position.start_offset
14
26
  end
15
27
 
28
+
29
+ #
30
+ # Get a node's end position.
31
+ #
32
+ # @param node [org.jrubyparser.Node] a node in an abstract syntax tree
33
+ #
34
+ # @return [Fixnum] Number representing the node's end offset.
16
35
  def node_end(node)
17
36
  node.position.end_offset
18
37
  end
19
38
 
39
+
40
+ #
41
+ # Determines if the node beginning with nd_start and ending with nd_end
42
+ # falls inside an anchor. In other words, is the current node inside
43
+ # another node _which also changed_ and has a match in the other file. (If it
44
+ # is an insertion or deletion it gets wrapped in a span so we aren't
45
+ # concerned with those.)
46
+ #
47
+ # In a pair of tags, the opening tag only has some of the position
48
+ # information, so they are marked with a meaningless -1. But the closing tags
49
+ # have the information needed- start and end offsets.
50
+ #
51
+ # Subnodes were added to the array after the outer nodes, so we check them
52
+ # here, once, against each tag, and decide how to process them as we
53
+ # generate the HTML.
54
+ #
55
+ # @param tags [Array] A list of all tags so far recorded.
56
+ # @param nd_start [Fixnum] Number representing the node's starting position.
57
+ # @param nd_end [Fixnum] Number representing the node's ending position.
58
+ #
59
+ # @return [TrueClass, FalseClass] Is it inside an anchor?
20
60
  def inside_anchor?(tags, nd_start, nd_end)
21
61
  tags.each do |t|
22
62
  if nd_end < t.idx && nd_start > t.start && t.start != -1
data/lib/smart_diff.rb CHANGED
@@ -22,6 +22,13 @@ class SmartDiff
22
22
  @node_diff = diff()
23
23
  end
24
24
 
25
+
26
+ #
27
+ # Read the source code into a string.
28
+ #
29
+ # @param file_name [String] the path to a file containing ruby source
30
+ #
31
+ # @return [String] The code read in from the file path passed in.
25
32
  def read(file_name)
26
33
  path = Pathname.new(file_name).expand_path
27
34
  if path.exist?
@@ -31,10 +38,24 @@ class SmartDiff
31
38
  end
32
39
  end
33
40
 
41
+
42
+ #
43
+ # Parse the source into an abstract syntax tree.
44
+ # @param code_to_parse [String] Ruby source code.
45
+ # @param file_name [String] The path to the file containing code_to_parse
46
+ #
47
+ # @return [org.jrubyparser.Node] A Node object representing the code as an
48
+ # AST.
34
49
  def parse(code_to_parse, file_name)
35
50
  JRubyParser.parse(code_to_parse, { :filename => file_name })
36
51
  end
37
52
 
53
+
54
+ #
55
+ # Create a diff of the two AST objects.
56
+ #
57
+ # @return [java.util.ArrayList] An ArrayList containing
58
+ # org.jrubyparser.DeepDiff objects.
38
59
  def diff()
39
60
  @code_one = read(@file_one)
40
61
  @code_two = read(@file_two)
@@ -26,7 +26,7 @@ describe "Htmlize" do
26
26
 
27
27
  it "should return the entry for a previously set hash" do
28
28
  html = Htmlize.new
29
- html.uid_hash = { banana: "cow" }
29
+ html.uid_hash = { :banana => "cow" }
30
30
  html.uid(:banana).should =~ /cow/
31
31
  end
32
32
 
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_diff
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.0.1
4
+ version: 0.0.3
6
5
  platform: ruby
7
6
  authors:
8
7
  - Eric West
@@ -18,13 +17,11 @@ dependencies:
18
17
  - - ~>
19
18
  - !ruby/object:Gem::Version
20
19
  version: 0.5.1
21
- none: false
22
20
  requirement: !ruby/object:Gem::Requirement
23
21
  requirements:
24
22
  - - ~>
25
23
  - !ruby/object:Gem::Version
26
24
  version: 0.5.1
27
- none: false
28
25
  prerelease: false
29
26
  type: :runtime
30
27
  - !ruby/object:Gem::Dependency
@@ -34,13 +31,11 @@ dependencies:
34
31
  - - '>='
35
32
  - !ruby/object:Gem::Version
36
33
  version: '0'
37
- none: false
38
34
  requirement: !ruby/object:Gem::Requirement
39
35
  requirements:
40
36
  - - '>='
41
37
  - !ruby/object:Gem::Version
42
38
  version: '0'
43
- none: false
44
39
  prerelease: false
45
40
  type: :development
46
41
  - !ruby/object:Gem::Dependency
@@ -50,13 +45,11 @@ dependencies:
50
45
  - - '>='
51
46
  - !ruby/object:Gem::Version
52
47
  version: '0'
53
- none: false
54
48
  requirement: !ruby/object:Gem::Requirement
55
49
  requirements:
56
50
  - - '>='
57
51
  - !ruby/object:Gem::Version
58
52
  version: '0'
59
- none: false
60
53
  prerelease: false
61
54
  type: :development
62
55
  description: Create Semantic Diffs of Ruby source code based on the AST.
@@ -86,6 +79,7 @@ files:
86
79
  - doc/top-level-namespace.html
87
80
  - example/bar.rb
88
81
  - example/foo.rb
82
+ - example/foo.rb-bar.rb.html
89
83
  - lib/smart_diff.rb
90
84
  - lib/smart_diff/htmlize.rb
91
85
  - lib/smart_diff/utils.rb
@@ -96,11 +90,12 @@ files:
96
90
  - web/nav.js
97
91
  - bin/smart_diff
98
92
  - README.md
99
- - foo.rb-bar.rb.html
93
+ - Example.html
100
94
  - Rakefile
101
95
  homepage: http://github.com/edubkendo/smart_diff.git
102
96
  licenses:
103
97
  - GPL
98
+ metadata: {}
104
99
  post_install_message:
105
100
  rdoc_options: []
106
101
  require_paths:
@@ -109,22 +104,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
109
104
  requirements:
110
105
  - - '>='
111
106
  - !ruby/object:Gem::Version
112
- segments:
113
- - 0
114
- hash: 2
115
107
  version: '0'
116
- none: false
117
108
  required_rubygems_version: !ruby/object:Gem::Requirement
118
109
  requirements:
119
110
  - - '>='
120
111
  - !ruby/object:Gem::Version
121
112
  version: '0'
122
- none: false
123
113
  requirements: []
124
114
  rubyforge_project:
125
- rubygems_version: 1.8.24
115
+ rubygems_version: 2.1.3
126
116
  signing_key:
127
- specification_version: 3
117
+ specification_version: 4
128
118
  summary: Ruby clone of psydiff
129
119
  test_files:
130
120
  - spec/smart_diff/htmlize_spec.rb