yard 0.6.8 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of yard might be problematic. Click here for more details.

Files changed (224) hide show
  1. data/.yardopts +1 -0
  2. data/ChangeLog +723 -0
  3. data/README.md +16 -6
  4. data/docs/CodeObjects.md +10 -16
  5. data/docs/GettingStarted.md +232 -32
  6. data/docs/Glossary.md +1 -2
  7. data/docs/Handlers.md +10 -16
  8. data/docs/Overview.md +14 -13
  9. data/docs/Parser.md +13 -22
  10. data/docs/Tags.md +209 -16
  11. data/docs/Templates.md +237 -26
  12. data/docs/WhatsNew.md +178 -2
  13. data/lib/yard.rb +13 -10
  14. data/lib/yard/autoload.rb +22 -18
  15. data/lib/yard/cli/command.rb +13 -12
  16. data/lib/yard/cli/command_parser.rb +20 -19
  17. data/lib/yard/cli/config.rb +19 -19
  18. data/lib/yard/cli/diff.rb +46 -21
  19. data/lib/yard/cli/gems.rb +11 -11
  20. data/lib/yard/cli/graph.rb +13 -13
  21. data/lib/yard/cli/help.rb +1 -1
  22. data/lib/yard/cli/list.rb +22 -0
  23. data/lib/yard/cli/server.rb +17 -17
  24. data/lib/yard/cli/stats.rb +32 -32
  25. data/lib/yard/cli/yardoc.rb +181 -135
  26. data/lib/yard/cli/yri.rb +29 -29
  27. data/lib/yard/code_objects/base.rb +101 -101
  28. data/lib/yard/code_objects/class_object.rb +20 -20
  29. data/lib/yard/code_objects/constant_object.rb +1 -1
  30. data/lib/yard/code_objects/extended_method_object.rb +5 -5
  31. data/lib/yard/code_objects/extra_file_object.rb +89 -0
  32. data/lib/yard/code_objects/macro_object.rb +215 -0
  33. data/lib/yard/code_objects/method_object.rb +30 -30
  34. data/lib/yard/code_objects/module_object.rb +1 -1
  35. data/lib/yard/code_objects/namespace_object.rb +39 -39
  36. data/lib/yard/code_objects/proxy.rb +38 -38
  37. data/lib/yard/code_objects/root_object.rb +1 -1
  38. data/lib/yard/config.rb +40 -40
  39. data/lib/yard/core_ext/array.rb +2 -2
  40. data/lib/yard/core_ext/file.rb +11 -11
  41. data/lib/yard/core_ext/insertion.rb +10 -10
  42. data/lib/yard/core_ext/module.rb +2 -2
  43. data/lib/yard/core_ext/string.rb +2 -2
  44. data/lib/yard/core_ext/symbol_hash.rb +14 -14
  45. data/lib/yard/docstring.rb +122 -54
  46. data/lib/yard/globals.rb +2 -2
  47. data/lib/yard/handlers/base.rb +216 -127
  48. data/lib/yard/handlers/processor.rb +65 -27
  49. data/lib/yard/handlers/ruby/alias_handler.rb +6 -3
  50. data/lib/yard/handlers/ruby/attribute_handler.rb +7 -6
  51. data/lib/yard/handlers/ruby/base.rb +50 -31
  52. data/lib/yard/handlers/ruby/class_condition_handler.rb +11 -11
  53. data/lib/yard/handlers/ruby/class_handler.rb +10 -10
  54. data/lib/yard/handlers/ruby/class_variable_handler.rb +3 -3
  55. data/lib/yard/handlers/ruby/constant_handler.rb +7 -7
  56. data/lib/yard/handlers/ruby/exception_handler.rb +2 -2
  57. data/lib/yard/handlers/ruby/extend_handler.rb +1 -1
  58. data/lib/yard/handlers/ruby/legacy/alias_handler.rb +8 -5
  59. data/lib/yard/handlers/ruby/legacy/attribute_handler.rb +6 -5
  60. data/lib/yard/handlers/ruby/legacy/base.rb +42 -27
  61. data/lib/yard/handlers/ruby/legacy/class_condition_handler.rb +9 -9
  62. data/lib/yard/handlers/ruby/legacy/class_handler.rb +13 -12
  63. data/lib/yard/handlers/ruby/legacy/class_variable_handler.rb +3 -6
  64. data/lib/yard/handlers/ruby/legacy/constant_handler.rb +5 -8
  65. data/lib/yard/handlers/ruby/legacy/exception_handler.rb +1 -1
  66. data/lib/yard/handlers/ruby/legacy/extend_handler.rb +1 -0
  67. data/lib/yard/handlers/ruby/legacy/macro_handler.rb +40 -0
  68. data/lib/yard/handlers/ruby/legacy/method_handler.rb +10 -10
  69. data/lib/yard/handlers/ruby/legacy/mixin_handler.rb +4 -3
  70. data/lib/yard/handlers/ruby/legacy/module_handler.rb +2 -1
  71. data/lib/yard/handlers/ruby/legacy/private_constant_handler.rb +4 -4
  72. data/lib/yard/handlers/ruby/legacy/visibility_handler.rb +2 -1
  73. data/lib/yard/handlers/ruby/legacy/yield_handler.rb +3 -3
  74. data/lib/yard/handlers/ruby/macro_handler.rb +41 -0
  75. data/lib/yard/handlers/ruby/macro_handler_methods.rb +130 -0
  76. data/lib/yard/handlers/ruby/method_condition_handler.rb +1 -1
  77. data/lib/yard/handlers/ruby/method_handler.rb +13 -13
  78. data/lib/yard/handlers/ruby/mixin_handler.rb +4 -4
  79. data/lib/yard/handlers/ruby/module_handler.rb +2 -1
  80. data/lib/yard/handlers/ruby/private_constant_handler.rb +4 -4
  81. data/lib/yard/handlers/ruby/struct_handler_methods.rb +11 -11
  82. data/lib/yard/handlers/ruby/visibility_handler.rb +1 -1
  83. data/lib/yard/handlers/ruby/yield_handler.rb +5 -5
  84. data/lib/yard/logging.rb +11 -11
  85. data/lib/yard/parser/base.rb +8 -8
  86. data/lib/yard/parser/c_parser.rb +42 -33
  87. data/lib/yard/parser/ruby/ast_node.rb +62 -61
  88. data/lib/yard/parser/ruby/legacy/ruby_lex.rb +66 -66
  89. data/lib/yard/parser/ruby/legacy/ruby_parser.rb +4 -4
  90. data/lib/yard/parser/ruby/legacy/statement.rb +11 -11
  91. data/lib/yard/parser/ruby/legacy/statement_list.rb +15 -15
  92. data/lib/yard/parser/ruby/legacy/token_list.rb +9 -9
  93. data/lib/yard/parser/ruby/ruby_parser.rb +51 -37
  94. data/lib/yard/parser/source_parser.rb +271 -46
  95. data/lib/yard/rake/yardoc_task.rb +18 -17
  96. data/lib/yard/registry.rb +64 -64
  97. data/lib/yard/registry_store.rb +34 -34
  98. data/lib/yard/rubygems/backports.rb +8 -0
  99. data/lib/yard/rubygems/backports/LICENSE.txt +57 -0
  100. data/lib/yard/rubygems/backports/MIT.txt +20 -0
  101. data/lib/yard/rubygems/backports/gem.rb +8 -0
  102. data/lib/yard/rubygems/backports/source_index.rb +353 -0
  103. data/lib/yard/rubygems/specification.rb +2 -2
  104. data/lib/yard/serializers/base.rb +20 -20
  105. data/lib/yard/serializers/file_system_serializer.rb +28 -24
  106. data/lib/yard/serializers/process_serializer.rb +3 -3
  107. data/lib/yard/serializers/stdout_serializer.rb +6 -6
  108. data/lib/yard/serializers/yardoc_serializer.rb +17 -17
  109. data/lib/yard/server/adapter.rb +12 -12
  110. data/lib/yard/server/commands/base.rb +26 -26
  111. data/lib/yard/server/commands/display_file_command.rb +3 -2
  112. data/lib/yard/server/commands/display_object_command.rb +5 -5
  113. data/lib/yard/server/commands/frames_command.rb +1 -1
  114. data/lib/yard/server/commands/library_command.rb +7 -7
  115. data/lib/yard/server/commands/library_index_command.rb +2 -2
  116. data/lib/yard/server/commands/list_command.rb +8 -8
  117. data/lib/yard/server/commands/search_command.rb +8 -8
  118. data/lib/yard/server/commands/static_file_command.rb +3 -3
  119. data/lib/yard/server/doc_server_helper.rb +6 -3
  120. data/lib/yard/server/doc_server_serializer.rb +1 -1
  121. data/lib/yard/server/library_version.rb +45 -45
  122. data/lib/yard/server/rack_adapter.rb +10 -10
  123. data/lib/yard/server/router.rb +28 -28
  124. data/lib/yard/server/static_caching.rb +5 -5
  125. data/lib/yard/server/templates/default/fulldoc/html/css/custom.css +3 -3
  126. data/lib/yard/server/templates/default/fulldoc/html/js/live.js +1 -1
  127. data/lib/yard/server/templates/default/layout/html/breadcrumb.erb +2 -2
  128. data/lib/yard/server/templates/default/layout/html/headers.erb +13 -8
  129. data/lib/yard/server/templates/default/layout/html/setup.rb +7 -0
  130. data/lib/yard/server/templates/doc_server/full_list/html/full_list.erb +2 -2
  131. data/lib/yard/server/templates/doc_server/full_list/html/setup.rb +14 -4
  132. data/lib/yard/server/templates/doc_server/library_list/html/contents.erb +2 -2
  133. data/lib/yard/server/templates/doc_server/library_list/html/headers.erb +2 -2
  134. data/lib/yard/server/templates/doc_server/processing/html/processing.erb +1 -1
  135. data/lib/yard/server/templates/doc_server/search/html/search.erb +1 -1
  136. data/lib/yard/server/webrick_adapter.rb +2 -2
  137. data/lib/yard/tags/default_factory.rb +19 -19
  138. data/lib/yard/tags/default_tag.rb +1 -1
  139. data/lib/yard/tags/library.rb +68 -63
  140. data/lib/yard/tags/option_tag.rb +1 -1
  141. data/lib/yard/tags/overload_tag.rb +9 -9
  142. data/lib/yard/tags/ref_tag_list.rb +2 -2
  143. data/lib/yard/tags/tag.rb +7 -7
  144. data/lib/yard/templates/engine.rb +31 -31
  145. data/lib/yard/templates/erb_cache.rb +1 -1
  146. data/lib/yard/templates/helpers/base_helper.rb +46 -32
  147. data/lib/yard/templates/helpers/filter_helper.rb +2 -2
  148. data/lib/yard/templates/helpers/html_helper.rb +120 -81
  149. data/lib/yard/templates/helpers/html_syntax_highlight_helper.rb +4 -4
  150. data/lib/yard/templates/helpers/markup/rdoc_markup.rb +9 -9
  151. data/lib/yard/templates/helpers/markup_helper.rb +37 -30
  152. data/lib/yard/templates/helpers/method_helper.rb +7 -7
  153. data/lib/yard/templates/helpers/text_helper.rb +7 -7
  154. data/lib/yard/templates/helpers/uml_helper.rb +3 -3
  155. data/lib/yard/templates/section.rb +14 -14
  156. data/lib/yard/templates/template.rb +54 -54
  157. data/lib/yard/verifier.rb +27 -27
  158. data/spec/cli/list_spec.rb +8 -0
  159. data/spec/cli/yardoc_spec.rb +58 -10
  160. data/spec/code_objects/extra_file_object_spec.rb +132 -0
  161. data/spec/code_objects/macro_object_spec.rb +154 -0
  162. data/spec/docstring_spec.rb +90 -0
  163. data/spec/handlers/base_spec.rb +22 -0
  164. data/spec/handlers/examples/macro_handler_001.rb.txt +73 -0
  165. data/spec/handlers/examples/method_handler_001.rb.txt +17 -0
  166. data/spec/handlers/macro_handler_spec.rb +140 -0
  167. data/spec/handlers/method_handler_spec.rb +28 -0
  168. data/spec/handlers/processor_spec.rb +4 -0
  169. data/spec/handlers/spec_helper.rb +1 -1
  170. data/spec/parser/c_parser_spec.rb +47 -16
  171. data/spec/parser/examples/extrafile.c.txt +8 -0
  172. data/spec/parser/examples/multifile.c.txt +6 -0
  173. data/spec/parser/ruby/ruby_parser_spec.rb +5 -0
  174. data/spec/parser/source_parser_spec.rb +235 -0
  175. data/spec/rake/yardoc_task_spec.rb +22 -17
  176. data/spec/serializers/file_system_serializer_spec.rb +6 -0
  177. data/spec/server/commands/library_command_spec.rb +39 -0
  178. data/spec/spec_helper.rb +14 -0
  179. data/spec/templates/examples/method001.html +6 -6
  180. data/spec/templates/examples/method002.html +4 -4
  181. data/spec/templates/examples/method003.html +10 -10
  182. data/spec/templates/examples/method005.html +2 -2
  183. data/spec/templates/examples/module001.dot +2 -0
  184. data/spec/templates/examples/module001.html +76 -37
  185. data/spec/templates/examples/module001.txt +1 -1
  186. data/spec/templates/helpers/base_helper_spec.rb +7 -2
  187. data/spec/templates/helpers/html_helper_spec.rb +49 -5
  188. data/spec/templates/helpers/markup_helper_spec.rb +9 -8
  189. data/spec/templates/module_spec.rb +7 -0
  190. data/spec/templates/onefile_spec.rb +47 -0
  191. data/templates/default/fulldoc/html/css/style.css +7 -5
  192. data/templates/default/fulldoc/html/full_list.erb +13 -10
  193. data/templates/default/fulldoc/html/full_list_files.erb +1 -1
  194. data/templates/default/fulldoc/html/js/app.js +16 -14
  195. data/templates/default/fulldoc/html/js/full_list.js +7 -6
  196. data/templates/default/fulldoc/html/setup.rb +78 -17
  197. data/templates/default/layout/html/files.erb +1 -1
  198. data/templates/default/layout/html/headers.erb +11 -7
  199. data/templates/default/layout/html/search.erb +4 -4
  200. data/templates/default/layout/html/setup.rb +28 -8
  201. data/templates/default/module/html/inherited_attributes.erb +17 -0
  202. data/templates/default/module/setup.rb +1 -1
  203. data/templates/default/onefile/html/files.erb +2 -2
  204. data/templates/default/onefile/html/layout.erb +1 -1
  205. data/templates/default/onefile/html/setup.rb +7 -5
  206. data/templates/default/tags/html/option.erb +1 -1
  207. data/templates/default/tags/html/tag.erb +3 -3
  208. data/templates/guide/class/html/setup.rb +1 -0
  209. data/templates/guide/docstring/html/setup.rb +1 -0
  210. data/templates/guide/fulldoc/html/css/style.css +91 -0
  211. data/templates/guide/fulldoc/html/js/app.js +33 -0
  212. data/templates/guide/fulldoc/html/setup.rb +54 -0
  213. data/templates/guide/layout/html/layout.erb +81 -0
  214. data/templates/guide/layout/html/setup.rb +24 -0
  215. data/templates/guide/method/html/header.erb +18 -0
  216. data/templates/guide/method/html/setup.rb +21 -0
  217. data/templates/guide/module/html/header.erb +7 -0
  218. data/templates/guide/module/html/method_list.erb +5 -0
  219. data/templates/guide/module/html/setup.rb +26 -0
  220. data/templates/guide/tags/html/setup.rb +8 -0
  221. metadata +40 -7
  222. data/lib/yard/handlers/ruby/legacy/process_handler.rb +0 -13
  223. data/lib/yard/handlers/ruby/process_handler.rb +0 -18
  224. data/spec/handlers/process_handler_spec.rb +0 -17
