Skip to content

Commit

Permalink
Support for named arguments of Ruby >= 2.1
Browse files Browse the repository at this point in the history
  • Loading branch information
IvanUkhov committed Jun 23, 2014
1 parent ae04919 commit 815c690
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 33 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ rvm:
- 1.8.7
- 1.9.3
- 2.0.0
- 2.1.0
# - jruby # see http://jira.codehaus.org/browse/JRUBY-7007
# - ruby-head # RedCloth does not compile on head
# - 1.8.6 # Does not work on travis-ci
Expand Down
40 changes: 34 additions & 6 deletions lib/yard/handlers/ruby/method_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,41 @@ class YARD::Handlers::Ruby::MethodHandler < YARD::Handlers::Ruby::Base

def format_args
args = statement.parameters

params = []
params += args.required_params.map {|a| [a.source, nil] } if args.required_params
params += args.optional_params.map {|a| [a[0].source, a[1].source] } if args.optional_params
params << ["*" + args.splat_param.source, nil] if args.splat_param
params << ["**" + args.keyword_param.source, nil] if args.keyword_param
params += args.required_end_params.map {|a| [a.source, nil] } if args.required_end_params
params << ["&" + args.block_param.source, nil] if args.block_param

if args.unnamed_required_params
params += args.unnamed_required_params.map { |a| [a.source, nil] }
end

if args.unnamed_optional_params
params += args.unnamed_optional_params.map do |a|
[a[0].source, a[1].source]
end
end

if args.splat_param
params << ['*' + args.splat_param.source, nil]
end

if args.unnamed_end_params
params += args.unnamed_end_params.map { |a| [a.source, nil] }
end

if args.named_params
params += args.named_params.map do |a|
[a[0].source, a[1] ? a[1].source : nil]
end
end

if args.double_splat_param
params << ['**' + args.double_splat_param.source, nil]
end

if args.block_param
params << ['&' + args.block_param.source, nil]
end

params
end

Expand Down
48 changes: 38 additions & 10 deletions lib/yard/parser/ruby/ast_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -376,18 +376,46 @@ def kw?; true end
end

class ParameterNode < AstNode
def required_params; self[0] end
def required_end_params; self[3] end
def splat_param; self[2] ? self[2][0] : nil end
def block_param; self[-1] ? self[-1][0] : nil end
def optional_params
optional = self[1] || []
if self[-3] && self[-3][0] && self[-3][0].type == :default_arg
optional += self[-3]
def unnamed_required_params
self[0]
end

def unnamed_optional_params
return @unnamed_optional_params if defined?(@unnamed_optional_params)

params = self[1] || []
if self[-3] && self[-3][0] && self[-3][0].type == :unnamed_optional_arg
params += self[-3]
end

@unnamed_optional_params = params.empty? ? nil : params
end

def named_params
return @named_params if defined?(@named_params)

if YARD.ruby2? && self[-3] && self[-3][0] && self[-3][0].type == :named_arg
@named_params = self[-3]
else
@named_params = nil
end
optional.empty? ? nil : optional
end
def keyword_param; YARD.ruby2? ? self[-2] : nil end

def splat_param
self[2] ? self[2][0] : nil
end

def unnamed_end_params
self[3]
end

def double_splat_param
YARD.ruby2? ? self[-2] : nil
end

def block_param
self[-1] ? self[-1][0] : nil
end
end

class MethodCallNode < AstNode
Expand Down
24 changes: 13 additions & 11 deletions lib/yard/parser/ruby/ruby_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -452,21 +452,23 @@ def on_void_stmt

def on_params(*args)
args.map! do |arg|
if arg.class == Array
if arg.first.class == Array
arg.map! do |sub_arg|
if sub_arg.class == Array
AstNode.new(:default_arg, sub_arg, :listline => lineno..lineno, :listchar => charno..charno)
else
sub_arg
end
next arg unless arg.class == Array

