flyerhzm-metric_fu 1.0.0

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.
Files changed (92) hide show
  1. data/HISTORY +164 -0
  2. data/MIT-LICENSE +22 -0
  3. data/Manifest.txt +25 -0
  4. data/README.textile +27 -0
  5. data/Rakefile +18 -0
  6. data/TODO +9 -0
  7. data/lib/base/base_template.rb +145 -0
  8. data/lib/base/configuration.rb +180 -0
  9. data/lib/base/generator.rb +167 -0
  10. data/lib/base/graph.rb +39 -0
  11. data/lib/base/md5_tracker.rb +52 -0
  12. data/lib/base/report.rb +100 -0
  13. data/lib/generators/churn.rb +90 -0
  14. data/lib/generators/flay.rb +34 -0
  15. data/lib/generators/flog.rb +167 -0
  16. data/lib/generators/rails_best_practices.rb +31 -0
  17. data/lib/generators/rcov.rb +87 -0
  18. data/lib/generators/reek.rb +64 -0
  19. data/lib/generators/roodi.rb +32 -0
  20. data/lib/generators/saikuro.rb +208 -0
  21. data/lib/generators/stats.rb +43 -0
  22. data/lib/graphs/engines/bluff.rb +98 -0
  23. data/lib/graphs/engines/gchart.rb +119 -0
  24. data/lib/graphs/flay_grapher.rb +20 -0
  25. data/lib/graphs/flog_grapher.rb +40 -0
  26. data/lib/graphs/grapher.rb +11 -0
  27. data/lib/graphs/rails_best_practices_grapher.rb +20 -0
  28. data/lib/graphs/rcov_grapher.rb +20 -0
  29. data/lib/graphs/reek_grapher.rb +32 -0
  30. data/lib/graphs/roodi_grapher.rb +20 -0
  31. data/lib/metric_fu.rb +31 -0
  32. data/lib/templates/awesome/awesome_template.rb +37 -0
  33. data/lib/templates/awesome/churn.html.erb +19 -0
  34. data/lib/templates/awesome/css/buttons.css +82 -0
  35. data/lib/templates/awesome/css/default.css +75 -0
  36. data/lib/templates/awesome/css/integrity.css +335 -0
  37. data/lib/templates/awesome/css/reset.css +7 -0
  38. data/lib/templates/awesome/flay.html.erb +33 -0
  39. data/lib/templates/awesome/flog.html.erb +53 -0
  40. data/lib/templates/awesome/index.html.erb +31 -0
  41. data/lib/templates/awesome/layout.html.erb +30 -0
  42. data/lib/templates/awesome/rails_best_practices.html.erb +27 -0
  43. data/lib/templates/awesome/rcov.html.erb +42 -0
  44. data/lib/templates/awesome/reek.html.erb +40 -0
  45. data/lib/templates/awesome/roodi.html.erb +27 -0
  46. data/lib/templates/awesome/saikuro.html.erb +71 -0
  47. data/lib/templates/awesome/stats.html.erb +41 -0
  48. data/lib/templates/javascripts/bluff-min.js +1 -0
  49. data/lib/templates/javascripts/excanvas.js +19 -0
  50. data/lib/templates/javascripts/js-class.js +1 -0
  51. data/lib/templates/standard/churn.html.erb +31 -0
  52. data/lib/templates/standard/default.css +64 -0
  53. data/lib/templates/standard/flay.html.erb +34 -0
  54. data/lib/templates/standard/flog.html.erb +53 -0
  55. data/lib/templates/standard/index.html.erb +41 -0
  56. data/lib/templates/standard/rails_best_practices.html.erb +29 -0
  57. data/lib/templates/standard/rcov.html.erb +43 -0
  58. data/lib/templates/standard/reek.html.erb +42 -0
  59. data/lib/templates/standard/roodi.html.erb +29 -0
  60. data/lib/templates/standard/saikuro.html.erb +84 -0
  61. data/lib/templates/standard/standard_template.rb +26 -0
  62. data/lib/templates/standard/stats.html.erb +55 -0
  63. data/spec/base/base_template_spec.rb +161 -0
  64. data/spec/base/configuration_spec.rb +269 -0
  65. data/spec/base/generator_spec.rb +244 -0
  66. data/spec/base/graph_spec.rb +24 -0
  67. data/spec/base/md5_tracker_spec.rb +57 -0
  68. data/spec/base/report_spec.rb +139 -0
  69. data/spec/generators/churn_spec.rb +152 -0
  70. data/spec/generators/flay_spec.rb +104 -0
  71. data/spec/generators/flog_spec.rb +238 -0
  72. data/spec/generators/reek_spec.rb +126 -0
  73. data/spec/generators/saikuro_spec.rb +58 -0
  74. data/spec/generators/stats_spec.rb +74 -0
  75. data/spec/graphs/engines/bluff_spec.rb +15 -0
  76. data/spec/graphs/engines/gchart_spec.rb +15 -0
  77. data/spec/graphs/flay_grapher_spec.rb +37 -0
  78. data/spec/graphs/flog_grapher_spec.rb +45 -0
  79. data/spec/graphs/rcov_grapher_spec.rb +37 -0
  80. data/spec/graphs/reek_grapher_spec.rb +46 -0
  81. data/spec/graphs/roodi_grapher_spec.rb +37 -0
  82. data/spec/resources/saikuro/app/controllers/sessions_controller.rb_cyclo.html +10 -0
  83. data/spec/resources/saikuro/app/controllers/users_controller.rb_cyclo.html +16 -0
  84. data/spec/resources/saikuro/index_cyclo.html +155 -0
  85. data/spec/resources/saikuro_sfiles/thing.rb_cyclo.html +11 -0
  86. data/spec/resources/yml/20090630.yml +7844 -0
  87. data/spec/spec.opts +8 -0
  88. data/spec/spec_helper.rb +7 -0
  89. data/tasks/metric_fu.rake +22 -0
  90. data/vendor/_fonts/monaco.ttf +0 -0
  91. data/vendor/saikuro/saikuro.rb +1219 -0
  92. metadata +234 -0