@@ -1,4 +1,5 @@
1
1
  require 'stringio'
2
+ require 'ostruct'
2
3
 
3
4
  begin require 'continuation'; rescue LoadError; end
4
5
 
@@ -6,52 +7,63 @@ module YARD
6
7
  module Parser
7
8
  # Raised when an object is recognized but cannot be documented. This
8
9
  # generally occurs when the Ruby syntax used to declare an object is
9
- # too dynamic in nature.
10
+ # too dynamic in nature.
10
11
  class UndocumentableError < Exception; end
11
-
12
+
12
13
  # Raised when the parser sees a Ruby syntax error
13
14
  class ParserSyntaxError < UndocumentableError; end
14
-
15
- # A LoadOrderError occurs when a handler needs to modify a
15
+
16
+ # A LoadOrderError occurs when a handler needs to modify a
16
17
  # {CodeObjects::NamespaceObject} (usually by adding a child to it)
17
- # that has not yet been resolved.
18
- #
18
+ # that has not yet been resolved.
19
+ #
19
20
  # @see Handers::Base#ensure_loaded!
20
21
  class LoadOrderError < Exception; end
21
-
22
+
22
23
  # Responsible for parsing a source file into the namespace. Parsing
23
24
  # also invokes handlers to process the parsed statements and generate
