yard 0.5.5 → 0.5.6
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.
- data/ChangeLog +131 -0
- data/README.md +16 -12
- data/Rakefile +2 -1
- data/docs/GettingStarted.md +1 -107
- data/docs/Tags.md +250 -53
- data/docs/WhatsNew.md +13 -0
- data/lib/rubygems_plugin.rb +18 -17
- data/lib/yard.rb +1 -1
- data/lib/yard/autoload.rb +4 -0
- data/lib/yard/cli/yardoc.rb +5 -4
- data/lib/yard/cli/yri.rb +18 -3
- data/lib/yard/code_objects/base.rb +5 -4
- data/lib/yard/code_objects/class_object.rb +1 -1
- data/lib/yard/code_objects/constant_object.rb +1 -1
- data/lib/yard/code_objects/method_object.rb +3 -2
- data/lib/yard/code_objects/namespace_object.rb +3 -3
- data/lib/yard/code_objects/proxy.rb +4 -0
- data/lib/yard/docstring.rb +3 -2
- data/lib/yard/handlers/base.rb +1 -1
- data/lib/yard/handlers/ruby/class_condition_handler.rb +5 -1
- data/lib/yard/handlers/ruby/class_handler.rb +41 -1
- data/lib/yard/handlers/ruby/legacy/class_condition_handler.rb +5 -1
- data/lib/yard/handlers/ruby/legacy/class_handler.rb +44 -0
- data/lib/yard/handlers/ruby/struct_handler_methods.rb +128 -0
- data/lib/yard/parser/base.rb +55 -0
- data/lib/yard/parser/c_parser.rb +5 -1
- data/lib/yard/parser/ruby/ast_node.rb +4 -4
- data/lib/yard/parser/ruby/legacy/ruby_parser.rb +26 -0
- data/lib/yard/parser/ruby/legacy/statement_list.rb +0 -4
- data/lib/yard/parser/ruby/ruby_parser.rb +45 -2
- data/lib/yard/parser/source_parser.rb +69 -45
- data/lib/yard/serializers/file_system_serializer.rb +1 -1
- data/lib/yard/tags/default_factory.rb +1 -13
- data/lib/yard/tags/library.rb +3 -1
- data/lib/yard/templates/erb_cache.rb +5 -2
- data/lib/yard/templates/helpers/html_helper.rb +1 -0
- data/lib/yard/templates/template.rb +2 -1
- data/spec/cli/yardoc_spec.rb +6 -6
- data/spec/handlers/class_condition_handler_spec.rb +11 -0
- data/spec/handlers/class_handler_spec.rb +121 -0
- data/spec/handlers/examples/class_handler_001.rb.txt +19 -0
- data/spec/parser/base_spec.rb +25 -0
- data/spec/parser/source_parser_spec.rb +63 -0
- data/templates/default/layout/html/setup.rb +1 -1
- data/templates/default/tags/html/option.erb +1 -1
- metadata +21 -5
@@ -0,0 +1,128 @@
|
|
1
|
+
module YARD::Handlers::Ruby::StructHandlerMethods
|
2
|
+
include YARD::CodeObjects
|
3
|
+
|
4
|
+
# Extracts the user's defined @member tag for a given class and its member. Returns
|
5
|
+
# nil if the user did not define a @member tag for this struct entry.
|
6
|
+
#
|
7
|
+
# @param [ClassObject] klass the class whose tags we're searching
|
8
|
+
# @param [String] member the name of the struct member we need
|
9
|
+
# @param [Symbol] type reader method, or writer method?
|
10
|
+
# @return [Tags::Tag, nil] the tag matching the request, or nil if not found
|
11
|
+
def member_tag_for_member(klass, member, type = :read)
|
12
|
+
specific_tag = type == :read ? :attr_reader : :attr_writer
|
13
|
+
(klass.tags(specific_tag) + klass.tags(:attr)).find {|tag| tag.name == member}
|
14
|
+
end
|
15
|
+
|
16
|
+
# Determines whether to create an attribute method based on the class's
|
17
|
+
# tags.
|
18
|
+
#
|
19
|
+
# @param [ClassObject] klass the class whose tags we're searching
|
20
|
+
# @param [String] member the name of the struct member we need
|
21
|
+
# @param [Symbol] type (:read) reader method, or writer method?
|
22
|
+
# @return [Boolean] should the attribute be created?
|
23
|
+
def create_member_method?(klass, member, type = :read)
|
24
|
+
return true if (klass.tags(:attr) + klass.tags(:attr_reader) + klass.tags(:attr_writer)).empty?
|
25
|
+
return true if member_tag_for_member(klass, member, type)
|
26
|
+
return !member_tag_for_member(klass, member, :write) if type == :read
|
27
|
+
return !member_tag_for_member(klass, member, :read)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Gets the return type for the member in a nicely formatted string. Used
|
31
|
+
# to be injected into auto-generated docstrings.
|
32
|
+
#
|
33
|
+
# @param [ClassObject] klass the class whose tags we're searching
|
34
|
+
# @param [String] member the name of the struct member whose return type we need
|
35
|
+
# @return [String] the user-declared type of the struct member, or [Object] if
|
36
|
+
# the user did not define a type for this member.
|
37
|
+
def return_type_from_tag(member_tag)
|
38
|
+
(member_tag && member_tag.types) ? member_tag.types : "Object"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Creates the auto-generated docstring for the getter method of a struct's
|
42
|
+
# member. This is used so the generated documentation will look just like that
|
43
|
+
# of an attribute defined using attr_accessor.
|
44
|
+
#
|
45
|
+
# @param [ClassObject] klass the class whose members we're working with
|
46
|
+
# @param [String] member the name of the member we're generating documentation for
|
47
|
+
# @return [String] a docstring to be attached to the getter method for this member
|
48
|
+
def add_reader_tags(klass, new_method, member)
|
49
|
+
member_tag = member_tag_for_member(klass, member, :read)
|
50
|
+
return_type = return_type_from_tag(member_tag)
|
51
|
+
getter_doc_text = member_tag ? member_tag.text : "Returns the value of attribute #{member}"
|
52
|
+
new_method.docstring.replace(getter_doc_text)
|
53
|
+
new_method.docstring.add_tag YARD::Tags::Tag.new(:return, "the current value of #{member}", return_type)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Creates the auto-generated docstring for the setter method of a struct's
|
57
|
+
# member. This is used so the generated documentation will look just like that
|
58
|
+
# of an attribute defined using attr_accessor.
|
59
|
+
#
|
60
|
+
# @param [ClassObject] klass the class whose members we're working with
|
61
|
+
# @param [String] member the name of the member we're generating documentation for
|
62
|
+
# @return [String] a docstring to be attached to the setter method for this member
|
63
|
+
def add_writer_tags(klass, new_method, member)
|
64
|
+
member_tag = member_tag_for_member(klass, member, :write)
|
65
|
+
return_type = return_type_from_tag(member_tag)
|
66
|
+
setter_doc_text = member_tag ? member_tag.text : "Sets the attribute #{member}"
|
67
|
+
new_method.docstring.replace(setter_doc_text)
|
68
|
+
new_method.docstring.add_tag YARD::Tags::Tag.new(:param, "the value to set the attribute #{member} to.", return_type, "value")
|
69
|
+
new_method.docstring.add_tag YARD::Tags::Tag.new(:return, "the newly set value", return_type)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Creates and registers a class object with the given name and superclass name.
|
73
|
+
# Returns it for further use.
|
74
|
+
#
|
75
|
+
# @param [String] classname the name of the class
|
76
|
+
# @param [String] superclass the name of the superclass
|
77
|
+
# @return [ClassObject] the class object for further processing/method attaching
|
78
|
+
def create_class(classname, superclass)
|
79
|
+
register ClassObject.new(namespace, classname) do |o|
|
80
|
+
o.superclass = superclass if superclass
|
81
|
+
o.superclass.type = :class if o.superclass.is_a?(Proxy)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Creates the setter (writer) method and attaches it to the class as an attribute.
|
86
|
+
# Also sets up the docstring to prettify the documentation output.
|
87
|
+
#
|
88
|
+
# @param [ClassObject] klass the class to attach the method to
|
89
|
+
# @param [String] member the name of the member we're generating a method for
|
90
|
+
def create_writer(klass, member)
|
91
|
+
# We want to convert these members into attributes just like
|
92
|
+
# as if they were declared using attr_accessor.
|
93
|
+
new_meth = register MethodObject.new(klass, "#{member}=", :instance) do |o|
|
94
|
+
o.parameters = [['value', nil]]
|
95
|
+
o.signature ||= "def #{member}=(value)"
|
96
|
+
o.source ||= "#{o.signature}\n @#{member} = value\nend"
|
97
|
+
end
|
98
|
+
add_writer_tags(klass, new_meth, member)
|
99
|
+
klass.attributes[:instance][member][:write] = new_meth
|
100
|
+
end
|
101
|
+
|
102
|
+
# Creates the getter (reader) method and attaches it to the class as an attribute.
|
103
|
+
# Also sets up the docstring to prettify the documentation output.
|
104
|
+
#
|
105
|
+
# @param [ClassObject] klass the class to attach the method to
|
106
|
+
# @param [String] member the name of the member we're generating a method for
|
107
|
+
def create_reader(klass, member)
|
108
|
+
new_meth = register MethodObject.new(klass, member, :instance) do |o|
|
109
|
+
o.signature ||= "def #{member}"
|
110
|
+
o.source ||= "#{o.signature}\n @#{member}\nend"
|
111
|
+
end
|
112
|
+
add_reader_tags(klass, new_meth, member)
|
113
|
+
klass.attributes[:instance][member][:read] = new_meth
|
114
|
+
end
|
115
|
+
|
116
|
+
# Creates the given member methods and attaches them to the given ClassObject.
|
117
|
+
#
|
118
|
+
# @param [ClassObject] klass the class to generate attributes for
|
119
|
+
# @param [Array<String>] members a list of member names
|
120
|
+
def create_attributes(klass, members)
|
121
|
+
# For each parameter, add reader and writers
|
122
|
+
members.each do |member|
|
123
|
+
klass.attributes[:instance][member] = SymbolHash[:read => nil, :write => nil]
|
124
|
+
create_writer klass, member if create_member_method?(klass, member, :write)
|
125
|
+
create_reader klass, member if create_member_method?(klass, member, :read)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module YARD
|
2
|
+
module Parser
|
3
|
+
# Represents the abstract base parser class that parses source code in
|
4
|
+
# a specific way. A parser should implement {#parse}, {#tokenize} and
|
5
|
+
# {#enumerator}.
|
6
|
+
#
|
7
|
+
# == Registering a Custom Parser
|
8
|
+
# To register a parser, see {SourceParser.register_parser_type}
|
9
|
+
#
|
10
|
+
# @abstract
|
11
|
+
# @see #parse
|
12
|
+
# @see #tokenize
|
13
|
+
# @see #enumerator
|
14
|
+
class Base
|
15
|
+
# Convenience method to create a new parser and {#parse}
|
16
|
+
def self.parse(source, filename = nil)
|
17
|
+
new(source, filename).parse
|
18
|
+
end
|
19
|
+
|
20
|
+
# This default constructor does nothing. The subclass is responsible for
|
21
|
+
# storing the source contents and filename if they are required.
|
22
|
+
# @param [String] source the source contents
|
23
|
+
# @param [String] filename the name of the file if from disk
|
24
|
+
def initialize(source, filename)
|
25
|
+
raise NotImplementedError, "invalid parser implementation"
|
26
|
+
end
|
27
|
+
|
28
|
+
# This method should be implemented to parse the source and return itself.
|
29
|
+
# @abstract
|
30
|
+
# @return [Base] this method should return itself
|
31
|
+
def parse
|
32
|
+
raise NotImplementedError, "#{self.class} must implement #parse"
|
33
|
+
end
|
34
|
+
|
35
|
+
# This method should be implemented to tokenize given source
|
36
|
+
# @abstract
|
37
|
+
# @return [Array] a list/tree of lexical tokens
|
38
|
+
def tokenize
|
39
|
+
raise NotImplementedError, "#{self.class} does not support tokenization"
|
40
|
+
end
|
41
|
+
|
42
|
+
# This method should be implemented to return a list of semantic tokens
|
43
|
+
# representing the source code to be post-processed. Otherwise the method
|
44
|
+
# should return nil.
|
45
|
+
#
|
46
|
+
# @abstract
|
47
|
+
# @return [Array] a list of semantic tokens representing the source code
|
48
|
+
# to be post-processed
|
49
|
+
# @return [nil] if no post-processing should be done
|
50
|
+
def enumerator
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/yard/parser/c_parser.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
module YARD
|
6
6
|
module Parser
|
7
|
-
class CParser
|
7
|
+
class CParser < Base
|
8
8
|
def initialize(source, file = '(stdin)')
|
9
9
|
@file = file
|
10
10
|
@namespaces = {}
|
@@ -18,6 +18,10 @@ module YARD
|
|
18
18
|
parse_includes
|
19
19
|
end
|
20
20
|
|
21
|
+
def tokenize
|
22
|
+
raise NotImplementedError, "no tokenization support for C/C++ files"
|
23
|
+
end
|
24
|
+
|
21
25
|
private
|
22
26
|
|
23
27
|
def remove_var_prefix(var)
|
@@ -38,8 +38,8 @@ 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 :type, :parent, :docstring, :
|
42
|
-
|
41
|
+
attr_accessor :type, :parent, :docstring, :docstring_range, :source
|
42
|
+
attr_writer :source_range, :line_range, :file, :full_source
|
43
43
|
alias comments docstring
|
44
44
|
alias comments_range docstring_range
|
45
45
|
alias to_s source
|
@@ -235,12 +235,12 @@ module YARD
|
|
235
235
|
q.group(3, 's(', ')') do
|
236
236
|
q.seplist(objs, nil, :each) do |v|
|
237
237
|
if v == :__last__
|
238
|
-
q.seplist(options, nil, :each) do |k,
|
238
|
+
q.seplist(options, nil, :each) do |k, v2|
|
239
239
|
q.group(3) do
|
240
240
|
q.text k
|
241
241
|
q.group(3) do
|
242
242
|
q.text ': '
|
243
|
-
q.pp
|
243
|
+
q.pp v2
|
244
244
|
end
|
245
245
|
end
|
246
246
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module YARD
|
2
|
+
module Parser
|
3
|
+
module Ruby
|
4
|
+
module Legacy
|
5
|
+
# Legacy Ruby parser
|
6
|
+
class RubyParser < Parser::Base
|
7
|
+
def initialize(source, filename)
|
8
|
+
@source = source
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse
|
12
|
+
@parse ||= StatementList.new(@source)
|
13
|
+
end
|
14
|
+
|
15
|
+
def tokenize
|
16
|
+
@tokenize ||= TokenList.new(@source)
|
17
|
+
end
|
18
|
+
|
19
|
+
def enumerator
|
20
|
+
@parse
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -3,12 +3,25 @@ require 'ripper'
|
|
3
3
|
module YARD
|
4
4
|
module Parser
|
5
5
|
module Ruby
|
6
|
-
|
6
|
+
# Ruby 1.9 parser
|
7
|
+
class RubyParser < Parser::Base
|
8
|
+
def initialize(source, filename)
|
9
|
+
@parser = RipperParser.new(source, filename)
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse; @parser.parse end
|
13
|
+
def tokenize; @parser.tokens end
|
14
|
+
def enumerator; @parser.enumerator end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Internal parser class
|
18
|
+
class RipperParser < Ripper
|
7
19
|
attr_reader :ast, :charno, :comments, :file, :tokens
|
8
20
|
alias root ast
|
9
21
|
|
10
22
|
def initialize(source, filename, *args)
|
11
23
|
super
|
24
|
+
@last_ns_token = nil
|
12
25
|
@file = filename
|
13
26
|
@source = source
|
14
27
|
@tokens = []
|
@@ -107,6 +120,7 @@ module YARD
|
|
107
120
|
eof
|
108
121
|
elsif /_add(_.+)?\z/ =~ event
|
109
122
|
module_eval(<<-eof, __FILE__, __LINE__ + 1)
|
123
|
+
undef on_#{event} if instance_method(:on_#{event})
|
110
124
|
def on_#{event}(list, item)
|
111
125
|
list.push(item)
|
112
126
|
list
|
@@ -114,12 +128,14 @@ module YARD
|
|
114
128
|
eof
|
115
129
|
elsif MAPPINGS.has_key?(event)
|
116
130
|
module_eval(<<-eof, __FILE__, __LINE__ + 1)
|
131
|
+
undef on_#{event} if instance_method(:on_#{event})
|
117
132
|
def on_#{event}(*args)
|
118
133
|
visit_event #{node_class}.new(:#{event}, args)
|
119
134
|
end
|
120
135
|
eof
|
121
136
|
else
|
122
137
|
module_eval(<<-eof, __FILE__, __LINE__ + 1)
|
138
|
+
undef on_#{event} if instance_method(:on_#{event})
|
123
139
|
def on_#{event}(*args)
|
124
140
|
#{node_class}.new(:#{event}, args, listline: lineno..lineno, listchar: charno...charno)
|
125
141
|
end
|
@@ -130,6 +146,7 @@ module YARD
|
|
130
146
|
SCANNER_EVENTS.each do |event|
|
131
147
|
ast_token = AST_TOKENS.include?(event)
|
132
148
|
module_eval(<<-eof, __FILE__, __LINE__ + 1)
|
149
|
+
undef on_#{event} if instance_method(:on_#{event})
|
133
150
|
def on_#{event}(tok)
|
134
151
|
visit_ns_token(:#{event}, tok, #{ast_token.inspect})
|
135
152
|
end
|
@@ -139,6 +156,7 @@ module YARD
|
|
139
156
|
REV_MAPPINGS.select {|k| k.is_a?(Symbol) }.each do |event, value|
|
140
157
|
ast_token = AST_TOKENS.include?(event)
|
141
158
|
module_eval(<<-eof, __FILE__, __LINE__ + 1)
|
159
|
+
undef on_#{event} if instance_method(:on_#{event})
|
142
160
|
def on_#{event}(tok)
|
143
161
|
(@map[:#{event}] ||= []) << [lineno, charno]
|
144
162
|
visit_ns_token(:#{event}, tok, #{ast_token.inspect})
|
@@ -148,6 +166,7 @@ module YARD
|
|
148
166
|
|
149
167
|
[:kw, :op].each do |event|
|
150
168
|
module_eval(<<-eof, __FILE__, __LINE__ + 1)
|
169
|
+
undef on_#{event} if instance_method(:on_#{event})
|
151
170
|
def on_#{event}(tok)
|
152
171
|
unless @last_ns_token == [:kw, "def"] ||
|
153
172
|
(@tokens.last && @tokens.last[0] == :symbeg)
|
@@ -160,6 +179,7 @@ module YARD
|
|
160
179
|
|
161
180
|
[:sp, :nl, :ignored_nl].each do |event|
|
162
181
|
module_eval(<<-eof, __FILE__, __LINE__ + 1)
|
182
|
+
undef on_#{event} if instance_method(:on_#{event})
|
163
183
|
def on_#{event}(tok)
|
164
184
|
add_token(:#{event}, tok)
|
165
185
|
@charno += tok.length
|
@@ -201,6 +221,28 @@ module YARD
|
|
201
221
|
end
|
202
222
|
end
|
203
223
|
|
224
|
+
undef on_program
|
225
|
+
undef on_bodystmt
|
226
|
+
undef on_assoc_new
|
227
|
+
undef on_hash
|
228
|
+
undef on_bare_assoc_hash
|
229
|
+
undef on_assoclist_from_args
|
230
|
+
undef on_aref
|
231
|
+
undef on_rbracket
|
232
|
+
undef on_qwords_new
|
233
|
+
undef on_string_literal
|
234
|
+
undef on_lambda
|
235
|
+
undef on_string_content
|
236
|
+
undef on_rescue
|
237
|
+
undef on_void_stmt
|
238
|
+
undef on_params
|
239
|
+
undef on_label
|
240
|
+
undef on_comment
|
241
|
+
undef on_embdoc_beg
|
242
|
+
undef on_embdoc
|
243
|
+
undef on_embdoc_end
|
244
|
+
undef on_parse_error
|
245
|
+
|
204
246
|
def on_program(*args)
|
205
247
|
args.first
|
206
248
|
end
|
@@ -241,6 +283,7 @@ module YARD
|
|
241
283
|
[:if_mod, :unless_mod, :while_mod].each do |kw|
|
242
284
|
node_class = AstNode.node_class_for(kw)
|
243
285
|
module_eval(<<-eof, __FILE__, __LINE__ + 1)
|
286
|
+
undef on_#{kw} if instance_method(:on_#{kw})
|
244
287
|
def on_#{kw}(*args)
|
245
288
|
sr = args.last.source_range.first..args.first.source_range.last
|
246
289
|
lr = args.last.line_range.first..args.first.line_range.last
|
@@ -340,7 +383,7 @@ module YARD
|
|
340
383
|
def insert_comments
|
341
384
|
root.traverse do |node|
|
342
385
|
next if node.type == :list || node.parent.type != :list
|
343
|
-
|
386
|
+
(node.line - 2).upto(node.line) do |line|
|
344
387
|
comment = @comments[line]
|
345
388
|
if comment && !comment.empty?
|
346
389
|
node.docstring = comment
|
@@ -22,22 +22,19 @@ module YARD
|
|
22
22
|
# also invokes handlers to process the parsed statements and generate
|
23
23
|
# any code objects that may be recognized.
|
24
24
|
#
|
25
|
+
# == Custom Parsers
|
26
|
+
# SourceParser allows custom parsers to be registered and called when
|
27
|
+
# a certain filetype is recognized. To register a parser and hook it
|
28
|
+
# up to a set of file extensions, call {register_parser_type}
|
29
|
+
#
|
30
|
+
# @see register_parser_type
|
25
31
|
# @see Handlers::Base
|
26
32
|
# @see CodeObjects::Base
|
27
33
|
class SourceParser
|
28
34
|
class << self
|
29
|
-
#
|
30
|
-
|
31
|
-
# blocks of code.
|
32
|
-
#
|
33
|
-
# @return [Symbol] the parser type
|
34
|
-
attr_accessor :parser_type
|
35
|
-
undef parser_type=
|
35
|
+
# @return [Symbol] the default parser type (defaults to :ruby)
|
36
|
+
attr_reader :parser_type
|
36
37
|
|
37
|
-
# Sets the parser and makes sure it's a valid type
|
38
|
-
#
|
39
|
-
# @param [Symbol] value the new parser type
|
40
|
-
# @return [void]
|
41
38
|
def parser_type=(value)
|
42
39
|
@parser_type = validated_parser_type(value)
|
43
40
|
end
|
@@ -86,6 +83,49 @@ module YARD
|
|
86
83
|
new(ptype).tokenize(content)
|
87
84
|
end
|
88
85
|
|
86
|
+
# Registers a new parser type.
|
87
|
+
#
|
88
|
+
# @example Registering a parser for "java" files
|
89
|
+
# SourceParser.register_parser_type :java, JavaParser, 'java'
|
90
|
+
# @param [Symbol] type a symbolic name for the parser type
|
91
|
+
# @param [Base] parser_klass a class that implements parsing and tokenization
|
92
|
+
# @param [Array<String>, String, Regexp] extensions a list of extensions or a
|
93
|
+
# regex to match against the file extension
|
94
|
+
# @return [void]
|
95
|
+
# @see Parser::Base
|
96
|
+
def register_parser_type(type, parser_klass, extensions = nil)
|
97
|
+
unless Base > parser_klass
|
98
|
+
raise ArgumentError, "expecting parser_klass to be a subclass of YARD::Parser::Base"
|
99
|
+
end
|
100
|
+
parser_type_extensions[type.to_sym] = extensions if extensions
|
101
|
+
parser_types[type.to_sym] = parser_klass
|
102
|
+
end
|
103
|
+
|
104
|
+
# @return [Hash{Symbol=>Object}] a list of registered parser types
|
105
|
+
def parser_types; @@parser_types ||= {} end
|
106
|
+
def parser_types=(value) @@parser_types = value end
|
107
|
+
|
108
|
+
# @return [Hash] a list of registered parser type extensions
|
109
|
+
def parser_type_extensions; @@parser_type_extensions ||= {} end
|
110
|
+
def parser_type_extensions=(value) @@parser_type_extensions = value end
|
111
|
+
|
112
|
+
# Finds a parser type that is registered for the extension. If no
|
113
|
+
# type is found, the default Ruby type is returned.
|
114
|
+
#
|
115
|
+
# @return [Symbol] the parser type to be used for the extension
|
116
|
+
def parser_type_for_extension(extension)
|
117
|
+
type = parser_type_extensions.find do |t, exts|
|
118
|
+
if exts.is_a?(Array)
|
119
|
+
exts.include?(extension)
|
120
|
+
elsif exts.is_a?(String)
|
121
|
+
exts == extension
|
122
|
+
elsif exts.is_a?(Regexp)
|
123
|
+
extension =~ exts
|
124
|
+
end
|
125
|
+
end
|
126
|
+
validated_parser_type(type ? type.first : :ruby)
|
127
|
+
end
|
128
|
+
|
89
129
|
# Returns the validated parser type. Basically, enforces that :ruby
|
90
130
|
# type is never set from Ruby 1.8
|
91
131
|
#
|
@@ -94,7 +134,7 @@ module YARD
|
|
94
134
|
def validated_parser_type(type)
|
95
135
|
RUBY18 && type == :ruby ? :ruby18 : type
|
96
136
|
end
|
97
|
-
|
137
|
+
|
98
138
|
private
|
99
139
|
|
100
140
|
# Parses a list of files in a queue. If a {LoadOrderError} is caught,
|
@@ -124,6 +164,10 @@ module YARD
|
|
124
164
|
|
125
165
|
self.parser_type = :ruby
|
126
166
|
|
167
|
+
register_parser_type :ruby, Ruby::RubyParser if RUBY19
|
168
|
+
register_parser_type :ruby18, Ruby::Legacy::RubyParser
|
169
|
+
register_parser_type :c, CParser, ['c', 'cc', 'cxx', 'cpp']
|
170
|
+
|
127
171
|
# The filename being parsed by the parser.
|
128
172
|
attr_reader :file
|
129
173
|
|
@@ -163,7 +207,8 @@ module YARD
|
|
163
207
|
content = content.read if content.respond_to? :read
|
164
208
|
end
|
165
209
|
|
166
|
-
@parser =
|
210
|
+
@parser = parser_class.new(content, file)
|
211
|
+
@parser.parse
|
167
212
|
post_process
|
168
213
|
@parser
|
169
214
|
rescue ArgumentError, NotImplementedError => e
|
@@ -177,16 +222,8 @@ module YARD
|
|
177
222
|
# @param [String] content the block of code to tokenize
|
178
223
|
# @return [Array] a list of tokens
|
179
224
|
def tokenize(content)
|
180
|
-
|
181
|
-
|
182
|
-
raise NotImplementedError, "no support for C/C++ files"
|
183
|
-
when :ruby18
|
184
|
-
Ruby::Legacy::TokenList.new(content)
|
185
|
-
when :ruby
|
186
|
-
Ruby::RubyParser.parse(content).tokens
|
187
|
-
else
|
188
|
-
raise ArgumentError, "invalid parser type or unrecognized file"
|
189
|
-
end
|
225
|
+
@parser = parser_class.new(content, file)
|
226
|
+
@parser.tokenize
|
190
227
|
end
|
191
228
|
|
192
229
|
private
|
@@ -205,8 +242,9 @@ module YARD
|
|
205
242
|
# @return [void]
|
206
243
|
def post_process
|
207
244
|
return unless @parser.respond_to? :enumerator
|
245
|
+
return unless enumerator = @parser.enumerator
|
208
246
|
post = Handlers::Processor.new(@file, @load_order_errors, @parser_type)
|
209
|
-
post.process(
|
247
|
+
post.process(enumerator)
|
210
248
|
end
|
211
249
|
|
212
250
|
def parser_type=(value)
|
@@ -218,29 +256,15 @@ module YARD
|
|
218
256
|
# @param [String] filename the filename to use to guess the parser type
|
219
257
|
# @return [Symbol] a parser type that matches the filename
|
220
258
|
def parser_type_for_filename(filename)
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
else # when "rb", "rbx", "erb"
|
225
|
-
parser_type == :ruby18 ? :ruby18 : :ruby
|
226
|
-
end
|
259
|
+
ext = (File.extname(filename)[1..-1] || "").downcase
|
260
|
+
type = self.class.parser_type_for_extension(ext)
|
261
|
+
parser_type == :ruby18 && type == :ruby ? :ruby18 : type
|
227
262
|
end
|
228
263
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
def parse_statements(content)
|
234
|
-
case parser_type
|
235
|
-
when :c
|
236
|
-
CParser.new(content, file).parse
|
237
|
-
when :ruby18
|
238
|
-
Ruby::Legacy::StatementList.new(content)
|
239
|
-
when :ruby
|
240
|
-
Ruby::RubyParser.parse(content, file)
|
241
|
-
else
|
242
|
-
raise ArgumentError, "invalid parser type or unrecognized file"
|
243
|
-
end
|
264
|
+
def parser_class
|
265
|
+
klass = self.class.parser_types[parser_type]
|
266
|
+
raise ArgumentError, "invalid parser type '#{parser_type}' or unrecognized file", caller[1..-1] if !klass
|
267
|
+
klass
|
244
268
|
end
|
245
269
|
end
|
246
270
|
end
|