Class: Htmlize

Inherits:
Object
  • Object
show all
Includes:
Utils
Defined in:
lib/smart_diff/htmlize.rb

Overview

Given information about two Ruby files, and their semantic diff, creates an HTML file to represent the diff information in an intuitive and appealing visual format.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils

get_install_path, inside_anchor?, node_end, node_start

Constructor Details

#initializeHtmlize

Returns a new instance of Htmlize.



55
56
57
58
# File 'lib/smart_diff/htmlize.rb', line 55

def initialize
  @uid_count = -1
  @uid_hash = {}
end

Instance Attribute Details

#uid_countObject

Returns the value of attribute uid_count.



53
54
55
# File 'lib/smart_diff/htmlize.rb', line 53

def uid_count
  @uid_count
end

#uid_hashObject

Returns the value of attribute uid_hash.



53
54
55
# File 'lib/smart_diff/htmlize.rb', line 53

def uid_hash
  @uid_hash
end

Instance Method Details

#apply_tags(s, tags) ⇒ String

Does HTML escaping on both tags and text, and places the tags around the appropriate text.

Parameters:

  • s (String)

    The text from one of the files.

  • tags (String)

    The tags belonging to one of the files.

Returns:

  • (String)

    The tagged text.



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/smart_diff/htmlize.rb', line 182

def apply_tags(s, tags)
  tags = tags.sort_by { |x| [x.idx, -x.start] }
  curr = 0
  out = ""
  tags.each do |t|
    while curr < t.idx && curr < s.length
      out << CGI::escapeHTML(s[curr])
      curr += 1
    end
    out << t.tag
  end
  while curr < s.length
    out << CGI::escapeHTML(s[curr])
    curr += 1
  end
  return out
end

#change_class(change) ⇒ String

Determines whether the change is an insertion, deletion or modification.

Parameters:

  • change (Change)

    The Change object to be checked.

Returns:

  • (String)

    Either a ‘c’, ‘d’, or ‘i’



250
251
252
253
254
255
256
257
258
# File 'lib/smart_diff/htmlize.rb', line 250

def change_class(change)
  if !change.old_node
    return 'd'
  elsif !change.new_node
    return 'i'
  else
    return 'c'
  end
end

#change_tags(changes, side) ⇒ Array

Works through the Change objects in the diff, creating the appropriate HTML tags for each.

Parameters:

  • changes (Array)

    An array of Change objects.

  • side (String)

    Tells us which side of the page to create tags for.

Returns:

  • (Array)

    The tags to place around the text.



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/smart_diff/htmlize.rb', line 209

def change_tags(changes, side)
  tags = []
  changes.each do |c|

    key = side =~ /left/ ? c.old_node : c.new_node
    if key
      nd_start = node_start(key)
      nd_end = node_end(key)

      if c.old_node && c.new_node
        if inside_anchor?(tags, nd_start, nd_end)
          if change_class(c) =~ /c/
            # no op
            # we don't nest anchors inside other anchors
          else
            # In this case, we have an insertion or deletion
            tags << Tag.new(span_start(c), nd_start)
            tags << Tag.new('</span>', nd_end, nd_start)
          end
        else
          # Link up the matching nodes with anchor tags
          tags << Tag.new(link_start(c, side), nd_start)
          tags << Tag.new('</a>', nd_end, nd_start)
        end
      else
        # Wrap a span around the insertion or deletion.
        tags << Tag.new(span_start(c), nd_start)
        tags << Tag.new('</span>', nd_end, nd_start)
      end
    end
  end
  return tags
end

#clear_uidObject



60
61
62
63
# File 'lib/smart_diff/htmlize.rb', line 60

def clear_uid()
  @uid_count = -1
  @uid_hash = {}
end

#create_html(changes, file1, file2, text1, text2) ⇒ String

Takes in the information about the diff and writes out a file of HTML.

Parameters:

  • changes (Array)

    An array of Changes, the diff

  • file1 (String)

    path to the first file.

  • file2 (String)

    path to the second file.

  • text1 (String)

    the text from the first file

  • text2 (String)

    the text from the second file.