24
25
  # any code objects that may be recognized.
25
- #
26
+ #
26
27
  # == Custom Parsers
27
28
  # SourceParser allows custom parsers to be registered and called when
28
29
  # a certain filetype is recognized. To register a parser and hook it
29
30
  # up to a set of file extensions, call {register_parser_type}
30
- #
31
+ #
31
32
  # @see register_parser_type
32
33
  # @see Handlers::Base
33
34
  # @see CodeObjects::Base
34
- class SourceParser
35
+ class SourceParser
35
36
  SHEBANG_LINE = /\A\s*#!\S+/
36
37
  ENCODING_LINE = /\A(?:\s*#*!.*\r?\n)?\s*#+.*coding\s*[:=]{1,2}\s*(\S+)/i
37
38
 
39
+ # Byte order marks for various encodings
40
+ # @since 0.7.0
41
+ ENCODING_BYTE_ORDER_MARKS = {
42
+ 'utf-8' => "\xEF\xBB\xBF",
43
+ # Not yet supported
44
+ #'utf-16be' => "\xFE\xFF",
45
+ #'utf-16le' => "\xFF\xFE",
46
+ #'utf-32be' => "\x00\x00\xFF\xFE",
47
+ #'utf-32le' => "\xFF\xFE",
48
+ }
49
+
38
50
  class << self
39
51
  # @return [Symbol] the default parser type (defaults to :ruby)
40
52
  attr_reader :parser_type
41
-
53
+
42
54
  def parser_type=(value)
43
55
  @parser_type = validated_parser_type(value)
44
56
  end
45
-
57
+
46
58
  # Parses a path or set of paths
47
- #
59
+ #
48
60
  # @param [String, Array<String>] paths a path, glob, or list of paths to
49
61
  # parse
50
62
  # @param [Array<String, Regexp>] excluded a list of excluded path matchers
51
63
  # @param [Fixnum] level the logger level to use during parsing. See
52
64
  # {YARD::Logger}
53
- # @return the parser object that was used to parse the source.
54
- def parse(paths = ["lib/**/*.rb", "ext/**/*.c"], excluded = [], level = log.level)
65
+ # @return the parser object that was used to parse the source.
66
+ def parse(paths = ["{lib,app}/**/*.rb", "ext/**/*.c"], excluded = [], level = log.level)
55
67
  log.debug("Parsing #{paths.inspect} with `#{parser_type}` parser")
56
68
  excluded = excluded.map do |path|
57
69
  case path
@@ -68,27 +80,27 @@ module YARD
68
80
  parse_in_order(*files.uniq)
69
81
  end
70
82
  end
71
-
83
+
72
84
  # Parses a string +content+
73
- #
85
+ #
74
86
  # @param [String] content the block of code to parse
75
87
  # @param [Symbol] ptype the parser type to use. See {parser_type}.
76
88
  # @return the parser object that was used to parse +content+
77
89
  def parse_string(content, ptype = parser_type)
78
90
  new(ptype).parse(StringIO.new(content))
79
91
  end
80
-
92
+
81
93
  # Tokenizes but does not parse the block of code
82
- #
94
+ #
83
95
  # @param [String] content the block of code to tokenize
84
96
  # @param [Symbol] ptype the parser type to use. See {parser_type}.
85
97
  # @return [Array] a list of tokens
86
98
  def tokenize(content, ptype = parser_type)
87
99
  new(ptype).tokenize(content)
88
100
  end
89
-
101
+
90
102
  # Registers a new parser type.
91
- #
103
+ #
92
104
  # @example Registering a parser for "java" files
93
105
  # SourceParser.register_parser_type :java, JavaParser, 'java'
94
106
  # @param [Symbol] type a symbolic name for the parser type
@@ -104,7 +116,7 @@ module YARD
104
116
  parser_type_extensions[type.to_sym] = extensions if extensions
105
117
  parser_types[type.to_sym] = parser_klass
106
118
  end
107
-
119
+
108
120
  # @return [Hash{Symbol=>Object}] a list of registered parser types
109
121
  # @private
110
122
  # @since 0.5.6
@@ -112,7 +124,7 @@ module YARD
112
124
  undef parser_types
113
125
  def parser_types; @@parser_types ||= {} end
114
126
  def parser_types=(value) @@parser_types = value end
115
-
127
+
116
128
  # @return [Hash] a list of registered parser type extensions
117
129
  # @private
118
130
  # @since 0.5.6
@@ -123,7 +135,7 @@ module YARD
123
135
 
124
136
  # Finds a parser type that is registered for the extension. If no
125
137
  # type is found, the default Ruby type is returned.
126
- #
138
+ #
127
139
  # @return [Symbol] the parser type to be used for the extension
128
140
  # @since 0.5.6
129
141
  def parser_type_for_extension(extension)
@@ -132,10 +144,10 @@ module YARD
132
144
  end
133
145
  validated_parser_type(type ? type.first : :ruby)
134
146
  end
135
-
147
+
136
148
  # Returns the validated parser type. Basically, enforces that :ruby
137
149
  # type is never set if the Ripper library is not available
138
- #
150
+ #
139
151
  # @param [Symbol] type the parser type to set
140
152
  # @return [Symbol] the validated parser type
141
153
  # @private
@@ -143,16 +155,193 @@ module YARD
143
155
  !defined?(::Ripper) && type == :ruby ? :ruby18 : type
144
156
  end
145
157
 
146
- private
158
+ # @group Parser Callbacks
159
+
160
+ # Registers a callback to be called before a list of files is parsed
161
+ # via {parse}. The block passed to this method will be called on
162
+ # subsequent parse calls.
163
+ #
164
+ # @example Installing a simple callback
165
+ # SourceParser.before_parse_list do |files, globals|
166
+ # puts "Starting to parse..."
167
+ # end
168
+ # YARD.parse('lib/**/*.rb')
169
+ # # prints "Starting to parse..."
170
+ #
171
+ # @example Setting global state
172
+ # SourceParser.before_parse_list do |files, globals|
173
+ # globals.method_count = 0
174
+ # end
175
+ # SourceParser.after_parse_list do |files, globals|
176
+ # puts "Found #{globals.method_count} methods"
177
+ # end
178
+ # class MyCountHandler < Handlers::Ruby::Base
179
+ # handles :def, :defs
180
+ # process { globals.method_count += 1 }
181
+ # end
182
+ # YARD.parse
183
+ # # Prints: "Found 37 methods"
184
+ #
185
+ # @example Using a global callback to cancel parsing
186
+ # SourceParser.before_parse_list do |files, globals|
187
+ # return false if files.include?('foo.rb')
188
+ # end
189
+ #
190
+ # YARD.parse(['foo.rb', 'bar.rb']) # callback cancels this method
191
+ # YARD.parse('bar.rb') # parses normally
192
+ #
193
+ # @yield [files, globals] the yielded block is called once before
194
+ # parsing all files
195
+ # @yieldparam [Array<String>] files the list of files that will be parsed.
196
+ # @yieldparam [OpenStruct] globals a global structure to store arbitrary
197
+ # state for post processing (see {Handlers::Processor#globals})
198
+ # @yieldreturn [Boolean] if the block returns +false+, parsing is
199
+ # cancelled.
200
+ # @return [Proc] the yielded block
201
+ # @see after_parse_list
202
+ # @see before_parse_file
203
+ # @since 0.7.0
204
+ def before_parse_list(&block)
205
+ before_parse_list_callbacks << block
206
+ end
207
+
208
+ # Registers a callback to be called after a list of files is parsed
209
+ # via {parse}. The block passed to this method will be called on
210
+ # subsequent parse calls.
211
+ #
212
+ # @example Printing results after parsing occurs
213
+ # SourceParser.after_parse_list do
214
+ # puts "Finished parsing!"
215
+ # end
216
+ # YARD.parse
217
+ # # Prints "Finished parsing!" after parsing files
218
+ # @yield [files, globals] the yielded block is called once before
219
+ # parsing all files
220
+ # @yieldparam [Array<String>] files the list of files that will be parsed.
221
+ # @yieldparam [OpenStruct] globals a global structure to store arbitrary
222
+ # state for post processing (see {Handlers::Processor#globals})
223
+ # @yieldreturn [void] the return value for the block is ignored.
224
+ # @return [Proc] the yielded block
225
+ # @see before_parse_list
226
+ # @see before_parse_file
227
+ # @since 0.7.0
228
+ def after_parse_list(&block)
229
+ after_parse_list_callbacks << block
230
+ end
231
+
232
+ # Registers a callback to be called before an individual file is parsed.
233
+ # The block passed to this method will be called on subsequent parse
234
+ # calls.
235
+ #
236
+ # To register a callback that is called before the entire list of files
237
+ # is processed, see {before_parse_list}.
238
+ #
239
+ # @example Installing a simple callback
240
+ # SourceParser.before_parse_file do |parser|
241
+ # puts "I'm parsing #{parser.file}"
242
+ # end
243
+ # YARD.parse('lib/**/*.rb')
244
+ # # prints:
245
+ # "I'm parsing lib/foo.rb"
246
+ # "I'm parsing lib/foo_bar.rb"
247
+ # "I'm parsing lib/last_file.rb"
248
+ #
249
+ # @example Cancel parsing of any test_*.rb files
250
+ # SourceParser.before_parse_file do |parser|
251
+ # return false if parser.file =~ /^test_.+\.rb$/
252
+ # end
253
+ #
254
+ # @yield [parser] the yielded block is called once before each
255
+ # file that is parsed. This might happen many times for a single
256
+ # codebase.
257
+ # @yieldparam [SourceParser] parser the parser object that will {#parse}
258
+ # the file.
259
+ # @yieldreturn [Boolean] if the block returns +false+, parsing for
260
+ # the file is cancelled.
261
+ # @return [Proc] the yielded block
262
+ # @see after_parse_file
263
+ # @see before_parse_list
264
+ # @since 0.7.0
265
+ def before_parse_file(&block)
266
+ before_parse_file_callbacks << block
267
+ end
268
+
269
+ # Registers a callback to be called after an individual file is parsed.
270
+ # The block passed to this method will be called on subsequent parse
271
+ # calls.
272
+ #
273
+ # To register a callback that is called after the entire list of files
274
+ # is processed, see {after_parse_list}.
275
+ #
276
+ # @example Printing the length of each file after it is parsed
277
+ # SourceParser.after_parse_file do |parser|
278
+ # puts "#{parser.file} is #{parser.contents.size} characters"
279
+ # end
280
+ # YARD.parse('lib/**/*.rb')
281
+ # # prints:
282
+ # "lib/foo.rb is 1240 characters"
283
+ # "lib/foo_bar.rb is 248 characters"
284
+ #
285
+ # @yield [parser] the yielded block is called once after each file
286
+ # that is parsed. This might happen many times for a single codebase.
287
+ # @yieldparam [SourceParser] parser the parser object that parsed
288
+ # the file.
289
+ # @yieldreturn [void] the return value for the block is ignored.
290
+ # @return [Proc] the yielded block
291
+ # @see before_parse_file
292
+ # @see after_parse_list
293
+ # @since 0.7.0
294
+ def after_parse_file(&block)
295
+ after_parse_file_callbacks << block
296
+ end
297
+
298
+ # @return [Array<Proc>] the list of callbacks to be called before
299
+ # parsing a list of files. Should only be used for testing.
300
+ # @since 0.7.0
301
+ def before_parse_list_callbacks
302
+ @before_parse_list_callbacks ||= []
303
+ end
304
+
305
+ # @return [Array<Proc>] the list of callbacks to be called after
306
+ # parsing a list of files. Should only be used for testing.
307
+ # @since 0.7.0
308
+ def after_parse_list_callbacks
309
+ @after_parse_list_callbacks ||= []
310
+ end
311
+
312
+ # @return [Array<Proc>] the list of callbacks to be called before
313
+ # parsing a file. Should only be used for testing.
314
+ # @since 0.7.0
315
+ def before_parse_file_callbacks
316
+ @before_parse_file_callbacks ||= []
317
+ end
147
318
 
319
+ # @return [Array<Proc>] the list of callbacks to be called after
320
+ # parsing a file. Should only be used for testing.
321
+ # @since 0.7.0
322
+ def after_parse_file_callbacks
323
+ @after_parse_file_callbacks ||= []
324
+ end
325
+
326
+ # @endgroup
327
+
328
+ private
329
+
148
330
  # Parses a list of files in a queue. If a {LoadOrderError} is caught,
149
331
  # the file is moved to the back of the queue with a Continuation object
150
332
  # that can continue processing the file.
151
- #
333
+ #
152
334
  # @param [Array<String>] files a list of files to queue for parsing
153
335
  # @return [void]
154
336
  def parse_in_order(*files)
337
+ global_state = OpenStruct.new
155
338
  files = files.sort_by {|x| x.length if x }
339
+ files_copy = files.dup
340
+
341
+ before_parse_list_callbacks.each do |cb|
342
+ return if cb.call(files_copy, global_state) == false
343
+ end
344
+
156
345
  while file = files.shift
157
346
  begin
158
347
  if file.is_a?(Array) && file.last.is_a?(Continuation)
@@ -160,13 +349,17 @@ module YARD
160
349
  file.last.call
161
350
  elsif file.is_a?(String)
162
351
  log.debug("Processing #{file}...")
163
- new(parser_type, true).parse(file)
352
+ new(parser_type, true, global_state).parse(file)
164
353
  end
165
354
  rescue LoadOrderError => e
166
355
  # Out of order file. Push the context to the end and we'll call it
167
356
  files.push([file, e.message])
168
357
  end
169
358
  end
359
+
360
+ after_parse_list_callbacks.each do |cb|
361
+ cb.call(files_copy, global_state)
362
+ end
170
363
  end
171
364
  end
172
365
 
@@ -175,21 +368,32 @@ module YARD
175
368
  register_parser_type :c, CParser, ['c', 'cc', 'cxx', 'cpp']
176
369
 
177
370
  self.parser_type = :ruby
178
-
179
- # The filename being parsed by the parser.
371
+
372
+ # @return [String] the filename being parsed by the parser.
180
373
  attr_reader :file
181
-
182
- # The parser type associated with the parser instance. This should
183
- # be set by the {#initialize constructor}.
374
+
375
+ # @return [Symbol] the parser type associated with the parser instance.
376
+ # This should be set by the {#initialize constructor}.
184
377
  attr_reader :parser_type
378
+
379
+ # @return [OpenStruct] an open struct containing arbitrary global state
380
+ # shared between files and handlers.
381
+ # @since 0.7.0
382
+ attr_reader :globals
383
+
384
+ # @return [String] the contents of the file to be parsed
385
+ # @since 0.7.0
386
+ attr_reader :contents
185
387
 
186
388
  # Creates a new parser object for code parsing with a specific parser type.
187
- #
389
+ #
188
390
  # @param [Symbol] parser_type the parser type to use
189
391
  # @param [Boolean] load_order_errors whether or not to raise the {LoadOrderError}
190
- def initialize(parser_type = SourceParser.parser_type, load_order_errors = false)
392
+ # @param [OpenStruct] globals global state to be re-used across separate source files
393
+ def initialize(parser_type = SourceParser.parser_type, load_order_errors = false, globals = nil)
191
394
  @load_order_errors = load_order_errors
192
395
  @file = '(stdin)'
396
+ @globals = globals || OpenStruct.new
193
397
  self.parser_type = parser_type
194
398
  end
195
399
 
@@ -214,28 +418,39 @@ module YARD
214
418
  else
215
419
  content = content.read if content.respond_to? :read
216
420
  end
217
-
421
+
422
+ @contents = content
218
423
  @parser = parser_class.new(content, file)
424
+
425
+ self.class.before_parse_file_callbacks.each do |cb|
426
+ return @parser if cb.call(self) == false
427
+ end
428
+
219
429
  @parser.parse
220
430
  post_process
431
+
432
+ self.class.after_parse_file_callbacks.each do |cb|
433
+ cb.call(self)
434
+ end
435
+
221
436
  @parser
222
437
  rescue ArgumentError, NotImplementedError => e
223
438
  log.warn("Cannot parse `#{file}': #{e.message}")
224
439
  rescue ParserSyntaxError => e
225
440
  log.warn(e.message.capitalize)
226
441
  end
227
-
442
+
228
443
  # Tokenizes but does not parse the block of code using the current {#parser_type}
229
- #
444
+ #
230
445
  # @param [String] content the block of code to tokenize
231
446
  # @return [Array] a list of tokens
232
447
  def tokenize(content)
233
448
  @parser = parser_class.new(content, file)
234
449
  @parser.tokenize
235
450
  end
236
-
451
+
237
452
  private
238
-
453
+
239
454
  # Searches for encoding line and forces encoding
240
455
  # @since 0.5.3
241
456
  def convert_encoding(content)
@@ -243,25 +458,35 @@ module YARD
243
458
  if content =~ ENCODING_LINE
244
459
  content.force_encoding($1)
245
460
  else
461
+ old_encoding = content.encoding
462
+ content.force_encoding('binary')
463
+ ENCODING_BYTE_ORDER_MARKS.each do |encoding, bom|
464
+ bom.force_encoding('binary')
465
+ if content[0,bom.size] == bom
466
+ content.force_encoding(encoding)
467
+ return content
468
+ end
469
+ end
470
+ content.force_encoding(old_encoding)
246
471
  content
247
472
  end
248
473
  end
249
474
 
250
475
  # Runs a {Handlers::Processor} object to post process the parsed statements.
251
- # @return [void]
476
+ # @return [void]
252
477
  def post_process
253
478
  return unless @parser.respond_to? :enumerator
254
479
  return unless enumerator = @parser.enumerator
255
- post = Handlers::Processor.new(@file, @load_order_errors, @parser_type)
480
+ post = Handlers::Processor.new(@file, @load_order_errors, @parser_type, @globals)
256
481
  post.process(enumerator)
257
482
  end
258
483
 
259
484
  def parser_type=(value)
260
485
  @parser_type = self.class.validated_parser_type(value)
261
486
  end
262
-
487
+
263
488
  # Guesses the parser type to use depending on the file extension.
264
- #
489
+ #
265
490
  # @param [String] filename the filename to use to guess the parser type
266
491
  # @return [Symbol] a parser type that matches the filename
267
492
  def parser_type_for_filename(filename)
@@ -269,7 +494,7 @@ module YARD
269
494
  type = self.class.parser_type_for_extension(ext)
270
495
  parser_type == :ruby18 && type == :ruby ? :ruby18 : type
271
496
  end
272
-
497
+
273
498
  # @since 0.5.6
274
499
  def parser_class
275
500
  klass = self.class.parser_types[parser_type]