solargraph 0.60.3 → 0.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/plugins.yml +1 -5
  3. data/.github/workflows/rspec.yml +3 -1
  4. data/CHANGELOG.md +26 -0
  5. data/Gemfile +0 -3
  6. data/lib/solargraph/api_map/index.rb +15 -2
  7. data/lib/solargraph/api_map/store.rb +13 -0
  8. data/lib/solargraph/api_map.rb +60 -81
  9. data/lib/solargraph/cache_dir.rb +58 -0
  10. data/lib/solargraph/collection/base.rb +50 -0
  11. data/lib/solargraph/collection/core.rb +17 -0
  12. data/lib/solargraph/collection/gem.rb +57 -0
  13. data/lib/solargraph/collection/rbs.rb +49 -0
  14. data/lib/solargraph/collection/stdlib.rb +25 -0
  15. data/lib/solargraph/collection.rb +31 -0
  16. data/lib/solargraph/complex_type.rb +18 -5
  17. data/lib/solargraph/convention.rb +5 -3
  18. data/lib/solargraph/diagnostics/require_not_found.rb +0 -4
  19. data/lib/solargraph/external.rb +159 -0
  20. data/lib/solargraph/language_server/message/text_document/definition.rb +4 -1
  21. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -2
  22. data/lib/solargraph/library.rb +11 -24
  23. data/lib/solargraph/metagem.rb +80 -0
  24. data/lib/solargraph/parser/parser_gem/node_chainer.rb +22 -3
  25. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +14 -5
  26. data/lib/solargraph/pin/closure.rb +17 -0
  27. data/lib/solargraph/pin/factory_parameter.rb +84 -0
  28. data/lib/solargraph/pin/method.rb +7 -4
  29. data/lib/solargraph/pin.rb +1 -0
  30. data/lib/solargraph/rbs_map/base.rb +31 -0
  31. data/lib/solargraph/rbs_map/conversions.rb +3 -1
  32. data/lib/solargraph/rbs_map/{core_map.rb → core.rb} +18 -39
  33. data/lib/solargraph/rbs_map/core_fills.rb +1 -1
  34. data/lib/solargraph/rbs_map/gem.rb +13 -0
  35. data/lib/solargraph/rbs_map/helpers.rb +60 -0
  36. data/lib/solargraph/rbs_map/path.rb +19 -0
  37. data/lib/solargraph/rbs_map/stdlib.rb +24 -0
  38. data/lib/solargraph/rbs_map.rb +7 -198
  39. data/lib/solargraph/repo.rb +177 -0
  40. data/lib/solargraph/shell.rb +49 -99
  41. data/lib/solargraph/source/chain/call.rb +190 -67
  42. data/lib/solargraph/source/chain/literal.rb +17 -17
  43. data/lib/solargraph/source/chain/parameter.rb +79 -0
  44. data/lib/solargraph/source/chain.rb +44 -5
  45. data/lib/solargraph/source/source_chainer.rb +40 -44
  46. data/lib/solargraph/source_map/clip.rb +5 -3
  47. data/lib/solargraph/source_map/mapper.rb +6 -2
  48. data/lib/solargraph/source_map.rb +10 -5
  49. data/lib/solargraph/type_checker/rules.rb +2 -1
  50. data/lib/solargraph/version.rb +1 -1
  51. data/lib/solargraph/workspace.rb +0 -1
  52. data/lib/solargraph/yard_map/cache.rb +1 -1
  53. data/lib/solargraph/yard_map/directives/attribute_directive.rb +1 -1
  54. data/lib/solargraph/yard_map/directives/domain_directive.rb +1 -1
  55. data/lib/solargraph/yard_map/directives/method_directive.rb +1 -1
  56. data/lib/solargraph/yard_map/directives/override_directive.rb +1 -1
  57. data/lib/solargraph/yard_map/directives/parse_directive.rb +1 -1
  58. data/lib/solargraph/yard_map/directives/scope_directive.rb +178 -0
  59. data/lib/solargraph/yard_map/directives/visibility_directive.rb +1 -1
  60. data/lib/solargraph/yard_map/directives.rb +5 -2
  61. data/lib/solargraph/yard_map/helpers.rb +1 -1
  62. data/lib/solargraph/yard_map/macro.rb +34 -5
  63. data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -1
  64. data/lib/solargraph/yard_map/mapper/to_method.rb +1 -1
  65. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -1
  66. data/lib/solargraph/yard_map/mapper.rb +37 -4
  67. data/lib/solargraph/yard_map.rb +1 -1
  68. data/lib/solargraph/yardoc.rb +43 -60
  69. data/lib/solargraph.rb +6 -3
  70. data/solargraph.gemspec +1 -1
  71. metadata +22 -9
  72. data/lib/solargraph/doc_map.rb +0 -439
  73. data/lib/solargraph/gem_pins.rb +0 -87
  74. data/lib/solargraph/pin_cache.rb +0 -248
  75. data/lib/solargraph/rbs_map/stdlib_map.rb +0 -71
  76. data/lib/solargraph/workspace/gemspecs.rb +0 -373
