solargraph 0.47.2 → 0.49.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/rspec.yml +1 -1
  4. data/CHANGELOG.md +20 -0
  5. data/LICENSE +1 -1
  6. data/README.md +8 -0
  7. data/SPONSORS.md +2 -4
  8. data/lib/solargraph/api_map/store.rb +13 -1
  9. data/lib/solargraph/api_map.rb +30 -12
  10. data/lib/solargraph/cache.rb +51 -0
  11. data/lib/solargraph/complex_type/type_methods.rb +10 -6
  12. data/lib/solargraph/complex_type/unique_type.rb +57 -0
  13. data/lib/solargraph/complex_type.rb +30 -1
  14. data/lib/solargraph/convention/rakefile.rb +17 -0
  15. data/lib/solargraph/convention.rb +2 -0
  16. data/lib/solargraph/diagnostics/rubocop.rb +17 -3
  17. data/lib/solargraph/diagnostics/rubocop_helpers.rb +3 -1
  18. data/lib/solargraph/language_server/host.rb +22 -18
  19. data/lib/solargraph/language_server/message/extended/download_core.rb +1 -5
  20. data/lib/solargraph/language_server/message/initialize.rb +2 -0
  21. data/lib/solargraph/language_server/message/text_document/formatting.rb +1 -1
  22. data/lib/solargraph/language_server/message/text_document/signature_help.rb +1 -6
  23. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +10 -3
  24. data/lib/solargraph/library.rb +21 -20
  25. data/lib/solargraph/parser/legacy/node_processors/casgn_node.rb +12 -2
  26. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +24 -3
  27. data/lib/solargraph/parser/rubyvm/class_methods.rb +6 -1
  28. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +13 -2
  29. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +20 -8
  30. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +14 -3
  31. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +14 -3
  32. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +4 -2
  33. data/lib/solargraph/parser/rubyvm/node_wrapper.rb +47 -0
  34. data/lib/solargraph/pin/base.rb +5 -2
  35. data/lib/solargraph/pin/conversions.rb +2 -6
  36. data/lib/solargraph/pin/method.rb +100 -10
  37. data/lib/solargraph/pin/namespace.rb +4 -1
  38. data/lib/solargraph/pin/parameter.rb +8 -3
  39. data/lib/solargraph/pin/signature.rb +23 -0
  40. data/lib/solargraph/pin.rb +1 -0
  41. data/lib/solargraph/rbs_map/conversions.rb +394 -0
  42. data/lib/solargraph/rbs_map/core_fills.rb +61 -0
  43. data/lib/solargraph/rbs_map/core_map.rb +38 -0
  44. data/lib/solargraph/rbs_map/core_signs.rb +33 -0
  45. data/lib/solargraph/rbs_map/stdlib_map.rb +36 -0
  46. data/lib/solargraph/rbs_map.rb +73 -0
  47. data/lib/solargraph/shell.rb +38 -30
  48. data/lib/solargraph/source/chain/call.rb +34 -23
  49. data/lib/solargraph/source/chain.rb +21 -6
  50. data/lib/solargraph/source_map/clip.rb +5 -0
  51. data/lib/solargraph/source_map/mapper.rb +2 -0
  52. data/lib/solargraph/type_checker.rb +71 -65
  53. data/lib/solargraph/version.rb +1 -1
  54. data/lib/solargraph/views/environment.erb +2 -2
  55. data/lib/solargraph/workspace.rb +11 -14
  56. data/lib/solargraph/yard_map/mapper/to_method.rb +7 -4
  57. data/lib/solargraph/yard_map.rb +34 -193
  58. data/lib/solargraph.rb +2 -2
  59. data/solargraph.gemspec +8 -6
  60. metadata +43 -36
  61. data/lib/solargraph/compat.rb +0 -37
  62. data/lib/solargraph/yard_map/core_docs.rb +0 -170
  63. data/lib/solargraph/yard_map/core_fills.rb +0 -208
  64. data/lib/solargraph/yard_map/core_gen.rb +0 -76
  65. data/lib/solargraph/yard_map/rdoc_to_yard.rb +0 -140
  66. data/lib/solargraph/yard_map/stdlib_fills.rb +0 -43
  67. data/yardoc/2.2.2.tar.gz +0 -0
@@ -9,13 +9,8 @@ module Solargraph
9
9
  line = params['position']['line']
10
10
  col = params['position']['character']
