rfs 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/MIT-LICENSE +7 -0
  2. data/Rakefile.rb +193 -0
  3. data/bin/RenameFileSet.rbw +14 -0
  4. data/bin/rfs.rb +214 -0
  5. data/bin/rfsd.rb +8 -0
  6. data/description.txt +24 -0
  7. data/lib/RenameFileSet.bak.rb +372 -0
  8. data/lib/errors.rb +15 -0
  9. data/lib/filters.rb +68 -0
  10. data/lib/gui.rb +216 -0
  11. data/lib/innate/array.rb +104 -0
  12. data/lib/innate/coverage.rb +315 -0
  13. data/lib/innate/debug.rb +12 -0
  14. data/lib/innate/debugger.rb +944 -0
  15. data/lib/innate/file.rb +5 -0
  16. data/lib/innate/filelines.rb +148 -0
  17. data/lib/innate/kernel.rb +41 -0
  18. data/lib/innate/metaid.rb +30 -0
  19. data/lib/innate/mkdirs.rb +12 -0
  20. data/lib/innate/regexp.rb +80 -0
  21. data/lib/innate/reload.rb +11 -0
  22. data/lib/innate/roman.rb +72 -0
  23. data/lib/innate/scriptlines.rb +60 -0
  24. data/lib/innate/string.rb +56 -0
  25. data/lib/innate/test/all_tests.rb +11 -0
  26. data/lib/innate/test/files/mkdirs_dummy.file +1 -0
  27. data/lib/innate/test/files/reloadtarget.rb +5 -0
  28. data/lib/innate/test/files/reloadtarget1.rb +5 -0
  29. data/lib/innate/test/files/reloadtarget2.rb +5 -0
  30. data/lib/innate/test/test_coverage.rb +13 -0
  31. data/lib/innate/test/testarray.rb +98 -0
  32. data/lib/innate/test/testfile.rb +15 -0
  33. data/lib/innate/test/testfilelines.rb +106 -0
  34. data/lib/innate/test/testkernel.rb +30 -0
  35. data/lib/innate/test/testmkdirs.rb +19 -0
  36. data/lib/innate/test/testregexp.rb +86 -0
  37. data/lib/innate/test/testreload.rb +61 -0
  38. data/lib/innate/test/testroman.rb +54 -0
  39. data/lib/innate/test/testscriptlines.rb +39 -0
  40. data/lib/innate/test/teststring.rb +52 -0
  41. data/lib/innate/test/testtitlecase.rb +20 -0
  42. data/lib/innate/titlecase.rb +64 -0
  43. data/lib/innate/tracerequire.rb +22 -0
  44. data/lib/namesource.rb +53 -0
  45. data/lib/options.rb +64 -0
  46. data/lib/providers.rb +93 -0
  47. data/lib/regexp.rb +56 -0
  48. data/lib/rename_functions.rb +142 -0
  49. data/lib/renamer.rb +210 -0
  50. data/lib/results.rb +83 -0
  51. data/lib/test/test_helper.rb +147 -0
  52. data/tests/dir/file1 +0 -0
  53. data/tests/dir/file2 +0 -0
  54. data/tests/dir/file3 +0 -0
  55. data/tests/test_helper.rb +147 -0
  56. data/tests/test_rename_functions.rb +605 -0
  57. metadata +105 -0