@@ -227,7 +227,7 @@ module Solargraph
227
227
  expected = expected.downcast_to_literal_if_possible
228
228
  inferred = downcast_to_literal_if_possible
229
229
 
230
- return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
230
+ return duck_types_match?(api_map, expected, inferred, rules) if expected.duck_type?
231
231
 
232
232
  if rules.include? :allow_any_match
233
233
  inferred.any? do |inf|
@@ -245,18 +245,31 @@ module Solargraph
245
245
  # @param api_map [ApiMap]
246
246
  # @param expected [ComplexType, UniqueType]
247
247
  # @param inferred [ComplexType, UniqueType]
248
+ # @param rules [Array<Symbol>]
248
249
  # @return [Boolean]
249
- def duck_types_match? api_map, expected, inferred
250
+ def duck_types_match? api_map, expected, inferred, rules = []
250
251
  raise ArgumentError, 'Expected type must be duck type' unless expected.duck_type?
252
+ allow_any_match = rules.include?(:allow_any_match)
251
253
  expected.each do |exp|
252
254
  next unless exp.duck_type?
253
- quack = exp.to_s[1..]
254
- # @sg-ignore Need to add nil check here
255
- return false if api_map.get_method_stack(inferred.namespace, quack, scope: inferred.scope).empty?
255
+ quack = exp.to_s[1..] || ''
256
+ matched = allow_any_match ? inferred.any? { |inf| duck_type_provides?(api_map, inf, quack) } : inferred.all? { |inf| duck_type_provides?(api_map, inf, quack) }
257
+ return false unless matched
256
258
  end
257
259
  true
258
260
  end
259
261
 
262
+ # @param api_map [ApiMap]
263
+ # @param inf [UniqueType]
264
+ # @param quack [String]
265
+ # @return [Boolean]
266
+ def duck_type_provides? api_map, inf, quack
267
+ return true if inf.duck_type? && inf.to_s[1..] == quack
268
+
269
+ !api_map.get_method_stack(inf.namespace, quack, scope: inf.scope).empty?
270
+ end
271
+ private :duck_type_provides?
272
+
260
273
  # @return [String]
261
274
  def rooted_tags
262
275
  map(&:rooted_tag).join(', ')
@@ -38,12 +38,14 @@ module Solargraph
38
38
  result
39
39
  end
40
40
 
41
- # @param doc_map [DocMap]
41
+ # @todo Determine what argument needs to be passed here
42
+ #
43
+ # @param object [Object]
42
44
  # @return [Environ]
43
- def self.for_global doc_map
45
+ def self.for_global object
44
46
  result = Environ.new
45
47
  @@conventions.each do |conv|
46
- result.merge conv.global(doc_map)
48
+ result.merge conv.global(object)
47
49
  end
48
50
  result
49
51
  end
@@ -13,10 +13,6 @@ module Solargraph
13
13
  # @sg-ignore Need to add nil check here
14
14
  map = api_map.source_map(source.filename)
