jsduck 4.10.4 → 5.0.0.beta01

Sign up to get free protection for your applications and to get access to all the features.
Files changed (183) hide show
  1. data/.travis.yml +0 -1
  2. data/README.md +32 -6
  3. data/Rakefile +10 -18
  4. data/bin/compare +5 -5
  5. data/bin/jsduck +2 -3
  6. data/jsduck.gemspec +3 -4
  7. data/lib/jsduck/aggregator.rb +21 -80
  8. data/lib/jsduck/app.rb +7 -14
  9. data/lib/jsduck/app_data.rb +4 -5
  10. data/lib/jsduck/assets.rb +4 -7
  11. data/lib/jsduck/base_type.rb +53 -0
  12. data/lib/jsduck/batch_parser.rb +8 -87
  13. data/lib/jsduck/batch_processor.rb +77 -0
  14. data/lib/jsduck/categories/auto.rb +83 -0
  15. data/lib/jsduck/categories/class_name.rb +63 -0
  16. data/lib/jsduck/categories/factory.rb +113 -0
  17. data/lib/jsduck/categories/file.rb +75 -0
  18. data/lib/jsduck/class.rb +3 -9
  19. data/lib/jsduck/class_doc_expander.rb +1 -1
  20. data/lib/jsduck/css/lexer.rb +203 -0
  21. data/lib/jsduck/css/parser.rb +121 -0
  22. data/lib/jsduck/doc/comment.rb +40 -0
  23. data/lib/jsduck/doc/map.rb +23 -0
  24. data/lib/jsduck/doc/parser.rb +128 -0
  25. data/lib/jsduck/doc/processor.rb +52 -0
  26. data/lib/jsduck/doc/scanner.rb +76 -0
  27. data/lib/jsduck/doc/standard_tag_parser.rb +154 -0
  28. data/lib/jsduck/doc/subproperties.rb +64 -0
  29. data/lib/jsduck/docs_code_comparer.rb +31 -0
  30. data/lib/jsduck/export_writer.rb +2 -2
  31. data/lib/jsduck/exporter/app.rb +16 -4
  32. data/lib/jsduck/exporter/full.rb +2 -2
  33. data/lib/jsduck/format/batch.rb +58 -0
  34. data/lib/jsduck/format/class.rb +62 -0
  35. data/lib/jsduck/format/doc.rb +172 -0
  36. data/lib/jsduck/format/html_stack.rb +109 -0
  37. data/lib/jsduck/format/shortener.rb +55 -0
  38. data/lib/jsduck/format/subproperties.rb +64 -0
  39. data/lib/jsduck/guides.rb +32 -14
  40. data/lib/jsduck/index_html.rb +3 -1
  41. data/lib/jsduck/inline/auto_link.rb +2 -2
  42. data/lib/jsduck/inline/link.rb +4 -3
  43. data/lib/jsduck/inline/link_renderer.rb +2 -2
  44. data/lib/jsduck/inline/video.rb +8 -2
  45. data/lib/jsduck/js/ast.rb +361 -0
  46. data/lib/jsduck/js/esprima.rb +39 -0
  47. data/lib/jsduck/{esprima → js/esprima}/esprima.js +0 -0
  48. data/lib/jsduck/js/evaluator.rb +70 -0
  49. data/lib/jsduck/js/ext_patterns.rb +70 -0
  50. data/lib/jsduck/js/function.rb +206 -0
  51. data/lib/jsduck/js/node.rb +194 -0
  52. data/lib/jsduck/js/node_array.rb +36 -0
  53. data/lib/jsduck/js/parser.rb +223 -0
  54. data/lib/jsduck/js/serializer.rb +263 -0
  55. data/lib/jsduck/js/utils.rb +21 -0
  56. data/lib/jsduck/logger.rb +3 -13
  57. data/lib/jsduck/members_index.rb +3 -4
  58. data/lib/jsduck/merger.rb +25 -145
  59. data/lib/jsduck/options.rb +29 -132
  60. data/lib/jsduck/parser.rb +76 -0
  61. data/lib/jsduck/process/accessors.rb +133 -0
  62. data/lib/jsduck/process/circular_deps.rb +58 -0
  63. data/lib/jsduck/process/enums.rb +91 -0
  64. data/lib/jsduck/process/ext4_events.rb +43 -0
  65. data/lib/jsduck/process/global_members.rb +36 -0
  66. data/lib/jsduck/process/ignored_classes.rb +16 -0
  67. data/lib/jsduck/process/importer.rb +58 -0
  68. data/lib/jsduck/process/inherit_doc.rb +197 -0
  69. data/lib/jsduck/process/lint.rb +135 -0
  70. data/lib/jsduck/process/overrides.rb +99 -0
  71. data/lib/jsduck/process/return_values.rb +72 -0
  72. data/lib/jsduck/process/versions.rb +102 -0
  73. data/lib/jsduck/relations.rb +5 -0
  74. data/lib/jsduck/render/class.rb +144 -0
  75. data/lib/jsduck/render/sidebar.rb +97 -0
  76. data/lib/jsduck/render/signature.rb +94 -0
  77. data/lib/jsduck/render/subproperties.rb +99 -0
  78. data/lib/jsduck/render/tags.rb +38 -0
  79. data/lib/jsduck/search_data.rb +19 -13
  80. data/lib/jsduck/source/file.rb +8 -17
  81. data/lib/jsduck/tag/abstract.rb +4 -7
  82. data/lib/jsduck/tag/accessor.rb +10 -0
  83. data/lib/jsduck/tag/alias.rb +61 -0
  84. data/lib/jsduck/tag/alternate_class_names.rb +17 -0
  85. data/lib/jsduck/tag/aside.rb +28 -31
  86. data/lib/jsduck/tag/author.rb +9 -5
  87. data/lib/jsduck/tag/boolean_tag.rb +24 -0
  88. data/lib/jsduck/tag/cfg.rb +45 -0
  89. data/lib/jsduck/tag/chainable.rb +5 -7
  90. data/lib/jsduck/tag/class.rb +28 -0
  91. data/lib/jsduck/tag/class_list_tag.rb +40 -0
  92. data/lib/jsduck/tag/constructor.rb +24 -0
  93. data/lib/jsduck/tag/css_mixin.rb +17 -0
  94. data/lib/jsduck/tag/css_var.rb +29 -0
  95. data/lib/jsduck/tag/default.rb +31 -0
  96. data/lib/jsduck/tag/deprecated.rb +13 -27
  97. data/lib/jsduck/tag/deprecated_tag.rb +58 -0
  98. data/lib/jsduck/tag/doc.rb +32 -0
  99. data/lib/jsduck/tag/docauthor.rb +4 -5
  100. data/lib/jsduck/tag/enum.rb +70 -0
  101. data/lib/jsduck/tag/event.rb +28 -0
  102. data/lib/jsduck/tag/evented.rb +10 -0
  103. data/lib/jsduck/tag/extends.rb +45 -0
  104. data/lib/jsduck/tag/ftype.rb +18 -0
  105. data/lib/jsduck/tag/hide.rb +4 -11
  106. data/lib/jsduck/tag/ignore.rb +6 -7
  107. data/lib/jsduck/tag/inheritable.rb +10 -0
  108. data/lib/jsduck/tag/inheritdoc.rb +48 -0
  109. data/lib/jsduck/tag/markdown.rb +8 -6
  110. data/lib/jsduck/tag/member.rb +24 -0
  111. data/lib/jsduck/tag/method.rb +35 -0
  112. data/lib/jsduck/tag/mixins.rb +26 -0
  113. data/lib/jsduck/tag/name.rb +36 -0
  114. data/lib/jsduck/tag/new.rb +13 -27
  115. data/lib/jsduck/tag/override.rb +37 -0
  116. data/lib/jsduck/tag/overrides.rb +29 -0
  117. data/lib/jsduck/tag/param.rb +87 -0
  118. data/lib/jsduck/tag/preventable.rb +19 -10
  119. data/lib/jsduck/tag/private.rb +28 -13
  120. data/lib/jsduck/tag/property.rb +39 -0
  121. data/lib/jsduck/tag/protected.rb +5 -7
  122. data/lib/jsduck/tag/ptype.rb +18 -0
  123. data/lib/jsduck/tag/readonly.rb +4 -7
  124. data/lib/jsduck/tag/removed.rb +21 -29
  125. data/lib/jsduck/tag/required.rb +11 -9
  126. data/lib/jsduck/tag/requires.rb +12 -0
  127. data/lib/jsduck/tag/return.rb +47 -0
  128. data/lib/jsduck/tag/since.rb +19 -11
  129. data/lib/jsduck/tag/singleton.rb +15 -0
  130. data/lib/jsduck/tag/static.rb +5 -7
  131. data/lib/jsduck/tag/subproperties.rb +23 -0
  132. data/lib/jsduck/tag/tag.rb +208 -0
  133. data/lib/jsduck/tag/template.rb +14 -9
  134. data/lib/jsduck/tag/throws.rb +38 -0
  135. data/lib/jsduck/tag/type.rb +48 -0
  136. data/lib/jsduck/tag/uses.rb +12 -0
  137. data/lib/jsduck/tag/xtype.rb +30 -0
  138. data/lib/jsduck/tag_loader.rb +39 -0
  139. data/lib/jsduck/tag_registry.rb +189 -0
  140. data/lib/jsduck/type_parser.rb +3 -3
  141. data/lib/jsduck/web_writer.rb +2 -2
  142. data/lib/jsduck/welcome.rb +1 -1
  143. metadata +578 -538
  144. data/lib/jsduck/accessors.rb +0 -136
  145. data/lib/jsduck/ast.rb +0 -524
  146. data/lib/jsduck/auto_categories.rb +0 -80
  147. data/lib/jsduck/batch_formatter.rb +0 -60
  148. data/lib/jsduck/categories.rb +0 -73
  149. data/lib/jsduck/categories_class_name.rb +0 -37
  150. data/lib/jsduck/circular_deps.rb +0 -56
  151. data/lib/jsduck/class_formatter.rb +0 -102
  152. data/lib/jsduck/columns.rb +0 -56
  153. data/lib/jsduck/css_lexer.rb +0 -201
  154. data/lib/jsduck/css_parser.rb +0 -119
  155. data/lib/jsduck/doc_ast.rb +0 -319
  156. data/lib/jsduck/doc_formatter.rb +0 -142
  157. data/lib/jsduck/doc_parser.rb +0 -611
  158. data/lib/jsduck/doc_type.rb +0 -59
  159. data/lib/jsduck/enum.rb +0 -73
  160. data/lib/jsduck/esprima.rb +0 -51
  161. data/lib/jsduck/evaluator.rb +0 -69
  162. data/lib/jsduck/ext_patterns.rb +0 -58
  163. data/lib/jsduck/file_categories.rb +0 -76
  164. data/lib/jsduck/function_ast.rb +0 -206
  165. data/lib/jsduck/guide_anchors.rb +0 -32
  166. data/lib/jsduck/guide_toc.rb +0 -49
  167. data/lib/jsduck/html_stack.rb +0 -105
  168. data/lib/jsduck/importer.rb +0 -121
  169. data/lib/jsduck/inherit_doc.rb +0 -193
  170. data/lib/jsduck/js_parser.rb +0 -221
  171. data/lib/jsduck/lint.rb +0 -133
  172. data/lib/jsduck/meta_tag.rb +0 -88
  173. data/lib/jsduck/meta_tag_loader.rb +0 -67
  174. data/lib/jsduck/meta_tag_registry.rb +0 -111
  175. data/lib/jsduck/meta_tag_renderer.rb +0 -34
  176. data/lib/jsduck/news.rb +0 -128
  177. data/lib/jsduck/override.rb +0 -87
  178. data/lib/jsduck/renderer.rb +0 -361
  179. data/lib/jsduck/return_values.rb +0 -72
  180. data/lib/jsduck/serializer.rb +0 -262
  181. data/lib/jsduck/shortener.rb +0 -58
  182. data/lib/jsduck/signature_renderer.rb +0 -91
  183. data/lib/jsduck/source/file_parser.rb +0 -72
