yard 0.6.4 → 0.6.5

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 (107) hide show
  1. data/ChangeLog +341 -0
  2. data/LICENSE +1 -1
  3. data/README.md +31 -6
  4. data/Rakefile +22 -3
  5. data/docs/Tags.md +5 -1
  6. data/docs/WhatsNew.md +18 -1
  7. data/lib/rubygems_plugin.rb +3 -99
  8. data/lib/yard.rb +1 -1
  9. data/lib/yard/autoload.rb +37 -35
  10. data/lib/yard/cli/config.rb +25 -2
  11. data/lib/yard/cli/graph.rb +1 -1
  12. data/lib/yard/cli/yardoc.rb +4 -0
  13. data/lib/yard/code_objects/base.rb +17 -9
  14. data/lib/yard/code_objects/method_object.rb +0 -9
  15. data/lib/yard/code_objects/proxy.rb +6 -0
  16. data/lib/yard/docstring.rb +5 -0
  17. data/lib/yard/handlers/base.rb +3 -1
  18. data/lib/yard/handlers/processor.rb +1 -1
  19. data/lib/yard/handlers/ruby/alias_handler.rb +9 -8
  20. data/lib/yard/handlers/ruby/class_handler.rb +3 -3
  21. data/lib/yard/handlers/ruby/legacy/alias_handler.rb +9 -7
  22. data/lib/yard/handlers/ruby/legacy/method_handler.rb +7 -0
  23. data/lib/yard/handlers/ruby/legacy/private_constant_handler.rb +21 -0
  24. data/lib/yard/handlers/ruby/method_condition_handler.rb +1 -1
  25. data/lib/yard/handlers/ruby/method_handler.rb +7 -0
  26. data/lib/yard/handlers/ruby/private_constant_handler.rb +36 -0
  27. data/lib/yard/parser/ruby/ast_node.rb +41 -24
  28. data/lib/yard/parser/ruby/legacy/ruby_parser.rb +3 -0
  29. data/lib/yard/parser/ruby/legacy/statement.rb +2 -0
  30. data/lib/yard/parser/ruby/legacy/statement_list.rb +27 -4
  31. data/lib/yard/parser/ruby/ruby_parser.rb +71 -36
  32. data/lib/yard/parser/source_parser.rb +11 -10
  33. data/lib/yard/registry.rb +62 -24
  34. data/lib/yard/registry_store.rb +18 -5
  35. data/lib/yard/rubygems/doc_manager.rb +75 -0
  36. data/lib/yard/rubygems/specification.rb +23 -0
  37. data/lib/yard/serializers/process_serializer.rb +1 -1
  38. data/lib/yard/serializers/yardoc_serializer.rb +7 -2
  39. data/lib/yard/server/commands/display_object_command.rb +1 -1
  40. data/lib/yard/server/commands/library_command.rb +2 -2
  41. data/lib/yard/tags/default_factory.rb +1 -1
  42. data/lib/yard/tags/library.rb +2 -2
  43. data/lib/yard/templates/helpers/base_helper.rb +19 -0
  44. data/lib/yard/templates/helpers/html_helper.rb +22 -9
  45. data/lib/yard/templates/helpers/html_syntax_highlight_helper.rb +28 -0
  46. data/lib/yard/templates/helpers/markup_helper.rb +14 -22
  47. data/lib/yard/templates/template.rb +1 -1
  48. data/spec/cli/config_spec.rb +20 -0
  49. data/spec/cli/yardoc_spec.rb +12 -0
  50. data/spec/code_objects/base_spec.rb +13 -0
  51. data/spec/code_objects/proxy_spec.rb +9 -0
  52. data/spec/config_spec.rb +4 -1
  53. data/spec/docstring_spec.rb +5 -0
  54. data/spec/handlers/alias_handler_spec.rb +14 -1
  55. data/spec/handlers/attribute_handler_spec.rb +1 -1
  56. data/spec/handlers/base_spec.rb +21 -21
  57. data/spec/handlers/class_condition_handler_spec.rb +1 -1
  58. data/spec/handlers/class_handler_spec.rb +1 -1
  59. data/spec/handlers/class_variable_handler_spec.rb +1 -1
  60. data/spec/handlers/constant_handler_spec.rb +2 -2
  61. data/spec/handlers/examples/alias_handler_001.rb.txt +14 -0
  62. data/spec/handlers/examples/method_handler_001.rb.txt +6 -0
  63. data/spec/handlers/examples/module_handler_001.rb.txt +4 -0
  64. data/spec/handlers/examples/private_constant_handler_001.rb.txt +8 -0
  65. data/spec/handlers/exception_handler_spec.rb +1 -1
  66. data/spec/handlers/extend_handler_spec.rb +1 -1
  67. data/spec/handlers/method_condition_handler_spec.rb +1 -1
  68. data/spec/handlers/method_handler_spec.rb +7 -1
  69. data/spec/handlers/mixin_handler_spec.rb +1 -1
  70. data/spec/handlers/module_handler_spec.rb +5 -1
  71. data/spec/handlers/private_constant_handler_spec.rb +24 -0
  72. data/spec/handlers/process_handler_spec.rb +1 -1
  73. data/spec/handlers/ruby/base_spec.rb +4 -4
  74. data/spec/handlers/visibility_handler_spec.rb +1 -1
  75. data/spec/handlers/yield_handler_spec.rb +1 -1
  76. data/spec/parser/base_spec.rb +3 -5
  77. data/spec/parser/c_parser_spec.rb +1 -1
  78. data/spec/parser/ruby/ast_node_spec.rb +23 -26
  79. data/spec/parser/ruby/legacy/statement_list_spec.rb +9 -0
  80. data/spec/parser/ruby/ruby_parser_spec.rb +179 -177
  81. data/spec/parser/source_parser_spec.rb +41 -7
  82. data/spec/rake/yardoc_task_spec.rb +3 -3
  83. data/spec/registry_spec.rb +52 -0
  84. data/spec/registry_store_spec.rb +71 -1
  85. data/spec/serializers/yardoc_serializer_spec.rb +18 -7
  86. data/spec/server/rack_adapter_spec.rb +2 -2
  87. data/spec/spec_helper.rb +10 -0
  88. data/spec/tags/default_factory_spec.rb +122 -120
  89. data/spec/templates/helpers/base_helper_spec.rb +38 -14
  90. data/spec/templates/helpers/html_helper_spec.rb +19 -0
  91. data/spec/templates/helpers/html_syntax_highlight_helper_spec.rb +10 -6
  92. data/spec/templates/helpers/markup_helper_spec.rb +21 -5
  93. data/templates/default/class/dot/superklass.erb +1 -1
  94. data/templates/default/docstring/setup.rb +1 -1
  95. data/templates/default/fulldoc/html/css/style.css +12 -4
  96. data/templates/default/fulldoc/html/js/app.js +1 -1
  97. data/templates/default/fulldoc/html/js/jquery.js +5 -143
  98. data/templates/default/layout/html/files.erb +11 -0
  99. data/templates/default/layout/html/headers.erb +1 -1
  100. data/templates/default/layout/html/index.erb +2 -49
  101. data/templates/default/layout/html/listing.erb +4 -0
  102. data/templates/default/layout/html/objects.erb +32 -0
  103. data/templates/default/layout/html/setup.rb +1 -1
  104. data/templates/default/module/dot/info.erb +1 -1
  105. data/templates/default/module/dot/setup.rb +1 -1
  106. metadata +15 -6
  107. data/lib/yard/templates/helpers/html_syntax_highlight_helper18.rb +0 -25