15
15
  map.requires.each { |ref| refs[ref.name] = ref }
16
- api_map.missing_docs.each do |r|
17
- next unless refs.key?(r)
18
- result.push docs_error(r, refs[r].location)
19
- end
20
16
  api_map.unresolved_requires.each do |r|
21
17
  next unless refs.key?(r)
22
18
  result.push require_error(r, refs[r].location)
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module Solargraph
6
+ # @todo This class might need a way to track changes to the repo, e.g.,
7
+ # bundle or dependency updates
8
+ #
9
+ class External
10
+ # @return [String]
11
+ attr_reader :directory
12
+
13
+ # @return [Array<String>]
14
+ attr_reader :requires
15
+
16
+ # @param directory [String]
17
+ # @param requires [Array<String>]
18
+ def initialize directory, requires
19
+ @repo = Repo.new(directory)
20
+ @directory = directory
21
+ @requires = requires
22
+ update!
23
+ end
24
+
25
+ def unresolved_requires
26
+ @unresolved_requires ||= []
27
+ end
28
+
29
+ def unresolved_dependencies
30
+ @unresolved_dependencies ||= []
31
+ end
32
+
33
+ def loaded_gems
34
+ @loaded_gems ||= Set.new
35
+ end
36
+
37
+ def unloaded_gems
38
+ @unloaded_gems ||= Set.new
39
+ end
40
+
41
+ def pins
42
+ @pins ||= []
43
+ end
44
+
45
+ # @return [Integer] incremented whenever the external pin set is rebuilt
46
+ def generation
47
+ @generation ||= 0
48
+ end
49
+
50
+ # @param new_requires [Array<String>]
51
+ # @return [Boolean]
52
+ def update new_requires
53
+ return false if requires == new_requires && !cache_changed?
54
+
55
+ requires.replace new_requires
56
+ update!
57
+ true
58
+ end
59
+
60
+ # @return [Array<String>]
61
+ def rbs_collection_paths
62
+ @rbs_collection_paths ||= read_rbs_collection_paths
63
+ end
64
+
65
+ # @return [String, nil]
66
+ def rbs_collection_config_path
67
+ # @todo Get rid of the '*' case
68
+ @rbs_collection_config_path ||= unless directory.nil? || directory.empty? || directory == '*'
69
+ yaml_file = File.join(directory, 'rbs_collection.yaml')
70
+ yaml_file if File.file?(yaml_file)
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def update!
77
+ @generation = generation + 1
78
+ clear_all
79
+ load_requires
80
+ load_rbs_collection
81
+ end
82
+
83
+ def cache_changed?
84
+ unloaded_gems.any? { |gem| Collection::Gem.cached?(gem) }
85
+ end
86
+
87
+ def load_requires
88
+ bundler_require = false
89
+
90
+ requires.uniq.each do |path|
91
+ if path == 'bundler/require'
92
+ bundler_require = true
93
+ end
94
+ if RbsMap::Stdlib.has?(path)
95
+ pins.concat Collection::Stdlib.load(path)
96
+ else
97
+ metagem = @repo.find_by_path(path)
98
+ next unresolved_requires.push(path) unless metagem
99
+ process_gem metagem
100
+ end
101
+ end
102
+
103
+ return unless bundler_require
104
+
105
+ @repo.find_by_group(:default).each { |metagem| process_gem metagem }
106
+ end
107
+
108
+ def load_rbs_collection
109
+ rbs_collection_pins = rbs_collection_paths.flat_map { |path| Collection::Rbs.load(path) }
110
+ pins.replace RbsMap::Helpers.combine(pins, rbs_collection_pins)
111
+ end
112
+
113
+ def clear_all
114
+ pins.clear
115
+ unresolved_requires.clear
116
+ unresolved_dependencies.clear
117
+ loaded_gems.clear
118
+ unloaded_gems.clear
119
+ end
120
+
121
+ def process_gem metagem
122
+ return if loaded_gems.include?(metagem) || unloaded_gems.include?(metagem)
123
+
124
+ if metagem.cacheable?
125
+ if Collection::Gem.cached?(metagem)
126
+ loaded_gems.add metagem
127
+ pins.concat Collection::Gem.load(metagem)
128
+ else
129
+ unloaded_gems.add metagem
130
+ end
131
+ else
132
+ loaded_gems.add metagem
133
+ pins.concat Collection::Gem.load(metagem)
134
+ end
135
+ load_dependencies metagem
136
+ end
137
+
138
+ def load_dependencies parent
139
+ parent.dependencies.each do |name|
140
+ next if loaded_gems.map(&:name).include?(name) || unloaded_gems.map(&:name).include?(name)
141
+ metagem = @repo.find_by_name(name)
142
+ next unresolved_dependencies.push(name) unless metagem
143
+ process_gem metagem
144
+ end
145
+ end
146
+
147
+ # @return [Array<String>]
148
+ def read_rbs_collection_paths
149
+ return [] unless rbs_collection_config_path
150
+
151
+ yaml = YAML.load_file(rbs_collection_config_path)
152
+ [File.expand_path(yaml.fetch('path'), directory)].concat(
153
+ yaml.fetch('sources', [])
154
+ .select { |source| source['type'] == 'local' && source['path'] }
155
+ .map { |source| File.expand_path(source['path'], directory) }
156
+ ).compact
157
+ end
158
+ end
159
+ end
@@ -29,7 +29,10 @@ module Solargraph
29
29
 
