jsduck 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +10 -5
- data/jsduck.gemspec +7 -3
- data/lib/jsduck/aggregator.rb +7 -10
- data/lib/jsduck/app.rb +34 -12
- data/lib/jsduck/cfg_table.rb +2 -2
- data/lib/jsduck/doc_parser.rb +23 -23
- data/lib/jsduck/event_table.rb +2 -2
- data/lib/jsduck/lexer.rb +54 -41
- data/lib/jsduck/method_table.rb +2 -2
- data/lib/jsduck/page.rb +12 -5
- data/lib/jsduck/parser.rb +27 -19
- data/lib/jsduck/property_table.rb +2 -2
- data/lib/jsduck/table.rb +39 -10
- data/lib/jsduck/timer.rb +40 -0
- metadata +19 -4
data/README.md
CHANGED
@@ -106,10 +106,11 @@ For hacking fork it from github.
|
|
106
106
|
$ cd jsduck
|
107
107
|
$ rake --tasks
|
108
108
|
|
109
|
-
JsDuck depends on [json][] and [
|
109
|
+
JsDuck depends on [json][], [RDiscount][], and [parallel][] plus [RSpec][] for tests.
|
110
110
|
|
111
111
|
[json]: http://flori.github.com/json/
|
112
112
|
[RDiscount]: https://github.com/rtomayko/rdiscount
|
113
|
+
[parallel]: https://github.com/grosser/parallel
|
113
114
|
[RSpec]: http://rspec.info/
|
114
115
|
|
115
116
|
|
@@ -182,10 +183,6 @@ Missing features and TODO
|
|
182
183
|
* Support for custom @tags. Ext-doc supports this, I personally have
|
183
184
|
never used this feature, so I'm thinking it's not really needed.
|
184
185
|
|
185
|
-
* Speed improvements. JsDuck is clearly slower than ext-doc, but I
|
186
|
-
haven't so far done almost no optimizations, so there should be some
|
187
|
-
pretty low-hanging fruits to pick.
|
188
|
-
|
189
186
|
|
190
187
|
Copying
|
191
188
|
-------
|
@@ -198,6 +195,14 @@ JsDuck was developed by [Rene Saarsoo](http://triin.net).
|
|
198
195
|
Changelog
|
199
196
|
---------
|
200
197
|
|
198
|
+
* 0.3 - Performance improvements
|
199
|
+
* Significant peed improvements - most importantly utilizing
|
200
|
+
multiple CPU-s (if available) to speed things up. On my 4-core
|
201
|
+
box JsDuck is now even faster than ext-doc.
|
202
|
+
* Printing of performance info in verbose mode
|
203
|
+
* Support for comma-first coding style
|
204
|
+
* Few other fixes to JavaScript parsing
|
205
|
+
|
201
206
|
* 0.2 - most features of ext-doc supported.
|
202
207
|
* Links from documentation to source code
|
203
208
|
* Syntax highlighting of code examples
|
data/jsduck.gemspec
CHANGED
@@ -2,8 +2,8 @@ Gem::Specification.new do |s|
|
|
2
2
|
s.required_rubygems_version = ">= 1.3.7"
|
3
3
|
|
4
4
|
s.name = 'jsduck'
|
5
|
-
s.version = '0.
|
6
|
-
s.date = '2011-
|
5
|
+
s.version = '0.3'
|
6
|
+
s.date = '2011-02-08'
|
7
7
|
s.summary = "Simple JavaScript Duckumentation generator"
|
8
8
|
s.description = "Better ext-doc like JavaScript documentation generator for ExtJS"
|
9
9
|
s.homepage = "https://github.com/nene/jsduck"
|
@@ -11,11 +11,15 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.email = "nene@triin.net"
|
12
12
|
s.rubyforge_project = s.name
|
13
13
|
|
14
|
-
s.files = `git ls-files`.split("\n").find_all
|
14
|
+
s.files = `git ls-files`.split("\n").find_all do |file|
|
15
|
+
file !~ /spec.rb$/ && file !~ /benchmark/
|
16
|
+
end
|
17
|
+
|
15
18
|
s.executables = ["jsduck"]
|
16
19
|
|
17
20
|
s.add_dependency 'rdiscount'
|
18
21
|
s.add_dependency 'json'
|
22
|
+
s.add_dependency 'parallel'
|
19
23
|
|
20
24
|
s.require_path = 'lib'
|
21
25
|
end
|
data/lib/jsduck/aggregator.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'jsduck/parser'
|
2
|
-
require 'jsduck/doc_parser'
|
3
1
|
require 'jsduck/merger'
|
4
2
|
|
5
3
|
module JsDuck
|
@@ -12,22 +10,21 @@ module JsDuck
|
|
12
10
|
@classes = {}
|
13
11
|
@orphans = []
|
14
12
|
@current_class = nil
|
15
|
-
@doc_parser = DocParser.new
|
16
13
|
@merger = Merger.new
|
17
14
|
end
|
18
15
|
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# #result method.
|
16
|
+
# Combines chunk of parsed JavaScript together with previously
|
17
|
+
# added chunks. The resulting documentation is accumulated inside
|
18
|
+
# this class and can be later accessed through #result method.
|
22
19
|
#
|
23
|
-
# - input
|
20
|
+
# - input parse result from JsDuck::Parser
|
24
21
|
# - filename name of the JS file where it came from
|
25
22
|
# - html_filename name of the HTML file where the source was saved.
|
26
23
|
#
|
27
|
-
def
|
24
|
+
def aggregate(input, filename="", html_filename="")
|
28
25
|
@current_class = nil
|
29
|
-
|
30
|
-
doc =
|
26
|
+
input.each do |docset|
|
27
|
+
doc = docset[:comment]
|
31
28
|
code = docset[:code]
|
32
29
|
href = html_filename + "#line-" + docset[:linenr].to_s
|
33
30
|
register(add_href(@merger.merge(doc, code), href, filename))
|
data/lib/jsduck/app.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
require 'jsduck/parser'
|
2
3
|
require 'jsduck/aggregator'
|
3
4
|
require 'jsduck/source_formatter'
|
4
5
|
require 'jsduck/class'
|
@@ -6,8 +7,10 @@ require 'jsduck/tree'
|
|
6
7
|
require 'jsduck/tree_icons'
|
7
8
|
require 'jsduck/subclasses'
|
8
9
|
require 'jsduck/page'
|
10
|
+
require 'jsduck/timer'
|
9
11
|
require 'json'
|
10
12
|
require 'fileutils'
|
13
|
+
require 'parallel'
|
11
14
|
|
12
15
|
module JsDuck
|
13
16
|
|
@@ -24,26 +27,42 @@ module JsDuck
|
|
24
27
|
@template_dir = nil
|
25
28
|
@input_files = []
|
26
29
|
@verbose = false
|
30
|
+
@timer = Timer.new
|
27
31
|
end
|
28
32
|
|
29
33
|
# Call this after input parameters set
|
30
34
|
def run
|
31
35
|
copy_template(@template_dir, @output_dir)
|
32
|
-
|
33
|
-
|
34
|
-
|
36
|
+
|
37
|
+
parsed_files = @timer.time(:parsing) { parallel_parse(@input_files) }
|
38
|
+
result = @timer.time(:aggregating) { aggregate(parsed_files) }
|
39
|
+
classes = @timer.time(:aggregating) { filter_classes(result) }
|
40
|
+
@timer.time(:generating) { write_tree(@output_dir+"/output/tree.js", classes) }
|
41
|
+
@timer.time(:generating) { write_pages(@output_dir+"/output", classes) }
|
42
|
+
|
43
|
+
@timer.report if @verbose
|
35
44
|
end
|
36
45
|
|
37
|
-
#
|
38
|
-
|
39
|
-
def parse_files(filenames)
|
40
|
-
agr = Aggregator.new
|
46
|
+
# Parses the files in parallel using as many processes as available CPU-s
|
47
|
+
def parallel_parse(filenames)
|
41
48
|
src = SourceFormatter.new(@output_dir + "/source")
|
42
|
-
filenames
|
49
|
+
Parallel.map(filenames) do |fname|
|
43
50
|
puts "Parsing #{fname} ..." if @verbose
|
44
51
|
code = IO.read(fname)
|
45
|
-
|
46
|
-
|
52
|
+
{
|
53
|
+
:name => fname,
|
54
|
+
:src_name => src.write(code, fname),
|
55
|
+
:data => Parser.new(code).parse,
|
56
|
+
}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Aggregates parsing results sequencially
|
61
|
+
def aggregate(parsed_files)
|
62
|
+
agr = Aggregator.new
|
63
|
+
parsed_files.each do |file|
|
64
|
+
puts "Aggregating #{file[:name]} ..." if @verbose
|
65
|
+
agr.aggregate(file[:data], File.basename(file[:name]), File.basename(file[:src_name]))
|
47
66
|
end
|
48
67
|
agr.result
|
49
68
|
end
|
@@ -73,12 +92,15 @@ module JsDuck
|
|
73
92
|
end
|
74
93
|
|
75
94
|
# Writes documentation page for each class
|
95
|
+
# We do it in parallel using as many processes as available CPU-s
|
76
96
|
def write_pages(path, docs)
|
77
97
|
subclasses = Subclasses.new(docs)
|
78
|
-
|
98
|
+
cache = {}
|
99
|
+
Parallel.each(docs) do |cls|
|
79
100
|
filename = path + "/" + cls[:name] + ".html"
|
80
101
|
puts "Writing to #{filename} ..." if @verbose
|
81
|
-
|
102
|
+
html = Page.new(cls, subclasses, cache).to_html
|
103
|
+
File.open(filename, 'w') {|f| f.write(html) }
|
82
104
|
end
|
83
105
|
end
|
84
106
|
|
data/lib/jsduck/cfg_table.rb
CHANGED
data/lib/jsduck/doc_parser.rb
CHANGED
@@ -48,7 +48,7 @@ module JsDuck
|
|
48
48
|
# - and those without it
|
49
49
|
input.each_line do |line|
|
50
50
|
line.chomp!
|
51
|
-
if line =~ /\A\s*\*\s?(.*)\Z/
|
51
|
+
if line =~ /\A\s*\*\s?(.*)\Z/
|
52
52
|
result << $1
|
53
53
|
else
|
54
54
|
result << line
|
@@ -64,39 +64,39 @@ module JsDuck
|
|
64
64
|
def parse_loop
|
65
65
|
add_tag(:default)
|
66
66
|
while !@input.eos? do
|
67
|
-
if look(/@class\b/)
|
67
|
+
if look(/@class\b/)
|
68
68
|
at_class
|
69
|
-
elsif look(/@extends\b/)
|
69
|
+
elsif look(/@extends\b/)
|
70
70
|
at_extends
|
71
|
-
elsif look(/@singleton\b/)
|
71
|
+
elsif look(/@singleton\b/)
|
72
72
|
boolean_at_tag(/@singleton/, :singleton)
|
73
|
-
elsif look(/@event\b/)
|
73
|
+
elsif look(/@event\b/)
|
74
74
|
at_event
|
75
|
-
elsif look(/@method\b/)
|
75
|
+
elsif look(/@method\b/)
|
76
76
|
at_method
|
77
|
-
elsif look(/@constructor\b/)
|
77
|
+
elsif look(/@constructor\b/)
|
78
78
|
boolean_at_tag(/@constructor/, :constructor)
|
79
|
-
elsif look(/@param\b/)
|
79
|
+
elsif look(/@param\b/)
|
80
80
|
at_param
|
81
|
-
elsif look(/@returns?\b/)
|
81
|
+
elsif look(/@returns?\b/)
|
82
82
|
at_return
|
83
|
-
elsif look(/@cfg\b/)
|
83
|
+
elsif look(/@cfg\b/)
|
84
84
|
at_cfg
|
85
|
-
elsif look(/@property\b/)
|
85
|
+
elsif look(/@property\b/)
|
86
86
|
at_property
|
87
|
-
elsif look(/@type\b/)
|
87
|
+
elsif look(/@type\b/)
|
88
88
|
at_type
|
89
|
-
elsif look(/@xtype\b/)
|
89
|
+
elsif look(/@xtype\b/)
|
90
90
|
at_xtype
|
91
|
-
elsif look(/@member\b/)
|
91
|
+
elsif look(/@member\b/)
|
92
92
|
at_member
|
93
|
-
elsif look(/@static\b/)
|
93
|
+
elsif look(/@static\b/)
|
94
94
|
boolean_at_tag(/@static/, :static)
|
95
|
-
elsif look(/@(private|ignore|hide|protected)\b/)
|
95
|
+
elsif look(/@(private|ignore|hide|protected)\b/)
|
96
96
|
boolean_at_tag(/@(private|ignore|hide|protected)/, :private)
|
97
|
-
elsif look(/@/)
|
97
|
+
elsif look(/@/)
|
98
98
|
@current_tag[:doc] += @input.scan(/@/)
|
99
|
-
elsif look(/[^@]/)
|
99
|
+
elsif look(/[^@]/)
|
100
100
|
@current_tag[:doc] += @input.scan(/[^@]+/)
|
101
101
|
end
|
102
102
|
end
|
@@ -183,9 +183,9 @@ module JsDuck
|
|
183
183
|
match(/@type/)
|
184
184
|
add_tag(:type)
|
185
185
|
skip_horiz_white
|
186
|
-
if look(/\{/)
|
186
|
+
if look(/\{/)
|
187
187
|
@current_tag[:type] = typedef
|
188
|
-
elsif look(/\S/)
|
188
|
+
elsif look(/\S/)
|
189
189
|
@current_tag[:type] = @input.scan(/\S+/)
|
190
190
|
end
|
191
191
|
skip_white
|
@@ -217,7 +217,7 @@ module JsDuck
|
|
217
217
|
# matches {type} if possible and sets it on @current_tag
|
218
218
|
def maybe_type
|
219
219
|
skip_horiz_white
|
220
|
-
if look(/\{/)
|
220
|
+
if look(/\{/)
|
221
221
|
@current_tag[:type] = typedef
|
222
222
|
end
|
223
223
|
end
|
@@ -225,7 +225,7 @@ module JsDuck
|
|
225
225
|
# matches identifier name if possible and sets it on @current_tag
|
226
226
|
def maybe_name
|
227
227
|
skip_horiz_white
|
228
|
-
if look(/\w/)
|
228
|
+
if look(/\w/)
|
229
229
|
@current_tag[:name] = ident
|
230
230
|
end
|
231
231
|
end
|
@@ -233,7 +233,7 @@ module JsDuck
|
|
233
233
|
# matches ident.chain if possible and sets it on @current_tag
|
234
234
|
def maybe_ident_chain(propname)
|
235
235
|
skip_horiz_white
|
236
|
-
if look(/\w/)
|
236
|
+
if look(/\w/)
|
237
237
|
@current_tag[propname] = ident_chain
|
238
238
|
end
|
239
239
|
end
|
data/lib/jsduck/event_table.rb
CHANGED
data/lib/jsduck/lexer.rb
CHANGED
@@ -38,7 +38,7 @@ module JsDuck
|
|
38
38
|
tok = @tokens[i]
|
39
39
|
i += 1
|
40
40
|
return false if tok == nil
|
41
|
-
if t.instance_of?(Symbol)
|
41
|
+
if t.instance_of?(Symbol)
|
42
42
|
tok[:type] == t
|
43
43
|
else
|
44
44
|
tok[:value] == t
|
@@ -67,40 +67,60 @@ module JsDuck
|
|
67
67
|
end
|
68
68
|
|
69
69
|
# Goes through the whole input and tokenizes it
|
70
|
+
#
|
71
|
+
# For efficency we look for tokens in order of frequency in
|
72
|
+
# JavaScript source code:
|
73
|
+
#
|
74
|
+
# - first check for most common operators.
|
75
|
+
# - then for identifiers and keywords.
|
76
|
+
# - then strings
|
77
|
+
# - then comments
|
78
|
+
#
|
79
|
+
# The remaining token types are less frequent, so these are left
|
80
|
+
# to the end.
|
81
|
+
#
|
70
82
|
def tokenize
|
71
83
|
@tokens = []
|
72
84
|
while !@input.eos? do
|
73
|
-
|
74
|
-
if @input.check(/[
|
85
|
+
skip_white
|
86
|
+
if @input.check(/[.(),;={}:]/)
|
75
87
|
@tokens << {
|
76
|
-
:type => :
|
77
|
-
:value =>
|
88
|
+
:type => :operator,
|
89
|
+
:value => @input.scan(/./)
|
78
90
|
}
|
79
|
-
elsif @input.check(
|
91
|
+
elsif @input.check(/[a-zA-Z_]/)
|
80
92
|
value = @input.scan(/\w+/)
|
81
93
|
@tokens << {
|
82
94
|
:type => KEYWORDS[value] ? :keyword : :ident,
|
83
95
|
:value => value
|
84
96
|
}
|
85
|
-
elsif @input.check(
|
86
|
-
@tokens << {
|
87
|
-
:type => :doc_comment,
|
88
|
-
# Calculate current line number, starting with 1
|
89
|
-
:linenr => @input.string[0...@input.pos].count("\n") + 1,
|
90
|
-
:value => @input.scan_until(/\*\/|\Z/)
|
91
|
-
}
|
92
|
-
elsif @input.check(/"/) then
|
97
|
+
elsif @input.check(/'/)
|
93
98
|
@tokens << {
|
94
99
|
:type => :string,
|
95
|
-
:value => eval(@input.scan(/
|
100
|
+
:value => eval(@input.scan(/'([^'\\]|\\.)*'/))
|
96
101
|
}
|
97
|
-
elsif @input.check(/
|
102
|
+
elsif @input.check(/"/)
|
98
103
|
@tokens << {
|
99
104
|
:type => :string,
|
100
|
-
:value => eval(@input.scan(/
|
105
|
+
:value => eval(@input.scan(/"([^"\\]|\\.)*"/))
|
101
106
|
}
|
102
|
-
elsif @input.check(/\//)
|
103
|
-
|
107
|
+
elsif @input.check(/\//)
|
108
|
+
# Several things begin with dash:
|
109
|
+
# - comments, regexes, division-operators
|
110
|
+
if @input.check(/\/\*\*/)
|
111
|
+
@tokens << {
|
112
|
+
:type => :doc_comment,
|
113
|
+
# Calculate current line number, starting with 1
|
114
|
+
:linenr => @input.string[0...@input.pos].count("\n") + 1,
|
115
|
+
:value => @input.scan_until(/\*\/|\Z/)
|
116
|
+
}
|
117
|
+
elsif @input.check(/\/\*/)
|
118
|
+
# skip multiline comment
|
119
|
+
@input.scan_until(/\*\/|\Z/)
|
120
|
+
elsif @input.check(/\/\//)
|
121
|
+
# skip line comment
|
122
|
+
@input.scan_until(/\n|\Z/)
|
123
|
+
elsif regex?
|
104
124
|
@tokens << {
|
105
125
|
:type => :regex,
|
106
126
|
:value => @input.scan(/\/([^\/\\]|\\.)*\/[gim]*/)
|
@@ -111,7 +131,20 @@ module JsDuck
|
|
111
131
|
:value => @input.scan(/\//)
|
112
132
|
}
|
113
133
|
end
|
114
|
-
elsif @input.check(
|
134
|
+
elsif @input.check(/[0-9]+/)
|
135
|
+
nr = @input.scan(/[0-9]+(\.[0-9]*)?/)
|
136
|
+
@tokens << {
|
137
|
+
:type => :number,
|
138
|
+
# When number ends with ".", append "0" so Ruby eval will work
|
139
|
+
:value => eval(/\.$/ =~ nr ? nr+"0" : nr)
|
140
|
+
}
|
141
|
+
elsif @input.check(/\$/)
|
142
|
+
value = @input.scan(/\$\w*/)
|
143
|
+
@tokens << {
|
144
|
+
:type => :ident,
|
145
|
+
:value => value
|
146
|
+
}
|
147
|
+
elsif @input.check(/./)
|
115
148
|
@tokens << {
|
116
149
|
:type => :operator,
|
117
150
|
:value => @input.scan(/./)
|
@@ -128,7 +161,7 @@ module JsDuck
|
|
128
161
|
# - closing square-bracket ]
|
129
162
|
# Otherwise it's a beginning of regex
|
130
163
|
def regex?
|
131
|
-
if @tokens.last
|
164
|
+
if @tokens.last
|
132
165
|
type = @tokens.last[:type]
|
133
166
|
value = @tokens.last[:value]
|
134
167
|
if type == :ident || type == :number
|
@@ -142,26 +175,6 @@ module JsDuck
|
|
142
175
|
return true
|
143
176
|
end
|
144
177
|
|
145
|
-
def skip_white_and_comments
|
146
|
-
skip_white
|
147
|
-
while multiline_comment? || line_comment? do
|
148
|
-
if multiline_comment? then
|
149
|
-
@input.scan_until(/\*\/|\Z/)
|
150
|
-
elsif line_comment? then
|
151
|
-
@input.scan_until(/\n|\Z/)
|
152
|
-
end
|
153
|
-
skip_white
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def multiline_comment?
|
158
|
-
@input.check(/\/\*[^*]/)
|
159
|
-
end
|
160
|
-
|
161
|
-
def line_comment?
|
162
|
-
@input.check(/\/\//)
|
163
|
-
end
|
164
|
-
|
165
178
|
def skip_white
|
166
179
|
@input.scan(/\s+/)
|
167
180
|
end
|
data/lib/jsduck/method_table.rb
CHANGED
data/lib/jsduck/page.rb
CHANGED
@@ -9,9 +9,16 @@ module JsDuck
|
|
9
9
|
|
10
10
|
# Creates HTML documentation page for one class.
|
11
11
|
class Page
|
12
|
-
|
12
|
+
# Initializes doc page generator
|
13
|
+
#
|
14
|
+
# - cls : the Class object for which to generate documentation
|
15
|
+
# - subclasses : lookup table for easy access to subclasses
|
16
|
+
# - cache : cache for already generated HTML rows for class members
|
17
|
+
#
|
18
|
+
def initialize(cls, subclasses = {}, cache = {})
|
13
19
|
@cls = cls
|
14
20
|
@subclasses = subclasses
|
21
|
+
@cache = cache
|
15
22
|
@formatter = DocFormatter.new(cls.full_name)
|
16
23
|
end
|
17
24
|
|
@@ -23,10 +30,10 @@ module JsDuck
|
|
23
30
|
abstract,
|
24
31
|
description,
|
25
32
|
"<div class='hr'></div>",
|
26
|
-
CfgTable.new(@cls).to_html,
|
27
|
-
PropertyTable.new(@cls).to_html,
|
28
|
-
MethodTable.new(@cls).to_html,
|
29
|
-
EventTable.new(@cls).to_html,
|
33
|
+
CfgTable.new(@cls, @cache).to_html,
|
34
|
+
PropertyTable.new(@cls, @cache).to_html,
|
35
|
+
MethodTable.new(@cls, @cache).to_html,
|
36
|
+
EventTable.new(@cls, @cache).to_html,
|
30
37
|
"</div>",
|
31
38
|
].join("\n")
|
32
39
|
end
|
data/lib/jsduck/parser.rb
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
require 'jsduck/lexer'
|
2
|
+
require 'jsduck/doc_parser'
|
2
3
|
|
3
4
|
module JsDuck
|
4
5
|
|
5
6
|
class Parser
|
6
7
|
def initialize(input)
|
7
8
|
@lex = Lexer.new(input)
|
9
|
+
@doc_parser = DocParser.new
|
8
10
|
@docs = []
|
9
11
|
end
|
10
12
|
|
11
13
|
# Parses the whole JavaScript block and returns array where for
|
12
14
|
# each doc-comment there is a hash of three values: the comment
|
13
|
-
#
|
14
|
-
# and parsed structure of the code that
|
15
|
-
# comment.
|
15
|
+
# structure created by DocParser, number of the line where the
|
16
|
+
# comment starts, and parsed structure of the code that
|
17
|
+
# immediately follows the comment.
|
16
18
|
#
|
17
19
|
# For example with the following JavaScript input:
|
18
20
|
#
|
@@ -26,7 +28,10 @@ module JsDuck
|
|
26
28
|
#
|
27
29
|
# [
|
28
30
|
# {
|
29
|
-
# :comment =>
|
31
|
+
# :comment => [
|
32
|
+
# {:tagname => :default, :doc => "Method description"},
|
33
|
+
# {:tagname => :return, :type => "Number", :doc => ""},
|
34
|
+
# ],
|
30
35
|
# :linenr => 1,
|
31
36
|
# :code => {
|
32
37
|
# :type => :assignment,
|
@@ -45,10 +50,10 @@ module JsDuck
|
|
45
50
|
#
|
46
51
|
def parse
|
47
52
|
while !@lex.empty? do
|
48
|
-
if look(:doc_comment)
|
53
|
+
if look(:doc_comment)
|
49
54
|
comment = @lex.next(true)
|
50
55
|
@docs << {
|
51
|
-
:comment => comment[:value],
|
56
|
+
:comment => @doc_parser.parse(comment[:value]),
|
52
57
|
:linenr => comment[:linenr],
|
53
58
|
:code => code_block
|
54
59
|
}
|
@@ -64,15 +69,18 @@ module JsDuck
|
|
64
69
|
|
65
70
|
# <code-block> := <function> | <var-declaration> | <assignment> | <property-literal>
|
66
71
|
def code_block
|
67
|
-
if look("function")
|
72
|
+
if look("function")
|
68
73
|
function
|
69
|
-
elsif look("var")
|
74
|
+
elsif look("var")
|
70
75
|
var_declaration
|
71
|
-
elsif look(:ident, ":") || look(:string, ":")
|
76
|
+
elsif look(:ident, ":") || look(:string, ":")
|
72
77
|
property_literal
|
73
|
-
elsif look(:ident) || look("
|
78
|
+
elsif look(",", :ident, ":") || look(",", :string, ":")
|
79
|
+
match(",")
|
80
|
+
property_literal
|
81
|
+
elsif look(:ident) || look("this")
|
74
82
|
maybe_assignment
|
75
|
-
elsif look(:string)
|
83
|
+
elsif look(:string)
|
76
84
|
{:type => :assignment, :left => [match(:string)]}
|
77
85
|
else
|
78
86
|
{:type => :nop}
|
@@ -115,7 +123,7 @@ module JsDuck
|
|
115
123
|
# <maybe-assignment> := <ident-chain> [ "=" <expression> ]
|
116
124
|
def maybe_assignment
|
117
125
|
left = ident_chain
|
118
|
-
if look("=")
|
126
|
+
if look("=")
|
119
127
|
match("=")
|
120
128
|
right = expression
|
121
129
|
end
|
@@ -143,17 +151,17 @@ module JsDuck
|
|
143
151
|
# <expression> := <function> | <ext-extend> | <literal>
|
144
152
|
# <literal> := <string> | <boolean> | <number> | <regex>
|
145
153
|
def expression
|
146
|
-
if look("function")
|
154
|
+
if look("function")
|
147
155
|
function
|
148
|
-
elsif look("Ext", ".", "extend")
|
156
|
+
elsif look("Ext", ".", "extend")
|
149
157
|
ext_extend
|
150
|
-
elsif look(:string)
|
158
|
+
elsif look(:string)
|
151
159
|
{:type => :literal, :class => "String"}
|
152
|
-
elsif look("true") || look("false")
|
160
|
+
elsif look("true") || look("false")
|
153
161
|
{:type => :literal, :class => "Boolean"}
|
154
|
-
elsif look(:number)
|
162
|
+
elsif look(:number)
|
155
163
|
{:type => :literal, :class => "Number"}
|
156
|
-
elsif look(:regex)
|
164
|
+
elsif look(:regex)
|
157
165
|
{:type => :literal, :class => "RegExp"}
|
158
166
|
end
|
159
167
|
end
|
@@ -182,7 +190,7 @@ module JsDuck
|
|
182
190
|
# Matches all arguments, returns the value of last match
|
183
191
|
# When the whole sequence doesn't match, throws exception
|
184
192
|
def match(*args)
|
185
|
-
if look(*args)
|
193
|
+
if look(*args)
|
186
194
|
last = nil
|
187
195
|
args.length.times { last = @lex.next }
|
188
196
|
last
|
data/lib/jsduck/table.rb
CHANGED
@@ -8,8 +8,18 @@ module JsDuck
|
|
8
8
|
# @row_class, and implement the signature_suffix() and extra_doc()
|
9
9
|
# methods.
|
10
10
|
class Table
|
11
|
-
|
11
|
+
# Initializes class member table generator
|
12
|
+
#
|
13
|
+
# - cls : the class for which to generate the table.
|
14
|
+
#
|
15
|
+
# - cache : shared cache of already generated HTML rows. If Foo
|
16
|
+
# inherits from Bar and we have already generated members table
|
17
|
+
# for Bar, then we don't have to re-render all the HTML the
|
18
|
+
# methods from Bar, but can just look them up from cache.
|
19
|
+
#
|
20
|
+
def initialize(cls, cache={})
|
12
21
|
@cls = cls
|
22
|
+
@cache = cache
|
13
23
|
@formatter = DocFormatter.new(cls.full_name)
|
14
24
|
end
|
15
25
|
|
@@ -29,20 +39,39 @@ module JsDuck
|
|
29
39
|
].join("\n")
|
30
40
|
end
|
31
41
|
|
42
|
+
# Returns HTML row for class member.
|
43
|
+
#
|
44
|
+
# When HTML for member has already been rendered we can pick it
|
45
|
+
# from cache and only fill in some details which differ from class
|
46
|
+
# to class.
|
47
|
+
#
|
48
|
+
# Otherwise perform the rendering of HTML and save it to cache.
|
32
49
|
def row(item)
|
50
|
+
cache_key = "#{item[:member]}-#{@row_class}-#{item[:name]}"
|
51
|
+
if @cache[cache_key]
|
52
|
+
html = @cache[cache_key]
|
53
|
+
else
|
54
|
+
html = @cache[cache_key] = create_row(item)
|
55
|
+
end
|
56
|
+
inherited = inherited?(item) ? 'inherited' : ''
|
57
|
+
owner = inherited?(item) ? member_link(item) : Class.short_name(item[:member])
|
58
|
+
html.sub(/!!--inherited--!!/, inherited).sub(/!!--owner-class--!!/, owner)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Generates HTML for the row, leaving in placeholders for owner
|
62
|
+
# class name and inherited class.
|
63
|
+
def create_row(item)
|
33
64
|
p_doc = primary_doc(item)
|
34
65
|
e_doc = extra_doc(item)
|
35
66
|
description = expandable_desc(p_doc, e_doc)
|
36
67
|
expandable = expandable?(p_doc, e_doc) ? 'expandable' : ''
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
"</tr>",
|
45
|
-
].join("")
|
68
|
+
return <<-EOHTML
|
69
|
+
<tr class='#{@row_class} #{expandable} !!--inherited--!!'>
|
70
|
+
<td class='micon'><a href='#expand' class='exi'> </a></td>
|
71
|
+
<td class='sig'>#{signature(item)}<div class='mdesc'>#{description}</div></td>
|
72
|
+
<td class='msource'>!!--owner-class--!!</td>
|
73
|
+
</tr>
|
74
|
+
EOHTML
|
46
75
|
end
|
47
76
|
|
48
77
|
def member_link(item)
|
data/lib/jsduck/timer.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module JsDuck
|
2
|
+
|
3
|
+
# Helper for timing execution of named code blocks.
|
4
|
+
#
|
5
|
+
# timer = Timer.new
|
6
|
+
# a = timer.time(:sum) { 5 + 5 }
|
7
|
+
# b = timer.time(:sum) { 5 + 5 }
|
8
|
+
# c = timer.time(:mult) { 5 * 5 }
|
9
|
+
# d = timer.time(:mult) { 5 * 5 }
|
10
|
+
# timer.report
|
11
|
+
#
|
12
|
+
# The #report method will print sum of the time spent in each
|
13
|
+
# category.
|
14
|
+
#
|
15
|
+
class Timer
|
16
|
+
def initialize
|
17
|
+
@timings = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
# Performs timing of one code block.
|
21
|
+
# Returns the same value that code block returns.
|
22
|
+
def time(name)
|
23
|
+
begin_time = Time.now
|
24
|
+
result = yield
|
25
|
+
interval = Time.now - begin_time
|
26
|
+
if @timings[name]
|
27
|
+
@timings[name] += interval
|
28
|
+
else
|
29
|
+
@timings[name] = interval
|
30
|
+
end
|
31
|
+
result
|
32
|
+
end
|
33
|
+
|
34
|
+
# prints timings report to console
|
35
|
+
def report
|
36
|
+
@timings.each {|name, time| puts "#{name}:\t#{time} seconds" }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsduck
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 13
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: "0.
|
8
|
+
- 3
|
9
|
+
version: "0.3"
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Rene Saarsoo
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-
|
17
|
+
date: 2011-02-08 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -45,6 +45,20 @@ dependencies:
|
|
45
45
|
version: "0"
|
46
46
|
type: :runtime
|
47
47
|
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: parallel
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
type: :runtime
|
61
|
+
version_requirements: *id003
|
48
62
|
description: Better ext-doc like JavaScript documentation generator for ExtJS
|
49
63
|
email: nene@triin.net
|
50
64
|
executables:
|
@@ -79,6 +93,7 @@ files:
|
|
79
93
|
- lib/jsduck/source_formatter.rb
|
80
94
|
- lib/jsduck/subclasses.rb
|
81
95
|
- lib/jsduck/table.rb
|
96
|
+
- lib/jsduck/timer.rb
|
82
97
|
- lib/jsduck/tree.rb
|
83
98
|
- lib/jsduck/tree_icons.rb
|
84
99
|
- template/index.html
|