jsduck 4.10.4 → 5.0.0.beta01

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