30
30
  # @return [Array<Hash>, nil]
31
31
  def require_location
32
- # @todo Terrible hack
32
+ # @todo Terrible hack - move this logic to [Solargraph::Source::Chain::Parameter]
33
+ # @example
34
+ # require 'click_me'
35
+ # ^^^^^^^^^^
33
36
  lib = host.library_for(params['textDocument']['uri'])
34
37
  rloc = Solargraph::Location.new(uri_to_file(params['textDocument']['uri']),
35
38
  Solargraph::Range.from_to(@line, @column, @line, @column))
@@ -11,8 +11,7 @@ module Solargraph
11
11
  contents = []
12
12
  suggestions = host.definitions_at(params['textDocument']['uri'], line, col)
13
13
  last_link = nil
14
- # @sg-ignore Need to add nil check here
15
- suggestions.each do |pin|
14
+ suggestions&.each do |pin|
16
15
  parts = []
17
16
  this_link = host.options['enablePages'] ? pin.link_documentation : pin.text_documentation
18
17
  parts.push this_link if !this_link.nil? && this_link != last_link
@@ -576,14 +576,8 @@ module Solargraph
576
576
  return unless source
577
577
  # @sg-ignore Wrong argument type for Solargraph::Workspace#has_file?: filename expected String, received String, nil
578
578
  return unless @current == source || workspace.has_file?(source.filename)
579
- if source_map_hash.key?(source.filename)
580
- new_map = Solargraph::SourceMap.map(source)
581
- # @sg-ignore OK if source.filename is nil
582
- source_map_hash[source.filename] = new_map
583
- else
584
- # @sg-ignore OK if source.filename is nil
585
- source_map_hash[source.filename] = Solargraph::SourceMap.map(source)
586
- end
579
+ # @sg-ignore OK if source.filename is nil
580
+ source_map_hash[source.filename] = Solargraph::SourceMap.map(source)
587
581
  end
588
582
 
589
583
  # @return [Set<Gem::Specification>]
@@ -595,10 +589,10 @@ module Solargraph
595
589
  def cache_next_gemspec
596
590
  return if @cache_progress
597
591
 
598
- spec = cacheable_specs.first
592
+ spec = api_map.unloaded_gems.first
599
593
  return end_cache_progress unless spec
600
594
 
601
- pending = api_map.uncached_gemspecs.length - cache_errors.length - 1
595
+ pending = api_map.unloaded_gems.length - cache_errors.length - 1
602
596
 
603
597
  if Yardoc.processing?(spec)