@@ -24,6 +24,10 @@ module YARD
24
24
 
25
25
  # @return [String] the raw documentation (including raw tag text)
26
26
  attr_reader :all
27
+
28
+ # @return [Boolean] whether the docstring was started with "##"
29
+ attr_reader :hash_flag
30
+ def hash_flag=(v) @hash_flag = v == nil ? false : v end
27
31
 
28
32
  # Matches a tag at the start of a comment line
29
33
  META_MATCH = /^@([a-z_0-9]+)(?:\s+(.*))?$/i
@@ -43,6 +47,7 @@ module YARD
43
47
  def initialize(content = '', object = nil)
44
48
  @object = object
45
49
  @summary = nil
50
+ @hash_flag = false
46
51
 
47
52
  self.all = content
48
53
  end
@@ -4,7 +4,7 @@ module YARD
4
4
  # an operation on an object's namespace but the namespace could
5
5
  # not be resolved.
6
6
  class NamespaceMissingError < Parser::UndocumentableError
7
- # The object the error occured on
7
+ # The object the error occurred on
8
8
  # @return [CodeObjects::Base] a code object
9
9
  attr_accessor :object
10
10
 
@@ -394,6 +394,7 @@ module YARD
394
394
 
395
395
  # Add docstring if there is one.
396
396
  object.docstring = statement.comments if statement.comments
