Class: YARD::Handlers::RBS::MethodHandler
| Relationships & Source Files | |
| Super Chains via Extension / Inclusion / Inheritance | |
|
Class Chain:
self,
Base,
::YARD::Handlers::Base
|
|
|
Instance Chain:
|
|
| Inherits: |
YARD::Handlers::RBS::Base
|
| Defined in: | lib/yard/handlers/rbs/method_handler.rb |
Overview
Handles RBS method definitions (def name: signature).
Creates a ::YARD::CodeObjects::MethodObject for each declaration
and infers @param, @return, @yield, and @yieldparam tags from the
::YARD::Handlers::RBS type signature when those tags are absent from the docstring.
Constant Summary
::YARD::CodeObjects - Included
BUILTIN_ALL, BUILTIN_CLASSES, BUILTIN_EXCEPTIONS, BUILTIN_EXCEPTIONS_HASH, BUILTIN_MODULES, CONSTANTMATCH, CONSTANTSTART, CSEP, CSEPQ, ISEP, ISEPQ, METHODMATCH, METHODNAMEMATCH, NAMESPACEMATCH, NSEP, NSEPQ, PROXY_MATCH
Class Attribute Summary
::YARD::Handlers::Base - Inherited
| .namespace_only | Declares that the handler should only be called when inside a |
| .namespace_only? | |
Class Method Summary
-
.rbs_type_to_yard_types(rbs) ⇒ Array<String>
Convert an
::YARD::Handlers::RBStype string to an array of::YARDtype strings. -
.bracket_depth(str)
private
Return the bracket depth of the full string (should be 0 for well-formed types).
-
.split_on_pipe(str)
private
Split
stron {|} that are not inside brackets.
Base - Inherited
::YARD::Handlers::Base - Inherited
| .clear_subclasses | Clear all registered subclasses. |
| .handlers, | |
| .handles | Declares the statement type which will be processed by this handler. |
| .handles? | This class is implemented by |
| .in_file | Declares that a handler should only be called when inside a filename by its basename or a regex match for the full path. |
| .inherited, .matches_file?, .new, | |
| .process | Generates a #process method, equivalent to +def process; ... |
| .subclasses | Returns all registered handler subclasses. |
Instance Attribute Summary
::YARD::Handlers::Base - Inherited
| #extra_state | Share state across different handlers inside of a file. |
| #globals |
|
| #namespace, #namespace=, #owner, #owner=, #parser, #scope, #scope=, #statement, #visibility, #visibility= | |
Instance Method Summary
-
#process ⇒ void
Main processing callback.
-
#add_overload_tag(obj, meth_name, sig)
private
Add an @overload tag for one signature overload.
-
#add_param_return_tags(obj, sig)
private
Add @param / @return / @yield / @yieldparam from a single overload sig.
-
#add_yield_tags(obj, blk)
private
Add @yield and @yieldparam tags from a parsed block type.
-
#apply_signature_tags(obj, sigs)
private
Apply tags from all overload signatures to the method object.
-
#extract_type_and_name(str)
private
Split a type+name string like "Array[String] names" into ["Array[String]", "names"].
-
#find_matching(str, start, open, close) ⇒ nil
private
Find the index of the matching close bracket starting from
start. -
#parse_block_type(inner)
private
Parse the inside of a
{ ... }block type, e.g. -
#parse_function_type(sig) ⇒ Hash
private
Parse a single
::YARD::Handlers::RBSfunction type string (one overload) into its components. -
#parse_params_list(str)
private
Parse a comma-separated parameter list (content inside outer parens).
-
#parse_single_param(param)
private
Parse one parameter from an
::YARD::Handlers::RBSparam list. -
#split_by_comma(str)
private
Split
stron commas that are not inside brackets.
Base - Inherited
| #parse_block | Recurse into the body of a namespace statement. |
::YARD::Handlers::Base - Inherited
| #abort! | Aborts a handler by raising |
| #call_params, #caller_method, | |
| #ensure_loaded! | Ensures that a specific |
| #parse_block | Parses the semantic "block" contained in the statement node. |
| #process | The main handler method called by the parser on a statement that matches the |
| #push_state | Executes a given block with specific state values for |
| #register | Do some post processing on a list of code objects. |
| #register_docstring | Registers any docstring found for the object and expands macros. |
| #register_dynamic | Registers the object as dynamic if the object is defined inside a method or block (owner != namespace). |
| #register_ensure_loaded | Ensures that the object's namespace is loaded before attaching it to the namespace. |
| #register_file_info | Registers the file/line of the declaration with the object. |
| #register_group | Registers the object as being inside a specific group. |
| #register_module_function | Registers the same method information on the module function, if the object was defined as a module function. |
| #register_source, | |
| #register_transitive_tags | Registers any transitive tags from the namespace on the object. |
| #register_visibility | Registers visibility on a method object. |
Constructor Details
This class inherits a constructor from YARD::Handlers::Base
Class Method Details
.bracket_depth(str) (private)
Return the bracket depth of the full string (should be 0 for well-formed types).
# File 'lib/yard/handlers/rbs/method_handler.rb', line 317
def self.bracket_depth(str) depth = 0 str.each_char do |c| case c when '(', '[', '{' then depth += 1 when ')', ']', '}' then depth -= 1 end end depth end
.rbs_type_to_yard_types(rbs) ⇒ Array<String>
Convert an ::YARD::Handlers::RBS type string to an array of ::YARD type strings.
# File 'lib/yard/handlers/rbs/method_handler.rb', line 30
def self.rbs_type_to_yard_types(rbs) rbs = rbs.strip return ['void'] if rbs == 'void' return ['Boolean'] if rbs == 'bool' return ['Object'] if rbs == 'untyped' return ['nil'] if rbs == 'nil' # Strip outer parentheses: `(String | Integer)` → recurse on inner. if rbs.start_with?('(') && rbs.end_with?(')') && bracket_depth(rbs[1..-2]) == 0 return rbs_type_to_yard_types(rbs[1..-2]) end # `Type?` is shorthand for `Type | nil` when the ? is outermost. if rbs =~ /\A(.+)\?\z/ && bracket_depth($1) == 0 return rbs_type_to_yard_types($1) + ['nil'] end split_on_pipe(rbs).map { |t| t.strip } end
.split_on_pipe(str) (private)
Split str on {|} that are not inside brackets.
# File 'lib/yard/handlers/rbs/method_handler.rb', line 289
def self.split_on_pipe(str) depth = 0 parts = [] cur = String.new('') str.each_char do |c| case c when '(', '[', '{' depth += 1 cur << c when ')', ']', '}' depth -= 1 cur << c when '|' if depth == 0 parts << cur.strip cur = String.new('') else cur << c end else cur << c end end parts << cur.strip unless cur.strip.empty? parts end
Instance Method Details
#add_overload_tag(obj, meth_name, sig) (private)
Add an @overload tag for one signature overload.
# File 'lib/yard/handlers/rbs/method_handler.rb', line 86
def add_overload_tag(obj, meth_name, sig) parsed = parse_function_type(sig) param_sigs = parsed[:params].reject { |p| p[:block] }.map.with_index do |p, idx| p[:name] || "arg#{idx}" end # Build the overload tag text: signature line + nested @param/@return lines. lines = ["#{meth_name}(#{param_sigs.join(', ')})"] parsed[:params].reject { |p| p[:block] }.each_with_index do |p, idx| pname = p[:name] || "arg#{idx}" lines << " @param #{pname} [#{p[:types].join(', ')}]" end if (blk = parsed[:block_param]) (obj, blk) end lines << " @return [#{parsed[:return_types].join(', ')}]" obj.add_tag YARD::Tags::OverloadTag.new(:overload, lines.join("\n")) end
#add_param_return_tags(obj, sig) (private)
Add @param / @return / @yield / @yieldparam from a single overload sig.
# File 'lib/yard/handlers/rbs/method_handler.rb', line 66
def (obj, sig) parsed = parse_function_type(sig) parsed[:params].each do |p| next if p[:block] # block param handled via @yield below tag_name = p[:name] ? p[:name].to_s : nil next if tag_name && obj.(:param).any? { |t| t.name == tag_name } obj.add_tag YARD::Tags::Tag.new(:param, '', p[:types], tag_name) end if (blk = parsed[:block_param]) (obj, blk) end unless obj.has_tag?(:return) obj.add_tag YARD::Tags::Tag.new(:return, '', parsed[:return_types]) end end
#add_yield_tags(obj, blk) (private)
Add @yield and @yieldparam tags from a parsed block type.
# File 'lib/yard/handlers/rbs/method_handler.rb', line 108
def (obj, blk) return if obj.has_tag?(:yield) && obj.has_tag?(:yieldparam) obj.add_tag YARD::Tags::Tag.new(:yield, '') unless obj.has_tag?(:yield) blk[:params].each_with_index do |p, idx| pname = p[:name] || "arg#{idx}" next if obj.(:yieldparam).any? { |t| t.name == pname } obj.add_tag YARD::Tags::Tag.new(:yieldparam, '', p[:types], pname) end unless obj.has_tag?(:yieldreturn) obj.add_tag YARD::Tags::Tag.new(:yieldreturn, '', self.class.rbs_type_to_yard_types(blk[:return_type] || 'void')) end end
#apply_signature_tags(obj, sigs) (private)
Apply tags from all overload signatures to the method object.
# File 'lib/yard/handlers/rbs/method_handler.rb', line 53
def (obj, sigs) return if sigs.nil? || sigs.empty? if sigs.length == 1 # Single signature: add @param and @return directly. (obj, sigs.first) else # Multiple signatures: add @overload tags. sigs.each { |sig| add_overload_tag(obj, statement.name, sig) } end end
#extract_type_and_name(str) (private)
Split a type+name string like "Array[String] names" into ["Array[String]", "names"]. The name is the trailing lowercase identifier (if any).
# File 'lib/yard/handlers/rbs/method_handler.rb', line 213
def extract_type_and_name(str) str = str.strip if str =~ /\A(.*\S)\s+([a-z_]\w*)\z/m type_part = $1.strip name_part = $2 # Exclude RBS type keywords from being mistaken for names. unless %w[void untyped nil bool top bottom self instance class].include?(name_part) return [type_part, name_part] unless type_part.empty? end end [str, nil] end
#find_matching(str, start, open, close) ⇒ nil (private)
Find the index of the matching close bracket starting from start.
# File 'lib/yard/handlers/rbs/method_handler.rb', line 247
def find_matching(str, start, open, close) depth = 0 (start...str.length).each do |i| case str[i] when open then depth += 1 when close depth -= 1 return i if depth == 0 end end nil end
#parse_block_type(inner) (private)
Parse the inside of a { ... } block type, e.g. "(Integer) -> String".
# File 'lib/yard/handlers/rbs/method_handler.rb', line 227
def parse_block_type(inner) inner = inner.strip params = [] ret = nil if inner.start_with?('(') close = find_matching(inner, 0, '(', ')') raise YARD::Parser::UndocumentableError, "malformed block type (unclosed '('): #{inner}" if close.nil? params = parse_params_list(inner[1...close]) rest = inner[close + 1..-1].lstrip else rest = inner end ret = $1.strip if rest =~ /\A->\s*(.*)\z/ { :params => params, :return_type => ret } end
#parse_function_type(sig) ⇒ Hash (private)
Parse a single ::YARD::Handlers::RBS function type string (one overload) into its components.
# File 'lib/yard/handlers/rbs/method_handler.rb', line 125
def parse_function_type(sig) sig = sig.strip return { :params => [], :block_param => nil, :return_types => ['void'] } if sig.empty? remaining = sig params = [] block_param = nil # 1. Extract positional/keyword params: leading `(...)`. if remaining.start_with?('(') close = find_matching(remaining, 0, '(', ')') raise YARD::Parser::UndocumentableError, "malformed signature (unclosed '('): #{sig}" if close.nil? params_str = remaining[1...close] remaining = remaining[close + 1..-1].lstrip params = parse_params_list(params_str) end # 2. Extract block type: `{ ... }`. if remaining.start_with?('{') close = find_matching(remaining, 0, '{', '}') raise YARD::Parser::UndocumentableError, "malformed signature (unclosed '{'): #{sig}" if close.nil? block_inner = remaining[1...close] remaining = remaining[close + 1..-1].lstrip block_param = parse_block_type(block_inner) end # 3. Return type after `->`. return_types = if remaining =~ /\A->\s*(.*)\z/ self.class.rbs_type_to_yard_types($1.strip) else ['void'] end { :params => params, :block_param => block_param, :return_types => return_types } end
#parse_params_list(str) (private)
Parse a comma-separated parameter list (content inside outer parens).
# File 'lib/yard/handlers/rbs/method_handler.rb', line 162
def parse_params_list(str) str = str.strip return [] if str.empty? split_by_comma(str).map { |p| parse_single_param(p.strip) }.compact end
#parse_single_param(param) (private)
Parse one parameter from an ::YARD::Handlers::RBS param list.
# File 'lib/yard/handlers/rbs/method_handler.rb', line 170
def parse_single_param(param) return nil if param.empty? optional = false rest = false # Optional marker `?`. if param.start_with?('?') && !param.start_with?('?(') optional = true param = param[1..-1].lstrip end # Double-splat `**` (rest keyword). if param.start_with?('**') rest = true param = param[2..-1].lstrip # Single-splat `*` (rest positional). elsif param.start_with?('*') && !param.start_with?('*)') rest = true param = param[1..-1].lstrip end # Block-type proc: `^(...)`. if param.start_with?('^') return { :name => nil, :types => [param], :optional => false, :rest => false, :block => true } end # Keyword parameter: `name: Type` or `?name: Type`. if param =~ /\A([a-z_]\w*)\s*:\s*(.*)\z/ && !rest kw_name = $1 kw_type = $2.strip return { :name => "#{kw_name}:", :types => self.class.rbs_type_to_yard_types(kw_type), :optional => optional, :rest => false } end # Positional: `Type [param_name]`. type_str, param_name = extract_type_and_name(param) { :name => param_name, :types => self.class.rbs_type_to_yard_types(type_str), :optional => optional, :rest => rest } end
#process ⇒ void
This method returns an undefined value.
Main processing callback
# File 'lib/yard/handlers/rbs/method_handler.rb', line 10
process do meth_scope = statement.visibility == :class ? :class : :instance obj = register MethodObject.new(namespace, statement.name, meth_scope) (obj, statement.signatures) # For initialize, ensure the return type is the class, not void. if statement.name == 'initialize' = obj.(:return) if .none? || (.length == 1 && .first.types == ['void']) obj.docstring.(:return) obj.add_tag YARD::Tags::Tag.new(:return, "a new instance of #{namespace.name}", [namespace.name.to_s]) end end end
#split_by_comma(str) (private)
Split str on commas that are not inside brackets.
# File 'lib/yard/handlers/rbs/method_handler.rb', line 261
def split_by_comma(str) depth = 0 parts = [] cur = String.new('') str.each_char do |c| case c when '(', '[', '{' depth += 1 cur << c when ')', ']', '}' depth -= 1 cur << c when ',' if depth == 0 parts << cur.strip cur = String.new('') else cur << c end else cur << c end end parts << cur.strip unless cur.strip.empty? parts end