11
11
  suggestions = host.signatures_at(params['textDocument']['uri'], line, col)
12
- info = []
13
- suggestions.each do |pin|
14
- info.concat pin.overloads.map(&:signature_help)
15
- info.push pin.signature_help
16
- end
17
12
  set_result({
18
- signatures: info
13
+ signatures: suggestions.flat_map { |pin| pin.signature_help }
19
14
  })
20
15
  rescue FileNotFoundError => e
21
16
  Logging.logger.warn "[#{e.class}] #{e.message}"
@@ -10,22 +10,29 @@ module Solargraph::LanguageServer::Message::Workspace
10
10
 
11
11
  def process
12
12
  need_catalog = false
13
+ to_create = []
14
+ to_delete = []
15
+
13
16
  # @param change [Hash]
14
17
  params['changes'].each do |change|
15
18
  if change['type'] == CREATED
16
- host.create change['uri']
19
+ to_create << change['uri']
17
20
  need_catalog = true
18
21
  elsif change['type'] == CHANGED
19
22
  next if host.open?(change['uri'])
20
- host.create change['uri']
23
+ to_create << change['uri']
21
24
  need_catalog = true
22
25
  elsif change['type'] == DELETED
23
- host.delete change['uri']
26
+ to_delete << change['uri']
24
27
  need_catalog = true
25
28
  else
26
29
  set_error Solargraph::LanguageServer::ErrorCodes::INVALID_PARAMS, "Unknown change type ##{change['type']} for #{uri_to_file(change['uri'])}"
27
30
  end
28
31
  end
32
+
33
+ host.create *to_create
34
+ host.delete *to_delete
35
+
29
36
  # Force host to catalog libraries after file changes (see castwide/solargraph#139)
30
37
  host.catalog if need_catalog
31
38
  end
@@ -106,36 +106,37 @@ module Solargraph
106
106
  result
107
107
  end
108
108
 
109
- # Create a file source from a file on disk. The file is ignored if it is
109
+ # Create file sources from files on disk. A file is ignored if it is
110
110
  # neither open in the library nor included in the workspace.
111
111
  #
112
- # @param filename [String]
113
- # @return [Boolean] True if the file was added to the workspace.
114
- def create_from_disk filename
112
+ # @param filenames [Array<String>]
113
+ # @return [Boolean] True if at least one file was added to the workspace.
114
+ def create_from_disk *filenames
115
115
  result = false
116
116
  mutex.synchronize do
117
- next if File.directory?(filename) || !File.exist?(filename)
118
- next unless contain?(filename) || open?(filename) || workspace.would_merge?(filename)
119
- source = Solargraph::Source.load_string(File.read(filename), filename)
120
- workspace.merge(source)
121
- maybe_map source
122
- result = true
117
+ sources = filenames
118
+ .reject { |filename| File.directory?(filename) || !File.exist?(filename) }
119
+ .map { |filename| Solargraph::Source.load_string(File.read(filename), filename) }
120
+ result = workspace.merge(*sources)
121
+ sources.each { |source| maybe_map source }
123
122
  end
124
123
  result
125
124
  end
126
125
 
127
- # Delete a file from the library. Deleting a file will make it unavailable
126
+ # Delete files from the library. Deleting a file will make it unavailable
128
127
  # for checkout and optionally remove it from the workspace unless the
129
128
  # workspace configuration determines that it should still exist.
130
129
  #
131
- # @param filename [String]
132
- # @return [Boolean] True if the file was deleted
133
- def delete filename
134
- detach filename
130
+ # @param filenames [Array<String>]
131
+ # @return [Boolean] True if any file was deleted
132
+ def delete *filenames
135
133
  result = false
136
- mutex.synchronize do
137
- result = workspace.remove(filename)
138
- @synchronized = !result if synchronized?
134
+ filenames.each do |filename|
135
+ detach filename
136
+ mutex.synchronize do
137
+ result ||= workspace.remove(filename)
138
+ @synchronized = !result if synchronized?
139
+ end
139
140
  end
140
141
  result
141
142
  end
@@ -267,7 +268,7 @@ module Solargraph
267
268
  next unless source_map_hash.key?(full)
268
269
  return Location.new(full, Solargraph::Range.from_to(0, 0, 0, 0))
269
270
  end
270
- api_map.yard_map.require_reference(pin.name)
271
+ # api_map.yard_map.require_reference(pin.name)
271
272
  rescue FileNotFoundError
