jsduck 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -117,26 +117,35 @@ JsDuck depends on [json][], [RDiscount][], and [parallel][] plus [RSpec][] for t
117
117
  Usage
118
118
  -----
119
119
 
120
- Just call it from command line with output directory and a list of
121
- JavaScript files:
120
+ Just call it from command line with output directory and a directory
121
+ containing your JavaScript files:
122
122
 
123
- $ jsduck --verbose --output some/dir your/project/*.js
123
+ $ jsduck --verbose --output some/dir your/project/js
124
124
 
125
- To specify a lot of files you should probably create a script that
126
- generates a file list and passes it through `xargs` to `jsduck`.
125
+ The `--verbose` flag creates a lot of output, but at least you will
126
+ see that something is happening.
127
127
 
128
- For example to generate documentation for ExtJS:
128
+ You pass in both directories and JavaScript files. For example to
129
+ generate docs for ExtJS 3, the simplest way is the following:
129
130
 
130
- $ find ext-3.3.1/src/ -name '*.js' | egrep -v 'locale/|test/|adapter/' | xargs jsduck -v -o output/
131
+ $ jsduck -v -o output/ ext-3.3.1/src/
131
132
 
132
- The `--verbose` flag creates a lot of output, but at least you will
133
- see that something is happening.
133
+ But this will give you a bunch of warnings, so you should better
134
+ create a script that takes just the files really needed and passes
135
+ them through `xargs` to `jsduck`:
134
136
 
135
- Here's how the resulting documentation will look:
137
+ $ find ext-3.3.1/src/ -name '*.js' | egrep -v 'locale/|test/|adapter/' | xargs jsduck -v -o output/
138
+
139
+ Here's how the resulting documentation will look (ExtJS 3.3.1):
136
140
 
137
141
  * [JsDuck generated documentation](http://triin.net/temp/jsduck/)
138
142
  * [Official ExtJS documentation](http://dev.sencha.com/deploy/dev/docs/) (for comparison)
139
143
 
144
+ Here's the same for ExtJS 4 Preview 5:
145
+
146
+ * [JsDuck generated documentation](http://triin.net/temp/jsduck4/)
147
+ * [Official ExtJS documentation](http://dev.sencha.com/deploy/ext-4.0-pr5/docs/) (for comparison)
148
+
140
149
 
141
150
  Documentation
142
151
  -------------
@@ -178,8 +187,6 @@ missing.
178
187
  Missing features and TODO
179
188
  -------------------------
180
189
 
181
- * Search, not just searching from official ExtJS documentation.
182
-
183
190
  * Support for custom @tags. Ext-doc supports this, I personally have
184
191
  never used this feature, so I'm thinking it's not really needed.
185
192
 
@@ -189,12 +196,21 @@ Copying
189
196
 
190
197
  JsDuck is distributed under the terms of the GNU General Public License version 3.
191
198
 
192
- JsDuck was developed by [Rene Saarsoo](http://triin.net).
199
+ JsDuck was developed by [Rene Saarsoo](http://triin.net),
200
+ with contributions from [Ondřej Jirman](https://github.com/megous).
193
201
 
194
202
 
195
203
  Changelog
196
204
  ---------
197
205
 
206
+ * 0.5 - Search and export
207
+ * Search from the actually generated docs (not through sencha.com)
208
+ * JSON export with --json switch.
209
+ * Listing of mixed into classes.
210
+ * Option to control or disable parallel processing.
211
+ * Accepting directories as input (those are scanned for .js files)
212
+ * Many bug fixes.
213
+
198
214
  * 0.4 - Ext4 support
199
215
  * Support for Ext.define() syntax from ExtJS 4.
200
216
  * Showing @xtype and @author information on generated pages.
data/Rakefile CHANGED
@@ -3,11 +3,11 @@ require 'rake'
3
3
 
4
4
  $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
5
5
 
6
- require 'spec'
7
- require 'spec/rake/spectask'
8
- Spec::Rake::SpecTask.new(:spec) do |spec|
9
- spec.spec_opts = ["--color"]
10
- spec.spec_files = FileList["spec/**/*_spec.rb"]
6
+ require 'rspec'
7
+ require 'rspec/core/rake_task'
8
+ RSpec::Core::RakeTask.new(:spec) do |spec|
9
+ spec.rspec_opts = ["--color"]
10
+ spec.pattern = "spec/**/*_spec.rb"
11
11
  end
12
12
 
13
13
  desc "Build gem locally"
data/bin/jsduck CHANGED
@@ -24,7 +24,7 @@ app = JsDuck::App.new
24
24
  app.template_dir = File.dirname(File.dirname(__FILE__)) + "/template"
25
25
 
26
26
  opts = OptionParser.new do | opts |
27
- opts.banner = "Usage: jsduck [options] files..."
27
+ opts.banner = "Usage: jsduck [options] files/dirs..."
28
28
 
29
29
  opts.on('-o', '--output=PATH', "Directory to output all this amazing documentation.") do |path|
30
30
  app.output_dir = path
@@ -34,6 +34,17 @@ opts = OptionParser.new do | opts |
34
34
  app.template_dir = path
35
35
  end
36
36
 
37
+ opts.on('--json', "Produces JSON export instead of HTML documentation.") do |path|
38
+ app.export = :json
39
+ end
40
+
41
+ # For debugging it's often useful to set --processes=0 to get deterministic results.
42
+ opts.on('-p', '--processes=COUNT', "The number of parallel processes to use.",
43
+ "Defaults to the number of processors/cores.",
44
+ "Set to 0 to disable parallel processing completely.") do |count|
45
+ app.processes = count.to_i
46
+ end
47
+
37
48
  opts.on('-v', '--verbose', "This will fill up your console.") do
38
49
  app.verbose = true
39
50
  end
@@ -44,7 +55,20 @@ opts = OptionParser.new do | opts |
44
55
  end
45
56
  end
46
57
 
47
- app.input_files = opts.parse!(ARGV)
58
+ js_files = []
59
+ # scan directories for .js files
60
+ opts.parse!(ARGV).each do |fname|
61
+ if File.exists?(fname)
62
+ if File.directory?(fname)
63
+ Dir[fname+"/**/*.js"].each {|f| js_files << f }
64
+ else
65
+ js_files << fname
66
+ end
67
+ else
68
+ puts "Warning: File #{fname} not found"
69
+ end
70
+ end
71
+ app.input_files = js_files
48
72
 
