silhouette 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,68 @@
1
+ TABLE {
2
+ FONT-SIZE: 11px
3
+ }
4
+
5
+ TABLE.code {
6
+ border:black 1px solid;
7
+ }
8
+
9
+ BODY {
10
+ FONT-FAMILY: sans-serif;
11
+ }
12
+ .coverageHit {
13
+ border-right:#dcdcdc 1px solid;
14
+ background-color:#c8c8f0;
15
+ }
16
+ .coverageCount, .lineCount { border-right:#dcdcdc 1px solid; }
17
+ .coverageMissing, .coverageMissed {
18
+ border-right:#dcdcdc 1px solid;
19
+ background-color:#300;
20
+ }
21
+ .coverageMissed { border-right:#dcdcdc 1px solid; }
22
+ .lineNonCode {
23
+ border-right:#dcdcdc 1px solid;
24
+ background-color:#f0f0f0;
25
+ }
26
+ .lineCount {
27
+ border-right:#dcdcdc 1px solid;
28
+ background-color:#c8c8f0;
29
+ }
30
+ .sourceLineHighlight {
31
+ background-color:#200;
32
+ }
33
+
34
+ .sourceLineGoodHighlight {
35
+ background-color:#023103;
36
+ }
37
+
38
+ .coverageMethod {
39
+ background-color:#1a4400;
40
+ }
41
+
42
+ .sourceLinePartialHighlight {
43
+ background-color:#202020;
44
+ }
45
+
46
+ .sourceLine {
47
+ color:#ffffff;
48
+ background-color:#171717;
49
+ }
50
+
51
+ .normal { color: #ffffff; }
52
+ .comment { color: #99462e; }
53
+ .keyword { color: #79a8df; }
54
+ .method { color: #b3b3b3; }
55
+ .class { color: #6486c7; }
56
+ .module { color: #6486c7; }
57
+ .punct { color: #cdcdcd; }
58
+ .symbol { color: #2368ff; }
59
+ .string { color: #b6ac99; }
60
+ .char { color: #F07; }
61
+ .ident { color: #ffffff; }
62
+ .constant { color: #fffa00; }
63
+ .regex { color: #dd585d; }
64
+ .number { color: #2368ff; }
65
+ .attribute { color: #3d93a9; }
66
+ .global { color: #7FB; }
67
+ .expr { color: #e2e2e2; }
68
+ .escape { color: #277; }
@@ -0,0 +1,176 @@
1
+ module Silhouette
2
+ class InvalidFormat < Exception; end
3
+
4
+ def self.emitters
5
+ out = []
6
+ constants.each do |name|
7
+ con = const_get(name)
8
+ if Class === con and con.superclass == Emitter
9
+ out << con
10
+ end
11
+ end
12
+ out
13
+ end
14
+
15
+ def self.find_emitter(file)
16
+
17
+ if !File.exists? file
18
+ bz = "#{file}.bz2"
19
+ file = bz if File.exists?(bz)
20
+ end
21
+
22
+ if !File.exists? file
23
+ gz = "#{file}.gz"
24
+ file = gz if File.exists?(gz)
25
+ end
26
+
27
+ if file == "-"
28
+ io = STDIN
29
+
30
+ elsif /.bz2$/.match(file)
31
+ io = IO.popen("bzip2 -dc '#{file}'")
32
+ elsif /.gz$/.match(file)
33
+ io = IO.popen("gzip -dc '#{file}'")
34
+ else
35
+ raise "Unknown file" unless File.exists?(io)
36
+ io = File.open(file)
37
+ end
38
+
39
+ emitters.each do |em|
40
+ begin
41
+ return em.new(io)
42
+ rescue InvalidFormat
43
+ end
44
+ end
45
+
46
+ raise InvalidFormat, "Unable to find valid emitter"
47
+ end
48
+
49
+ class Emitter
50
+ def initialize(processor=nil)
51
+ @processor = processor
52
+ end
53
+
54
+ attr_accessor :processor
55
+ end
56
+
57
+ class BinaryEmitter < Emitter
58
+ MAGIC = "<>"
59
+
60
+ def initialize(file, processor=nil)
61
+ if file.kind_of? String
62
+ raise "Unknown file" unless File.exists?(file)
63
+ @io = File.open(file)
64
+ else
65
+ @io = file
66
+ end
67
+
68
+ magic = @io.read(2)
69
+ raise InvalidFormat unless magic == MAGIC
70
+
71
+ @method_size = "S"
72
+ @file_size = "S"
73
+ @ret_call_fmt = "i#{@method_size}#{@file_size}ii"
74
+ @ret_call_size = 16
75
+ super(processor)
76
+ end
77
+
78
+ class DoneParsing < Exception; end
79
+
80
+ def parse
81
+ begin
82
+ loop { emit }
83
+ rescue DoneParsing
84
+ end
85
+ end
86
+
87
+ FIXED_SIZE = {
88
+ ?c => 20,
89
+ ?r => 20,
90
+ ?@ => 8
91
+ }
92
+
93
+ def next_cmd
94
+ str = @io.read(1)
95
+ raise DoneParsing unless str
96
+
97
+ cmd = str[0]
98
+ if [?!, ?*, ?&].include? cmd
99
+ size = @io.read(4).unpack("i").first
100
+ else
101
+ size = FIXED_SIZE[cmd]
102
+ end
103
+
104
+ [cmd, size, @io.read(size)]
105
+ end
106
+
107
+ def parse
108
+ begin
109
+ # Use "while true" instead of "loop" because loop is
110
+ # really a method call.
111
+ while true
112
+ data = @io.read(1)
113
+ raise DoneParsing unless data
114
+
115
+ cmd = data[0]
116
+
117
+ # These are hardcoded in here for speed.
118
+ if cmd == ?r or cmd == ?c or cmd == ?l
119
+ size = @ret_call_size
120
+ elsif cmd == ?@
121
+ size = 8
122
+ else
123
+ size = @io.read(4).unpack("i").first
124
+ end
125
+
126
+ proc = @processor
127
+ data = @io.read(size)
128
+
129
+ case cmd
130
+ when ?r
131
+ parts = data.unpack(@ret_call_fmt)
132
+ @processor.process_return(*parts)
133
+ # return [:return, *parts]
134
+ when ?c
135
+ parts = data.unpack(@ret_call_fmt)
136
+ @processor.process_call(*parts)
137
+ when ?l
138
+ parts = data.unpack(@ret_call_fmt)
139
+ @processor.process_line(*parts)
140
+ # return [:call, *parts]
141
+ when ?!
142
+ parts = data.unpack("Z#{size - 8}ii")
143
+ @processor.process_start(*parts)
144
+ # return [:start, *parts]
145
+ when ?@
146
+ parts = data.unpack("ii")
147
+ @processor.process_end(*parts)
148
+ # return [:end, *parts]
149
+ when ?&
150
+ parts = data.unpack("ia#{size - 4}")
151
+ parts += parts.pop.split("\0")
152
+ @processor.process_method(*parts)
153
+ # return [:method, *parts]
154
+ when ?*
155
+ parts = data.unpack("iZ#{size - 4}")
156
+ @processor.process_file(*parts)
157
+ # return [:file, *parts]
158
+ when ?(
159
+ @method_size = "I"
160
+ @ret_call_size += 2
161
+ @ret_call_fmt = "i#{@method_size}#{@file_size}ii"
162
+ when ?)
163
+ @file_size = "I"
164
+ @ret_call_size += 2
165
+ @ret_call_fmt = "i#{@method_size}#{@file_size}ii"
166
+ else
167
+ raise "Unknown type '#{cmd.chr}'"
168
+ end
169
+ end
170
+
171
+ # This means we're done.
172
+ rescue DoneParsing
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,138 @@
1
+ require 'syntax/convertors/html'
2
+
3
+ module Silhouette
4
+ class MethodFinder < Syntax::Convertors::HTML
5
+ class MethodPosition
6
+ def initialize(name, klass)
7
+ @name = name
8
+ @klass = klass
9
+ @misses = 0
10
+ @loc = 0
11
+ end
12
+
13
+ def total
14
+ @last_line - @first_line
15
+ end
16
+
17
+ attr_accessor :first_line, :last_line, :name, :klass, :misses, :loc
18
+
19
+ def coverage
20
+ loc_hit = @loc - @misses
21
+ ((loc_hit / @loc.to_f) * 100).to_i
22
+ end
23
+ end
24
+
25
+ class ClassPosition
26
+ def initialize(name)
27
+ @name = name
28
+ @misses = 0
29
+ end
30
+
31
+ def total
32
+ @last_line - @first_line
33
+ end
34
+
35
+ attr_accessor :first_line, :last_line, :name, :misses
36
+ end
37
+
38
+ def initialize(tok)
39
+ super
40
+ @line_no = 1
41
+ @inner_ends = 0
42
+ @class_path = []
43
+ @class_stack = []
44
+ @modules = []
45
+ @methods = []
46
+ @method_stack = []
47
+ @current_method = nil
48
+ @html = ""
49
+ @line_start = true
50
+ end
51
+
52
+ attr_reader :methods, :modules
53
+
54
+ HAS_END = %w!if begin while for until case unless!
55
+
56
+ def process_token(tok)
57
+ # p [tok, tok.group]
58
+ case tok.group
59
+ when :normal, :string, :constant
60
+ nls = tok.count("\n")
61
+ @line_no += nls
62
+ @line_start = true if nls > 0
63
+ when :class, :module
64
+ # How to handle classes inside conditions? Don't count them.
65
+ return unless @inner_ends == 0
66
+ @class_path << tok.to_s
67
+ klass = ClassPosition.new(@class_path.join("::"))
68
+ klass.first_line = @line_no
69
+ @modules << klass
70
+ @class_stack << klass
71
+ # puts "start class: #{@class_path.inspect}"
72
+ when :method
73
+ return unless @inner_ends == 0
74
+ # Handle nested methods by just ignoring the nested ones.
75
+ if @class_path.last == :def
76
+ @inner_ends += 1
77
+ else
78
+ meth = MethodPosition.new(tok, @class_path.join("::"))
79
+ meth.first_line = @line_no
80
+ @methods << meth
81
+ @method_stack << meth
82
+ @class_path << :def
83
+ # puts "start meth: #{@class_path.inspect}, #{tok}"
84
+ end
85
+ when :keyword
86
+ if HAS_END.include?(tok.to_s) and @line_start
87
+ @inner_ends += 1
88
+ return
89
+ elsif tok == "do"
90
+ @inner_ends += 1
91
+ return
92
+ end
93
+
94
+ if tok == "end"
95
+ if @inner_ends == 0
96
+ was_in = @class_path.pop
97
+ if was_in == :def
98
+ # puts "Done with method: #{@class_path.inspect}"
99
+ @method_stack.last.last_line = @line_no
100
+ @method_stack.pop
101
+ else
102
+ fin = @class_stack.pop
103
+ if fin
104
+ fin.last_line = @line_no
105
+ end
106
+ end
107
+ else
108
+ @inner_ends -= 1
109
+ # puts "pending ends: #{@inner_ends}"
110
+ end
111
+ end
112
+ end
113
+
114
+ if tok.group != :normal
115
+ @line_start = false
116
+ end
117
+ end
118
+
119
+ def find(text)
120
+ @tokenizer.tokenize(text) do |tok|
121
+ process_token tok
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def html_escape(tok)
128
+ process_token(tok)
129
+ super
130
+ end
131
+ end
132
+ end
133
+ require 'pp'
134
+ if $0 == __FILE__
135
+ con = Silhouette::MethodFinder.for_syntax "ruby"
136
+ con.convert ARGF.read
137
+ pp con
138
+ end
@@ -0,0 +1,63 @@
1
+ TABLE {
2
+ FONT-SIZE: 11px
3
+ }
4
+
5
+ TABLE.code {
6
+ border:black 1px solid;
7
+ }
8
+
9
+ BODY {
10
+ FONT-FAMILY: sans-serif;
11
+ }
12
+ .coverageHit {
13
+ border-right:#dcdcdc 1px solid;
14
+ background-color:#c8c8f0;
15
+ }
16
+ .coverageCount, .lineCount { border-right:#dcdcdc 1px solid; }
17
+ .coverageMissing, .coverageMissed {
18
+ border-right:#dcdcdc 1px solid;
19
+ background-color:#F0C8C8;
20
+ }
21
+ .coverageMissed { border-right:#dcdcdc 1px solid; }
22
+ .lineNonCode {
23
+ border-right:#dcdcdc 1px solid;
24
+ background-color:#f0f0f0;
25
+ }
26
+ .lineCount {
27
+ border-right:#dcdcdc 1px solid;
28
+ background-color:#c8c8f0;
29
+ }
30
+ .sourceLineHighlight {
31
+ background-color:#F0C8C8;
32
+ }
33
+
34
+ .sourceLineGoodHighlight {
35
+ background-color:#c1ffd1;
36
+ }
37
+
38
+ .coverageMethod {
39
+ background-color:#c1ffd1;
40
+ }
41
+
42
+ .sourceLinePartialHighlight {
43
+ background-color:#f1f1f1;
44
+ }
45
+
46
+ .normal {}
47
+ .comment { color: #005; font-style: italic; }
48
+ .keyword { color: #0000dd; }
49
+ .method { color: #077; }
50
+ .class { color: #074; }
51
+ .module { color: #050; }
52
+ .punct { color: #447; font-weight: bold; }
53
+ .symbol { color: #099; }
54
+ .string { color: #b58401; }
55
+ .char { color: #F07; }
56
+ .ident { color: #004; }
57
+ .constant { color: #07F; }
58
+ .regex { color: #B66; background: #FEF; }
59
+ .number { color: #e15858;; }
60
+ .attribute { color: #3d93a9; }
61
+ .global { color: #7FB; }
62
+ .expr { color: #227; }
63
+ .escape { color: #277; }
@@ -0,0 +1,3 @@
1
+ require 'silhouette/processor'
2
+ require 'silhouette/converter'
3
+ require 'silhouette/emitters'