@@ -0,0 +1,8 @@
1
+ --colour
2
+ --format
3
+ specdoc
4
+ --reverse
5
+ --timeout
6
+ 20
7
+ --loadby
8
+ mtime
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'spec/autorun'
3
+ require 'date'
4
+ require 'construct'
5
+
6
+ require File.join(File.dirname(__FILE__), '/../lib/metric_fu.rb')
7
+ include MetricFu
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+ namespace :metrics do
3
+ desc "Generate all metrics reports"
4
+ task :all do
5
+ MetricFu::Configuration.run {}
6
+ MetricFu.metrics.each {|metric| MetricFu.report.add(metric) }
7
+ MetricFu.report.save_output(MetricFu.report.to_yaml,
8
+ MetricFu.base_directory,
9
+ "report.yml")
10
+ MetricFu.report.save_output(MetricFu.report.to_yaml,
11
+ MetricFu.data_directory,
12
+ "#{Time.now.strftime("%Y%m%d")}.yml")
13
+ MetricFu.report.save_templatized_report
14
+
15
+ MetricFu.graphs.each {|graph| MetricFu.graph.add(graph, MetricFu.graph_engine) }
16
+ MetricFu.graph.generate
17
+
18
+ if MetricFu.report.open_in_browser?
19
+ MetricFu.report.show_in_browser(MetricFu.output_directory)
20
+ end
21
+ end
22
+ end
Binary file
@@ -0,0 +1,1219 @@
1
+ # $Id: saikuro 33 2006-12-07 16:09:55Z zev $
2
+ # Version 0.2
3
+ # == Usage
4
+ #
5
+ # ruby saikuro.rb [ -h ] [-o output_directory] [-f type] [ -c, -t ]
6
+ # [ -y, -w, -e, -k, -s, -d - number ] ( -p file | -i directory )
7
+ #
8
+ # == Help
9
+ #
10
+ # -o, --output_directory (directory) : A directory to ouput the results in.
11
+ # The current directory is used if this option is not passed.
12
+ #
13
+ # -h, --help : This help message.
14
+ #
15
+ # -f, --formater (html | text) : The format to output the results in.
16
+ # The default is html
17
+ #
18
+ # -c, --cyclo : Compute the cyclomatic complexity of the input.
19
+ #
20
+ # -t, --token : Count the number of tokens per line of the input.
21
+ #
22
+ # -y, --filter_cyclo (number) : Filter the output to only include methods
23
+ # whose cyclomatic complexity are greater than the passed number.
24
+ #
25
+ # -w, --warn_cyclo (number) : Highlight with a warning methods whose
26
+ # cyclomatic complexity are greather than or equal to the passed number.
27
+ #
28
+ #
29
+ # -e, --error_cyclo (number) : Highligh with an error methods whose
30
+ # cyclomatic complexity are greather than or equal to the passed number.
31
+ #
32
+ #
33
+ # -k, --filter_token (number) : Filter the output to only include lines
34
+ # whose token count are greater than the passed number.
35
+ #
36
+ #
37
+ # -s, --warn_token (number) : Highlight with a warning lines whose
38
+ # token count are greater than or equal to the passed number.
39
+ #
40
+ #
41
+ # -d, --error_token (number) : Highlight with an error lines whose
42
+ # token count are greater than or equal to the passed number.
43
+ #
44
+ #
45
+ # -p, --parse_file (file) : A file to use as input.
46
+ #
47
+ # -i, --input_directory (directory) : All ruby files found recursively
48
+ # inside the directory are passed as input.
49
+
50
+ # == License
51
+ # Saikruo uses the BSD license.
52
+ #
53
+ # Copyright (c) 2005, Ubiquitous Business Technology (http://ubit.com)
54
+ # All rights reserved.
55
+ #
56
+ # Redistribution and use in source and binary forms, with or without
57
+ # modification, are permitted provided that the following conditions are
58
+ # met:
59
+ #
60
+ #
61
+ # * Redistributions of source code must retain the above copyright
62
+ # notice, this list of conditions and the following disclaimer.
63
+ #
64
+ # * Redistributions in binary form must reproduce the above
65
+ # copyright notice, this list of conditions and the following
66
+ # disclaimer in the documentation and/or other materials provided
67
+ # with the distribution.
68
+ #
69
+ # * Neither the name of Ubiquitous Business Technology nor the names
70
+ # of its contributors may be used to endorse or promote products
71
+ # derived from this software without specific prior written
72
+ # permission.
73
+ #
74
+ #
75
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
76
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
77
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
78
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
79
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
80
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
81
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
82
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
83
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
84
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
85
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
86
+ #
87
+ # == Author
88
+ # Zev Blut (zb@ubit.com)
89
+
90
+ require 'irb/ruby-lex'
91
+ require 'yaml'
92
+
93
+ # States to watch for
94
+ # once in def get the token after space, because it may also
95
+ # be something like + or << for operator overloading.
96
+
97
+ # Counts the number of tokens in each line.
98
+ class TokenCounter
99
+ include RubyToken
100
+
101
+ attr_reader :current_file
102
+
103
+ def initialize
104
+ @files = Hash.new
105
+ @tokens_per_line = Hash.new(0)
106
+ @current_file = ""
107
+ end
108
+
109
+ # Mark file to associate with the token count.
110
+ def set_current_file(file)
111
+ @current_file = file
112
+ @tokens_per_line = Hash.new(0)
113
+ @files[@current_file] = @tokens_per_line
114
+ end
115
+
116
+ # Iterate through all tracked files, passing the
117
+ # the provided formater the token counts.
118
+ def list_tokens_per_line(formater)
119
+ formater.start_count(@files.size)
120
+ @files.each do |fname, tok_per_line|
121
+ formater.start_file(fname)
122
+ tok_per_line.sort.each do |line,num|
123
+ formater.line_token_count(line,num)
124
+ end
125
+ formater.end_file
126
+ end
127
+ end
128
+
129
+ # Count the token for the passed line.
130
+ def count_token(line_no,token)
131
+ case token
132
+ when TkSPACE, TkNL, TkRD_COMMENT
133
+ # Do not count these as tokens
134
+ when TkCOMMENT
135
+ # Ignore this only for comments in a statement?
136
+ # Ignore TkCOLON,TkCOLON2 and operators? like "." etc..
137
+ when TkRBRACK, TkRPAREN, TkRBRACE
138
+ # Ignore the closing of an array/index/hash/paren
139
+ # The opening is counted, but no more.
140
+ # Thus [], () {} is counted as 1 token not 2.
141
+ else
142
+ # may want to filter out comments...
143
+ @tokens_per_line[line_no] += 1
144
+ end
145
+ end
146
+
147
+ end
148
+
149
+ # Main class and structure used to compute the
150
+ # cyclomatic complexity of Ruby programs.
151
+ class ParseState
152
+ include RubyToken
153
+ attr_accessor :name, :children, :complexity, :parent, :lines
154
+
155
+ @@top_state = nil
156
+ def ParseState.make_top_state()
157
+ @@top_state = ParseState.new(nil)
158
+ @@top_state.name = "__top__"
159
+ @@top_state
160
+ end
161
+
162
+ @@token_counter = TokenCounter.new
163
+ def ParseState.set_token_counter(counter)
164
+ @@token_counter = counter
165
+ end
166
+ def ParseState.get_token_counter
167
+ @@token_counter
168
+ end
169
+
170
+ def initialize(lexer,parent=nil)
171
+ @name = ""
172
+ @children = Array.new
173
+ @complexity = 0
174
+ @parent = parent
175
+ @lexer = lexer
176
+ @run = true
177
+ # To catch one line def statements, We always have one line.
178
+ @lines = 0
179
+ @last_token_line_and_char = Array.new
180
+ end
181
+
182
+ def top_state?
183
+ self == @@top_state
184
+ end
185
+
186
+ def lexer=(lexer)
187
+ @run = true
188
+ @lexer = lexer
189
+ end
190
+
191
+ def make_state(type,parent = nil)
192
+ cstate = type.new(@lexer,self)
193
+ parent.children<< cstate
194
+ cstate
195
+ end
196
+
197
+ def calc_complexity
198
+ complexity = @complexity
199
+ children.each do |child|
200
+ complexity += child.calc_complexity
201
+ end
202
+ complexity
203
+ end
204
+
205
+ def calc_lines
206
+ lines = @lines
207
+ children.each do |child|
208
+ lines += child.calc_lines
209
+ end
210
+ lines
211
+ end
212
+
213
+ def compute_state(formater)
214
+ if top_state?
215
+ compute_state_for_global(formater)
216
+ end
217
+
218
+ @children.each do |s|
219
+ s.compute_state(formater)
220
+ end
221
+ end
222
+
223
+ def compute_state_for_global(formater)
224
+ global_def, @children = @children.partition do |s|
225
+ !s.kind_of?(ParseClass)
226
+ end
227
+ return if global_def.empty?
228
+ gx = global_def.inject(0) { |c,s| s.calc_complexity }
229
+ gl = global_def.inject(0) { |c,s| s.calc_lines }
230
+ formater.start_class_compute_state("Global", "", gx, gl)
231
+ global_def.each do |s|
232
+ s.compute_state(formater)
233
+ end
234
+ formater.end_class_compute_state("")
235
+ end
236
+
237
+ # Count the tokens parsed if true else ignore them.
238
+ def count_tokens?
239
+ true
240
+ end
241
+
242
+ def parse
243
+ while @run do
244
+ tok = @lexer.token
245
+ @run = false if tok.nil?
246
+ if lexer_loop?(tok)
247
+ STDERR.puts "Lexer loop at line : #{@lexer.line_no} char #{@lexer.char_no}."
248
+ @run = false
249
+ end
250
+ @last_token_line_and_char<< [@lexer.line_no.to_i, @lexer.char_no.to_i, tok]
251
+ if $VERBOSE
252
+ puts "DEBUG: #{@lexer.line_no} #{tok.class}:#{tok.name if tok.respond_to?(:name)}"
253
+ end
254
+ @@token_counter.count_token(@lexer.line_no, tok) if count_tokens?
255
+ parse_token(tok)
256
+ end
257
+ end
258
+
259
+ # Ruby-Lexer can go into a loop if the file does not end with a newline.
260
+ def lexer_loop?(token)
261
+ return false if @last_token_line_and_char.empty?
262
+ loop_flag = false
263
+ last = @last_token_line_and_char.last
264
+ line = last[0]
265
+ char = last[1]
266
+ ltok = last[2]
267
+
268
+ if ( (line == @lexer.line_no.to_i) &&
269
+ (char == @lexer.char_no.to_i) &&
270
+ (ltok.class == token.class) )
271
+ # We are potentially in a loop
272
+ if @last_token_line_and_char.size >= 3
273
+ loop_flag = true
274
+ end
275
+ else
276
+ # Not in a loop so clear stack
277
+ @last_token_line_and_char = Array.new
278
+ end
279
+
280
+ loop_flag
281
+ end
282
+
283
+ def do_begin_token(token)
284
+ make_state(EndableParseState, self)
285
+ end
286
+
287
+ def do_class_token(token)
288
+ make_state(ParseClass,self)
289
+ end
290
+
291
+ def do_module_token(token)
292
+ make_state(ParseModule,self)
293
+ end
294
+
295
+ def do_def_token(token)
296
+ make_state(ParseDef,self)
297
+ end
298
+
299
+ def do_constant_token(token)
300
+ nil
301
+ end
302
+
303
+ def do_identifier_token(token)
304
+ if (token.name == "__END__" && token.char_no.to_i == 0)
305
+ # The Ruby code has stopped and the rest is data so cease parsing.
306
+ @run = false
307
+ end
308
+ nil
309
+ end
310
+
311
+ def do_right_brace_token(token)
312
+ nil
313
+ end
314
+
315
+ def do_end_token(token)
316
+ end_debug
317
+ nil
318
+ end
319
+
320
+ def do_block_token(token)
321
+ make_state(ParseBlock,self)
322
+ end
323
+
324
+ def do_conditional_token(token)
325
+ make_state(ParseCond,self)
326
+ end
327
+
328
+ def do_conditional_do_control_token(token)
329
+ make_state(ParseDoCond,self)
330
+ end
331
+
332
+ def do_case_token(token)
333
+ make_state(EndableParseState, self)
334
+ end
335
+
336
+ def do_one_line_conditional_token(token)
337
+ # This is an if with no end
338
+ @complexity += 1
339
+ #STDOUT.puts "got IF_MOD: #{self.to_yaml}" if $VERBOSE
340
+ #if state.type != "class" && state.type != "def" && state.type != "cond"
341
+ #STDOUT.puts "Changing IF_MOD Parent" if $VERBOSE
342
+ #state = state.parent
343
+ #@run = false
344
+ nil
345
+ end
346
+
347
+ def do_else_token(token)
348
+ STDOUT.puts "Ignored/Unknown Token:#{token.class}" if $VERBOSE
349
+ nil
350
+ end
351
+
352
+ def do_comment_token(token)
353
+ make_state(ParseComment, self)
354
+ end
355
+
356
+ def do_symbol_token(token)
357
+ make_state(ParseSymbol, self)
358
+ end
359
+
360
+ def parse_token(token)
361
+ state = nil
362
+ case token
363
+ when TkCLASS
364
+ state = do_class_token(token)
365
+ when TkMODULE
366
+ state = do_module_token(token)
367
+ when TkDEF
368
+ state = do_def_token(token)
369
+ when TkCONSTANT
370
+ # Nothing to do with a constant at top level?
371
+ state = do_constant_token(token)
372
+ when TkIDENTIFIER,TkFID
373
+ # Nothing to do at top level?
374
+ state = do_identifier_token(token)
375
+ when TkRBRACE
376
+ # Nothing to do at top level
377
+ state = do_right_brace_token(token)
378
+ when TkEND
379
+ state = do_end_token(token)
380
+ # At top level this might be an error...
381
+ when TkDO,TkfLBRACE
382
+ state = do_block_token(token)
383
+ when TkIF,TkUNLESS
384
+ state = do_conditional_token(token)
385
+ when TkWHILE,TkUNTIL,TkFOR
386
+ state = do_conditional_do_control_token(token)
387
+ when TkELSIF #,TkELSE
388
+ @complexity += 1
389
+ when TkELSE
390
+ # Else does not increase complexity
391
+ when TkCASE
392
+ state = do_case_token(token)
393
+ when TkWHEN
394
+ @complexity += 1
395
+ when TkBEGIN
396
+ state = do_begin_token(token)
397
+ when TkRESCUE
398
+ # Maybe this should add complexity and not begin
399
+ @complexity += 1
400
+ when TkIF_MOD, TkUNLESS_MOD, TkUNTIL_MOD, TkWHILE_MOD, TkQUESTION
401
+ state = do_one_line_conditional_token(token)
402
+ when TkNL
403
+ #
404
+ @lines += 1
405
+ when TkRETURN
406
+ # Early returns do not increase complexity as the condition that
407
+ # calls the return is the one that increases it.
408
+ when TkCOMMENT
409
+ state = do_comment_token(token)
410
+ when TkSYMBEG
411
+ state = do_symbol_token(token)
412
+ when TkError
413
+ STDOUT.puts "Lexer received an error for line #{@lexer.line_no} char #{@lexer.char_no}"
414
+ else
415
+ state = do_else_token(token)
416
+ end
417
+ state.parse if state
418
+ end
419
+
420
+ def end_debug
421
+ STDOUT.puts "got an end: #{@name} in #{self.class.name}" if $VERBOSE
422
+ if @parent.nil?
423
+ STDOUT.puts "DEBUG: Line #{@lexer.line_no}"
424
+ STDOUT.puts "DEBUG: #{@name}; #{self.class}"
425
+ # to_yaml can cause an infinite loop?
426
+ #STDOUT.puts "TOP: #{@@top_state.to_yaml}"
427
+ #STDOUT.puts "TOP: #{@@top_state.inspect}"
428
+
429
+ # This may not be an error?
430
+ #exit 1
431
+ end
432
+ end
433
+
434
+ end
435
+
436
+ # Read and consume tokens in comments until a new line.
437
+ class ParseComment < ParseState
438
+
439
+ # While in a comment state do not count the tokens.
440
+ def count_tokens?
441
+ false
442
+ end
443
+
444
+ def parse_token(token)
445
+ if token.is_a?(TkNL)
446
+ @lines += 1
447
+ @run = false
448
+ end
449
+ end
450
+ end
451
+
452
+ class ParseSymbol < ParseState
453
+ def initialize(lexer, parent = nil)
454
+ super
455
+ STDOUT.puts "STARTING SYMBOL" if $VERBOSE
456
+ end
457
+
458
+ def parse_token(token)
459
+ STDOUT.puts "Symbol's token is #{token.class}" if $VERBOSE
460
+ # Consume the next token and stop
461
+ @run = false
462
+ nil
463
+ end
464
+ end
465
+
466
+ class EndableParseState < ParseState
467
+ def initialize(lexer,parent=nil)
468
+ super(lexer,parent)
469
+ STDOUT.puts "Starting #{self.class}" if $VERBOSE
470
+ end
471
+
472
+ def do_end_token(token)
473
+ end_debug
474
+ @run = false
475
+ nil
476
+ end
477
+ end
478
+
479
+ class ParseClass < EndableParseState
480
+ def initialize(lexer,parent=nil)
481
+ super(lexer,parent)
482
+ @type_name = "Class"
483
+ end
484
+
485
+ def do_constant_token(token)
486
+ @name = token.name if @name.empty?
487
+ nil
488
+ end
489
+
490
+ def compute_state(formater)
491
+ # Seperate the Module and Class Children out
492
+ cnm_children, @children = @children.partition do |child|
493
+ child.kind_of?(ParseClass)
494
+ end
495
+
496
+ formater.start_class_compute_state(@type_name,@name,self.calc_complexity,self.calc_lines)
497
+ super(formater)
498
+ formater.end_class_compute_state(@name)
499
+
500
+ cnm_children.each do |child|
501
+ child.name = @name + "::" + child.name
502
+ child.compute_state(formater)
503
+ end
504
+ end
505
+ end
506
+
507
+ class ParseModule < ParseClass
508
+ def initialize(lexer,parent=nil)
509
+ super(lexer,parent)
510
+ @type_name = "Module"
511
+ end
512
+ end
513
+
514
+ class ParseDef < EndableParseState
515
+
516
+ def initialize(lexer,parent=nil)
517
+ super(lexer,parent)
518
+ @complexity = 1
519
+ @looking_for_name = true
520
+ @first_space = true
521
+ end
522
+
523
+ # This way I don't need to list all possible overload
524
+ # tokens.
525
+ def create_def_name(token)
526
+ case token
527
+ when TkSPACE
528
+ # mark first space so we can stop at next space
529
+ if @first_space
530
+ @first_space = false
531
+ else
532
+ @looking_for_name = false
533
+ end
534
+ when TkNL,TkLPAREN,TkfLPAREN,TkSEMICOLON
535
+ # we can also stop at a new line or left parenthesis
536
+ @looking_for_name = false
537
+ when TkDOT
538
+ @name<< "."
539
+ when TkCOLON2
540
+ @name<< "::"
541
+ when TkASSIGN
542
+ @name<< "="
543
+ when TkfLBRACK
544
+ @name<< "["
545
+ when TkRBRACK
546
+ @name<< "]"
547
+ else
548
+ begin
549
+ @name<< token.name.to_s
550
+ rescue Exception => err
551
+ #what is this?
552
+ STDOUT.puts @@token_counter.current_file
553
+ STDOUT.puts @name
554
+ STDOUT.puts token.inspect
555
+ STDOUT.puts err.message
556
+ exit 1
557
+ end
558
+ end
559
+ end
560
+
561
+ def parse_token(token)
562
+ if @looking_for_name
563
+ create_def_name(token)
564
+ end
565
+ super(token)
566
+ end
567
+
568
+ def compute_state(formater)
569
+ formater.def_compute_state(@name, self.calc_complexity, self.calc_lines)
570
+ super(formater)
571
+ end
572
+ end
573
+
574
+ class ParseCond < EndableParseState
575
+ def initialize(lexer,parent=nil)
576
+ super(lexer,parent)
577
+ @complexity = 1
578
+ end
579
+ end
580
+
581
+ class ParseDoCond < ParseCond
582
+ def initialize(lexer,parent=nil)
583
+ super(lexer,parent)
584
+ @looking_for_new_line = true
585
+ end
586
+
587
+ # Need to consume the do that can appear at the
588
+ # end of these control structures.
589
+ def parse_token(token)
590
+ if @looking_for_new_line
591
+ if token.is_a?(TkDO)
592
+ nil
593
+ else
594
+ if token.is_a?(TkNL)
595
+ @looking_for_new_line = false
596
+ end
597
+ super(token)
598
+ end
599
+ else
600
+ super(token)
601
+ end
602
+ end
603
+
604
+ end
605
+
606
+ class ParseBlock < EndableParseState
607
+
608
+ def initialize(lexer,parent=nil)
609
+ super(lexer,parent)
610
+ @complexity = 1
611
+ @lbraces = Array.new
612
+ end
613
+
614
+ # Because the token for a block and hash right brace is the same,
615
+ # we need to track the hash left braces to determine when an end is
616
+ # encountered.
617
+ def parse_token(token)
618
+ if token.is_a?(TkLBRACE)
619
+ @lbraces.push(true)
620
+ elsif token.is_a?(TkRBRACE)
621
+ if @lbraces.empty?
622
+ do_right_brace_token(token)
623
+ #do_end_token(token)
624
+ else
625
+ @lbraces.pop
626
+ end
627
+ else
628
+ super(token)
629
+ end
630
+ end
631
+
632
+ def do_right_brace_token(token)
633
+ # we are done ? what about a hash in a block :-/
634
+ @run = false
635
+ nil
636
+ end
637
+
638
+ end
639
+
640
+ # ------------ END Analyzer logic ------------------------------------
641
+
642
+ class Filter
643
+ attr_accessor :limit, :error, :warn
644
+
645
+ def initialize(limit = -1, error = 11, warn = 8)
646
+ @limit = limit
647
+ @error = error
648
+ @warn = warn
649
+ end
650
+
651
+ def ignore?(count)
652
+ count < @limit
653
+ end
654
+
655
+ def warn?(count)
656
+ count >= @warn
657
+ end
658
+
659
+ def error?(count)
660
+ count >= @error
661
+ end
662
+
663
+ end
664
+
665
+
666
+ class BaseFormater
667
+ attr_accessor :warnings, :errors, :current
668
+
669
+ def initialize(out, filter = nil)
670
+ @out = out
671
+ @filter = filter
672
+ reset_data
673
+ end
674
+
675
+ def warn_error?(num, marker)
676
+ klass = ""
677
+
678
+ if @filter.error?(num)
679
+ klass = ' class="error"'
680
+ @errors<< [@current, marker, num]
681
+ elsif @filter.warn?(num)
682
+ klass = ' class="warning"'
683
+ @warnings<< [@current, marker, num]
684
+ end
685
+
686
+ klass
687
+ end
688
+
689
+ def reset_data
690
+ @warnings = Array.new
691
+ @errors = Array.new
692
+ @current = ""
693
+ end
694
+
695
+ end
696
+
697
+ class TokenCounterFormater < BaseFormater
698
+
699
+ def start(new_out=nil)
700
+ reset_data
701
+ @out = new_out if new_out
702
+ @out.puts "Token Count"
703
+ end
704
+
705
+ def start_count(number_of_files)
706
+ @out.puts "Counting tokens for #{number_of_files} files."
707
+ end
708
+
709
+ def start_file(file_name)
710
+ @current = file_name
711
+ @out.puts "File:#{file_name}"
712
+ end
713
+
714
+ def line_token_count(line_number,number_of_tokens)
715
+ return if @filter.ignore?(number_of_tokens)
716
+ warn_error?(number_of_tokens, line_number)
717
+ @out.puts "Line:#{line_number} ; Tokens : #{number_of_tokens}"
718
+ end
719
+
720
+ def end_file
721
+ @out.puts ""
722
+ end
723
+
724
+ def end_count
725
+ end
726
+
727
+ def end
728
+ end
729
+
730
+ end
731
+
732
+ module HTMLStyleSheet
733
+ def HTMLStyleSheet.style_sheet
734
+ out = StringIO.new
735
+
736
+ out.puts "<style>"
737
+ out.puts 'body {'
738
+ out.puts ' margin: 20px;'
739
+ out.puts ' padding: 0;'
740
+ out.puts ' font-size: 12px;'
741
+ out.puts ' font-family: bitstream vera sans, verdana, arial, sans serif;'
742
+ out.puts ' background-color: #efefef;'
743
+ out.puts '}'
744
+ out.puts ''
745
+ out.puts 'table { '
746
+ out.puts ' border-collapse: collapse;'
747
+ out.puts ' /*border-spacing: 0;*/'
748
+ out.puts ' border: 1px solid #666;'
749
+ out.puts ' background-color: #fff;'
750
+ out.puts ' margin-bottom: 20px;'
751
+ out.puts '}'
752
+ out.puts ''
753
+ out.puts 'table, th, th+th, td, td+td {'
754
+ out.puts ' border: 1px solid #ccc;'
755
+ out.puts '}'
756
+ out.puts ''
757
+ out.puts 'table th {'
758
+ out.puts ' font-size: 12px;'
759
+ out.puts ' color: #fc0;'
760
+ out.puts ' padding: 4px 0;'
761
+ out.puts ' background-color: #336;'
762
+ out.puts '}'
763
+ out.puts ''
764
+ out.puts 'th, td {'
765
+ out.puts ' padding: 4px 10px;'
766
+ out.puts '}'
767
+ out.puts ''
768
+ out.puts 'td { '
769
+ out.puts ' font-size: 13px;'
770
+ out.puts '}'
771
+ out.puts ''
772
+ out.puts '.class_name {'
773
+ out.puts ' font-size: 17px;'
774
+ out.puts ' margin: 20px 0 0;'
775
+ out.puts '}'
776
+ out.puts ''
777
+ out.puts '.class_complexity {'
778
+ out.puts 'margin: 0 auto;'
779
+ out.puts '}'
780
+ out.puts ''
781
+ out.puts '.class_complexity>.class_complexity {'
782
+ out.puts ' margin: 0;'
783
+ out.puts '}'
784
+ out.puts ''
785
+ out.puts '.class_total_complexity, .class_total_lines, .start_token_count, .file_count {'
786
+ out.puts ' font-size: 13px;'
787
+ out.puts ' font-weight: bold;'
788
+ out.puts '}'
789
+ out.puts ''
790
+ out.puts '.class_total_complexity, .class_total_lines {'
791
+ out.puts ' color: #c00;'
792
+ out.puts '}'
793
+ out.puts ''
794
+ out.puts '.start_token_count, .file_count {'
795
+ out.puts ' color: #333;'
796
+ out.puts '}'
797
+ out.puts ''
798
+ out.puts '.warning {'
799
+ out.puts ' background-color: yellow;'
800
+ out.puts '}'
801
+ out.puts ''
802
+ out.puts '.error {'
803
+ out.puts ' background-color: #f00;'
804
+ out.puts '}'
805
+ out.puts "</style>"
806
+
807
+ out.string
808
+ end
809
+
810
+ def style_sheet
811
+ HTMLStyleSheet.style_sheet
812
+ end
813
+ end
814
+
815
+
816
+ class HTMLTokenCounterFormater < TokenCounterFormater
817
+ include HTMLStyleSheet
818
+
819
+ def start(new_out=nil)
820
+ reset_data
821
+ @out = new_out if new_out
822
+ @out.puts "<html>"
823
+ @out.puts style_sheet
824
+ @out.puts "<body>"
825
+ end
826
+
827
+ def start_count(number_of_files)
828
+ @out.puts "<div class=\"start_token_count\">"
829
+ @out.puts "Number of files: #{number_of_files}"
830
+ @out.puts "</div>"
831
+ end
832
+
833
+ def start_file(file_name)
834
+ @current = file_name
835
+ @out.puts "<div class=\"file_count\">"
836
+ @out.puts "<p class=\"file_name\">"
837
+ @out.puts "File: #{file_name}"
838
+ @out.puts "</p>"
839
+ @out.puts "<table width=\"100%\" border=\"1\">"
840
+ @out.puts "<tr><th>Line</th><th>Tokens</th></tr>"
841
+ end
842
+
843
+ def line_token_count(line_number,number_of_tokens)
844
+ return if @filter.ignore?(number_of_tokens)
845
+ klass = warn_error?(number_of_tokens, line_number)
846
+ @out.puts "<tr><td>#{line_number}</td><td#{klass}>#{number_of_tokens}</td></tr>"
847
+ end
848
+
849
+ def end_file
850
+ @out.puts "</table>"
851
+ end
852
+
853
+ def end_count
854
+ end
855
+
856
+ def end
857
+ @out.puts "</body>"
858
+ @out.puts "</html>"
859
+ end
860
+ end
861
+
862
+ class ParseStateFormater < BaseFormater
863
+
864
+ def start(new_out=nil)
865
+ reset_data
866
+ @out = new_out if new_out
867
+ end
868
+
869
+ def end
870
+ end
871
+
872
+ def start_class_compute_state(type_name,name,complexity,lines)
873
+ @current = name
874
+ @out.puts "-- START #{name} --"
875
+ @out.puts "Type:#{type_name} Name:#{name} Complexity:#{complexity} Lines:#{lines}"
876
+ end
877
+
878
+ def end_class_compute_state(name)
879
+ @out.puts "-- END #{name} --"
880
+ end
881
+
882
+ def def_compute_state(name,complexity,lines)
883
+ return if @filter.ignore?(complexity)
884
+ warn_error?(complexity, name)
885
+ @out.puts "Type:Def Name:#{name} Complexity:#{complexity} Lines:#{lines}"
886
+ end
887
+
888
+ end
889
+
890
+
891
+
892
+ class StateHTMLComplexityFormater < ParseStateFormater
893
+ include HTMLStyleSheet
894
+
895
+ def start(new_out=nil)
896
+ reset_data
897
+ @out = new_out if new_out
898
+ @out.puts "<html><head><title>Cyclometric Complexity</title></head>"
899
+ @out.puts style_sheet
900
+ @out.puts "<body>"
901
+ end
902
+
903
+ def end
904
+ @out.puts "</body>"
905
+ @out.puts "</html>"
906
+ end
907
+
908
+ def start_class_compute_state(type_name,name,complexity,lines)
909
+ @current = name
910
+ @out.puts "<div class=\"class_complexity\">"
911
+ @out.puts "<h2 class=\"class_name\">#{type_name} : #{name}</h2>"
912
+ @out.puts "<div class=\"class_total_complexity\">Total Complexity: #{complexity}</div>"
913
+ @out.puts "<div class=\"class_total_lines\">Total Lines: #{lines}</div>"
914
+ @out.puts "<table width=\"100%\" border=\"1\">"
915
+ @out.puts "<tr><th>Method</th><th>Complexity</th><th># Lines</th></tr>"
916
+ end
917
+
918
+ def end_class_compute_state(name)
919
+ @out.puts "</table>"
920
+ @out.puts "</div>"
921
+ end
922
+
923
+ def def_compute_state(name, complexity, lines)
924
+ return if @filter.ignore?(complexity)
925
+ klass = warn_error?(complexity, name)
926
+ @out.puts "<tr><td>#{name}</td><td#{klass}>#{complexity}</td><td>#{lines}</td></tr>"
927
+ end
928
+
929
+ end
930
+
931
+
932
+ module ResultIndexGenerator
933
+ def summarize_errors_and_warnings(enw, header)
934
+ return "" if enw.empty?
935
+ f = StringIO.new
936
+ erval = Hash.new { |h,k| h[k] = Array.new }
937
+ wval = Hash.new { |h,k| h[k] = Array.new }
938
+
939
+ enw.each do |fname, warnings, errors|
940
+ errors.each do |c,m,v|
941
+ erval[v] << [fname, c, m]
942
+ end
943
+ warnings.each do |c,m,v|
944
+ wval[v] << [fname, c, m]
945
+ end
946
+ end
947
+
948
+ f.puts "<h2 class=\"class_name\">Errors and Warnings</h2>"
949
+ f.puts "<table width=\"100%\" border=\"1\">"
950
+ f.puts header
951
+
952
+ f.puts print_summary_table_rows(erval, "error")
953
+ f.puts print_summary_table_rows(wval, "warning")
954
+ f.puts "</table>"
955
+
956
+ f.string
957
+ end
958
+
959
+ def print_summary_table_rows(ewvals, klass_type)
960
+ f = StringIO.new
961
+ ewvals.sort { |a,b| b <=> a}.each do |v, vals|
962
+ vals.sort.each do |fname, c, m|
963
+ f.puts "<tr><td><a href=\"./#{fname}\">#{c}</a></td><td>#{m}</td>"
964
+ f.puts "<td class=\"#{klass_type}\">#{v}</td></tr>"
965
+ end
966
+ end
967
+ f.string
968
+ end
969
+
970
+ def list_analyzed_files(files)
971
+ f = StringIO.new
972
+ f.puts "<h2 class=\"class_name\">Analyzed Files</h2>"
973
+ f.puts "<ul>"
974
+ files.each do |fname, warnings, errors|
975
+ readname = fname.split("_")[0...-1].join("_")
976
+ f.puts "<li>"
977
+ f.puts "<p class=\"file_name\"><a href=\"./#{fname}\">#{readname}</a>"
978
+ f.puts "</li>"
979
+ end
980
+ f.puts "</ul>"
981
+ f.string
982
+ end
983
+
984
+ def write_index(files, filename, title, header)
985
+ return if files.empty?
986
+
987
+ File.open(filename,"w") do |f|
988
+ f.puts "<html><head><title>#{title}</title></head>"
989
+ f.puts "#{HTMLStyleSheet.style_sheet}\n<body>"
990
+ f.puts "<h1>#{title}</h1>"
991
+
992
+ enw = files.find_all { |fn,w,e| (!w.empty? || !e.empty?) }
993
+
994
+ f.puts summarize_errors_and_warnings(enw, header)
995
+
996
+ f.puts "<hr/>"
997
+ f.puts list_analyzed_files(files)
998
+ f.puts "</body></html>"
999
+ end
1000
+ end
1001
+
1002
+ def write_cyclo_index(files, output_dir)
1003
+ header = "<tr><th>Class</th><th>Method</th><th>Complexity</th></tr>"
1004
+ write_index(files,
1005
+ "#{output_dir}/index_cyclo.html",
1006
+ "Index for cyclomatic complexity",
1007
+ header)
1008
+ end
1009
+
1010
+ def write_token_index(files, output_dir)
1011
+ header = "<tr><th>File</th><th>Line #</th><th>Tokens</th></tr>"
1012
+ write_index(files,
1013
+ "#{output_dir}/index_token.html",
1014
+ "Index for tokens per line",
1015
+ header)
1016
+ end
1017
+
1018
+ end
1019
+
1020
+ module Saikuro
1021
+ def Saikuro.analyze(files, state_formater, token_count_formater, output_dir)
1022
+
1023
+ idx_states = Array.new
1024
+ idx_tokens = Array.new
1025
+
1026
+ # parse each file
1027
+ files.each do |file|
1028
+ begin
1029
+ STDOUT.puts "Parsing #{file}"
1030
+ # create top state
1031
+ top = ParseState.make_top_state
1032
+ STDOUT.puts "TOP State made" if $VERBOSE
1033
+ token_counter = TokenCounter.new
1034
+ ParseState.set_token_counter(token_counter)
1035
+ token_counter.set_current_file(file)
1036
+
1037
+ STDOUT.puts "Setting up Lexer" if $VERBOSE
1038
+ lexer = RubyLex.new
1039
+ # Turn of this, because it aborts when a syntax error is found...
1040
+ lexer.exception_on_syntax_error = false
1041
+ lexer.set_input(File.new(file,"r"))
1042
+ top.lexer = lexer
1043
+ STDOUT.puts "Parsing" if $VERBOSE
1044
+ top.parse
1045
+
1046
+
1047
+ fdir_path = seperate_file_from_path(file)
1048
+ FileUtils.makedirs("#{output_dir}/#{fdir_path}")
1049
+
1050
+ if state_formater
1051
+ # output results
1052
+ state_io = StringIO.new
1053
+ state_formater.start(state_io)
1054
+ top.compute_state(state_formater)
1055
+ state_formater.end
1056
+
1057
+ fname = "#{file}_cyclo.html"
1058
+ puts "writing cyclomatic #{file}" if $VERBOSE
1059
+ File.open("#{output_dir}/#{fname}","w") do |f|
1060
+ f.write state_io.string
1061
+ end
1062
+ idx_states<< [
1063
+ fname,
1064
+ state_formater.warnings.dup,
1065
+ state_formater.errors.dup,
1066
+ ]
1067
+ end
1068
+
1069
+ if token_count_formater
1070
+ token_io = StringIO.new
1071
+ token_count_formater.start(token_io)
1072
+ token_counter.list_tokens_per_line(token_count_formater)
1073
+ token_count_formater.end
1074
+
1075
+ fname = "#{file}_token.html"
1076
+ puts "writing token #{file}" if $VERBOSE
1077
+ File.open("#{output_dir}/#{fname}","w") do |f|
1078
+ f.write token_io.string
1079
+ end
1080
+ idx_tokens<< [
1081
+ fname,
1082
+ token_count_formater.warnings.dup,
1083
+ token_count_formater.errors.dup,
1084
+ ]
1085
+ end
1086
+
1087
+ rescue RubyLex::SyntaxError => synerr
1088
+ STDOUT.puts "Lexer error for file #{file} on line #{lexer.line_no}"
1089
+ STDOUT.puts "#{synerr.class.name} : #{synerr.message}"
1090
+ rescue StandardError => err
1091
+ STDOUT.puts "Error while parsing file : #{file}"
1092
+ STDOUT.puts err.class,err.message,err.backtrace.join("\n")
1093
+ rescue Exception => ex
1094
+ STDOUT.puts "Error while parsing file : #{file}"
1095
+ STDOUT.puts ex.class,ex.message,ex.backtrace.join("\n")
1096
+ end
1097
+ end
1098
+
1099
+ [idx_states, idx_tokens]
1100
+ end
1101
+ end
1102
+
1103
+ if __FILE__ == $0
1104
+ require 'stringio'
1105
+ require 'getoptlong'
1106
+ require 'fileutils'
1107
+ require 'find'
1108
+ begin
1109
+ require 'rdoc/ri/ri_paths'
1110
+ require 'rdoc/usage'
1111
+ rescue LoadError
1112
+ # these requires cause problems in Ruby 1.9x that I'm not really sure how to fix
1113
+ require 'rdoc/ri/paths'
1114
+ #Still not sure what the equivalent of rdoc/usage is. I know it has been removed from 1.9
1115
+ end
1116
+ include ResultIndexGenerator
1117
+
1118
+ #Returns the path without the file
1119
+ def seperate_file_from_path(path)
1120
+ res = path.split("/")
1121
+ if res.size == 1
1122
+ ""
1123
+ else
1124
+ res[0..res.size - 2].join("/")
1125
+ end
1126
+ end
1127
+
1128
+ def get_ruby_files(input_path)
1129
+ files = Array.new
1130
+ input_path.split("|").each do |path|
1131
+ Find.find(path.strip) do |f|
1132
+ files << f if !FileTest.directory?(f) && f =~ /\.rb$/
1133
+ end
1134
+ end
1135
+ files
1136
+ end
1137
+
1138
+ files = Array.new
1139
+ output_dir = "./"
1140
+ formater = "html"
1141
+ state_filter = Filter.new(5)
1142
+ token_filter = Filter.new(10, 25, 50)
1143
+ comp_state = comp_token = false
1144
+ begin
1145
+ opt = GetoptLong.new(
1146
+ ["-o","--output_directory", GetoptLong::REQUIRED_ARGUMENT],
1147
+ ["-h","--help", GetoptLong::NO_ARGUMENT],
1148
+ ["-f","--formater", GetoptLong::REQUIRED_ARGUMENT],
1149
+ ["-c","--cyclo", GetoptLong::NO_ARGUMENT],
1150
+ ["-t","--token", GetoptLong::NO_ARGUMENT],
1151
+ ["-y","--filter_cyclo", GetoptLong::REQUIRED_ARGUMENT],
1152
+ ["-k","--filter_token", GetoptLong::REQUIRED_ARGUMENT],
1153
+ ["-w","--warn_cyclo", GetoptLong::REQUIRED_ARGUMENT],
1154
+ ["-s","--warn_token", GetoptLong::REQUIRED_ARGUMENT],
1155
+ ["-e","--error_cyclo", GetoptLong::REQUIRED_ARGUMENT],
1156
+ ["-d","--error_token", GetoptLong::REQUIRED_ARGUMENT],
1157
+ ["-p","--parse_file", GetoptLong::REQUIRED_ARGUMENT],
1158
+ ["-i","--input_directory", GetoptLong::REQUIRED_ARGUMENT],
1159
+ ["-v","--verbose", GetoptLong::NO_ARGUMENT]
1160
+ )
1161
+
1162
+ opt.each do |arg,val|
1163
+ case arg
1164
+ when "-o"
1165
+ output_dir = val
1166
+ when "-h"
1167
+ RDoc.usage('help')
1168
+ when "-f"
1169
+ formater = val
1170
+ when "-c"
1171
+ comp_state = true
1172
+ when "-t"
1173
+ comp_token = true
1174
+ when "-k"
1175
+ token_filter.limit = val.to_i
1176
+ when "-s"
1177
+ token_filter.warn = val.to_i
1178
+ when "-d"
1179
+ token_filter.error = val.to_i
1180
+ when "-y"
1181
+ state_filter.limit = val.to_i
1182
+ when "-w"
1183
+ state_filter.warn = val.to_i
1184
+ when "-e"
1185
+ state_filter.error = val.to_i
1186
+ when "-p"
1187
+ files<< val
1188
+ when "-i"
1189
+ files.concat(get_ruby_files(val))
1190
+ when "-v"
1191
+ STDOUT.puts "Verbose mode on"
1192
+ $VERBOSE = true
1193
+ end
1194
+
1195
+ end
1196
+ RDoc.usage if !comp_state && !comp_token
1197
+ rescue => err
1198
+ RDoc.usage
1199
+ end
1200
+
1201
+ if formater =~ /html/i
1202
+ state_formater = StateHTMLComplexityFormater.new(STDOUT,state_filter)
1203
+ token_count_formater = HTMLTokenCounterFormater.new(STDOUT,token_filter)
1204
+ else
1205
+ state_formater = ParseStateFormater.new(STDOUT,state_filter)
1206
+ token_count_formater = TokenCounterFormater.new(STDOUT,token_filter)
1207
+ end
1208
+
1209
+ state_formater = nil if !comp_state
1210
+ token_count_formater = nil if !comp_token
1211
+
1212
+ idx_states, idx_tokens = Saikuro.analyze(files,
1213
+ state_formater,
1214
+ token_count_formater,
1215
+ output_dir)
1216
+
1217
+ write_cyclo_index(idx_states, output_dir)
1218
+ write_token_index(idx_tokens, output_dir)
1219
+ end