272
273
  nil
273
274
  end
@@ -522,7 +523,7 @@ module Solargraph
522
523
  return unless source
523
524
  return unless @current == source || workspace.has_file?(source.filename)
524
525
  if source_map_hash.key?(source.filename)
525
- return if source_map_hash[source.filename].code == source.code &&
526
+ return if source_map_hash[source.filename].code == source.code &&
526
527
  source_map_hash[source.filename].source.synchronized? &&
527
528
  source.synchronized?
528
529
  if source.synchronized?
@@ -8,16 +8,26 @@ module Solargraph
8
8
  include Legacy::NodeMethods
9
9
 
10
10
  def process
11
- here = get_node_start_position(node)
12
11
  pins.push Solargraph::Pin::Constant.new(
13
12
  location: get_node_location(node),
14
13
  closure: region.closure,
15
- name: node.children[1].to_s,
14
+ name: const_name,
16
15
  comments: comments_for(node),
17
16
  assignment: node.children[2]
18
17
  )
19
18
  process_children
20
19
  end
20
+
21
+ private
22
+
23
+ # @return [String]
24
+ def const_name
25
+ if node.children[0]
26
+ Parser::NodeMethods.unpack_name(node.children[0]) + "::#{node.children[1]}"
27
+ else
28
+ node.children[1].to_s
29
+ end
30
+ end
21
31
  end
22
32
  end
23
33
  end
@@ -6,11 +6,32 @@ module Solargraph
6
6
  module NodeProcessors
7
7
  class SclassNode < Parser::NodeProcessor::Base
8
8
  def process
9
- # @todo Temporarily skipping remote metaclasses
10
- return unless node.children[0].is_a?(AST::Node) && node.children[0].type == :self
9
+ sclass = node.children[0]
10
+ if sclass.is_a?(AST::Node) && sclass.type == :self
11
+ closure = region.closure
12
+ elsif sclass.is_a?(AST::Node) && sclass.type == :casgn
13
+ names = [region.closure.namespace, region.closure.name]
14
+ if sclass.children[0].nil? && names.last != sclass.children[1].to_s
15
+ names << sclass.children[1].to_s
16
+ else
17
+ names.concat [NodeMethods.unpack_name(sclass.children[0]), sclass.children[1].to_s]
18
+ end
19
+ name = names.reject(&:empty?).join('::')
20
+ closure = Solargraph::Pin::Namespace.new(name: name, location: region.closure.location)
21
+ elsif sclass.is_a?(AST::Node) && sclass.type == :const
22
+ names = [region.closure.namespace, region.closure.name]
23
+ also = NodeMethods.unpack_name(sclass)
24
+ if also != region.closure.name
25
+ names << also
26
+ end
27
+ name = names.reject(&:empty?).join('::')
28
+ closure = Solargraph::Pin::Namespace.new(name: name, location: region.closure.location)
29
+ else
30
+ return
31
+ end
11
32
  pins.push Solargraph::Pin::Singleton.new(
12
33
  location: get_node_location(node),
13
- closure: region.closure
34
+ closure: closure
14
35
  )
15
36
  process_children region.update(visibility: :public, scope: :class, closure: pins.last)
16
37
  end
@@ -1,4 +1,5 @@
1
1
  require 'solargraph/parser/rubyvm/node_processors'
2
+ require 'solargraph/parser/rubyvm/node_wrapper'
2
3
 
3
4
  module Solargraph
4
5
  module Parser
@@ -7,8 +8,10 @@ module Solargraph
7
8
  # @param code [String]
8
9
  # @param filename [String]
9
10
  # @return [Array(Parser::AST::Node, Array<Parser::Source::Comment>)]
11
+ # @sg-ignore
10
12
  def parse_with_comments code, filename = nil
11
13
  node = RubyVM::AbstractSyntaxTree.parse(code).children[2]
14
+ node &&= RubyVM::AbstractSyntaxTree::NodeWrapper.from(node, code.lines)
12
15
  comments = CommentRipper.new(code).parse
13
16
  [node, comments]
14
17
  rescue ::SyntaxError => e
@@ -19,8 +22,10 @@ module Solargraph
19
22
  # @param filename [String, nil]
20
23
  # @param line [Integer]
21
24
  # @return [Parser::AST::Node]
25
+ # @sg-ignore
22
26
  def parse code, filename = nil, line = 0