49
73
  if app.input_files.length == 0
50
74
  puts "You should specify some input files, otherwise there's nothing I can do :("
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.4'
6
- s.date = '2011-02-28'
5
+ s.version = '0.5'
6
+ s.date = '2011-03-29'
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"
data/lib/jsduck/app.rb CHANGED
@@ -5,12 +5,14 @@ require 'jsduck/source_formatter'
5
5
  require 'jsduck/class'
6
6
  require 'jsduck/tree'
7
7
  require 'jsduck/tree_icons'
8
- require 'jsduck/subclasses'
8
+ require 'jsduck/members'
9
+ require 'jsduck/relations'
9
10
  require 'jsduck/page'
11
+ require 'jsduck/exporter'
10
12
  require 'jsduck/timer'
13
+ require 'jsduck/parallel_wrap'
11
14
  require 'json'
12
15
  require 'fileutils'
13
- require 'parallel'
14
16
 
15
17
  module JsDuck
16
18
 
@@ -21,32 +23,53 @@ module JsDuck
21
23
  attr_accessor :template_dir
22
24
  attr_accessor :input_files
23
25
  attr_accessor :verbose
26
+ attr_accessor :export
24
27
 
25
28
  def initialize
26
29
  @output_dir = nil
27
30
  @template_dir = nil
28
31
  @input_files = []
29
32
  @verbose = false
33
+ @export = nil
30
34
  @timer = Timer.new
35
+ @parallel = ParallelWrap.new
36
+ end
37
+
38
+ # Sets the nr of parallel processes to use.
39
+ # Set to 0 to disable parallelization completely.
40
+ def processes=(count)
41
+ @parallel = ParallelWrap.new(:in_processes => count)
31
42
  end
32
43
 
33
44
  # Call this after input parameters set
34
45
  def run
35
- copy_template(@template_dir, @output_dir)
46
+ clear_dir(@output_dir)
47
+ if @export
48
+ FileUtils.mkdir(@output_dir)
49
+ init_output_dirs(@output_dir)
50
+ else
51
+ copy_template(@template_dir, @output_dir)
52
+ end
36
53
 
37
54
  parsed_files = @timer.time(:parsing) { parallel_parse(@input_files) }
38
55
  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) }
56
+ relations = @timer.time(:aggregating) { filter_classes(result) }
57
+
58
+ if @export == :json
59
+ @timer.time(:generating) { write_json(@output_dir+"/output", relations) }
60
+ else
61
+ @timer.time(:generating) { write_tree(@output_dir+"/output/tree.js", relations) }
62
+ @timer.time(:generating) { write_members(@output_dir+"/output/members.js", relations) }
63
+ @timer.time(:generating) { write_pages(@output_dir+"/output", relations) }
64
+ end
42
65
 
43
66
  @timer.report if @verbose
44
67
  end
45
68
 
46
69
  # Parses the files in parallel using as many processes as available CPU-s
47
70
  def parallel_parse(filenames)
48
- src = SourceFormatter.new(@output_dir + "/source")
49
- Parallel.map(filenames) do |fname|
71
+ src = SourceFormatter.new(@output_dir + "/source", @export ? :format_pre : :format_page)
72
+ @parallel.map(filenames) do |fname|
50
73
  puts "Parsing #{fname} ..." if @verbose
51
74
  code = IO.read(fname)