604
598
  logger.info "Enqueuing cache of #{spec.name} #{spec.version} (already being processed)"
@@ -615,7 +609,10 @@ module Solargraph
615
609
  logger.info "Caching #{spec.name} #{spec.version}"
616
610
  Thread.new do
617
611
  report_cache_progress spec.name, pending
618
- _o, e, s = Open3.capture3(workspace.command_path, 'cache', spec.name, spec.version.to_s)
612
+
613
+ cmd = [workspace.command_path, 'cache', spec.name]
614
+ cmd.push('--directory', workspace.directory) if workspace.directory
615
+ _o, e, s = Open3.capture3(*cmd)
619
616
  if s.success?
620
617
  logger.info "Cached #{spec.name} #{spec.version}"
621
618
  else
@@ -630,17 +627,6 @@ module Solargraph
630
627
  end
631
628
  end
632
629
 
633
- # @return [Array<Gem::Specification>]
634
- def cacheable_specs
635
- cacheable = api_map.uncached_yard_gemspecs +
636
- api_map.uncached_rbs_collection_gemspecs -
637
- queued_gemspec_cache -
638
- cache_errors.to_a
639
- return cacheable unless cacheable.empty?
640
-
641
- queued_gemspec_cache
642
- end
643
-
644
630
  # @return [Array<Gem::Specification>]
645
631
  def queued_gemspec_cache
646
632
  @queued_gemspec_cache ||= []
@@ -696,8 +682,9 @@ module Solargraph
696
682
  source_map_hash.each_value { |map| find_external_requires(map) }
697
683
  api_map.catalog bench
698
684
  logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)"
699
- logger.info "#{api_map.uncached_yard_gemspecs.length} uncached YARD gemspecs"
700
- logger.info "#{api_map.uncached_rbs_collection_gemspecs.length} uncached RBS collection gemspecs"
685
+ # @todo This needs work
686
+ # logger.info "#{api_map.uncached_yard_gemspecs.length} uncached YARD gemspecs"
687
+ # logger.info "#{api_map.uncached_rbs_collection_gemspecs.length} uncached RBS collection gemspecs"
701
688
  cache_next_gemspec
702
689
  @sync_count = 0
703
690
  end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class Metagem
5
+ include Equality
6
+
7
+ # @return [String]
8
+ attr_reader :name
9
+
10
+ # @return [String]
11
+ attr_reader :full_path
12
+
13
+ # @return [String]
14
+ attr_reader :spec_file
15
+
16
+ # @return [String]
17
+ attr_reader :source
18
+
19
+ # @return [String]
20
+ attr_reader :version
21
+
22
+ # @return [Array<String>]
23
+ attr_reader :require_paths
24
+
25
+ # @return [Array<String>]
26
+ attr_reader :dependencies
27
+
28
+ # @return [String]
29
+ attr_reader :cache_name
30
+
31
+ def initialize name:, full_path:, spec_file:, source:, version:, require_paths:, dependencies:
32
+ @name = name
33
+ @full_path = full_path
34
+ @spec_file = spec_file
35
+ @source = source
36
+ @version = version
37
+ @require_paths = require_paths
38
+ @dependencies = dependencies
39
+ # @todo Possible change to cache names to make them more specific to sources
40
+ # @cache_name = "#{File.basename(full_path)}__#{source.gsub(/[^a-z0-9\-_]/i, '_')}" unless source.end_with?('Path')
41
+ # @todo Better path source detection
42
+ @cache_name = File.basename(full_path) unless source.start_with?('source at') || source.to_s.end_with?('::Path')
43
+ end
44
+
45
+ def cacheable?
46
+ !!@cache_name
47
+ end
48
+
49
+ def require? path
50
+ require_paths.any? do |req|
51
+ File.file?(File.join(full_path, req, "#{path}.rb"))
52
+ end
53
+ end
54
+
55
+ def to_specification
56
+ Gem::Specification.load(spec_file)
57
+ end
58
+
59
+ alias full_gem_path full_path
60
+
61
+ # @return [Array]
62
+ def equality_fields
63
+ # A version arrives as a String from a bundle definition and as a
64
+ # Gem::Version from a specification, so compare its string form.
65
+ [name, version.to_s, full_path, source]
66
+ end
67
+
68
+ # @param gem [Gem::Specification]
69
+ # @return [Metagem]
70
+ def self.from_specification gem
71
+ Metagem.new(name: gem.name,
72
+ full_path: gem.full_gem_path,
73
+ spec_file: gem.spec_file,
74
+ source: gem.source.to_s,
75
+ version: gem.version,
76
+ require_paths: gem.require_paths,
77
+ dependencies: gem.runtime_dependencies.map(&:name))
78
+ end
79
+ end
80
+ end
@@ -13,10 +13,12 @@ module Solargraph
13
13
  # @param node [Parser::AST::Node]