23
- RubyVM::AbstractSyntaxTree.parse(code).children[2]
27
+ node = RubyVM::AbstractSyntaxTree.parse(code).children[2]
28
+ node and RubyVM::AbstractSyntaxTree::NodeWrapper.from(node, code.lines)
24
29
  rescue ::SyntaxError => e
25
30
  raise Parser::SyntaxError, e.message
26
31
  end
@@ -9,12 +9,23 @@ module Solargraph
9
9
  pins.push Solargraph::Pin::Constant.new(
10
10
  location: get_node_location(node),
11
11
  closure: region.closure,
12
- name: node.children[0].to_s,
12
+ name: const_name,
13
13
  comments: comments_for(node),
14
- assignment: node.children[1]
14
+ assignment: node.children[2] || node.children[1]
15
15
  )
16
16
  process_children
17
17
  end
18
+
19
+ private
20
+
21
+ # @return [String]
22
+ def const_name
23
+ if Parser.is_ast_node?(node.children[0])
24
+ Parser::NodeMethods.unpack_name(node.children[0])
25
+ else
26
+ node.children[0].to_s
27
+ end
28
+ end
18
29
  end
19
30
  end
20
31
  end
@@ -6,6 +6,8 @@ module Solargraph
6
6
  module NodeProcessors
7
7
  class DefNode < Parser::NodeProcessor::Base
8
8
  def process
9
+ anon_splat = node_has_anon_splat?
10
+
9
11
  methpin = Solargraph::Pin::Method.new(
10
12
  location: get_node_location(node),
11
13
  closure: region.closure,
@@ -13,17 +15,19 @@ module Solargraph
13
15
  comments: comments_for(node),
14
16
  scope: region.scope || (region.closure.is_a?(Pin::Singleton) ? :class : :instance),
15
17
  visibility: region.visibility,
16
- node: node
18
+ node: node,
19
+ anon_splat: anon_splat
17
20
  )
18
- if methpin.name == 'initialize' and methpin.scope == :instance
21
+ if methpin.name == 'initialize' && methpin.scope == :instance
19
22
  pins.push Solargraph::Pin::Method.new(
20
23
  location: methpin.location,
21
24
  closure: methpin.closure,
22
25
  name: 'new',
23
26
  comments: methpin.comments,
24
27
  scope: :class,
25
- parameters: methpin.parameters
26
- )
28
+ parameters: methpin.parameters,
29
+ anon_splat: anon_splat
30
+ )
27
31
  # @todo Smelly instance variable access.
28
32
  pins.last.instance_variable_set(:@return_type, ComplexType::SELF)
29
33
  pins.push methpin
@@ -39,8 +43,9 @@ module Solargraph
39
43
  scope: :class,
40
44
  visibility: :public,
41
45
  parameters: methpin.parameters,
42
- node: methpin.node
43
- )
46
+ node: methpin.node,
47
+ anon_splat: anon_splat
48
+ )
44
49
  pins.push Solargraph::Pin::Method.new(
45
50
  location: methpin.location,
46
51
  closure: methpin.closure,
@@ -49,13 +54,20 @@ module Solargraph
49
54
  scope: :instance,
50
55
  visibility: :private,
51
56
  parameters: methpin.parameters,
52
- node: methpin.node
53
- )
57
+ node: methpin.node,
58
+ anon_splat: anon_splat
59
+ )
54
60
  else
55
61
  pins.push methpin
56
62
  end
57
63
  process_children region.update(closure: methpin, scope: methpin.scope)
58
64
  end
65
+
66
+ private
67
+
68
+ def node_has_anon_splat?
69
+ node.children[1]&.children&.first == [nil]
70
+ end
59
71
  end
60
72
  end
61
73
  end
@@ -11,6 +11,8 @@ module Solargraph
11
11
  s_visi = region.visibility
12
12
  s_visi = :public if region.scope != :class
13
13
  loc = get_node_location(node)
14
+ anon_splat = node_has_anon_splat?
15
+
14
16
  if node.children[0].is_a?(RubyVM::AbstractSyntaxTree::Node) && node.children[0].type == :SELF
15
17
  closure = region.closure
16
18
  else
@@ -26,7 +28,8 @@ module Solargraph
26
28
  comments: comments_for(node),
27
29
  scope: :class,
28
30
  visibility: :public,
29
- node: node
31
+ node: node,
32
+ anon_splat: anon_splat
30
33
  )