if arg.first.class == Array
arg.map! do |sub_arg|
next sub_arg unless sub_arg.class == Array
if sub_arg[0].type == :label
type = :named_arg
else
type = :unnamed_optional_arg
end
AstNode.new(type, sub_arg, :listline => lineno..lineno, :listchar => charno..charno)
end
AstNode.new(:list, arg, :listline => lineno..lineno, :listchar => charno..charno)
else
arg
end

AstNode.new(:list, arg, :listline => lineno..lineno, :listchar => charno..charno)
end

ParameterNode.new(:params, args, :listline => lineno..lineno, :listchar => charno..charno)
end

Expand Down
31 changes: 30 additions & 1 deletion spec/parser/ruby/ruby_parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,40 @@ def hello; end
ast.jump(:class).line_range.should == (2..4)
end

it 'should handle defs with unnamed argument with default values' do
ast = stmt('def hello(one, two = 2, three = 3) end').jump(:params)
ast.source.should == 'one, two = 2, three = 3'
end

it 'should handle defs with splats' do
ast = stmt('def hello(one, *two) end').jump(:params)
ast.source.should == 'one, *two'
end

if YARD.ruby2?
it 'should handle defs with named arguments with default values' do
ast = stmt('def hello(one, two: 2, three: 3) end').jump(:params)
ast.source.should == 'one, two: 2, three: 3'
end
end

if NAMED_OPTIONAL_ARGUMENTS
it 'should handle defs with named arguments without default values' do
ast = stmt('def hello(one, two:, three:) end').jump(:params)
ast.source.should == 'one, two:, three:'
end

it 'should handle defs with double splats' do
ast = stmt('def hello(one, **two) end').jump(:params)
ast.source.should == 'one, **two'
end
end

it "should end source properly on array reference" do
ast = stmt("AS[0, 1 ] ")
ast.source.should == 'AS[0, 1 ]'

ast = stmt("def x(a = S[1]) end").jump(:default_arg)
ast = stmt('def x(a = S[1]) end').jump(:params)
ast.source.should == 'a = S[1]'
end

Expand Down
20 changes: 15 additions & 5 deletions spec/parser/source_parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -708,10 +708,20 @@ def c; end
Registry.at('A::B#d').should_not be_nil
end

it 'supports keyword arguments' do
YARD.parse_string 'def foo(a: 1, b: 2, **kwargs) end'
args = [['a:', '1'], ['b:', '2'], ['**kwargs', nil]]
Registry.at('#foo').parameters.should eq(args)
end if YARD.ruby2?
if YARD.ruby2?
it 'supports named arguments with default values' do
YARD.parse_string 'def foo(a, b = 1, *c, d, e: 3, **f, &g) end'
args = [['a', nil], ['b', '1'], ['*c', nil], ['d', nil], ['e:', '3'], ['**f', nil], ['&g', nil]]
Registry.at('#foo').parameters.should eq(args)
end
end

if NAMED_OPTIONAL_ARGUMENTS && !LEGACY_PARSER
it 'supports named arguments without default values' do
YARD.parse_string 'def foo(a, b = 1, *c, d, e: 3, f:, **g, &h) end'
args = [['a', nil], ['b', '1'], ['*c', nil], ['d', nil], ['e:', '3'], ['f:', nil], ['**g', nil], ['&h', nil]]
Registry.at('#foo').parameters.should eq(args)
end
end
end
end
2 changes: 2 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ def self.parser_type; :ruby18 end
end if ENV['LEGACY']
end

NAMED_OPTIONAL_ARGUMENTS = RUBY_VERSION >= '2.1.0'

def parse_file(file, thisfile = __FILE__, log_level = log.level, ext = '.rb.txt')
Registry.clear
path = File.join(File.dirname(thisfile), 'examples', file.to_s + ext)
Expand Down

0 comments on commit 815c690

Please sign in to comment.