397
+ object.docstring.hash_flag = statement.comments_hash_flag
397
398
  object.docstring.line_range = statement.comments_range
398
399
 
399
400
  # Add group information
@@ -446,6 +447,7 @@ module YARD
446
447
  # finishes processing.
447
448
  def ensure_loaded!(object, max_retries = 1)
448
449
  return if object.root?
450
+ return object unless object.is_a?(Proxy)
449
451
  unless parser.load_order_errors
450
452
  if object.is_a?(Proxy)
451
453
  raise NamespaceMissingError, object
@@ -14,7 +14,7 @@ module YARD
14
14
  class Processor
15
15
 
16
16
  class << self
17
- # Registeres a new namespace for handlers of the given type.
17
+ # Registers a new namespace for handlers of the given type.
18
18
  # @since 0.6.0
19
19
  def register_handler_namespace(type, ns)
20
20
  namespace_for_handler[type] = ns
@@ -5,7 +5,7 @@ class YARD::Handlers::Ruby::AliasHandler < YARD::Handlers::Ruby::Base
5
5
  process do
6
6
  names = []
7
7
  if statement.type == :alias
8
- names = statement.map {|o| o.jump(:ident, :op, :kw, :const).first }
8
+ names = statement.map {|o| o.jump(:ident, :op, :kw, :const).source }
9
9
  elsif statement.call?
10
10
  statement.parameters(false).each do |obj|
11
11
  case obj.type
@@ -24,14 +24,15 @@ class YARD::Handlers::Ruby::AliasHandler < YARD::Handlers::Ruby::Base
24
24
  o.visibility = visibility
25
25
  o.scope = scope
26
26
  o.add_file(parser.file, statement.line)
27
- o.docstring = statement.comments
27
+ end
28
28
 
29
- if old_obj
30
- o.signature = old_obj.signature
31
- o.source = old_obj.source
32
- else
33
- o.signature = "def #{new_meth}" # this is all we know.
34
- end
29
+ if old_obj
30
+ new_obj.signature = old_obj.signature
31
+ new_obj.source = old_obj.source
32
+ new_obj.docstring = old_obj.docstring + YARD::Docstring.new(statement.comments)
33
+ new_obj.docstring.object = new_obj
34
+ else
35
+ new_obj.signature = "def #{new_meth}" # this is all we know.
35
36
  end
36
37
 
37
38
  namespace.aliases[new_obj] = old_meth
@@ -23,14 +23,14 @@ class YARD::Handlers::Ruby::ClassHandler < YARD::Handlers::Ruby::Base
23
23
  elsif klass
24
24
  create_attributes(klass, members_from_tags(klass))
25
25
  end
26
- parse_block(statement[2], namespace: klass)
26
+ parse_block(statement[2], :namespace => klass)
27
27
 
28
28
  if undocsuper
29
29
  raise YARD::Parser::UndocumentableError, 'superclass (class was added without superclass)'
30
30
  end
31
31
  elsif statement.type == :sclass
32
32
  if statement[0] == s(:var_ref, s(:kw, "self"))
33
- parse_block(statement[1], namespace: namespace, scope: :class)
33
+ parse_block(statement[1], :namespace => namespace, :scope => :class)
34
34
  else
35
35
  classname = statement[0].source
36
36
  proxy = Proxy.new(namespace, classname)
@@ -46,7 +46,7 @@ class YARD::Handlers::Ruby::ClassHandler < YARD::Handlers::Ruby::Base
46
46
 
47
47
  if classname[0,1] =~ /[A-Z]/
48
48
  register ClassObject.new(namespace, classname) if Proxy === proxy