Returns:

  • (String)

    The name of the HTML file that was written.



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/smart_diff/htmlize.rb', line 156

def create_html(changes, file1, file2, text1, text2)
  tags1 = change_tags(changes, 'left')
  tags2 = change_tags(changes, 'right')
  tagged_text1 = apply_tags(text1, tags1)
  tagged_text2 = apply_tags(text2, tags2)

  output_filename = "#{Pathname.new(file1).basename}-#{Pathname.new(file2).basename}.html"
  File.open(output_filename, "w") do |f|
    f.write html_header
    f.write write_html(tagged_text1, 'left')
    f.write write_html(tagged_text2, 'right')
    f.write html_footer
  end
  output_filename

end

Create the html for the bottom of the page.

Returns:

  • (String)

    the html footer.



115
116
117
118
# File 'lib/smart_diff/htmlize.rb', line 115

def html_footer
  out = %Q{</body>\n
           </html>\n}
end

#html_headerString

Construct the HTML for the top of the file.

Returns:

  • (String)

    HTML header.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/smart_diff/htmlize.rb', line 88

def html_header
  install_path = get_install_path

  js_filename = Pathname.new(install_path).join('web/nav.js')
  js_text = js_filename.read

  css_filename = Pathname.new(install_path).join('web/diff.css')
  css_text = css_filename.read

  out = %Q{<html>\n
  <head>\n
  <META http-equiv="Content-Type" content="text/html; charset=utf-8">\n
  <style>\n
  #{css_text}
  \n</style>\n
  <script type="text/javascript">\n
  #{js_text}
  \n</script>\n
  </head>\n
  <body>\n}
end

Create anchor tags for a Change object.

Parameters:

  • change (Change)

    The Change object to be wrapped.

  • side (String)

    Which side of the page, in other words, which file?

Returns:

  • (String)

    An anchor tag.



279
280
281
282
283
284
285
286
287
288
289
# File 'lib/smart_diff/htmlize.rb', line 279

def link_start(change, side)
  cls = change_class(change)

  if side == 'left'
    me, other = change.old_node, change.new_node
  else
    me, other = change.new_node, change.old_node
  end

  "<a id=#{qs(uid(me))} tid=#{qs(uid(other))} class=#{qs(cls)}>"
end

#qs(s) ⇒ String

Takes a string and returns it in quotes.

Parameters:

  • s (String)

    A string to be quoted.

Returns:

  • (String)

    The quoted string.



298
299
300
# File 'lib/smart_diff/htmlize.rb', line 298

def qs(s)
  "'#{s}'"
end

#span_start(change) ⇒ String

Takes a Change and creates a span tag.

Parameters:

  • change (Change)

    A single change, either an insertion or deletion.

Returns:

  • (String)

    A span tag based on the Change passed in.



267
268
269
# File 'lib/smart_diff/htmlize.rb', line 267

def span_start(change)
  "<span class=#{qs(change_class(change))}>"
end

#uid(node) ⇒ Fixnum

Give the node a uid, place it in the uid hash and up the count. If it already has one, fetch it from the hash and return it.

Parameters:

  • node (org.jrubyparser.Node)

    A node in the AST.

Returns:

  • (Fixnum)

    The uid of the node passed in.



74
75
76
77
78
79
80
81
# File 'lib/smart_diff/htmlize.rb', line 74

def uid(node)
  if @uid_hash.has_key?(node)
    return @uid_hash[node]
  end

  @uid_count = @uid_count + 1
  @uid_hash[node] = @uid_count.to_s
end

#write_html(text, side) ⇒ String

Takes in the text and outputs html.

Parameters:

  • text (String)

    the text from either side of the diff, w/ html tags.

  • side (String)

    left or right

Returns:

  • (String)

    All the html for one side of the diff.



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/smart_diff/htmlize.rb', line 128

def write_html(text, side)
  out = ""

  out << '<div id="' + side + '" class="src">'
  out << '<pre>'

  if side == 'left'
    out << '<a id="leftstart" tid="rightstart"></a>'
  else
    out << '<a id="rightstart" tid="leftstart"></a>'
  end

  out << text
  out << '</pre>'
  out << '</div>'
end