silhouette 1.0.0 → 2.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.
@@ -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'