14
14
  # @param filename [String, nil]
15
15
  # @param parent [Parser::AST::Node, nil]
16
- def initialize node, filename = nil, parent = nil
16
+ # @param tree [Array<Parser::AST::Node>, nil]
17
+ def initialize node, filename = nil, parent = nil, tree = nil
17
18
  @node = node
18
19
  @filename = filename
19
20
  @parent = parent
21
+ @tree = tree
20
22
  end
21
23
 
22
24
  # @return [Source::Chain]
@@ -29,9 +31,10 @@ module Solargraph
29
31
  # @param node [Parser::AST::Node]
30
32
  # @param filename [String, nil]
31
33
  # @param parent [Parser::AST::Node, nil]
34
+ # @param tree [Parser::AST::Node, nil]
32
35
  # @return [Source::Chain]
33
- def chain node, filename = nil, parent = nil
34
- NodeChainer.new(node, filename, parent).chain
36
+ def chain node, filename = nil, parent = nil, tree = nil
37
+ NodeChainer.new(node, filename, parent, tree).chain
35
38
  end
36
39
 
37
40
  # @param code [String]
@@ -150,6 +153,13 @@ module Solargraph
150
153
  elsif n.type == :array
151
154
  chained_children = n.children.map { |c| NodeChainer.chain(c) }
152
155
  result.push Source::Chain::Array.new(chained_children, n)
156
+ elsif inside_method_call? # If the node is inside a method call, it may be a parameter.
157
+ lit = infer_literal_node_type(n)
158
+ if lit
159
+ method_call_chain = NodeChainer.chain(send_node, @filename, nil, [])
160
+ literal = Chain::Literal.new(lit, n)
161
+ result.push Chain::Parameter.new(literal, method_call_chain)
162
+ end
153
163
  else
154
164
  lit = infer_literal_node_type(n)
155
165
  result.push(lit ? Chain::Literal.new(lit, n) : Chain::Link.new)
@@ -158,6 +168,15 @@ module Solargraph
158
168
  result
159
169
  end
160
170
 
171
+ def inside_method_call?
172
+ !send_node.nil? && send_node != @node
173
+ end
174
+
175
+ # @return [Parser::AST::Node, nil]
176
+ def send_node
177
+ @send_node ||= @tree&.find { |n| n.type == :send }
178
+ end
179
+
161
180
  # @param node [Parser::AST::Node]
162
181
  def hash_is_splatted? node
163
182
  return false unless Parser.is_ast_node?(node) && node.type == :hash
@@ -27,7 +27,6 @@ module Solargraph
27
27
  source: :parser
28
28
  )
29
29
  pins.push nspin
30
- Solargraph.logger.warn "Superclass: #{superclass_name}" if superclass_name&.start_with?('Array')
31
30
  if superclass_name
