class YARD::Handlers::RBS::MethodHandler
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 RBS type signature when those tags are absent from the docstring.
Public Class Methods
Source
# 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
Return the bracket depth of the full string (should be 0 for well-formed types).
Source
# 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
Source
# 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
Split str on ‘|` that are not inside brackets.
Private Instance Methods
Source
# 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]) add_yield_tags(obj, blk) end lines << " @return [#{parsed[:return_types].join(', ')}]" obj.add_tag YARD::Tags::OverloadTag.new(:overload, lines.join("\n")) end
Add an @overload tag for one signature overload.
Source
# 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
Source
# 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
Find the index of the matching close bracket starting from start. @return [nil] if no matching bracket is found (malformed input).
Source
# 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 the inside of a ‘{ … }` block type, e.g. “(Integer) -> String”.
Source
# 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 a single RBS function type string (one overload) into its components.
@param sig [String] e.g. “(String name, Integer age) -> String” @return [Hash] { :params => […], :block_param => Hash|nil, :return_types => […] }
Source
# 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 a comma-separated parameter list (content inside outer parens).
Source
# 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
Parse one parameter from an RBS param list.
Source
# 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
Split str on commas that are not inside brackets.