31
34
  pins.push Solargraph::Pin::Method.new(
32
35
  location: loc,
@@ -35,7 +38,8 @@ module Solargraph
35
38
  comments: comments_for(node),
36
39
  scope: :instance,
37
40
  visibility: :private,
38
- node: node
41
+ node: node,
42
+ anon_splat: anon_splat
39
43
  )
40
44
  else
41
45
  pins.push Solargraph::Pin::Method.new(
@@ -45,11 +49,18 @@ module Solargraph
45
49
  comments: comments_for(node),
46
50
  scope: :class,
47
51
  visibility: s_visi,
48
- node: node
52
+ node: node,
53
+ anon_splat: anon_splat
49
54
  )
50
55
  end
51
56
  process_children region.update(closure: pins.last, scope: :class)
52
57
  end
58
+
59
+ private
60
+
61
+ def node_has_anon_splat?
62
+ node.children[2]&.children&.first == [nil]
63
+ end
53
64
  end
54
65
  end
55
66
  end
@@ -6,11 +6,22 @@ module Solargraph
6
6
  module NodeProcessors
7
7
  class SclassNode < Parser::NodeProcessor::Base
8
8
  def process
9
- # @todo Temporarily skipping remote metaclasses
10
- return unless node.children[0].is_a?(RubyVM::AbstractSyntaxTree::Node) && node.children[0].type == :SELF
9
+ sclass = node.children[0]
10
+ if sclass.is_a?(RubyVM::AbstractSyntaxTree::Node) && sclass.type == :SELF
11
+ closure = region.closure
12
+ elsif sclass.is_a?(RubyVM::AbstractSyntaxTree::Node) && %i[CDECL CONST].include?(sclass.type)
13
+ names = [region.closure.namespace, region.closure.name]
14
+ if names.last != sclass.children[0].to_s
15
+ names << sclass.children[0].to_s
16
+ end
17
+ name = names.reject(&:empty?).join('::')
18
+ closure = Solargraph::Pin::Namespace.new(name: name, location: region.closure.location)
19
+ else
20
+ return
21
+ end
11
22
  pins.push Solargraph::Pin::Singleton.new(
12
23
  location: get_node_location(node),
13
- closure: region.closure
24
+ closure: closure
14
25
  )
15
26
  process_children region.update(visibility: :public, scope: :class, closure: pins.last)
16
27
  end
@@ -226,8 +226,10 @@ module Solargraph
226
226
 
227
227
  # @return [void]
228
228
  def process_private_constant
229
- return unless Parser.is_ast_node?(node.children.last)
230
- node.children.last.children[0..-2].each do |child|
229
+ arr = node.children[1]
230
+ return unless Parser.is_ast_node?(arr) && [:ARRAY, :LIST].include?(arr.type)
231
+
232
+ arr.children.compact.each do |child|
231
233
  if [:LIT, :STR].include?(child.type)
232
234
  cn = child.children[0].to_s
233
235
  ref = pins.select{|p| [Solargraph::Pin::Namespace, Solargraph::Pin::Constant].include?(p.class) && p.namespace == region.closure.full_context.namespace && p.name == cn}.first
@@ -0,0 +1,47 @@
1
+ require 'delegate'
2
+
3
+ module RubyVM::AbstractSyntaxTree
4
+ # Wrapper for RubyVM::AbstractSyntaxTree::Node. for return character based column
5
+ class NodeWrapper < SimpleDelegator
6
+ attr_reader :code
7
+ # @param node [RubyVM::AbstractSyntaxTree::Node] wrapped node to return character based column
8
+ # @param code [Array<String>] source code lines for generated this node
9
+ def initialize(node, code)
10
+ @code = code
11
+ super(node)
12
+ end
13
+
14
+ def self.from(node, code)
15
+ return node unless node.is_a?(RubyVM::AbstractSyntaxTree::Node) and !node.kind_of?(SimpleDelegator)
16
+
17
+ new(node, code)
18
+ end
19
+
20
+ def is_a?(type)
21
+ __getobj__.is_a?(type) || super.is_a?(type)
22
+ end
23
+
24
+ def class
25
+ __getobj__.class
26
+ end
27
+
28
+
29
+ def first_column
30
+ @first_column ||= begin
31
+ line = @code[__getobj__.first_lineno - 1] || ""
32
+ line.byteslice(0, __getobj__.first_column).length
33
+ end
34
+ end
35
+
36
+ def last_column
37
+ @last_column ||= begin
38
+ line = @code[__getobj__.last_lineno - 1] || ""
39
+ line.byteslice(0, __getobj__.last_column).length
40
+ end
41
+ end
42
+
43
+ def children
44
+ @children ||= __getobj__.children.map do |node| NodeWrapper.from(node, @code) end
45
+ end
46
+ end
47
+ end
@@ -21,9 +21,12 @@ module Solargraph
21
21
  # @return [String]
