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 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