jsduck 0.2 → 0.3

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.
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 [RDiscount][] plus [RSpec][] for tests.
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.2'
6
- s.date = '2011-01-10'
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 {|file| file !~ /spec.rb$/ }
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
@@ -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
- # Parses chunk of JavaScript. The resulting documentation is
20
- # accumulated inside this class and can be later accessed through
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 the JavaScript source
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 parse(input, filename="", html_filename="")
24
+ def aggregate(input, filename="", html_filename="")
28
25
  @current_class = nil
29
- Parser.new(input).parse.each do |docset|
30
- doc = @doc_parser.parse(docset[:comment])
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
- classes = filter_classes(parse_files(@input_files))
33
- write_tree(@output_dir+"/output/tree.js", classes)
34
- write_pages(@output_dir+"/output", classes)
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
- # Given array of filenames, parses all files and returns array of
38
- # documented items in all of those files.
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.each do |fname|
49
+ Parallel.map(filenames) do |fname|
43
50
  puts "Parsing #{fname} ..." if @verbose
44
51
  code = IO.read(fname)
45
- src_fname = src.write(code, fname)
46
- agr.parse(code, File.basename(fname), File.basename(src_fname))
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
- docs.each do |cls|
98
+ cache = {}
99
+ Parallel.each(docs) do |cls|
79
100
  filename = path + "/" + cls[:name] + ".html"
80
101
  puts "Writing to #{filename} ..." if @verbose
81
- File.open(filename, 'w') {|f| f.write( Page.new(cls, subclasses).to_html ) }
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
 
@@ -3,8 +3,8 @@ require 'jsduck/table'
3
3
  module JsDuck
4
4
 
5
5
  class CfgTable < Table
6
- def initialize(cls)
7
- super(cls)
6
+ def initialize(cls, cache={})
7
+ super(cls, cache)
8
8
  @type = :cfg
9
9
  @id = @cls.full_name + "-configs"
10
10
  @title = "Config Options"
@@ -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/ then
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/) then
67
+ if look(/@class\b/)
68
68
  at_class
69
- elsif look(/@extends\b/) then
69
+ elsif look(/@extends\b/)
70
70
  at_extends
71
- elsif look(/@singleton\b/) then
71
+ elsif look(/@singleton\b/)
72
72
  boolean_at_tag(/@singleton/, :singleton)
73
- elsif look(/@event\b/) then
73
+ elsif look(/@event\b/)
74
74
  at_event
75
- elsif look(/@method\b/) then
75
+ elsif look(/@method\b/)
76
76
  at_method
77
- elsif look(/@constructor\b/) then
77
+ elsif look(/@constructor\b/)
78
78
  boolean_at_tag(/@constructor/, :constructor)
79
- elsif look(/@param\b/) then
79
+ elsif look(/@param\b/)
80
80
  at_param
81
- elsif look(/@returns?\b/) then
81
+ elsif look(/@returns?\b/)
82
82
  at_return
83
- elsif look(/@cfg\b/) then
83
+ elsif look(/@cfg\b/)
84
84
  at_cfg
85
- elsif look(/@property\b/) then
85
+ elsif look(/@property\b/)
86
86
  at_property
87
- elsif look(/@type\b/) then
87
+ elsif look(/@type\b/)
88
88
  at_type
89
- elsif look(/@xtype\b/) then
89
+ elsif look(/@xtype\b/)
90
90
  at_xtype
91
- elsif look(/@member\b/) then
91
+ elsif look(/@member\b/)
92
92
  at_member
93
- elsif look(/@static\b/) then
93
+ elsif look(/@static\b/)
94
94
  boolean_at_tag(/@static/, :static)
95
- elsif look(/@(private|ignore|hide|protected)\b/) then
95
+ elsif look(/@(private|ignore|hide|protected)\b/)
96
96
  boolean_at_tag(/@(private|ignore|hide|protected)/, :private)
97
- elsif look(/@/) then
97
+ elsif look(/@/)
98
98
  @current_tag[:doc] += @input.scan(/@/)