22
22
  attr_reader :path
23
23
 
24
- # @param location [Solargraph::Location]
24
+ # @return [::Symbol]
25
+ attr_accessor :source
26
+
27
+ # @param location [Solargraph::Location, nil]
25
28
  # @param kind [Integer]
26
- # @param closure [Solargraph::Pin::Closure]
29
+ # @param closure [Solargraph::Pin::Closure, nil]
27
30
  # @param name [String]
28
31
  # @param comments [String]
29
32
  def initialize location: nil, closure: nil, name: '', comments: ''
@@ -34,12 +34,9 @@ module Solargraph
34
34
  end
35
35
  end
36
36
 
37
- # @return [Hash]
37
+ # @return [Array<Hash>]
38
38
  def signature_help
39
- @signature_help ||= {
40
- label: name + '(' + parameters.map(&:full).join(', ') + ')',
41
- documentation: documentation
42
- }
39
+ []
43
40
  end
44
41
 
45
42
  # @return [String]
@@ -47,7 +44,6 @@ module Solargraph
47
44
  # This property is not cached in an instance variable because it can
48
45
  # change when pins get proxied.
49
46
  detail = String.new
50
- detail += "(#{parameters.map(&:full).join(', ')}) " unless !is_a?(Pin::Method) || parameters.empty?
51
47
  detail += "=#{probed? ? '~' : (proxied? ? '^' : '>')} #{return_type.to_s}" unless return_type.undefined?
52
48
  detail.strip!
53
49
  return nil if detail.empty?
@@ -18,13 +18,18 @@ module Solargraph
18
18
 
19
19
  # @param visibility [::Symbol] :public, :protected, or :private
20
20
  # @param explicit [Boolean]
21
- def initialize visibility: :public, explicit: true, parameters: [], node: nil, attribute: false, **splat
21
+ # @param parameters [Array<Pin::Parameter>]
22
+ # @param node [Parser::AST::Node, RubyVM::AbstractSyntaxTree::Node]
23
+ # @param attribute [Boolean]
24
+ def initialize visibility: :public, explicit: true, parameters: [], node: nil, attribute: false, signatures: nil, anon_splat: false, **splat
22
25
  super(**splat)
23
26
  @visibility = visibility
24
27
  @explicit = explicit
25
28
  @parameters = parameters
26
29
  @node = node
27
30
  @attribute = attribute
31
+ @signatures = signatures
32
+ @anon_splat = anon_splat
28
33
  end
29
34
 
30
35
  # @return [Array<String>]
@@ -41,7 +46,45 @@ module Solargraph
41
46
  end
42
47
 
43
48
  def return_type
44
- @return_type ||= generate_complex_type
49
+ @return_type ||= ComplexType.try_parse(*signatures.map(&:return_type).map(&:to_s))
50
+ end
51
+
52
+ # @return [Array<Signature>]
53
+ def signatures
54
+ @signatures ||= begin
55
+ top_type = generate_complex_type
56
+ result = []
57
+ result.push Signature.new(parameters, top_type) if top_type.defined?
58
+ result.concat(overloads.map { |meth| Signature.new(meth.parameters, meth.return_type) })
59
+ result.push Signature.new(parameters, top_type) if result.empty?
60
+ result
61
+ end
62
+ end
63
+
64
+ # @return [String]
65
+ def detail
66
+ # This property is not cached in an instance variable because it can
67
+ # change when pins get proxied.
68
+ detail = String.new
69
+ detail += if signatures.length > 1
70
+ "(*) "
71
+ else
72
+ "(#{signatures.first.parameters.map(&:full).join(', ')}) " unless signatures.first.parameters.empty?
73
+ end.to_s
74
+ detail += "=#{probed? ? '~' : (proxied? ? '^' : '>')} #{return_type.to_s}" unless return_type.undefined?
75
+ detail.strip!
76
+ return nil if detail.empty?
77
+ detail
78
+ end
79
+
80
+ # @return [Array<Hash>]
81
+ def signature_help
82
+ @signature_help ||= signatures.map do |sig|
83
+ {
84
+ label: name + '(' + sig.parameters.map(&:full).join(', ') + ')',
85
+ documentation: documentation
86
+ }
87
+ end
45
88
  end