49
- parse_block(statement[1], namespace: proxy, scope: :class)
49
+ parse_block(statement[1], :namespace => proxy, :scope => :class)
50
50
  else
51
51
  raise YARD::Parser::UndocumentableError, "class '#{classname}'"
52
52
  end
@@ -5,7 +5,7 @@ class YARD::Handlers::Ruby::Legacy::AliasHandler < YARD::Handlers::Ruby::Legacy:
5
5
  process do
6
6
  if TkALIAS === statement.tokens.first
7
7
  tokens = statement.tokens[2..-1].to_s.split(/\s+/)
8
- names = [tokens[0], tokens[1]].map {|t| t.gsub(/^:/, '') }
8
+ names = [tokens[0], tokens[1]].map {|t| t.gsub(/^:(['"])?(.+?)\1?$|^(:)(.+)/, '\2') }
9
9
  else
10
10
  names = tokval_list(statement.tokens[2..-1], :attr)
11
11
  end
@@ -18,13 +18,15 @@ class YARD::Handlers::Ruby::Legacy::AliasHandler < YARD::Handlers::Ruby::Legacy:
18
18
  o.scope = scope
19
19
  o.add_file(parser.file, statement.tokens.first.line_no)
20
20
  o.docstring = statement.comments
21
+ end
21
22
 
22
- if old_obj
23
- o.signature = old_obj.signature
24
- o.source = old_obj.source
25
- else
26
- o.signature = "def #{new_meth}" # this is all we know.
27
- end
23
+ if old_obj
24
+ new_obj.signature = old_obj.signature
25
+ new_obj.source = old_obj.source
26
+ new_obj.docstring = old_obj.docstring + YARD::Docstring.new(statement.comments)
27
+ new_obj.docstring.object = new_obj
28
+ else
29
+ new_obj.signature = "def #{new_meth}" # this is all we know.
28
30
  end
29
31
 
30
32
  namespace.aliases[new_obj] = old_meth
@@ -31,6 +31,13 @@ class YARD::Handlers::Ruby::Legacy::MethodHandler < YARD::Handlers::Ruby::Legacy
31
31
  o.explicit = true
32
32
  o.parameters = args
33
33
  end
34
+
35
+ # delete any aliases referencing old method
36
+ nobj.aliases.each do |aobj, name|
37
+ next unless name == obj.name
38
+ nobj.aliases.delete(aobj)
39
+ end if nobj.is_a?(NamespaceObject)
40
+
34
41
  if mscope == :instance && meth == "initialize"
35
42
  unless obj.has_tag?(:return)
36
43
  obj.docstring.add_tag(YARD::Tags::Tag.new(:return,
@@ -0,0 +1,21 @@
1
+ # (see Ruby::PrivateConstantHandler)
2
+ class YARD::Handlers::Ruby::Legacy::PrivateConstantHandler < YARD::Handlers::Ruby::Legacy::Base
3
+ namespace_only
4
+ handles /\Aprivate_constant(\s|\(|$)/
5
+
6
+ process do
7
+ tokval_list(statement.tokens[2..-1], :attr, TkCONSTANT).each do |name|
8
+ privatize_constant name
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def privatize_constant(name)
15
+ const = Proxy.new(namespace, name)
16
+ ensure_loaded!(const)
17
+ const.visibility = :private
18
+ rescue NamespaceMissingError
19
+ raise UndocumentableError, "private visibility set on unrecognized constant: #{name}"
20
+ end
21
+ end
@@ -3,6 +3,6 @@ class YARD::Handlers::Ruby::MethodConditionHandler < YARD::Handlers::Ruby::Base
3
3
  handles :if_mod, :unless_mod
4
4
 
5
5
  process do
6
- parse_block(statement.then_block, owner: owner)
6
+ parse_block(statement.then_block, :owner => owner)
7
7
  end
8
8
  end
@@ -28,6 +28,13 @@ class YARD::Handlers::Ruby::MethodHandler < YARD::Handlers::Ruby::Base
28
28
  o.explicit = true
29
29
  o.parameters = args
30
30
  end
31
+
32
+ # delete any aliases referencing old method
33
+ nobj.aliases.each do |aobj, name|
34
+ next unless name == obj.name
35
+ nobj.aliases.delete(aobj)
36
+ end if nobj.is_a?(NamespaceObject)
37
+
31
38
  if mscope == :instance && meth == "initialize"
32
39
  unless obj.has_tag?(:return)
33
40
  obj.docstring.add_tag(YARD::Tags::Tag.new(:return,
@@ -0,0 +1,36 @@
1
+ # Sets visibility of a constant (class, module, const)
2
+ class YARD::Handlers::Ruby::PrivateConstantHandler < YARD::Handlers::Ruby::Base
3
+ namespace_only
4
+ handles method_call(:private_constant)
5
+
6
+ process do
7
+ errors = []
8
+ statement.parameters.each do |param|
9
+ next unless AstNode === param
10
+ begin
11
+ privatize_constant(param)
12
+ rescue UndocumentableError => err
13
+ errors << err.message
14
+ end
15
+ end
16
+ if errors.size > 0
17
+ msg = errors.size == 1 ? ": #{errors[0]}" : "s: #{errors.join(", ")}"
18
+ raise UndocumentableError, "private constant#{msg} for #{namespace.path}"
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def privatize_constant(node)
25
+ if node.literal? || (node.type == :var_ref && node[0].type == :const)
26
+ node = node.jump(:tstring_content, :const)
27
+ const = Proxy.new(namespace, node[0])
28
+ ensure_loaded!(const)
29
+ const.visibility = :private
30
+ else
31
+ raise UndocumentableError, "invalid argument to private_constant: #{node.source}"
32
+ end
33
+ rescue NamespaceMissingError
34
+ raise UndocumentableError, "private visibility set on unrecognized constant: #{node[0]}"
35
+ end
36
+ end
@@ -38,10 +38,12 @@ module YARD
38
38
  # list, like Strings or Symbols representing names. To return only
39
39
  # the AstNode children of the node, use {#children}.
40
40
  class AstNode < Array
41
+ attr_accessor :docstring_hash_flag
41
42
  attr_accessor :docstring, :docstring_range, :source, :group
42
43
  attr_writer :source_range, :line_range, :file, :full_source
43
44
  alias comments docstring
44
45
  alias comments_range docstring_range
46
+ alias comments_hash_flag docstring_hash_flag
45
47
  alias to_s source
46
48
 
47
49
  # @return [Symbol] the node's unique symbolic type
@@ -85,13 +87,13 @@ module YARD
85
87
 
86
88
  # List of all known keywords
87
89
  # @return [Hash]
88
- KEYWORDS = { class: true, alias: true, lambda: true, do_block: true,
89
- def: true, defs: true, begin: true, rescue: true, rescue_mod: true,
90
- if: true, if_mod: true, else: true, elsif: true, case: true,
91
- when: true, next: true, break: true, retry: true, redo: true,
92
- return: true, throw: true, catch: true, until: true, until_mod: true,
93
- while: true, while_mod: true, yield: true, yield0: true, zsuper: true,
94
- unless: true, unless_mod: true, for: true, super: true, return0: true }
90
+ KEYWORDS = { :class => true, :alias => true, :lambda => true, :do_block => true,
91
+ :def => true, :defs => true, :begin => true, :rescue => true, :rescue_mod => true,
92
+ :if => true, :if_mod => true, :else => true, :elsif => true, :case => true,
93
+ :when => true, :next => true, :break => true, :retry => true, :redo => true,
94
+ :return => true, :throw => true, :catch => true, :until => true, :until_mod => true,
95
+ :while => true, :while_mod => true, :yield => true, :yield0 => true, :zsuper => true,
96
+ :unless => true, :unless_mod => true, :for => true, :super => true, :return0 => true }
95
97
 
96
98
  # @group Creating an AstNode
97
99
 
@@ -108,10 +110,16 @@ module YARD
108
110
  MethodCallNode
109
111
  when :if, :elsif, :if_mod, :unless, :unless_mod
110
112
  ConditionalNode
111
- when /_ref\Z/
112
- ReferenceNode
113
113
  else
114
- AstNode
114
+ if type.to_s =~ /_ref\Z/
115
+ ReferenceNode
116
+ elsif type.to_s =~ /_literal\Z/
117
+ LiteralNode
118
+ elsif KEYWORDS.has_key?(type)
119
+ KeywordNode
120
+ else
121
+ AstNode
122
+ end
115
123
  end
116
124
  end
117
125
 
@@ -147,7 +155,7 @@ module YARD
147
155
 
148
156
  # @group Traversing a Node
149
157
 
150
- # Searches through the node and all descendents and returns the
158
+ # Searches through the node and all descendants and returns the
151
159
  # first node with a type matching any of +node_types+, otherwise
152
160
  # returns the original node (self).
153
161
  #
@@ -177,10 +185,10 @@ module YARD
177
185
  @children ||= select {|e| AstNode === e }
178
186
  end
179
187
 
180
- # Traverses the object and yields each node (including descendents) in order.
188
+ # Traverses the object and yields each node (including descendants) in order.
181
189
  #
182
- # @yield each descendent node in order
183
- # @yieldparam [AstNode] self, or a child/descendent node
190
+ # @yield each descendant node in order
191
+ # @yieldparam [AstNode] self, or a child/descendant node
184
192
  # @return [void]
185
193
  def traverse
186
194
  nodes = [self]
@@ -205,12 +213,12 @@ module YARD
205
213
 
206
214
  # @return [Boolean] whether the node is a literal value
207
215
  def literal?
208
- @literal ||= type =~ /_literal$/ ? true : false
216
+ false
209
217
  end
210
218
 
211
219
  # @return [Boolean] whether the node is a keyword
212
220
  def kw?
213
- @kw ||= KEYWORDS.has_key?(type)
221
+ false
214
222
  end
215
223
 
216
224
  # @return [Boolean] whether the node is a method call
@@ -249,26 +257,27 @@ module YARD
249
257
 
250
258
  # @return [nil] pretty prints the node
251
259
  def pretty_print(q)
252
- objs = [*self.dup, :__last__]
260
+ objs = self.dup + [:__last__]
253
261
  objs.unshift(type) if type && type != :list
254
262
 
255
- options = {}
263
+ options = []
256
264
  if @docstring
257
- options[:docstring] = docstring
265
+ options << ['docstring', docstring]
258
266
  end
259
267
  if @source_range || @line_range
260
- options[:line] = line_range
261
- options[:source] = source_range
268
+ options << ['line', line_range]
269
+ options << ['source', source_range]
262
270
  end
263
271
  objs.pop if options.size == 0
264
272
 
265
273
  q.group(3, 's(', ')') do
266
274
  q.seplist(objs, nil, :each) do |v|
267
275
  if v == :__last__
268
- q.seplist(options, nil, :each) do |k, v2|
276
+ q.seplist(options, nil, :each) do |arr|
277
+ k, v2 = *arr
269
278
  q.group(3) do
270
279
  q.text k
271
- q.group(3) do
280
+ q.group(3) do
272
281
  q.text ': '
273
282
  q.pp v2
274
283
  end
@@ -323,6 +332,14 @@ module YARD
323
332
  end
324
333
  end
325
334
 
335
+ class LiteralNode < AstNode
336
+ def literal?; true end
337
+ end
338
+
339
+ class KeywordNode < AstNode
340
+ def kw?; true end
341
+ end
342
+
326
343
  class ParameterNode < AstNode
327
344
  def required_params; self[0] end
328
345
  def required_end_params; self[3] end
@@ -360,7 +377,7 @@ module YARD
360
377
  end
361
378
  end
362
379
 
363
- class ConditionalNode < AstNode
380
+ class ConditionalNode < KeywordNode
364
381
  def condition?; true end
365
382
  def condition; first end
366
383
  def then_block; self[1] end
@@ -20,6 +20,9 @@ module YARD
20
20
  def enumerator
21
21
  @parse
22
22
  end
23
+
24
+ def encoding_line; @parse.encoding_line end
25
+ def shebang_line; @parse.shebang_line end
23
26
  end
24
27
  end
25
28
  end
@@ -3,11 +3,13 @@ module YARD
3
3
  class Statement
4
4
  attr_reader :tokens, :comments, :block
5
5
  attr_accessor :comments_range, :group
6
+ attr_accessor :comments_hash_flag
6
7
 
7
8
  def initialize(tokens, block = nil, comments = nil)
8
9
  @tokens = tokens
9
10
  @block = block
10
11
  @comments = comments
12
+ @comments_hash_flag = false
11
13
  end
12
14
 
13
15
  def first_line
@@ -2,6 +2,8 @@ module YARD
2
2
  module Parser::Ruby::Legacy
3
3
  class StatementList < Array
4
4
  include RubyToken
5
+
6
+ attr_accessor :shebang_line, :encoding_line
5
7
 
6
8
  # The following list of tokens will require a block to be opened
7
9
  # if used at the beginning of a statement.
@@ -13,6 +15,8 @@ module YARD
13
15
  # @param [TokenList, String] content the tokens to create the list from
14
16
  def initialize(content)
15
17
  @group = nil
18
+ @shebang_line = nil
19
+ @encoding_line = nil
16
20
  if content.is_a? TokenList
17
21
  @tokens = content.dup
18
22
  elsif content.is_a? String
@@ -41,6 +45,7 @@ module YARD
41
45
  @done = false
42
46
  @current_block = nil
43
47
  @comments_line = nil
48
+ @comments_hash_flag = nil
44
49
  @statement, @block, @comments = TokenList.new, nil, nil
45
50
  @last_tk, @last_ns_tk, @before_last_tk, @before_last_ns_tk = nil, nil, nil, nil
46
51
  @first_line = nil
@@ -50,8 +55,10 @@ module YARD
50
55
 
51
56
  @before_last_tk = @last_tk
52
57
  @last_tk = tk # Save last token
53
- @before_last_ns_tk = @last_ns_tk
54
- @last_ns_tk = tk unless [TkSPACE, TkNL, TkEND_OF_SCRIPT].include? tk.class
58
+ unless [TkSPACE, TkNL, TkEND_OF_SCRIPT].include? tk.class
59
+ @before_last_ns_tk = @last_ns_tk
60
+ @last_ns_tk = tk
61
+ end
55
62
  end
56
63
 
57
64
  # Return the code block with starting token and initial comments
@@ -65,6 +72,7 @@ module YARD
65
72
  stmt.group = @group
66
73
  if @comments && @comments_line
67
74
  stmt.comments_range = (@comments_line..(@comments_line + @comments.size - 1))
75
+ stmt.comments_hash_flag = @comments_hash_flag
68
76
  end
69
77
  stmt
70
78
  else
@@ -209,10 +217,24 @@ module YARD
209
217
  # @return [Boolean] whether or not +tk+ was processed as an initial comment
210
218
  def process_initial_comment(tk)
211
219
  if @statement.empty? && (@comments_last_line || 0) < tk.line_no - 2
212
- @comments = nil
220
+ @comments = nil
213
221
  end
214
222
 
215
223
  return unless tk.class == TkCOMMENT
224
+
225
+ case tk.text
226
+ when Parser::SourceParser::SHEBANG_LINE
227
+ if !@last_ns_tk && !@encoding_line
228
+ @shebang_line = tk.text
229
+ return
230
+ end
231
+ when Parser::SourceParser::ENCODING_LINE
232
+ if (@last_ns_tk.class == TkCOMMENT && @last_ns_tk.text == @shebang_line) ||
233
+ !@last_ns_tk
234
+ @encoding_line = tk.text
235
+ return
236
+ end
237
+ end
216
238
  return if !@statement.empty? && @comments
217
239
  return if @first_line && tk.line_no > @first_line
218
240
 
@@ -228,7 +250,8 @@ module YARD
228
250
  @comments += tk.text.gsub(/\A=begin.*\r?\n|\r?\n=end.*\r?\n?\Z/, '').split(/\r?\n/)
229
251
  @comments_last_line = tk.line_no + lines
230
252
  else
231
- @comments << tk.text.gsub(/^#+\s{0,1}/, '')
253
+ @comments << tk.text.gsub(/^(#+)\s{0,1}/, '')
254
+ @comments_hash_flag = $1 == '##' if @comments_hash_flag == nil
232
255
  @comments_last_line = tk.line_no
233
256
  end
234
257
  @comments.pop if @comments.size == 1 && @comments.first =~ /^\s*$/