52
75
  {
@@ -70,10 +93,10 @@ module JsDuck
70
93
  # Filters out class-documentations, converting them to Class objects.
71
94
  # For each other type, prints a warning message and discards it
72
95
  def filter_classes(docs)
73
- classes = {}
96
+ classes = []
74
97
  docs.each do |d|
75
98
  if d[:tagname] == :class
76
- classes[d[:name]] = Class.new(d, classes)
99
+ classes << Class.new(d)
77
100
  else
78
101
  type = d[:tagname].to_s
79
102
  name = d[:name]
@@ -82,38 +105,64 @@ module JsDuck
82
105
  puts "Warning: Ignoring #{type}: #{name} in #{file} line #{line}"
83
106
  end
84
107
  end
85
- classes.values
108
+ Relations.new(classes)
86
109
  end
87
110
 
88
- # Given array of doc-objects, generates namespace tree and writes in
111
+ # Given all classes, generates namespace tree and writes it
89
112
  # in JSON form into a file.
90
- def write_tree(filename, docs)
91
- tree = Tree.new.create(docs)
113
+ def write_tree(filename, relations)
114
+ tree = Tree.new.create(relations.classes)
92
115
  icons = TreeIcons.new.extract_icons(tree)
93
116
  js = "Docs.classData = " + JSON.generate( tree ) + ";"
94
117
  js += "Docs.icons = " + JSON.generate( icons ) + ";"
95
118
  File.open(filename, 'w') {|f| f.write(js) }
96
119
  end
97
120
 
121
+ # Given all classes, generates members data for search and writes in
122
+ # in JSON form into a file.
123
+ def write_members(filename, relations)
124
+ members = Members.new.create(relations.classes)
125
+ js = "Docs.membersData = " + JSON.generate( {:data => members} ) + ";"
126
+ File.open(filename, 'w') {|f| f.write(js) }
127
+ end
128
+
98
129
  # Writes documentation page for each class
99
130
  # We do it in parallel using as many processes as available CPU-s
100
- def write_pages(path, docs)
101
- subclasses = Subclasses.new(docs)
131
+ def write_pages(path, relations)
102
132
  cache = {}
103
- Parallel.each(docs) do |cls|
133
+ @parallel.each(relations.classes) do |cls|
104
134
  filename = path + "/" + cls[:name] + ".html"
105
135
  puts "Writing to #{filename} ..." if @verbose
106
- html = Page.new(cls, subclasses, cache).to_html
136
+ html = Page.new(cls, relations, cache).to_html
107
137
  File.open(filename, 'w') {|f| f.write(html) }
108
138
  end
109
139
  end
110
140
 
141
+ # Writes JSON export file for each class
142
+ def write_json(path, relations)
143
+ exporter = Exporter.new(relations)
144
+ @parallel.each(relations.classes) do |cls|
145
+ filename = path + "/" + cls[:name] + ".json"
146
+ puts "Writing to #{filename} ..." if @verbose
147
+ hash = exporter.export(cls)
148
+ json = JSON.pretty_generate(hash)
149
+ File.open(filename, 'w') {|f| f.write(json) }
150
+ end
151
+ end
152
+
111
153
  def copy_template(template_dir, dir)
112
154
  puts "Copying template files to #{dir}..." if @verbose
155
+ FileUtils.cp_r(template_dir, dir)
156
+ init_output_dirs(dir)
157
+ end
158
+
159
+ def clear_dir(dir)
113
160
  if File.exists?(dir)
114
161
  FileUtils.rm_r(dir)
115
162
  end
116
- FileUtils.cp_r(template_dir, dir)
163
+ end
164
+
165
+ def init_output_dirs(dir)
117
166
  FileUtils.mkdir(dir + "/output")
118
167
  FileUtils.mkdir(dir + "/source")
119
168
  end
data/lib/jsduck/class.rb CHANGED
@@ -4,9 +4,11 @@ module JsDuck
4
4
  # methods on it. Otherwise it acts like Hash, providing the []
5
5
  # method.
6
6
  class Class
7
- def initialize(doc, classes={})
7
+ attr_accessor :relations
8
+
9
+ def initialize(doc)
8
10
  @doc = doc
9
- @classes = classes
11
+ @relations = nil
10
12
  end
11
13
 
12
14
  def [](key)
@@ -15,13 +17,38 @@ module JsDuck
15
17
 
16
18
  # Returns instance of parent class, or nil if there is none
17
19
  def parent
18
- @doc[:extends] ? @classes[@doc[:extends]] : nil
20
+ @doc[:extends] ? lookup(@doc[:extends]) : nil
21
+ end
22
+
23
+ # Returns array of ancestor classes.
24
+ # Example result when asking ancestors of MyPanel might be:
25
+ #
26
+ # [Ext.util.Observable, Ext.Component, Ext.Panel]
27
+ #
28
+ def superclasses
29
+ p = parent
30
+ p ? p.superclasses + [p] : []
19
31
  end
20
32
 
21
33
  # Returns array of mixin class instances.
22
34
  # Returns empty array if no mixins
23
35
  def mixins
24
- @doc[:mixins] ? @doc[:mixins].collect {|classname| @classes[classname] } : []
36
+ @doc[:mixins] ? @doc[:mixins].collect {|classname| lookup(classname) }.compact : []
37
+ end
38
+
39
+ # Looks up class object by name
40
+ # When not found, prints warning message.
41
+ def lookup(classname)
42
+ if @relations[classname]
43
+ @relations[classname]
44
+ elsif classname != "Object"
45
+ puts "Warning: Class #{classname} not found in #{@doc[:filename]} line #{@doc[:linenr]}"
46
+ end
47
+ end
48
+
49
+ # Returns copy of @doc hash
50
+ def to_hash
51
+ @doc.clone
25
52
  end
26
53
 
27
54
  # Returns true when this class inherits from the specified class.
@@ -5,11 +5,27 @@ module JsDuck
5
5
 
6
6
  # Formats doc-comments
7
7
  class DocFormatter
8
- # Initializes instance to work in context of particular class, so
8
+ # CSS class to add to each link
9
+ attr_accessor :css_class
10
+
11
+ # Template for the href URL.
12
+ # Can contain %cls% which is replaced with actual classname.
13
+ # Also '#' and member name is appended to link if needed
14
+ attr_accessor :url_template
15
+
16
+ # Sets up instance to work in context of particular class, so
9
17
  # that when {@link #blah} is encountered it knows that
10
18
  # Context#blah is meant.
11
- def initialize(context)
12
- @context = context
19
+ attr_accessor :context
20
+
21
+ # Maximum length for text that doesn't get shortened, defaults to 120
22
+ attr_accessor :max_length
23
+
24
+ def initialize
25
+ @context = ""
26
+ @css_class = nil
27
+ @url_template = "%cls%"
28
+ @max_length = 120
13
29
  end
14
30
 
15
31
  # Replaces {@link Class#member link text} in given string with
@@ -17,7 +33,7 @@ module JsDuck
17
33
  # attribute links will also contain ext:cls and ext:member
18
34
  # attributes.
19
35
  def replace(input)
20
- input.gsub(/\{@link +(\S*?)(?: +(.+?))?\}/) do
36
+ input.gsub(/\{@link\s+(\S*?)(?:\s+(.+?))?\}/m) do
21
37
  target = $1
22
38
  text = $2
23
39
  if target =~ /^(.*)#(.*)$/
@@ -28,11 +44,6 @@ module JsDuck
28
44
  member = false
29
45
  end
30
46
 
31
- # Construct link attributes
32
- href = " href=\"output/#{cls}.html" + (member ? "#" + cls + "-" + member : "") + '"'
33
- ext_cls = ' ext:cls="' + cls + '"'
34
- ext_member = member ? ' ext:member="' + member + '"' : ""
35
-
36
47
  # Construct link text
37
48
  if text
38
49
  text = text
@@ -42,10 +53,20 @@ module JsDuck
42
53
  text = cls
43
54
  end
44
55
 
45
- "<a" + href + ext_cls + ext_member + ">" + text + "</a>"
56
+ link(cls, member, text)
46
57
  end
47
58
  end
48
59
 
60
+ # Creates HTML link to class and/or member
61
+ def link(cls, member, label)
62
+ anchor = member ? "#" + member : ""
63
+ url = @url_template.sub(/%cls%/, cls) + anchor
64
+ href = ' href="' + url + '"'
65
+ rel = ' rel="' + cls + anchor + '"'
66
+ cssCls = @css_class ? ' class="' + @css_class + '"' : ''
67
+ "<a" + href + rel + cssCls + ">" + label + "</a>"
68
+ end
69
+
49
70
  # Formats doc-comment for placement into HTML.
50
71
  # Renders it with Markdown-formatter and replaces @link-s.
51
72
  def format(input)
@@ -64,6 +85,36 @@ module JsDuck
64
85
  replace(RDiscount.new(input).to_html)
65
86
  end
66
87
 
88
+ # Shortens text if needed.
89
+ #
90
+ # 116 chars is also where ext-doc makes its cut, but unlike
91
+ # ext-doc we only make the cut when there's more than 120 chars.
92
+ #
93
+ # This way we don't get stupid expansions like:
94
+ #
95
+ # Blah blah blah some text...
96
+ #
97
+ # expanding to:
98
+ #
99
+ # Blah blah blah some text.
100
+ #
101
+ # Ellipsis is only added when input actually gets shortened.
102
+ def shorten(input)
103
+ if too_long?(input)
104
+ strip_tags(input)[0..(@max_length-4)] + "..."
105
+ else
106
+ input
107
+ end
108
+ end
109
+
110
+ # Returns true when input should get shortened.
111
+ def too_long?(input)
112
+ strip_tags(input).length > @max_length
113
+ end
114
+
115
+ def strip_tags(str)
116
+ str.gsub(/<.*?>/, "")
117
+ end
67
118
  end
68
119
 
69
120
  end
@@ -200,7 +200,7 @@ module JsDuck
200
200
  def at_xtype
201
201
  match(/@xtype/)
202
202
  add_tag(:xtype)
203
- maybe_name
203
+ maybe_ident_chain(:name)
204
204
  skip_white
205
205
  end
206
206
 
@@ -0,0 +1,75 @@
1
+ require 'jsduck/doc_formatter'
2
+
3
+ module JsDuck
4
+
5
+ # Export class data as hash with :cfg being replace with :cfgs and
6
+ # including all the inherited members too. Same for :properties,
7
+ # :methods, and :events.
8
+ #
9
+ # Also all the :doc elements will be formatted - converted from
10
+ # markdown to HTML and @links resolved.
11
+ class Exporter
12
+ attr_accessor :relations
13
+
14
+ def initialize(relations)
15
+ @relations = relations
16
+ @formatter = DocFormatter.new
17
+ @formatter.css_class = 'docClass'
18
+ end
19
+
20
+ # Returns all data in Class object as hash.
21
+ def export(cls)
22
+ h = cls.to_hash
23
+ h[:cfgs] = cls.members(:cfg)
24
+ h[:properties] = cls.members(:property)
25
+ h[:methods] = cls.members(:method)
26
+ h[:events] = cls.members(:event)
27
+ h.delete(:cfg)
28
+ h.delete(:property)
29
+ h.delete(:method)
30
+ h.delete(:event)
31
+ h[:component] = cls.inherits_from?("Ext.Component")
32
+ h[:superclasses] = cls.superclasses.collect {|c| c.full_name }
33
+ h[:subclasses] = @relations.subclasses(cls).collect {|c| c.full_name }
34
+ h[:mixedInto] = @relations.mixed_into(cls).collect {|c| c.full_name }
35
+ format_class(h)
36
+ end
37
+
38
+ # converts :doc properties from markdown to html and resolve @links
39
+ def format_class(c)
40
+ @formatter.context = c[:name]
41
+ c[:doc] = @formatter.format(c[:doc]) if c[:doc]
42
+ [:cfgs, :properties, :methods, :events].each do |type|
43
+ c[type] = c[type].map {|m| format_member(m) }
44
+ end
45
+ c
46
+ end
47
+
48
+ def format_member(m)
49
+ m = m.clone
50
+ m[:doc] = @formatter.format(m[:doc]) if m[:doc]
51
+ if m[:params] || @formatter.too_long?(m[:doc])
52
+ m[:shortDoc] = @formatter.shorten(m[:doc])
53
+ end
54
+ m[:params] = format_params(m[:params]) if m[:params]
55
+ m[:return] = format_return(m[:return]) if m[:return]
56
+ m
57
+ end
58
+
59
+ def format_params(params)
60
+ params.map do |p|
61
+ p = p.clone
62
+ p[:doc] = @formatter.format(p[:doc]) if p[:doc]
63
+ p
64
+ end
65
+ end
66
+
67
+ def format_return(r)
68
+ r = r.clone
69
+ r[:doc] = @formatter.format(r[:doc]) if r[:doc]
70
+ r
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -1,15 +1,21 @@
1
+ require 'jsduck/doc_formatter'
2
+
1
3
  module JsDuck
2
4
 
3
5
  # Creates the inheritance tree shown on class documentation page.
4
6
  class InheritanceTree
5
7
  def initialize(cls)
6
8
  @cls = cls
9
+ @formatter = DocFormatter.new
10
+ @formatter.context = cls.full_name
11
+ @formatter.css_class = 'docClass'
12
+ @formatter.url_template = 'output/%cls%.html'
7
13
  end
8
14
 
9
15
  # Renders the tree using HTML <pre> element
10
16
  def to_html
11
17
  i = -1
12
- html = ancestors(@cls).reverse.collect do |cls|
18
+ html = (@cls.superclasses + [@cls]).collect do |cls|
13
19
  i += 1
14
20
  make_indent(i) + make_link(cls)
15
21
  end.join("\n")
@@ -21,16 +27,6 @@ module JsDuck
21
27
  EOHTML
22
28
  end
23
29
 
24
- # Returns array of the names of ancestor classes for given class.
25
- # Including the name of the class itself.
26
- # Example result when ascing ancestors of MyPanel might be:
27
- #
28
- # [MyPanel, Ext.Panel, Ext.Component, Ext.util.Observable]
29
- #
30
- def ancestors(cls)
31
- cls.parent ? [cls] + ancestors(cls.parent) : [cls]
32
- end
33
-
34
30
  def make_indent(level)
35
31
  if level > 0
36
32
  (" " * level) + "<img src='resources/elbow-end.gif' alt=''>"
@@ -43,7 +39,7 @@ module JsDuck
43
39
  if cls == @cls
44
40
  cls.short_name
45
41
  else
46
- "<a href='output/#{cls.full_name}.html' ext:cls='#{cls.full_name}'>#{cls.short_name}</a>"
42
+ @formatter.link(cls.full_name, nil, cls.short_name)
47
43
  end
48
44
  end
49
45
  end
data/lib/jsduck/lexer.rb CHANGED
@@ -97,12 +97,12 @@ module JsDuck
97
97
  elsif @input.check(/'/)
98
98
  @tokens << {
99
99
  :type => :string,
100
- :value => eval(@input.scan(/'([^'\\]|\\.)*'/))
100
+ :value => @input.scan(/'([^'\\]|\\.)*'/).sub(/^'(.*)'$/, "\\1")
101
101
  }
102
102
  elsif @input.check(/"/)
103
103
  @tokens << {
104
104
  :type => :string,
105
- :value => eval(@input.scan(/"([^"\\]|\\.)*"/))
105
+ :value => @input.scan(/"([^"\\]|\\.)*"/).sub(/^"(.*)"$/, "\\1")
106
106
  }
107
107
  elsif @input.check(/\//)
108
108
  # Several things begin with dash:
@@ -6,7 +6,10 @@ module JsDuck
6
6
  # for use in documentation body.
7
7
  class LongParams
8
8
  def initialize(cls)
9
- @formatter = DocFormatter.new(cls.full_name)
9
+ @formatter = DocFormatter.new()
10
+ @formatter.context = cls.full_name
11
+ @formatter.css_class = 'docClass'
12
+ @formatter.url_template = 'output/%cls%.html'
10
13
  end
11
14
 
12
15
  def render(params)
@@ -0,0 +1,57 @@
1
+
2
+ module JsDuck
3
+
4
+ # Creates list of all members in all classes that is used by the
5
+ # searching feature in UI.
6
+ class Members
7
+ # Given list of class documentation objects returns an array of
8
+ # hashes describing all the members.
9
+ def create(docs)
10
+ list = []
11
+ docs.each do |cls|
12
+ [:cfg, :property, :method, :event].each do |type|
13
+ cls.members(type).each do |m|
14
+ list << member_node(m, cls)
15
+ end
16
+ end
17
+ end
18
+ list
19
+ end
20
+
21
+ # Creates structure representing one member
22
+ def member_node(member, cls)
23
+ return {
24
+ :cls => cls.full_name,
25
+ :member => member[:name],
26
+ :type => member[:tagname],
27
+ :doc => short_desc(member[:doc])
28
+ }
29
+ end
30
+
31
+ def short_desc(str)
32
+ tagless = first_sentence(strip_tags(strip_links(str)))
33
+ if tagless.length > 120
34
+ short_doc = tagless[0..116]
35
+ ellipsis = tagless.length > short_doc.length ? "..." : ""
36
+ tagless[0..116] + ellipsis
37
+ else
38
+ tagless
39
+ end
40
+ end
41
+
42
+ def strip_tags(str)
43
+ str.gsub(/<.*?>/, "")
44
+ end
45
+
46
+ def strip_links(str)
47
+ str = str.gsub(/\{@link +(\S*?)(?: +(.+?))?\}/, "\\1")
48
+ str = str.gsub(/#/, ".")
49
+ end
50
+
51
+ def first_sentence(str)
52
+ str.sub(/\A(.+?\.)\s.*\Z/m, "\\1")
53
+ end
54
+
55
+ end
56
+
57
+ end
data/lib/jsduck/merger.rb CHANGED
@@ -248,10 +248,13 @@ module JsDuck
248
248
  [implicit.length, explicit.length].max.times do |i|
249
249
  im = implicit[i] || {}
250
250
  ex = explicit[i] || {}
251
+ doc = ex[:doc] || im[:doc] || ""
251
252
  params << {
252
253
  :type => ex[:type] || im[:type] || "Object",
253
254
  :name => ex[:name] || im[:name] || "",
254
- :doc => ex[:doc] || im[:doc] || "",
255
+ :doc => doc,
256
+ # convert to boolean for JavaScript export, otherwise it's 0 or nil
257
+ :optional => !!(doc =~ /\(optional\)/),
255
258
  }
256
259
  end
257
260
  params
@@ -14,6 +14,10 @@ module JsDuck
14
14
  @row_class = "method-row"
15
15
  @short_params = ShortParams.new
16
16
  @long_params = LongParams.new(@cls)
17
+ @formatter = DocFormatter.new()
18
+ @formatter.context = @cls.full_name
19
+ @formatter.css_class = 'docClass'
20
+ @formatter.url_template = 'output/%cls%.html'
17
21
  end
18
22
 
19
23
  def signature_suffix(item)
@@ -33,7 +37,7 @@ module JsDuck
33
37
 
34
38
  def render_return(item)
35
39
  type = item[:return][:type]
36
- doc = item[:return][:doc]
40
+ doc = @formatter.format(item[:return][:doc])
37
41
  if type == "void" && doc.length == 0
38
42
  "<ul><li>void</li></ul>"
39
43
  else
data/lib/jsduck/page.rb CHANGED
@@ -12,14 +12,17 @@ module JsDuck
12
12
  # Initializes doc page generator
13
13
  #
14
14
  # - cls : the Class object for which to generate documentation
15
- # - subclasses : lookup table for easy access to subclasses
15
+ # - relations : access to subclasses, mixins, etc
16
16
  # - cache : cache for already generated HTML rows for class members
17
17
  #
18
- def initialize(cls, subclasses = {}, cache = {})
18
+ def initialize(cls, relations, cache = {})
19
19
  @cls = cls
20
- @subclasses = subclasses
20
+ @relations = relations
21
21
  @cache = cache
22
- @formatter = DocFormatter.new(cls.full_name)
22
+ @formatter = DocFormatter.new
23
+ @formatter.context = cls.full_name
24
+ @formatter.css_class = 'docClass'
25
+ @formatter.url_template = 'output/%cls%.html'
23
26
  end
24
27
 
25
28
  def to_html
@@ -50,36 +53,49 @@ module JsDuck
50
53
  def abstract
51
54
  [
52
55
  "<table cellspacing='0'>",
53
- abstract_row("Extends:", @cls.parent ? class_link(@cls.parent.full_name) : "Object"),
54
- @cls.mixins.length > 0 ? abstract_row("Mixins:", mixins) : "",
55
- abstract_row("Defind In:", file_link),
56
- @subclasses[@cls] ? abstract_row("Subclasses:", subclasses) : "",
57
- @cls[:xtype] ? abstract_row("xtype:", @cls[:xtype]) : "",
58
- @cls[:author] ? abstract_row("Author:", @cls[:author]) : "",
56
+ row("Extends:", extends_link),
57
+ classes_row("Mixins:", @cls.mixins),
58
+ row("Defind In:", file_link),
59
+ classes_row("Subclasses:", @relations.subclasses(@cls)),
60
+ classes_row("Mixed into:", @relations.mixed_into(@cls)),
61
+ boolean_row("xtype:", @cls[:xtype]),
62
+ boolean_row("Author:", @cls[:author]),
59
63
  "</table>",
60
64
  ].join("\n")
61
65
  end
62
66
 
63
67
  def class_link(class_name, label=nil)
64
68
  label = label || class_name
65
- "<a href='output/#{class_name}.html' ext:cls='#{class_name}'>#{label}</a>"
69
+ @formatter.link(class_name, nil, label || class_name)
66
70
  end
67
71
 
68
72
  def file_link
69
73
  "<a href='source/#{@cls[:href]}'>#{File.basename(@cls[:filename])}</a>"
70
74
  end
71
75
 
72
- def subclasses
73
- subs = @subclasses[@cls].sort {|a, b| a.short_name <=> b.short_name }
74
- subs.collect {|cls| class_link(cls.full_name, cls.short_name) }.join(", ")
76
+ def extends_link
77
+ if @cls[:extends]
78
+ @relations[@cls[:extends]] ? class_link(@cls[:extends]) : @cls[:extends]
79
+ else
80
+ "Object"
81
+ end
75
82
  end
76
83
 
77
- def mixins
78
- mixs = @cls.mixins.sort {|a, b| a.full_name <=> b.full_name }
79
- mixs.collect {|cls| class_link(cls.full_name, cls.short_name) }.join(", ")
84
+ def classes_row(label, classes)
85
+ if classes.length > 0
86
+ classes = classes.sort {|a, b| a.short_name <=> b.short_name }
87
+ html = classes.collect {|cls| class_link(cls.full_name, cls.short_name) }.join(", ")
88
+ row(label, html)
89
+ else
90
+ ""
91
+ end
80
92
  end
81
93
 
82
- def abstract_row(label, info)
94
+ def boolean_row(label, item)
95
+ item ? row(label, item) : ""
96
+ end
97
+
98
+ def row(label, info)
83
99
  "<tr><td class='label'>#{label}</td><td class='hd-info'>#{info}</td></tr>"
84
100
  end
85
101
 
@@ -0,0 +1,31 @@
1
+ require 'parallel'
2
+
3
+ module JsDuck
4
+
5
+ # Wrapper around the parallel gem that falls back to simple
6
+ # Array#map and Array#each when :in_processes => 0 specified.
7
+ class ParallelWrap
8
+
9
+ # Takes config object for parallel
10
+ def initialize(cfg = {})
11
+ @cfg = cfg
12
+ end
13
+
14
+ def each(arr, &block)
15
+ if @cfg[:in_processes] == 0
16
+ arr.each &block
17
+ else
18
+ Parallel.each(arr, @cfg, &block)
19
+ end
20
+ end
21
+
22
+ def map(arr, &block)
23
+ if @cfg[:in_processes] == 0
24
+ arr.map &block
25
+ else
26
+ Parallel.map(arr, @cfg, &block)
27
+ end
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,68 @@
1
+ module JsDuck
2
+
3
+ # Provides information about relations between classes.
4
+ #
5
+ # Also provides a place to look up classes by name.
6
+ #
7
+ # The constructor is initialized with array of all available
8
+ # classes.
9
+ class Relations
10
+ # Returns list of all classes
11
+ attr_reader :classes
12
+
13
+ def initialize(classes = [])
14
+ @classes = classes
15
+
16
+ # First build class lookup table; building lookup tables for
17
+ # mixins and subclasses will depend on that.
18
+ @lookup = {}
19
+ @classes.each do |cls|
20
+ @lookup[cls.full_name] = cls
21
+ cls.relations = self
22
+ end
23
+
24
+ @subs = {}
25
+ @mixes = {}
26
+ @classes.each do |cls|
27
+ reg_subclasses(cls)
28
+ reg_mixed_into(cls)
29
+ end
30
+ end
31
+
32
+ # Looks up class by name, nil if not found
33
+ def [](classname)
34
+ @lookup[classname]
35
+ end
36
+
37
+ def reg_subclasses(cls)
38
+ if !cls.parent
39
+ # do nothing
40
+ elsif @subs[cls.parent.full_name]
41
+ @subs[cls.parent.full_name] << cls
42
+ else
43
+ @subs[cls.parent.full_name] = [cls]
44
+ end
45
+ end
46
+
47
+ # Returns subclasses of particular class, empty array if none
48
+ def subclasses(cls)
49
+ @subs[cls.full_name] || []
50
+ end
51
+
52
+ def reg_mixed_into(cls)
53
+ cls.mixins.each do |mix|
54
+ if @mixes[mix.full_name]
55
+ @mixes[mix.full_name] << cls
56
+ else
57
+ @mixes[mix.full_name] = [cls]
58
+ end
59
+ end
60
+ end
61
+
62
+ # Returns classes having particular mixin, empty array if none
63
+ def mixed_into(cls)
64
+ @mixes[cls.full_name] || []
65
+ end
66
+ end
67
+
68
+ end
@@ -13,16 +13,12 @@ module JsDuck
13
13
 
14
14
  def render_single(param)
15
15
  str = "<code>#{param[:type]} #{param[:name]}</code>"
16
- if optional?(param)
16
+ if param[:optional]
17
17
  "<span title='Optional' class='optional'>[" + str + "]</span>"
18
18
  else
19
19
  str
20
20
  end
21
21
  end
22
-
23
- def optional?(param)
24
- return param[:doc] =~ /\(optional\)/
25
- end
26
22
  end
27
23
 
28
24
  end
@@ -8,16 +8,21 @@ module JsDuck
8
8
  class SourceFormatter
9
9
 
10
10
  # Initializes SourceFormatter to the directory where
11
- # HTML-formatted source files will be placed
12
- def initialize(output_dir)
11
+ # HTML-formatted source files will be placed.
12
+ #
13
+ # formatter can be either :format_page or :format_pre; with the
14
+ # first one the whole HTML page is created, otherwise just a
15
+ # contents of <pre> element.
16
+ def initialize(output_dir, formatter = :format_page)
13
17
  @output_dir = output_dir
18
+ @formatter = formatter
14
19
  end
15
20
 
16
21
  # Converts source to HTML and writes into file in output
17
22
  # directory. It returns the name of the file that it wrote.
18
23
  def write(source, filename)
19
24
  fname = uniq_html_filename(filename)
20
- File.open(fname, 'w') {|f| f.write(format(source)) }
25
+ File.open(fname, 'w') {|f| f.write(self.send(@formatter, source)) }
21
26
  fname
22
27
  end
23
28
 
@@ -36,7 +41,7 @@ module JsDuck
36
41
  end
37
42
 
38
43
  # Returns full source for HTML page
39
- def format(source)
44
+ def format_page(source)
40
45
  return <<-EOHTML
41
46
  <!DOCTYPE html>
42
47
  <html>
data/lib/jsduck/table.rb CHANGED
@@ -20,7 +20,10 @@ module JsDuck
20
20
  def initialize(cls, cache={})
21
21
  @cls = cls
22
22
  @cache = cache
23
- @formatter = DocFormatter.new(cls.full_name)
23
+ @formatter = DocFormatter.new
24
+ @formatter.context = cls.full_name
25
+ @formatter.css_class = 'docClass'
26
+ @formatter.url_template = 'output/%cls%.html'
24
27
  end
25
28
 
26
29
  def to_html
@@ -75,11 +78,7 @@ module JsDuck
75
78
  end
76
79
 
77
80
  def member_link(item)
78
- cls = item[:member]
79
- member = item[:name]
80
- "<a href='output/#{cls}.html##{member}' " +
81
- "ext:member='##{member}' " +
82
- "ext:cls='#{cls}'>#{Class.short_name(cls)}</a>"
81
+ @formatter.link(item[:member], item[:name], Class.short_name(item[:member]))
83
82
  end
84
83
 
85
84
  def signature(item)
@@ -88,24 +87,11 @@ module JsDuck
88
87
  return "<a id='#{id}'></a><b><a href='#{src}'>#{item[:name]}</a></b>" + signature_suffix(item)
89
88
  end
90
89
 
91
- # 116 chars is also where ext-doc makes its cut, but unlike
92
- # ext-doc we only make the cut when there's more than 120 chars.
93
- #
94
- # This way we don't get stupid expansions like:
95
- #
96
- # Blah blah blah some text...
97
- #
98
- # expanding to:
99
- #
100
- # Blah blah blah some text.
101
- #
90
+ # Creates either expandable or normal doc-entry
102
91
  def expandable_desc(p_doc, e_doc)
103
92
  if expandable?(p_doc, e_doc)
104
- # Only show ellipsis when primary_doc is shortened.
105
- tagless = strip_tags(p_doc)
106
- short_doc = tagless[0..116]
107
- ellipsis = tagless.length > short_doc.length ? "..." : ""
108
- "<div class='short'>#{short_doc}#{ellipsis}</div>" +
93
+ short_doc = @formatter.shorten(p_doc)
94
+ "<div class='short'>#{short_doc}</div>" +
109
95
  "<div class='long'>#{p_doc}#{e_doc}</div>"
110
96
  else
111
97
  p_doc
@@ -122,11 +108,7 @@ module JsDuck
122
108
  end
123
109
 
124
110
  def expandable?(p_doc, e_doc)
125
- strip_tags(p_doc).length > 120 || e_doc.length > 0
126
- end
127
-
128
- def strip_tags(str)
129
- str.gsub(/<.*?>/, "")
111
+ @formatter.too_long?(p_doc) || e_doc.length > 0
130
112
  end
131
113
 
132
114
  def inherited?(item)
data/template/index.html CHANGED
@@ -16,6 +16,7 @@
16
16
  <script type="text/javascript" src="resources/prettify/prettify.js"></script>
17
17
  <script type="text/javascript" src="resources/docs.js"></script>
18
18
  <script type="text/javascript" src="output/tree.js"></script>
19
+ <script type="text/javascript" src="output/members.js"></script>
19
20
  </head>
20
21
  <body scroll="no" id="docs">
21
22
 
@@ -250,10 +250,12 @@ a:hover {
250
250
  .icon-event {
251
251
  background-image: url(event.gif) !important;
252
252
  }
253
+ .icon-cfg,
253
254
  .icon-config {
254
255
  background-image: url(config.gif) !important;
255
256
  }
256
- .icon-prop {
257
+ .icon-prop,
258
+ .icon-property {
257
259
  background-image: url(prop.gif) !important;
258
260
  }
259
261
  .icon-method {
@@ -75,7 +75,7 @@ Ext.extend(ApiPanel, Ext.tree.TreePanel, {
75
75
  handler: function(){ this.root.collapse(true); },
76
76
  scope: this
77
77
  }]
78
- })
78
+ });
79
79
  ApiPanel.superclass.initComponent.call(this);
80
80
  },
81
81
  filterTree: function(t, e){
@@ -226,21 +226,20 @@ DocPanel = Ext.extend(Ext.Panel, {
226
226
  MainPanel = function(){
227
227
 
228
228
  this.searchStore = new Ext.data.Store({
229
- proxy: new Ext.data.ScriptTagProxy({
230
- url: 'http://extjs.com/playpen/api.php'
231
- }),
232
- reader: new Ext.data.JsonReader({
233
- root: 'data'
234
- },
235
- ['cls', 'member', 'type', 'doc']
236
- ),
237
- baseParams: {},
238
- listeners: {
239
- 'beforeload' : function(){
240
- this.baseParams.qt = Ext.getCmp('search-type').getValue();
241
- }
242
- }
243
- });
229
+ sortInfo: {
230
+ field: 'member',
231
+ direction: 'ASC'
232
+ },
233
+ reader: new Ext.data.JsonReader({
234
+ root: 'data',
235
+ fields: ['cls', 'member', 'type', 'doc']
236
+ })
237
+ });
238
+ this.searchStore.loadData(Docs.membersData);
239
+ this.searchStore.filterBy(function(r) {
240
+ return false;
241
+ });
242
+
244
243
 
245
244
  MainPanel.superclass.constructor.call(this, {
246
245
  id:'doc-body',
@@ -256,7 +255,7 @@ MainPanel = function(){
256
255
  items: {
257
256
  id:'welcome-panel',
258
257
  title: 'API Home',
259
- autoLoad: {url: 'welcome.html', callback: this.initSearch, scope: this},
258
+ autoLoad: {url: 'welcome.html', callback: this.initSearch, scope: this, delay: 100},
260
259
  iconCls:'icon-docs',
261
260
  autoScroll: true,
262
261
  tbar: [
@@ -291,18 +290,17 @@ Ext.extend(MainPanel, Ext.TabPanel, {
291
290
  },
292
291
 
293
292
  onClick: function(e, target){
294
- if(target = e.getTarget('a:not(.exi)', 3)){
295
- var cls = Ext.fly(target).getAttributeNS('ext', 'cls');
293
+ if((target = e.getTarget('a:not(.exi)', 3))){
296
294
  e.stopEvent();
297
- if(cls){
298
- var member = Ext.fly(target).getAttributeNS('ext', 'member');
299
- this.loadClass(target.href, cls, member);
295
+ if(/\bdocClass\b/.test(target.className)){
296
+ var m = target.rel.split("#");
297
+ this.loadClass(target.href, m[0], m[1]);
300
298
  }else if(target.className == 'inner-link'){
301
299
  this.getActiveTab().scrollToSection(target.href.split('#')[1]);
302
300
  }else{
303
301
  window.open(target.href);
304
302
  }
305
- }else if(target = e.getTarget('.micon', 2)){
303
+ }else if((target = e.getTarget('.micon', 2))){
306
304
  e.stopEvent();
307
305
  var tr = Ext.fly(target.parentNode);
308
306
  if(tr.hasClass('expandable')){
@@ -343,17 +341,18 @@ Ext.extend(MainPanel, Ext.TabPanel, {
343
341
  var resultTpl = new Ext.XTemplate(
344
342
  '<tpl for=".">',
345
343
  '<div class="search-item">',
346
- '<a class="member" ext:cls="{cls}" ext:member="{member}" href="output/{cls}.html">',
347
- '<img src="../resources/images/default/s.gif" class="item-icon icon-{type}"/>{member}',
344
+ '<a class="member docClass" rel="{cls}#{member}" href="output/{cls}.html">',
345
+ '<img src="resources/images/default/s.gif" class="item-icon icon-{type}"/>{member}',
348
346
  '</a> ',
349
- '<a class="cls" ext:cls="{cls}" href="output/{cls}.html">{cls}</a>',
347
+ '<a class="cls docClass" rel="{cls}" href="output/{cls}.html">{cls}</a>',
350
348
  '<p>{doc}</p>',
351
349
  '</div></tpl>'
352
350
  );
353
-
351
+
354
352
  var p = new Ext.DataView({
355
- applyTo: 'search',
353
+ applyTo: Ext.get('search') ? 'search' : Ext.getCmp('welcome-panel').body,
356
354
  tpl: resultTpl,
355
+ deferEmptyText: false,
357
356
  loadingText:'Searching...',
358
357
  store: this.searchStore,
359
358
  itemSelector: 'div.search-item',
@@ -452,12 +451,13 @@ Ext.app.SearchField = Ext.extend(Ext.form.TwinTriggerField, {
452
451
 
453
452
  onTrigger1Click : function(){
454
453
  if(this.hasSearch){
455
- this.store.baseParams[this.paramName] = '';
456
- this.store.removeAll();
457
- this.el.dom.value = '';
454
+ this.store.filterBy(function(r) {
455
+ return false;
456
+ });
457
+ this.el.dom.value = '';
458
458
  this.triggers[0].hide();
459
459
  this.hasSearch = false;
460
- this.focus();
460
+ this.focus();
461
461
  }
462
462
  },
463
463
 
@@ -471,12 +471,24 @@ Ext.app.SearchField = Ext.extend(Ext.form.TwinTriggerField, {
471
471
  Ext.Msg.alert('Invalid Search', 'You must enter a minimum of 2 characters to search the API');
472
472
  return;
473
473
  }
474
- this.store.baseParams[this.paramName] = v;
475
- var o = {start: 0};
476
- this.store.reload({params:o});
474
+
475
+ var type = Ext.getCmp('search-type').getValue();
476
+ this.store.filter("member", this.createRegex(type, v));
477
+
477
478
  this.hasSearch = true;
478
479
  this.triggers[0].show();
479
480
  this.focus();
481
+ },
482
+
483
+ createRegex: function(type, text) {
484
+ var safeText = Ext.escapeRe(text);
485
+ if (type === 'Starts with') {
486
+ return new RegExp("^" + safeText, "i");
487
+ } else if (type === 'Ends with') {
488
+ return new RegExp(safeText + "$", "i");
489
+ } else {
490
+ return new RegExp(safeText, "i");
491
+ }
480
492
  }
481
493
  });
482
494
 
@@ -548,6 +560,8 @@ Ext.extend(Ext.ux.SelectBox, Ext.form.ComboBox, {
548
560
  this.selectPrevPage();
549
561
  e.stopEvent();
550
562
  return;
563
+ default:
564
+ break;
551
565
  }
552
566
 
553
567
  // skip special keys other than the shift key
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: 3
4
+ hash: 1
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 4
9
- version: "0.4"
8
+ - 5
9
+ version: "0.5"
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-02-28 00:00:00 +02:00
17
+ date: 2011-03-29 00:00:00 +03:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -81,17 +81,20 @@ files:
81
81
  - lib/jsduck/doc_formatter.rb
82
82
  - lib/jsduck/doc_parser.rb
83
83
  - lib/jsduck/event_table.rb
84
+ - lib/jsduck/exporter.rb
84
85
  - lib/jsduck/inheritance_tree.rb
85
86
  - lib/jsduck/lexer.rb
86
87
  - lib/jsduck/long_params.rb
88
+ - lib/jsduck/members.rb
87
89
  - lib/jsduck/merger.rb
88
90
  - lib/jsduck/method_table.rb
89
91
  - lib/jsduck/page.rb
92
+ - lib/jsduck/parallel_wrap.rb
90
93
  - lib/jsduck/parser.rb
91
94
  - lib/jsduck/property_table.rb
95
+ - lib/jsduck/relations.rb
92
96
  - lib/jsduck/short_params.rb
93
97
  - lib/jsduck/source_formatter.rb
94
- - lib/jsduck/subclasses.rb
95
98
  - lib/jsduck/table.rb
96
99
  - lib/jsduck/timer.rb
97
100
  - lib/jsduck/tree.rb
@@ -1,27 +0,0 @@
1
- module JsDuck
2
-
3
- # Provides information about direct descendants of particular class.
4
- #
5
- # The constructor is initialized with array of all available
6
- # classes. Then through [] method subclasses of particlular class
7
- # can be asked for.
8
- class Subclasses
9
- def initialize(classes)
10
- @subs = {}
11
- classes.each do |cls|
12
- if !cls.parent
13
- # do nothing
14
- elsif @subs[cls.parent.full_name]
15
- @subs[cls.parent.full_name] << cls
16
- else
17
- @subs[cls.parent.full_name] = [cls]
18
- end
19
- end
20
- end
21
-
22
- def [](cls)
23
- @subs[cls.full_name]
24
- end
25
- end
26
-
27
- end