data/lib/gui.rb ADDED
@@ -0,0 +1,216 @@
1
+ require 'rubygems'
2
+ require_gem 'fxruby'
3
+ require 'stringio'
4
+ require 'results'
5
+
6
+ include Fox
7
+
8
+ #TO DO:
9
+ # change pattern to combobox with some useful patterns
10
+ # \d.+(\d+).+
11
+ # (\d+[AB])
12
+
13
+ #user interface layout
14
+ class RenameWindow < FXMainWindow
15
+ attr_accessor :renamer
16
+
17
+ def initialize(app)
18
+ super(app, 'Rename File Set', nil, nil, DECOR_ALL, 20, 100, 1000, 600)
19
+
20
+ FXHorizontalFrame.new(self, LAYOUT_FILL | PACK_UNIFORM_WIDTH) { |mainFrame|
21
+ # navigation on left
22
+ @dirList = FXDirList.new mainFrame, nil, 0, LAYOUT_FILL | FRAME_NORMAL
23
+ # controls in centre
24
+ FXVerticalFrame.new(mainFrame, LAYOUT_FILL | PACK_UNIFORM_WIDTH) { |controls|
25
+ FXLabel.new controls, 'Search (replace first capture):', nil, LAYOUT_FILL_X | JUSTIFY_LEFT
26
+ @search = FXTextField.new controls, 0, nil, LAYOUT_FILL
27
+
28
+ FXGroupBox.new(controls, 'Replacement Method', LAYOUT_FILL | FRAME_GROOVE | JUSTIFY_LEFT ) { |group|
29
+ @selReplace = FXRadioButton.new(group, 'Replace with:') { |r|
30
+ connectRadio r
31
+ }
32
+ @replace = FXTextField.new group, 30, nil, PACK_UNIFORM_WIDTH
33
+ @selAdd = FXRadioButton.new(group, 'Add to captured digits: ', nil, LAYOUT_FILL_Y) { |r|
34
+ connectRadio r
35
+ }
36
+ @add = FXSpinner.new(group, 10, nil, FRAME_NORMAL) { |s| s.range = (-500..500)}
37
+ @selRoman = FXRadioButton.new(group, 'Convert to Roman Numerals', nil, LAYOUT_FILL_X ) { |r|
38
+ connectRadio r
39
+ }
40
+ @selTapeNumbers = FXRadioButton.new(group, 'Convert Tape Numbers', nil, LAYOUT_FILL_X) { |r|
41
+ connectRadio r
42
+ }
43
+ @selFill = FXRadioButton.new(group, 'Replace capture with last match capture:', nil, LAYOUT_FILL_X) { |r|
44
+ connectRadio r
45
+ }
46
+ @fill = FXTextField.new group, 30, nil, PACK_UNIFORM_WIDTH
47
+ @selFile = FXRadioButton.new(group, 'Replace from files.txt', nil, LAYOUT_FILL_X) { |r|
48
+ connectRadio r
49
+ }
50
+ @selCount = FXRadioButton.new(group, 'Count', nil, LAYOUT_FILL_X) { |r|
51
+ connectRadio r
52
+ }
53
+ @selMarkChange = FXRadioButton.new(group, 'Mark Changes', nil, LAYOUT_FILL_X) { |r|
54
+ connectRadio r
55
+ }
56
+ @selFromFullName = FXRadioButton.new(group, 'Replace with capture in full name:', nil, LAYOUT_FILL_X) { |r|
57
+ connectRadio r
58
+ }
59
+ @fullNamePattern = FXTextField.new group, 30, nil, PACK_UNIFORM_WIDTH
60
+ @selMoveUp = FXRadioButton.new(group, 'Move to Parent Directory', nil, LAYOUT_FILL_X) { |r|
61
+ connectRadio r
62
+ }
63
+ @selCapitalize = FXRadioButton.new(group, 'Capitalize', nil, LAYOUT_FILL_X) { |r|
64
+ connectRadio r
65
+ }
66
+ }
67
+
68
+ FXButton.new(controls, '&List Files', nil, nil, 0, LAYOUT_FILL_X | BUTTON_NORMAL) { |b|
69
+ b.connect SEL_COMMAND, method(:onListClick)
70
+ }
71
+ FXButton.new(controls, '&Preview', nil, nil, 0, LAYOUT_FILL_X | FRAME_RAISED | FRAME_THICK) { |b|
72
+ b.connect SEL_COMMAND, method(:onPreviewClick)
73
+ }
74
+ FXButton.new(controls, '&Apply', nil, nil, 0, LAYOUT_FILL_X | FRAME_RAISED | FRAME_THICK) { |b|
75
+ b.connect SEL_COMMAND, method(:onReplaceClick)
76
+ }
77
+ }
78
+ # results on right
79
+ @results = FXText.new mainFrame, nil, 0, LAYOUT_FILL | FRAME_NORMAL | TEXT_READONLY
80
+ }
81
+
82
+ @selReplace.checkState = TRUE
83
+ onRadioUpdate @selReplace, nil, nil
84
+ end
85
+
86
+ def connectRadio(r)
87
+ r.connect SEL_COMMAND, method(:onRadioUpdate)
88
+ end
89
+
90
+ def onRadioUpdate(sender, sel, ptr)
91
+ sender.parent.each_child do |c|
92
+ if FXRadioButton === c and c != sender
93
+ c.checkState = FALSE
94
+ end
95
+ end
96
+ @selReplace.checked? ? @replace.enable : @replace.disable
97
+ @selAdd.checked? ? @add.enable : @add.disable
98
+ @selFill.checked? ? @fill.enable : @fill.disable
99
+ @selFromFullName.checked? ? @fullNamePattern.enable : @fullNamePattern.disable
100
+ end
101
+ end
102
+
103
+ #connections to main renaming worker
104
+ class RenameWindow
105
+ attr_accessor :renamer
106
+
107
+ def onPreviewClick(sender, sel, ptr)
108
+ start :preview
109
+ end
110
+
111
+ def onReplaceClick(sender, sel, ptr)
112
+ start :commit
113
+ end
114
+
115
+ def onListClick(sender, sel, ptr)
116
+ start :list
117
+ @dirList.setFocus
118
+ end
119
+
120
+ def selected_function
121
+ if @selReplace.checked?
122
+ :rename_replace
123
+ elsif @selFile.checked?
124
+ :rename_files_txt
125
+ elsif @selAdd.checked?
126
+ :rename_add
127
+ elsif @selCount.checked?
128
+ :rename_count
129
+ elsif @selFromFullName.checked?
130
+ :rename_from_full_name
131
+ elsif @selMoveUp.checked?
132
+ :rename_move_up
133
+ elsif @selRoman.checked?
134
+ :rename_roman
135
+ elsif @selMarkChange.checked?
136
+ :rename_mark_change
137
+ elsif @selTapeNumbers.checked?
138
+ :rename_tape_numbers
139
+ elsif @selCapitalize.checked?
140
+ :rename_capitalize
141
+ elsif @selFill.checked?
142
+ :rename_fill
143
+ else
144
+ throw "No operation selected, or selection unknown."
145
+ end
146
+ end
147
+
148
+ def start(action)
149
+ s = StringIO.new
150
+ @r = CollectResults.new s
151
+ @r.lineways = true
152
+ @r.list = action == :list
153
+
154
+ begin
155
+ fn = selected_function
156
+ if methods.include? fn.to_s
157
+ method(fn).call action
158
+ else
159
+ rename fn, :action => action
160
+ end
161
+ rescue => e
162
+ @r.output(:error, e)
163
+ end
164
+ @r.display
165
+ s.rewind
166
+ @results.text = s.read.lstrip
167
+ end
168
+
169
+ def rename(name, args = {})
170
+ args[:search] ||= PrepareRegexp.new @search.text
171
+ args[:filter] = Filter.add(args[:filter], Filter.new(/^\.+$/))
172
+ args[:file_provider] = Provider::File::NonRecursive.new(Provider::Folder::Tree.new(@dirList))
173
+
174
+ PrepareRegexp.each(args) do |k, v|
175
+ args[k] = t = v.create(false, false, true) do |mode, output|
176
+ @r.output(mode, output)
177
+ end
178
+ end
179
+
180
+ @renamer.run name, args do |*a|
181
+ @r.output(*a)
182
+ end
183
+ end
184
+
185
+ #special functions
186
+
187
+ def rename_replace action
188
+ rename :rename_replace, :replace => @replace.text, :action => action
189
+ end
190
+
191
+ def rename_add action
192
+ rename :rename_add, :add => @add.value, :action => action
193
+ end
194
+
195
+ def rename_fill action
196
+ rename :rename_fill, :fill => PrepareRegexp.new(@fill.text), :action => action
197
+ end
198
+
199
+ def rename_from_full_name action
200
+ rename :rename_from_full_name, :pattern => PrepareRegexp.new(@fullNamePattern.text), :action => action
201
+ end
202
+
203
+ def rename_mark_change action
204
+ rename(:rename_mark_change,
205
+ :replace_pattern => PrepareRegexp.new('()$/'), # I could add boxes for these options
206
+ :replace_text => '--changed--',
207
+ :action => action)
208
+ end
209
+
210
+ def rename_files_txt action
211
+ rename(:rename_name_source,
212
+ :source => NameFileSource.new('files.txt'),
213
+ :filter => Filter.new(/^files.txt$/),
214
+ :action => action)
215
+ end
216
+ end
@@ -0,0 +1,104 @@
1
+ class Array
2
+ def match? string
3
+ find do |i|
4
+ if i.is_a? String
5
+ i == string
6
+ elsif i.is_a? Regexp
7
+ i =~ string
8
+ end
9
+ end
10
+ end
11
+
12
+ def rows(width = 80, spaces = 2, spacer = ' ')
13
+ return '' if length == 0
14
+ best = nil
15
+ bestcw = nil
16
+ sorted = self # changed my mind: don't sort
17
+
18
+ 1.upto(width) do |columns|
19
+ space = spacer.length * spaces * (columns - 1)
20
+ break if columns + space > width
21
+ t = []
22
+ rows = []
23
+ cw = [0] * columns
24
+ result = sorted.each do |item|
25
+ if t.length == columns
26
+ break :fail unless check_cols cw, rows, t, width, space, columns
27
+ t = []
28
+ end
29
+ t << item.to_s
30
+ end
31
+ t += [''] * (columns - t.length)
32
+ unless result == :fail
33
+ result = :fail unless check_cols cw, rows, t, width, space, columns
34
+ end
35
+ if result != :fail
36
+ best = rows
37
+ bestcw = cw
38
+ end
39
+ end
40
+ fs = bestcw.collect {|w| "%#{w}-s" }.join(spacer * spaces)
41
+ best.collect {|row| format(fs, *row) }.join "\n"
42
+ end
43
+
44
+ def columns(width = 80, spaces = 2, spacer = ' ')
45
+ return '' if length == 0
46
+ best = nil
47
+ bestcw = nil
48
+ sorted = self # changed my mind: don't sort
49
+
50
+ 1.upto(width) do |columns|
51
+ space = spacer.length * spaces * (columns - 1)
52
+ rows = []
53
+ cols = []
54
+ cw = [0] * columns
55
+ col_len = length / columns
56
+ col_len += 1 if length > (columns * col_len)
57
+
58
+ temp = nil
59
+ if length % col_len > 0
60
+ temp = sorted + ([''] * (col_len - length % col_len))
61
+ else
62
+ temp = sorted
63
+ end
64
+
65
+ columns.times do |c|
66
+ t = temp[(col_len * c)...(col_len * (c + 1))]
67
+ cols << t
68
+ end
69
+ next if cols.last == [] or cols.last == nil
70
+
71
+ cols = cols.transpose
72
+ result = cols.each do |row|
73
+ break :fail unless check_cols cw, rows, row, width, space, columns
74
+ end
75
+ if result != :fail
76
+ best = rows
77
+ bestcw = cw
78
+ end
79
+ break if col_len == 1
80
+ end
81
+ fs = bestcw.collect {|w| "%#{w}-s" }.join(spacer * spaces)
82
+ best.collect {|row| format(fs, *row) }.join "\n"
83
+ end
84
+
85
+ def pc(w = 130, s = 2, sr = ' ')
86
+ puts sort.columns(w, s, sr)
87
+ end
88
+
89
+ def pr(w = 130, s = 2, sr = ' ')
90
+ puts sort.rows(w, s, sr)
91
+ end
92
+ end
93
+
94
+ #refactorings
95
+ class Array
96
+ private
97
+ def check_cols(cw, rows, t, width, space, columns)
98
+ idx = 0
99
+ cw.collect! {|w| r = (t[idx].length > w ? t[idx].length : w); idx += 1; r }
100
+ row_width = cw.inject(0){|tot, n| tot + n} + space
101
+ rows << t
102
+ (row_width < width) || columns == 1
103
+ end
104
+ end
@@ -0,0 +1,315 @@
1
+ # module COVERAGE__ originally (c) NAKAMURA Hiroshi, under Ruby's license
2
+ # module PrettyCoverage originally (c) Simon Strandgaard, under Ruby's license
3
+ # minor modifications by Mauricio Julio Fern�ndez Pradier
4
+
5
+ require 'fileutils'
6
+ require 'rbconfig'
7
+
8
+ include Config
9
+
10
+ module PrettyCoverage
11
+
12
+ class HTML
13
+ def write_page(body, title, css, filename)
14
+ html = <<EOHTML
15
+ <?xml version="1.0" encoding="ISO-8859-1"?>
16
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
17
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
18
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
19
+ <head><title>#{title}</title>
20
+ <style type="text/css">#{css}</style></head>
21
+ <body>#{body}</body></html>
22
+ EOHTML
23
+ File.open(filename, "w+") {|f| f.write(html) }
24
+ end
25
+
26
+ def escape(text)
27
+ text.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;")
28
+ end
29
+
30
+ def initialize
31
+ @files = {}
32
+ @files_codeonly = {}
33
+ @filenames = {}
34
+ @sidebar = ""
35
+ end
36
+
37
+ def output_dir(&block)
38
+ dir = "coverage"
39
+ if FileTest.directory?(dir)
40
+ FileUtils.rm_rf(dir)
41
+ end
42
+ FileUtils.mkdir(dir)
43
+ FileUtils.cd(dir) { block.call(dir) }
44
+ end
45
+
46
+ def load_sidebar
47
+ return unless FileTest.file?("coverage.sidebar")
48
+ data = nil
49
+ File.open("coverage.sidebar", "r") {|f| data = f.read}
50
+ @sidebar = "<div class=\"sidebar\">#{data}</div>"
51
+ end
52
+
53
+ def build_filenames
54
+ duplicates = Hash.new(0)
55
+ @files.keys.each do |filename, marked|
56
+ base = File.basename(filename)
57
+ absolute = File.expand_path(filename)
58
+ n = duplicates[base]
59
+ duplicates[base] += 1
60
+ if n > 0
61
+ base += n.to_s
62
+ end
63
+ @filenames[filename] = [base, absolute]
64
+ end
65
+ #p @filenames
66
+ end
67
+
68
+ def execute
69
+ puts "execute"
70
+ build_filenames
71
+ load_sidebar
72
+ output_dir do
73
+ create_file_index
74
+ @files.each do |file, line_marked|
75
+ create_file(file, line_marked, @files_codeonly[file])
76
+ end
77
+ end
78
+ end
79
+
80
+
81
+ def mk_filename(name)
82
+ base_absolute = @filenames[name]
83
+ raise "should not happen" unless base_absolute
84
+ base, absolute = base_absolute
85
+ return nil if absolute =~ /\A#{Regexp.escape(CONFIG["libdir"])}/
86
+ return nil if base =~ /test_/
87
+ [base + ".html", base, absolute]
88
+ end
89
+
90
+ def create_file_index
91
+ output_filename = "index.html"
92
+ rows = []
93
+ filestats = {}
94
+ filestats_code = {}
95
+ @files.sort_by{|k,v| k}.each do|file, line_marked|
96
+ url_filename = mk_filename(file)
97
+ next unless url_filename
98
+ percent = "%02.1f" % calc_coverage(line_marked)
99
+ percent2 = "%02.1f" % calc_coverage(@files_codeonly[file])
100
+ numlines = line_marked.transpose[1].size
101
+ if numlines
102
+ filestats[file] = [calc_coverage(line_marked), numlines]
103
+ filestats_code[file] = [calc_coverage(@files_codeonly[file]),
104
+ numlines]
105
+ end
106
+ url, filename, abspath = url_filename
107
+ cells = [
108
+ "<a href=\"#{url}\">#{filename}</a>",
109
+ "<tt>#{numlines}</tt>",
110
+ "&nbsp;", "&nbsp;", "&nbsp;",
111
+ "<tt>#{percent}%</tt>",
112
+ "&nbsp;", "&nbsp;", "&nbsp;",
113
+ "<tt>#{percent2}%</tt>"
114
+ ]
115
+ rows << cells.map{|cell| "<td>#{cell}</td>"}
116
+ end
117
+ rows.map!{|row| "<tr>#{row}</tr>"}
118
+ table = "<table>#{rows.join}</table>"
119
+ total_cov = 1.0 *
120
+ filestats.inject(0){|a,(k,v)| a + v[0] * v[1]} /
121
+ filestats.inject(0){|a,(k,v)| a + v[1]}
122
+ total_code_cov = 1.0 *
123
+ filestats_code.inject(0) {|a,(k,v)| a + v[0] * v[1]} /
124
+ filestats_code.inject(0){|a,(k,v)| a + v[1]}
125
+ body = "<h1>Average (with comments): %02.1f%%</h1>" % total_cov
126
+ body << "<h1>Average (code only): %02.1f%%</h1>" % total_code_cov
127
+ body << @sidebar
128
+ body << table
129
+ title = "coverage"
130
+ css = <<-EOCSS.gsub(/^\s*/, "")
131
+ body {
132
+ background-color: rgb(180, 180, 180);
133
+ }
134
+ span.marked {
135
+ background-color: rgb(185, 200, 200);
136
+ display: block;
137
+ }
138
+ div.overview {
139
+ border-bottom: 8px solid black;
140
+ }
141
+ div.sidebar {
142
+ float: right;
143
+ width: 300px;
144
+ border: 2px solid black;
145
+ margin-left: 10px;
146
+ padding-left: 10px;
147
+ padding-right: 10px;
148
+ margin-right: -10px;
149
+ background-color: rgb(185, 200, 200);
150
+ }
151
+ EOCSS
152
+ write_page(body, title, css, output_filename)
153
+ end
154
+
155
+ def add_file(file, line_marked)
156
+ percent = calc_coverage(line_marked)
157
+ path = File.expand_path(file)
158
+ return nil if path =~ /\A#{Regexp.escape(CONFIG["rubylibdir"])}/
159
+ return nil if path =~ /\A#{Regexp.escape(CONFIG["sitelibdir"])}/
160
+ #printf("file #{file} coverage=%02.1f%\n", percent)
161
+
162
+ # comments and empty lines.. we must
163
+ # propagate marked-value backwards
164
+ line_marked << ["", false]
165
+ (line_marked.size).downto(1) do |index|
166
+ line, marked = line_marked[index-1]
167
+ next_line, next_marked = line_marked[index]
168
+ if line =~ /^\s*(#|$)/ and marked == false
169
+ marked = next_marked
170
+ line_marked[index-1] = [line, marked]
171
+ end
172
+ end
173
+ line_marked.pop
174
+ @files[file] = line_marked
175
+ @files_codeonly[file] = line_marked.select do |(line, marked)|
176
+ line !~ /^\s*(#|$)/
177
+ end
178
+ end
179
+
180
+ def calc_coverage(line_marked)
181
+ marked = line_marked.transpose[1]
182
+ if marked
183
+ n = marked.inject(0) {|r, i| (i) ? (r+1) : r }
184
+ percent = n.to_f * 100 / marked.size
185
+ else
186
+ -1.0
187
+ end
188
+ end
189
+
190
+ def format_overview(file, line_marked, code_marked)
191
+ percent = "%02.1f" % calc_coverage(line_marked)
192
+ percent2 = "%02.1f" % calc_coverage(code_marked)
193
+ html = <<-EOHTML.gsub(/^\s*/, "")
194
+ <div class="overview">
195
+ <table>
196
+ <tr><td>filename</td><td><tt>#{file}</tt></td></tr>
197
+ <tr><td>total coverage</td><td>#{percent}%</td></tr>
198
+ <tr><td>code coverage</td><td>#{percent2}%</td></tr>
199
+ </table>
200
+ </div>
201
+ EOHTML
202
+ html
203
+ end
204
+
205
+ def format_lines(line_marked)
206
+ result = ""
207
+ last = nil
208
+ end_of_span = ""
209
+ format_line = "%#{line_marked.size.to_s.size}d"
210
+ line_no = 1
211
+ line_marked.each do |(line, marked)|
212
+ if marked != last
213
+ result += end_of_span
214
+ case marked
215
+ when Fixnum
216
+ if marked < 10
217
+ result += "<span class=\"marked\">"
218
+ else
219
+ result += "<span class=\"highmarks\">"
220
+ end
221
+ end_of_span = "</span>"
222
+ when false
223
+ end_of_span = ""
224
+ end
225
+ end
226
+ result += (format_line % line_no) + " " + escape(line) + "\n"
227
+ last = marked
228
+ line_no += 1
229
+ end
230
+ result += end_of_span
231
+ "<pre>#{result}</pre>"
232
+ end
233
+
234
+ def create_file(file, line_marked, code_marked)
235
+ url_filename = mk_filename(file)
236
+ return unless url_filename
237
+ output_filename, filename, abspath = url_filename
238
+ puts "outputting #{output_filename.inspect}"
239
+ body = format_overview(abspath, line_marked, code_marked) +
240
+ format_lines(line_marked)
241
+ title = filename + " - coverage"
242
+ css = <<-EOCSS.gsub(/^\s*/, "")
243
+ body {
244
+ background-color: rgb(180, 180, 180);
245
+ }
246
+ span.marked {
247
+ background-color: rgb(185, 200, 200);
248
+ display: block;
249
+ }
250
+ span.highmarks {
251
+ background-color: rgb(220, 200, 200);
252
+ display: block;
253
+ }
254
+ div.overview {
255
+ border-bottom: 8px solid black;
256
+ }
257
+ EOCSS
258
+ write_page(body, title, css, output_filename)
259
+ end
260
+ end # class HTML
261
+
262
+ end # module PrettyCoverage
263
+
264
+
265
+ SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
266
+
267
+
268
+ module COVERAGE__
269
+ COVER = Hash.new do |h, k|
270
+ a = [0]
271
+ if File.exists? k
272
+ File.open(k) do |f|
273
+ f.each_line {
274
+ a.push 0
275
+ }
276
+ end
277
+ else
278
+ a = [0] * 5000
279
+ end
280
+ h[k] = a
281
+ end
282
+
283
+ def self.trace_func(event, file, line, id, binding, klass)
284
+ case event
285
+ when 'c-call', 'c-return', 'class'
286
+ return
287
+ end
288
+ COVER[file][line] += 1
289
+ end
290
+
291
+ END {
292
+ set_trace_func(nil)
293
+ printer = PrettyCoverage::HTML.new
294
+ COVER.each do |file, lines|
295
+ next if SCRIPT_LINES__.has_key?(file) == false
296
+ lines = SCRIPT_LINES__[file]
297
+ covers = COVER[file]
298
+ line_status = []
299
+ 0.upto(lines.size - 1) do |c|
300
+ line = lines[c].chomp
301
+ if covers[c + 1] > 0
302
+ elsif /^\s*(?:begin\s*(?:#.*)?|ensure\s*(?:#.*)?|else\s*(?:#.*)?)$/ =~ line and covers[c + 2] > 0
303
+ covers[c + 1] = covers[c + 2]
304
+ elsif /^\s*(?:end|\})\s*$/ =~ line && covers[c] > 0
305
+ covers[c + 1] = covers[c]
306
+ end
307
+ line_status << [line, covers[c+1].nonzero? || false]
308
+ end
309
+ printer.add_file(file, line_status) unless line_status == []
310
+ end
311
+ printer.execute
312
+ } # END
313
+
314
+ set_trace_func(COVERAGE__.method(:trace_func).to_proc)
315
+ end