32
31
  pins.push Pin::Reference::Superclass.new(
33
32
  location: loc,
@@ -41,12 +40,22 @@ module Solargraph
41
40
 
42
41
  private
43
42
 
44
- # @param comments [String]
43
+ # Type arguments for a generic superclass in inline RBS syntax, e.g.,
44
+ # `class Foo < Array #[String]`. Only recognized where RBS defines
45
+ # it: directly after the superclass, on the same line, with no space
46
+ # between `#` and `[`. Anything else is an ordinary comment.
47
+ #
45
48
  # @return [String, nil]
46
49
  def parameters_from_inline_rbs
47
- source = region.source.code_for(node)
48
- match = source.match(/[^\n]*?#\s?+\[([^\]]*)/)
49
- return unless match && match[1]
50
+ superclass = node.children[1]
51
+ return unless superclass
52
+
53
+ source = region.source.code
54
+ pos = get_node_end_position(superclass)
55
+ offset = Position.line_char_to_offset(source, pos.line, pos.character)
56
+ eol = source.index("\n", offset) || source.length
57
+ match = source[offset...eol].to_s.match(/\A\s*#\[([^\]]*)\]/)
58
+ return unless match
50
59
 
51
60
  code = match[1].strip
52
61
  return if code.empty?
@@ -6,6 +6,23 @@ module Solargraph
6
6
  # @return [::Symbol] :class or :instance
7
7
  attr_reader :scope
8
8
 
9
+ # Override the pin's scope.
10
+ #
11
+ # Scope is normally determined by the syntax that declared the pin, but
12
+ # YARD's `@!scope` directive can change it after the fact. Any values
13
+ # derived from the scope, such as the pin's path and context, are
14
+ # recalculated.
15
+ #
16
+ # @param scope [::Symbol] :class or :instance
17
+ # @return [void]
18
+ def scope= scope
19
+ return if @scope == scope
20
+
21
+ @scope = scope
22
+ # remove cached values generated from scope
23
+ reset_generated!
24
+ end
25
+
9
26
  # @param scope [::Symbol] :class or :instance
10
27
  # @param generics [::Array<Pin::String>, nil]
11
28
  # @param generic_defaults [Hash{String => ComplexType}]
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Pin
5
+ # A method parameter with a literal value used in a factory method.
6
+ #
7
+ # @example
8
+ # RSpec.shared_examples 'some examples'
9
+ #
10
+ # FactoryParameter.new(
11
+ # method_name: 'shared_examples',
12
+ # method_namespace: 'RSpec',
13
+ # method_scope: :class,
14
+ # param_name: 'name',
15
+ # value: 'some examples',
16
+ # decl: :arg,
17
+ # return_type: 'void'
18
+ # )
19
+ class FactoryParameter < Base
20
+ # @return [String]
21
+ attr_reader :method_name
22
+ # @return [String]
23
+ attr_reader :method_namespace
24
+ # @return [Symbol] :class or :instance
25
+ attr_reader :method_scope
26
+ # @return [String, nil]
27
+ attr_reader :param_name
28
+ # @return [::String, ::Symbol] The literal value
29
+ attr_reader :value
30
+ # @return [::Symbol] :arg, :optarg, :kwarg, :kwoptarg, :restarg, :kwrestarg, :block, :blockarg
31
+ attr_reader :decl
32
+ # @return [Location, nil]
33
+ attr_reader :location
34
+
35
+ # @param method_name [String] The name of the method that this parameter belongs to
36
+ # @param method_namespace [String] The class of the method that this parameter belongs to
37
+ # @param method_scope [Symbol] The scope of the method, either :class or :instance
38
+ # @param param_name [String, nil] The name of the parameter
39
+ # @param value [String, Symbol] The value of the parameter
40
+ # @param decl [::Symbol] :arg, :kwarg
41
+ # @param location [Location, nil] The location of the parameter in the source code
42
+ # @param return_type [String, nil] The return type of the method that this parameter belongs to
43
+ def initialize method_name:,
44
+ method_namespace:,
45
+ method_scope:,
46
+ param_name:,
47
+ value:,
48
+ return_type:,
49
+ decl: :arg,
50
+ location: nil
51
+ super(location: location)
52
+ @method_name = method_name
53
+ @method_namespace = method_namespace
54
+ @method_scope = method_scope
55
+ @param_name = param_name
56
+ @value = value
57
+ @decl = decl
58
+ @return_type = return_type
59
+ end
60
+
61
+ def name
62
+ param_name
63
+ end
64
+
65
+ def text_documentation
66
+ "#{method_path}(#{param_name}) = #{value.inspect}"
67
+ end
68
+
69
+ # @return [String]
70
+ def method_path
71
+ # @sg-ignore false failure on method_scope "Wrong argument type for Solargraph::Pin::Base#==: other expected
72
+ # Solargraph::Pin::Base, received :instance"
73
+ @method_path ||= "#{method_namespace}#{method_scope == :instance ? '#' : '.'}#{method_name}"
74
+ end
75
+
76
+ private
77
+
78
+ def inner_desc
79
+ "method_path=#{method_path}, value=#{value.inspect}, decl=#{decl.inspect}, " \
80
+ "return_type=#{return_type&.to_s || 'nil'}"
81
+ end
82
+ end
83
+ end
84
+ end
@@ -148,12 +148,13 @@ module Solargraph
148
148
 
149
149
  # @param parameters [::Array<Parameter>]
150
150
  # @param return_type [ComplexType, nil]
151
+ # @param tag_docstring [YARD::Docstring] source of @yieldparam/@yieldreturn tags; pass an @overload tag's own docstring here
151
152
  # @return [Signature]
152
- def generate_signature parameters, return_type
153
+ def generate_signature parameters, return_type, tag_docstring = docstring
153
154
  # @type [Pin::Signature, nil]
154
155
  block = nil
155
- yieldparam_tags = docstring.tags(:yieldparam)
156
- yieldreturn_tags = docstring.tags(:yieldreturn)
156
+ yieldparam_tags = tag_docstring.tags(:yieldparam)
157
+ yieldreturn_tags = tag_docstring.tags(:yieldreturn)
157
158
  generics = docstring.tags(:generic).map(&:name)
158
159
  needs_block_param_signature =
159
160
  parameters.last&.block? || !yieldreturn_tags.empty? || !yieldparam_tags.empty?
@@ -747,7 +748,9 @@ module Solargraph
747
748
  top_type = generate_complex_type
748
749
  result = []
749
750
  result.push generate_signature(parameters, top_type) if top_type.defined?
750
- result.concat(overloads.map { |meth| generate_signature(meth.parameters, meth.return_type) }) unless overloads.empty?
751
+ # @param meth [Pin::Signature]
752
+ # @param tag [YARD::Tags::OverloadTag]
753
+ result.concat(overloads.zip(docstring.tags(:overload).select(&:parameters)).map { |meth, tag| generate_signature(meth.parameters, meth.return_type, tag.docstring) }) unless overloads.empty?
751
754
  result.push generate_signature(parameters, @return_type || ComplexType::UNDEFINED) if result.empty?
752
755
  result
753
756
  end
@@ -38,6 +38,7 @@ module Solargraph
38
38
  autoload :Until, 'solargraph/pin/until'
39
39
  autoload :While, 'solargraph/pin/while'
40
40
  autoload :Callable, 'solargraph/pin/callable'
41
+ autoload :FactoryParameter, 'solargraph/pin/factory_parameter'
41
42
  autoload :CompoundStatement,
42
43
  'solargraph/pin/compound_statement'
43
44
 
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module RbsMap
5
+ class Base
6
+ def pins
7
+ @pins ||= RbsMap::Conversions.new(loader: loader).pins
8
+ end
9
+
10
+ # @return [RBS::Repository]
11
+ def repository
12
+ @repository ||= RBS::Repository.new(no_stdlib: true)
13
+ end
14
+
15
+ # @return [RBS::EnvironmentLoader]
16
+ def loader
17
+ @loader ||= RBS::EnvironmentLoader.new(core_root: nil, repository: repository)
18
+ end
19
+
20
+ # @param path [String]
21
+ # @return [Array<Pin::Base>]
22
+ def path_pins path
23
+ pins.select { |p| p.path == path }
24
+ end
25
+
26
+ def self.pins(...)
27
+ new(...).pins
28
+ end
29
+ end
30
+ end
31
+ end