99
- elsif look(/[^@]/) then
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(/\{/) then
186
+ if look(/\{/)
187
187
  @current_tag[:type] = typedef
188
- elsif look(/\S/) then
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(/\{/) then
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/) then
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/) then
236
+ if look(/\w/)
237
237
  @current_tag[propname] = ident_chain
238
238
  end
239
239
  end
@@ -5,8 +5,8 @@ require 'jsduck/long_params'
5
5
  module JsDuck
6
6
 
7
7
  class EventTable < Table
8
- def initialize(cls)
9
- super(cls)
8
+ def initialize(cls, cache={})
9
+ super(cls, cache)
10
10
  @type = :event
11
11
  @id = @cls.full_name + "-events"
12
12
  @title = "Public Events"
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) then
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
- skip_white_and_comments
74
- if @input.check(/[0-9]+/) then
85
+ skip_white
86
+ if @input.check(/[.(),;={}:]/)
75
87
  @tokens << {
76
- :type => :number,
77
- :value => eval(@input.scan(/[0-9]+(\.[0-9]*)?/))
88
+ :type => :operator,
89
+ :value => @input.scan(/./)
78
90
  }
79
- elsif @input.check(/\w+/) then
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(/\/\*\*/) then
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(/'/) then
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(/\//) then
103
- if regex? then
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(/./) then
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 then
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
@@ -5,8 +5,8 @@ require 'jsduck/long_params'
5
5
  module JsDuck
6
6
 
7
7
  class MethodTable < Table
8
- def initialize(cls)
9
- super(cls)
8
+ def initialize(cls, cache={})
9
+ super(cls, cache)
10
10
  @type = :method
11
11
  @id = @cls.full_name + "-methods"
12
12
  @title = "Public Methods"
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
- def initialize(cls, subclasses = {})
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
- # itself as string, number of the line where the comment starts,
14
- # and parsed structure of the code that immediately follows the
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 => "/**\n * @param {String} foo\n */",
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) then
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") then
72
+ if look("function")
68
73
  function
69
- elsif look("var") then
74
+ elsif look("var")
70
75
  var_declaration
71
- elsif look(:ident, ":") || look(:string, ":") then
76
+ elsif look(:ident, ":") || look(:string, ":")
72
77
  property_literal
73
- elsif look(:ident) || look("this") then
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) then
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("=") then
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") then
154
+ if look("function")
147
155
  function
148
- elsif look("Ext", ".", "extend") then
156
+ elsif look("Ext", ".", "extend")
149
157
  ext_extend
150
- elsif look(:string) then
158
+ elsif look(:string)
151
159
  {:type => :literal, :class => "String"}
152
- elsif look("true") || look("false") then
160
+ elsif look("true") || look("false")
153
161
  {:type => :literal, :class => "Boolean"}
154
- elsif look(:number) then
162
+ elsif look(:number)
155
163
  {:type => :literal, :class => "Number"}
156
- elsif look(:regex) then
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) then
193
+ if look(*args)
186
194
  last = nil
187
195
  args.length.times { last = @lex.next }
188
196
  last
@@ -3,8 +3,8 @@ require 'jsduck/table'
3
3
  module JsDuck
4
4
 
5
5
  class PropertyTable < Table
6
- def initialize(cls)
7
- super(cls)
6
+ def initialize(cls, cache={})
7
+ super(cls, cache)
8
8
  @type = :property
9
9
  @id = @cls.full_name + "-props"
10
10
  @title = "Public Properties"
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
- def initialize(cls)
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
- inherited = inherited?(item) ? 'inherited' : ''
38
- source = inherited?(item) ? member_link(item) : Class.short_name(item[:member])
39
- [
40
- "<tr class='#{@row_class} #{expandable} #{inherited}'>",
41
- "<td class='micon'><a href='#expand' class='exi'>&nbsp;</a></td>",
42
- "<td class='sig'>#{signature(item)}<div class='mdesc'>#{description}</div></td>",
43
- "<td class='msource'>#{source}</td>",
44
- "</tr>",
45
- ].join("")
68
+ return <<-EOHTML
69
+ <tr class='#{@row_class} #{expandable} !!--inherited--!!'>
70
+ <td class='micon'><a href='#expand' class='exi'>&nbsp;</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)
@@ -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: 15
4
+ hash: 13
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
9
- version: "0.2"
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-01-10 00:00:00 +02:00
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