46
89
 
47
90
  def path
@@ -119,27 +162,60 @@ module Solargraph
119
162
  # @return [Array<Pin::Method>]
120
163
  def overloads
121
164
  @overloads ||= docstring.tags(:overload).map do |tag|
122
- Solargraph::Pin::Method.new(
123
- name: name,
124
- closure: self,
125
- # args: tag.parameters.map(&:first),
126
- parameters: tag.parameters.map do |src|
165
+ Pin::Signature.new(
166
+ tag.parameters.map do |src|
167
+ name, decl = parse_overload_param(src.first)
127
168
  Pin::Parameter.new(
128
169
  location: location,
129
170
  closure: self,
130
171
  comments: tag.docstring.all.to_s,
131
- name: src.first,
172
+ name: name,
173
+ decl: decl,
132
174
  presence: location ? location.range : nil,
133
- decl: :arg
175
+ return_type: param_type_from_name(tag, src.first)
134
176
  )
135
177
  end,
136
- comments: tag.docstring.all.to_s
178
+ ComplexType.try_parse(*tag.docstring.tags(:return).flat_map(&:types))
137
179
  )
138
180
  end
181
+ @overloads
182
+ end
183
+
184
+ def anon_splat?
185
+ @anon_splat
139
186
  end
140
187
 
141
188
  private
142
189
 
190
+ def select_decl name, asgn
191
+ if name.start_with?('**')
192
+ :kwrestarg
193
+ elsif name.start_with?('*')
194
+ :restarg
195
+ elsif name.start_with?('&')
196
+ :blockarg
197
+ elsif name.end_with?(':') && asgn
198
+ :kwoptarg
199
+ elsif name.end_with?(':')
200
+ :kwarg
201
+ elsif asgn
202
+ :optarg
203
+ else
204
+ :arg
205
+ end
206
+ end
207
+
208
+ def clean_param name
209
+ name.gsub(/[*&:]/, '')
210
+ end
211
+
212
+ # @param tag [YARD::Tags::OverloadTag]
213
+ def param_type_from_name(tag, name)
214
+ param = tag.tags(:param).select { |t| t.name == name }.first
215
+ return ComplexType::UNDEFINED unless param
216
+ ComplexType.try_parse(*param.types)
217
+ end
218
+
143
219
  # @return [ComplexType]
144
220
  def generate_complex_type
145
221
  tags = docstring.tags(:return).map(&:types).flatten.reject(&:nil?)
@@ -240,6 +316,20 @@ module Solargraph
240
316
  return ComplexType::UNDEFINED if types.empty?
241
317
  ComplexType.try_parse(*types.map(&:tag).uniq)
242
318
  end
319
+
320
+ # When YARD parses an overload tag, it includes rest modifiers in the parameters names.
321
+ #
322
+ # @param arg [String]
323
+ # @return [Array(String, Symbol)]
324
+ def parse_overload_param(name)
325
+ if name.start_with?('**')
326
+ [name[2..-1], :kwrestarg]
327
+ elsif name.start_with?('*')
328
+ [name[1..-1], :restarg]
329
+ else
330
+ [name, :arg]
331
+ end
332
+ end
243
333
  end
244
334
  end
245
335
  end
@@ -9,10 +9,12 @@ module Solargraph
9
9
  # @return [::Symbol] :class or :module
10
10
  attr_reader :type
11
11
 
12
+ attr_reader :parameters
13
+
12
14
  # @param type [::Symbol] :class or :module
13
15
  # @param visibility [::Symbol] :public or :private
14
16
  # @param gates [Array<String>]
15
- def initialize type: :class, visibility: :public, gates: [''], **splat
17
+ def initialize type: :class, visibility: :public, gates: [''], parameters: [], **splat
16
18
  # super(location, namespace, name, comments)
17
19
  super(**splat)
18
20
  @type = type
@@ -36,6 +38,7 @@ module Solargraph
36
38
  @closure = Pin::Namespace.new(name: closure_name, gates: [parts.join('::')])
37
39
  @context = nil
38
40
  end
41
+ @parameters = parameters
39
42
  end
40
43
 
41
44
  def namespace