@@ -0,0 +1,23 @@
1
+ module JsDuck
2
+ module Doc
3
+
4
+ # Helper for building at-tags lookup table.
5
+ class Map
6
+
7
+ # Builds map of at-tags for quick lookup
8
+ def self.build(docs)
9
+ map = {}
10
+ docs.each do |tag|
11
+ if map[tag[:tagname]]
12
+ map[tag[:tagname]] << tag
13
+ else
14
+ map[tag[:tagname]] = [tag]
15
+ end
16
+ end
17
+ map
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,128 @@
1
+ require 'strscan'
2
+ require 'jsduck/doc/comment'
3
+ require 'jsduck/doc/scanner'
4
+ require 'jsduck/tag_registry'
5
+ require 'jsduck/logger'
6
+
7
+ module JsDuck
8
+ module Doc
9
+
10
+ # Parses doc-comment into array of @tags
11
+ #
12
+ # For each @tag it produces Hash like the following:
13
+ #
14
+ # {
15
+ # :tagname => :cfg/:property/:type/:extends/...,
16
+ # :doc => "Some documentation for this tag",
17
+ # ...@tag specific stuff like :name, :type, and so on...
18
+ # }
19
+ #
20
+ # When doc-comment begins with comment, not preceded by @tag, then
21
+ # the comment will be placed into Hash with :tagname => :default.
22
+ #
23
+ # Unrecognized @tags are left as is into documentation as if they
24
+ # were normal text.
25
+ #
26
+ # @example, {@img}, {@link} and {@video} are parsed separately in
27
+ # JsDuck::DocFormatter.
28
+ #
29
+ class Parser < Doc::Scanner
30
+ def parse(input, filename="", linenr=0)
31
+ @filename = filename
32
+ @linenr = linenr
33
+ @tags = []
34
+ @input = StringScanner.new(Doc::Comment.purify(input))
35
+
36
+ parse_loop
37
+
38
+ strip_docs
39
+ @tags
40
+ end
41
+
42
+ # The parsing process can leave whitespace at the ends of
43
+ # doc-strings, here we get rid of it.
44
+ def strip_docs
45
+ @tags.each do |tag|
46
+ tag[:doc].strip! if tag[:doc]
47
+ end
48
+ end
49
+
50
+ # The main loop of the DocParser
51
+ def parse_loop
52
+ add_tag({:tagname => :doc, :doc => :multiline})
53
+
54
+ while !@input.eos? do
55
+ if look(/@/)
56
+ parse_at_tag
57
+ elsif look(/[^@]/)
58
+ skip_to_next_at_tag
59
+ end
60
+ end
61
+ end
62
+
63
+ # Appends new @tag to parsed tags list
64
+ def add_tag(tag)
65
+ @tags << tag
66
+
67
+ if tag[:doc] == :multiline
68
+ tag[:doc] = ""
69
+ @multiline_tag = tag
70
+ end
71
+ end
72
+
73
+ # Processes anything beginning with @-sign.
74
+ #
75
+ # - When @ is not followed by any word chars, do nothing.
76
+ # - When it's one of the builtin tags, process it as such.
77
+ # - When it's something else, print a warning.
78
+ #
79
+ def parse_at_tag
80
+ match(/@/)
81
+ name = look(/\w+/)
82
+
83
+ if !name
84
+ # ignore
85
+ elsif tag = TagRegistry.get_by_pattern(name)
86
+ match(/\w+/)
87
+ hw # Skip the whitespace right after the tag.
88
+
89
+ tags = tag.parse_doc(self)
90
+ if tags.is_a?(Hash)
91
+ add_tag(tags)
92
+ elsif tags.is_a?(Array)
93
+ tags.each {|t| add_tag(t) }
94
+ end
95
+
96
+ skip_white
97
+ else
98
+ Logger.warn(:tag, "Unsupported tag: @#{name}", @filename, @linenr)
99
+ @multiline_tag[:doc] += "@"
100
+ end
101
+ end
102
+
103
+ # Skips until the beginning of next @tag.
104
+ #
105
+ # There must be space before the next @tag - this ensures that we
106
+ # don't detect tags inside "foo@example.com" or "{@link}".
107
+ #
108
+ # Also check that the @tag is not part of an indented code block -
109
+ # in which case we also ignore the tag.
110
+ def skip_to_next_at_tag
111
+ @multiline_tag[:doc] += match(/[^@]+/)
112
+
113
+ while look(/@/) && (!prev_char_is_whitespace? || indented_as_code?)
114
+ @multiline_tag[:doc] += match(/@+[^@]+/)
115
+ end
116
+ end
117
+
118
+ def prev_char_is_whitespace?
119
+ @multiline_tag[:doc][-1,1] =~ /\s/
120
+ end
121
+
122
+ def indented_as_code?
123
+ @multiline_tag[:doc] =~ /^ {4,}[^\n]*\z/
124
+ end
125
+ end
126
+
127
+ end
128
+ end
@@ -0,0 +1,52 @@
1
+ require 'jsduck/tag_registry'
2
+
3
+ module JsDuck
4
+ module Doc
5
+
6
+ # Processes @tag data detected from doc-comment, transforming it
7
+ # into a class/member hash which can be then later further merged
8
+ # with code hash.
9
+ #
10
+ # Its main work is done through calling the #process_doc method of
11
+ # all the Tag classes that have registered themselves to process a
12
+ # particular set of @tags through defining a .tagname attribute.
13
+ class Processor
14
+ # Allow passing in filename and line for error reporting
15
+ attr_accessor :filename
16
+ attr_accessor :linenr
17
+
18
+ def initialize
19
+ @filename = ""
20
+ @linenr = 0
21
+ end
22
+
23
+ # Given tagname and map of tags from DocParser, produces docs
24
+ # of the type determined by tagname.
25
+ def process(tagname, doc_map)
26
+ hash = {
27
+ :tagname => tagname,
28
+ :doc => extract_doc(doc_map),
29
+ }
30
+
31
+ position = {:filename => @filename, :linenr => @linenr}
32
+
33
+ doc_map.each_pair do |name, value|
34
+ if tag = TagRegistry.get_by_name(name)
35
+ tag.process_doc(hash, value, position)
36
+ end
37
+ end
38
+
39
+ return hash
40
+ end
41
+
42
+ private
43
+
44
+ def extract_doc(doc_map)
45
+ tag = doc_map[:doc] ? doc_map[:doc].first : {}
46
+ return tag[:doc] || ""
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,76 @@
1
+ require 'jsduck/doc/standard_tag_parser'
2
+
3
+ module JsDuck
4
+ module Doc
5
+
6
+ # Abstract base class for parsing doc-comments.
7
+ #
8
+ # The methods of this class are to be called from implementations
9
+ # of concrete @tags. Although the @tag classes will get passed an
10
+ # instance of Doc::Parser, only methods of Doc::Scanner should be
11
+ # called by them.
12
+ #
13
+ class Scanner
14
+ def initialize
15
+ @ident_pattern = /[$\w-]+/
16
+ @ident_chain_pattern = /[$\w-]+(\.[$\w-]+)*/
17
+
18
+ @input = nil # set to StringScanner in subclass
19
+ end
20
+
21
+ # Provides access to StringScanner
22
+ attr_reader :input
23
+
24
+ # Parses standard pattern common in several builtin tags, which
25
+ # goes like this:
26
+ #
27
+ # @tag {Type} [some.name=default]
28
+ #
29
+ # See StandardTagParser#parse for details.
30
+ #
31
+ def standard_tag(cfg)
32
+ Doc::StandardTagParser.new(self).parse(cfg)
33
+ end
34
+
35
+ # matches chained.identifier.name and returns it
36
+ def ident_chain
37
+ @input.scan(@ident_chain_pattern)
38
+ end
39
+
40
+ # matches identifier and returns its name
41
+ def ident
42
+ @input.scan(@ident_pattern)
43
+ end
44
+
45
+ # Looks for the existance of pattern. Returns the matching
46
+ # string on success, nil on failure, but doesn't advance the
47
+ # scan pointer.
48
+ def look(re)
49
+ @input.check(re)
50
+ end
51
+
52
+ # Matches the given pattern and advances the scan pointer
53
+ # returning the string that matched. When the pattern doesn't
54
+ # match, nil is returned.
55
+ def match(re)
56
+ @input.scan(re)
57
+ end
58
+
59
+ # Skips all whitespace. Moves scan pointer to next non-whitespace
60
+ # character.
61
+ def skip_white
62
+ @input.scan(/\s+/)
63
+ end
64
+
65
+ # Skips horizontal whitespace (tabs and spaces). Moves scan
66
+ # pointer to next non-whitespace character or to the end of line.
67
+ # Returns self to allow chaining.
68
+ def hw
69
+ @input.scan(/[ \t]+/)
70
+ self
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,154 @@
1
+ module JsDuck
2
+ module Doc
3
+
4
+ # Helper in parsing the standard tag pattern with type definition
5
+ # followed by name and default value:
6
+ #
7
+ # @tag {Type} [some.name=default]
8
+ #
9
+ class StandardTagParser
10
+ # Initialized with Doc::Scanner instance
11
+ def initialize(doc_scanner)
12
+ @ds = doc_scanner
13
+ end
14
+
15
+ # Parses the standard tag pattern.
16
+ #
17
+ # Takes as parameter a configuration hash which can contain the
18
+ # following keys:
19
+ #
20
+ # - :tagname => The :tagname of the hash to return.
21
+ #
22
+ # - :type => True to parse {Type} section.
23
+ # Produces :type and :optional keys.
24
+ #
25
+ # - :name => Trye to parse [some.name=default] section.
26
+ # Produces :name, :default and :optional keys.
27
+ #
28
+ # Returns tag definition hash containing the given :tagname and a
29
+ # set of other fields depending on whether :type and :name configs
30
+ # were specified and how their matching succeeded.
31
+ #
32
+ def parse(cfg)
33
+ tag = {:tagname => cfg[:tagname]}
34
+ add_type(tag) if cfg[:type]
35
+ add_name_with_default(tag) if cfg[:name]
36
+ tag
37
+ end
38
+
39
+ private
40
+
41
+ # matches {type} if possible and sets it on given tag hash.
42
+ # Also checks for {optionality=} in type definition.
43
+ def add_type(tag)
44
+ if hw.look(/\{/)
45
+ tdf = typedef
46
+ tag[:type] = tdf[:type]
47
+ tag[:optional] = true if tdf[:optional]
48
+ end
49
+ end
50
+
51
+ # matches {...=} and returns text inside brackets
52
+ def typedef
53
+ match(/\{/)
54
+
55
+ name = parse_balanced(/\{/, /\}/, /[^{}'"]*/)
56
+
57
+ if name =~ /=$/
58
+ name = name.chop
59
+ optional = true
60
+ else
61
+ optional = nil
62
+ end
63
+
64
+ match(/\}/)
65
+
66
+ return {:type => name, :optional => optional}
67
+ end
68
+
69
+ # matches: <ident-chain> | "[" <ident-chain> [ "=" <default-value> ] "]"
70
+ def add_name_with_default(tag)
71
+ if hw.match(/\[/)
72
+ tag[:name] = hw.ident_chain
73
+ if hw.match(/=/)
74
+ hw
75
+ tag[:default] = default_value
76
+ end
77
+ hw.match(/\]/)
78
+ tag[:optional] = true
79
+ else
80
+ tag[:name] = hw.ident_chain
81
+ end
82
+ end
83
+
84
+ # Attempts to allow balanced braces in default value.
85
+ # When the nested parsing doesn't finish at closing "]",
86
+ # roll back to beginning and simply grab anything up to closing "]".
87
+ def default_value
88
+ start_pos = @ds.input.pos
89
+ value = parse_balanced(/\[/, /\]/, /[^\[\]'"]*/)
90
+ if look(/\]/)
91
+ value
92
+ else
93
+ @ds.input.pos = start_pos
94
+ match(/[^\]]*/)
95
+ end
96
+ end
97
+
98
+ # Helper method to parse a string up to a closing brace,
99
+ # balancing opening-closing braces in between.
100
+ #
101
+ # @param re_open The beginning brace regex
102
+ # @param re_close The closing brace regex
103
+ # @param re_rest Regex to match text without any braces and strings
104
+ def parse_balanced(re_open, re_close, re_rest)
105
+ result = parse_with_strings(re_rest)
106
+ while look(re_open)
107
+ result += match(re_open)
108
+ result += parse_balanced(re_open, re_close, re_rest)
109
+ result += match(re_close)
110
+ result += parse_with_strings(re_rest)
111
+ end
112
+ result
113
+ end
114
+
115
+ # Helper for parse_balanced to parse rest of the text between
116
+ # braces, taking account the strings which might occur there.
117
+ def parse_with_strings(re_rest)
118
+ result = match(re_rest)
119
+ while look(/['"]/)
120
+ result += parse_string('"') if look(/"/)
121
+ result += parse_string("'") if look(/'/)
122
+ result += match(re_rest)
123
+ end
124
+ result
125
+ end
126
+
127
+ # Parses "..." or '...' including the escape sequence \' or '\"
128
+ def parse_string(quote)
129
+ re_quote = Regexp.new(quote)
130
+ re_rest = Regexp.new("(?:[^"+quote+"\\\\]|\\\\.)*")
131
+ match(re_quote) + match(re_rest) + (match(re_quote) || "")
132
+ end
133
+
134
+ ### Forward these calls to Doc::Scanner
135
+
136
+ def ident_chain
137
+ @ds.ident_chain
138
+ end
139
+
140
+ def look(re)
141
+ @ds.look(re)
142
+ end
143
+
144
+ def match(re)
145
+ @ds.match(re)
146
+ end
147
+
148
+ def hw
149
+ @ds.hw
150
+ end
151
+ end
152
+
153
+ end
154
+ end
@@ -0,0 +1,64 @@
1
+ require 'jsduck/util/singleton'
2
+ require 'jsduck/logger'
3
+
4
+ module JsDuck
5
+ module Doc
6
+
7
+ # Detects nested structure of subproperties.
8
+ class Subproperties
9
+ include Util::Singleton
10
+
11
+ # Given array of e.g. @param tags from Doc::Parser with names
12
+ # containing dots:
13
+ #
14
+ # {:name => "foo"},
15
+ # {:name => "foo.bar"},
16
+ # {:name => "foo.baz"},
17
+ # {:name => "zap"},
18
+ #
19
+ # Produces nested structure:
20
+ #
21
+ # {:name => "foo", :properties => [
22
+ # {:name => "bar"},
23
+ # {:name => "baz"}]},
24
+ # {:name => "zap"},
25
+ #
26
+ # Secondly it takes a position argument which is used for
27
+ # logging warnings when bogus subproperty syntax is encountered.
28
+ def nest(raw_items, pos)
29
+ # First item can't be namespaced, if it is ignore the rest.
30
+ if raw_items[0] && raw_items[0][:name] =~ /\./
31
+ return [raw_items[0]]
32
+ end
33
+
34
+ # build name-index of all items
35
+ index = {}
36
+ raw_items.each {|it| index[it[:name]] = it }
37
+
38
+ # If item name has no dots, add it directly to items array.
39
+ # Otherwise look up the parent of item and add it as the
40
+ # property of that parent.
41
+ items = []
42
+ raw_items.each do |it|
43
+ if it[:name] =~ /^(.+)\.([^.]+)$/
44
+ it[:name] = $2
45
+ parent = index[$1]
46
+ if parent
47
+ parent[:properties] = [] unless parent[:properties]
48
+ parent[:properties] << it
49
+ else
50
+ msg = "Ignoring subproperty #{$1}.#{$2}, no parent found with name '#{$1}'."
51
+ Logger.warn(:subproperty, msg, pos[:filename], pos[:linenr])
52
+ end
53
+ else
54
+ items << it
55
+ end
56
+ end
57
+
58
+ return items
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end