solargraph 0.60.4 → 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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/plugins.yml +1 -1
  3. data/.github/workflows/rspec.yml +3 -1
  4. data/CHANGELOG.md +16 -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/convention.rb +5 -3
  17. data/lib/solargraph/diagnostics/require_not_found.rb +0 -4
  18. data/lib/solargraph/external.rb +159 -0
  19. data/lib/solargraph/language_server/message/text_document/definition.rb +4 -1
  20. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -2
  21. data/lib/solargraph/library.rb +11 -24
  22. data/lib/solargraph/metagem.rb +80 -0
  23. data/lib/solargraph/parser/parser_gem/node_chainer.rb +22 -3
  24. data/lib/solargraph/pin/closure.rb +17 -0
  25. data/lib/solargraph/pin/factory_parameter.rb +84 -0
  26. data/lib/solargraph/pin.rb +1 -0
  27. data/lib/solargraph/rbs_map/base.rb +31 -0
  28. data/lib/solargraph/rbs_map/conversions.rb +3 -1
  29. data/lib/solargraph/rbs_map/{core_map.rb → core.rb} +18 -39
  30. data/lib/solargraph/rbs_map/core_fills.rb +1 -1
  31. data/lib/solargraph/rbs_map/gem.rb +13 -0
  32. data/lib/solargraph/rbs_map/helpers.rb +60 -0
  33. data/lib/solargraph/rbs_map/path.rb +19 -0
  34. data/lib/solargraph/rbs_map/stdlib.rb +24 -0
  35. data/lib/solargraph/rbs_map.rb +7 -198
  36. data/lib/solargraph/repo.rb +177 -0
  37. data/lib/solargraph/shell.rb +49 -99
  38. data/lib/solargraph/source/chain/call.rb +29 -4
  39. data/lib/solargraph/source/chain/literal.rb +17 -17
  40. data/lib/solargraph/source/chain/parameter.rb +79 -0
  41. data/lib/solargraph/source/chain.rb +8 -0
  42. data/lib/solargraph/source/source_chainer.rb +40 -44
  43. data/lib/solargraph/source_map/clip.rb +5 -3
  44. data/lib/solargraph/source_map/mapper.rb +6 -2
  45. data/lib/solargraph/source_map.rb +10 -5
  46. data/lib/solargraph/type_checker/rules.rb +2 -1
  47. data/lib/solargraph/version.rb +1 -1
  48. data/lib/solargraph/workspace.rb +0 -1
  49. data/lib/solargraph/yard_map/cache.rb +1 -1
  50. data/lib/solargraph/yard_map/directives/attribute_directive.rb +1 -1
  51. data/lib/solargraph/yard_map/directives/domain_directive.rb +1 -1
  52. data/lib/solargraph/yard_map/directives/method_directive.rb +1 -1
  53. data/lib/solargraph/yard_map/directives/override_directive.rb +1 -1
  54. data/lib/solargraph/yard_map/directives/parse_directive.rb +1 -1
  55. data/lib/solargraph/yard_map/directives/scope_directive.rb +178 -0
  56. data/lib/solargraph/yard_map/directives/visibility_directive.rb +1 -1
  57. data/lib/solargraph/yard_map/directives.rb +5 -2
  58. data/lib/solargraph/yard_map/helpers.rb +1 -1
  59. data/lib/solargraph/yard_map/macro.rb +34 -5
  60. data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -1
  61. data/lib/solargraph/yard_map/mapper/to_method.rb +1 -1
  62. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -1
  63. data/lib/solargraph/yard_map/mapper.rb +2 -2
  64. data/lib/solargraph/yard_map.rb +1 -1
  65. data/lib/solargraph/yardoc.rb +43 -60
  66. data/lib/solargraph.rb +6 -3
  67. data/solargraph.gemspec +1 -1
  68. metadata +22 -9
  69. data/lib/solargraph/doc_map.rb +0 -439
  70. data/lib/solargraph/gem_pins.rb +0 -87
  71. data/lib/solargraph/pin_cache.rb +0 -248
  72. data/lib/solargraph/rbs_map/stdlib_map.rb +0 -71
  73. data/lib/solargraph/workspace/gemspecs.rb +0 -373
@@ -98,60 +98,37 @@ module Solargraph
98
98
  # @return [void]
99
99
  def clear
100
100
  puts 'Deleting all cached documentation (gems, core and stdlib)'
101
- Solargraph::PinCache.clear
101
+ Solargraph::CacheDir.clear
102
102
  end
103
103
  map 'clear-cache' => :clear
104
104
  map 'clear-cores' => :clear
105
105
 
106
- desc 'cache', 'Cache a gem', hide: true
107
- option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
108
- # @return [void]
109
- # @param gem [String]
110
- # @param version [String, nil]
111
- def cache gem, version = nil
112
- gemspec = Gem::Specification.find_by_name(gem, version)
113
-
114
- if options[:rebuild] || !PinCache.has_yard?(gemspec)
115
- pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
116
- PinCache.serialize_yard_gem(gemspec, pins)
117
- end
118
-
119
- workspace = Solargraph::Workspace.new(Dir.pwd) if File.exist?('rbs_collection.yaml')
120
- rbs_map = RbsMap.from_gemspec(gemspec, workspace&.rbs_collection_path, workspace&.rbs_collection_config_path)
121
- if options[:rebuild] || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
122
- PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, rbs_map.pins)
123
- end
124
- rescue Gem::MissingSpecError
125
- warn "Gem '#{gem}' not found"
126
- end
127
-
128
106
  desc 'uncache GEM [...GEM]', 'Delete specific cached gem documentation'
129
107
  long_desc %(
130
108
  Specify one or more gem names to clear. 'core' or 'stdlib' may
131
109
  also be specified to clear cached system documentation.
132
110
  Documentation will be regenerated as needed.
133
111
  )
112
+ option :directory, type: :string, desc: 'Workspace directory', default: Dir.pwd
134
113
  # @param gems [Array<String>]
135
114
  # @return [void]
136
115
  def uncache *gems
137
116
  raise ArgumentError, 'No gems specified.' if gems.empty?
117
+ repo = Solargraph::Repo.new(options[:directory])
118
+
138
119
  gems.each do |gem|
139
120
  if gem == 'core'
140
- PinCache.uncache_core
141
- next
142
- end
143
-
144
- if gem == 'stdlib'
145
- PinCache.uncache_stdlib
146
- next
121
+ Solargraph::Collection::Core.uncache
122
+ elsif gem == 'stdlib'
123
+ FileUtils.rm_rf CacheDir.stdlib_dir
124
+ else
125
+ metagem = repo.find_by_name(gem)
126
+ Solargraph::Collection::Gem.uncache(metagem) if metagem
147
127
  end
148
-
149
- spec = Gem::Specification.find_by_name(gem)
150
- PinCache.uncache_gem(spec, out: $stdout)
151
128
  end
152
129
  end
153
130
 
154
- desc 'gems [GEM[=VERSION]...] [STDLIB...] [core]', 'Cache documentation for
131
+ desc 'cache GEM_NAME [...GEM_NAME] [core]', 'Cache documentation for
155
132
  installed libraries'
156
133
  long_desc %( This command will cache the
157
134
  generated type documentation for the specified libraries. While
@@ -172,56 +149,51 @@ module Solargraph
172
149
  If the library is already cached, it will be rebuilt if the
173
150
  --rebuild option is set.
174
151
 
175
- Cached documentation is stored in #{PinCache.base_dir}, which
152
+ Cached documentation is stored in #{CacheDir.base_dir}, which
176
153
  can be stored between CI runs.
177
154
  )
155
+ option :directory, type: :string, desc: 'Workspace directory', default: Dir.pwd
178
156
  option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
179
157
  # @param names [Array<String>]
180
158
  # @return [void]
181
- def gems *names
182
- # print time with ms
183
- workspace = Solargraph::Workspace.new('.')
184
-
185
- if names.empty?
186
- Gem::Specification.to_a.each { |spec| do_cache spec, rebuild: options[:rebuild] }
187
- $stderr.puts "Documentation cached for all #{Gem::Specification.count} gems."
188
- else
189
- warn("Caching these gems: #{names}")
190
- names.each do |name|
191
- if name == 'core'
192
- # @sg-ignore cache_core and core? are dynamically defined
193
- PinCache.cache_core(out: $stdout) # if !PinCache.core? || options[:rebuild]
194
- next
195
- end
196
-
197
- gemspec = workspace.find_gem(*name.split('='))
198
- if gemspec.nil?
199
- warn "Gem '#{name}' not found"
200
- else
201
- if options[:rebuild] || !PinCache.has_yard?(gemspec)
202
- pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
203
- PinCache.serialize_yard_gem(gemspec, pins)
204
- end
205
-
206
- workspace = Solargraph::Workspace.new(Dir.pwd)
207
- rbs_map = RbsMap.from_gemspec(gemspec, workspace.rbs_collection_path, workspace.rbs_collection_config_path)
208
- if options[:rebuild] || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
209
- # cache pins even if result is zero, so we don't retry building pins
210
- pins = rbs_map.pins || []
211
- PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, pins)
212
- end
213
- end
214
- rescue Gem::MissingSpecError
215
- warn "Gem '#{name}' not found"
216
- rescue Gem::Requirement::BadRequirementError => e
217
- warn "Gem '#{name}' failed while loading"
218
- warn e.message
219
- # @sg-ignore Need to add nil check here
220
- warn e.backtrace.join("\n")
221
- end
222
- warn "Documentation cached for #{names.count} gems."
159
+ # @param [Array<Object>] gem_names
160
+ def cache *gem_names
161
+ repo = Solargraph::Repo.new(options[:directory])
162
+ metagems = if gem_names.empty?
163
+ if repo.bundled?
164
+ repo.bundled.select(&:cacheable?)
165
+ else
166
+ Gem::Specification.all_names
167
+ .map { |name| Gem::Specification.find_by_full_name(name) }
168
+ .map { |gemspec| Metagem.from_specification(gemspec) }
169
+ end
170
+ else
171
+ gem_names.each_with_object([]) do |name, result|
172
+ if name == 'core'
173
+ Collection::Core.uncache if options[:rebuild]
174
+ puts 'Caching core'
175
+ Collection::Core.load
176
+ else
177
+ # @todo Quick and dirty hack for solargraph-rspec require bug
178
+ # (see https://github.com/lekemula/solargraph-rspec/pull/38)
179
+ # TL;DR: `repo.find_by_path` should not be necessary
180
+ found = repo.find_by_name(name) || repo.find_by_path(name)
181
+ if found
182
+ result.push found
183
+ else
184
+ warn "Gem #{name} not found"
185
+ end
186
+ end
187
+ end
188
+ end
189
+ metagems.each do |metagem|
190
+ Collection::Gem.uncache(metagem) if options[:rebuild]
191
+ puts "Caching #{metagem.name} #{metagem.version} (#{metagem.cache_name})"
192
+ Collection::Gem.load(metagem)
223
193
  end
194
+ puts "Documentation cached for #{metagems.count} gems."
224
195
  end
196
+ map 'gems' => :cache
225
197
 
226
198
  desc 'reporters', 'Get a list of diagnostics reporters'
227
199
  # @return [void]
@@ -596,27 +568,5 @@ module Solargraph
596
568
  puts pin.inspect
597
569
  end
598
570
  end
599
-
600
- # @param gemspec [Gem::Specification, nil]
601
- # @param rebuild [Boolean]
602
- # @return [void]
603
- def do_cache gemspec, rebuild: false
604
- if gemspec.nil?
605
- warn "Gem '#{gemspec&.name}' not found"
606
- else
607
- if rebuild || !PinCache.has_yard?(gemspec)
608
- pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
609
- PinCache.serialize_yard_gem(gemspec, pins)
610
- end
611
-
612
- workspace = Solargraph::Workspace.new(Dir.pwd)
613
- rbs_map = RbsMap.from_gemspec(gemspec, workspace.rbs_collection_path, workspace.rbs_collection_config_path)
614
- if rebuild || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
615
- # cache pins even if result is zero, so we don't retry building pins
616
- pins = rbs_map.pins || []
617
- PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, pins)
618
- end
619
- end
620
- end
621
571
  end
622
572
  end
@@ -64,7 +64,9 @@ module Solargraph
64
64
  [stack.first].compact
65
65
  end
66
66
  pin_groups = [] if !api_map.loose_unions && pin_groups.any?(&:empty?)
67
- pins = pin_groups.flatten.uniq(&:path)
67
+ # Several union members can end up with the same pin and return type.
68
+ # @sg-ignore Array#flatten returns a bare Array
69
+ pins = pin_groups.flatten.uniq { |p| [p.path, p.return_type.rooted_tags] }
68
70
  return [] if pins.empty?
69
71
  inferred_pins(pins, api_map, name_pin, locals)
70
72
  end
@@ -153,9 +155,11 @@ module Solargraph
153
155
  # qualify(), however, happens in the namespace where
154
156
  # the docs were written - from the method pin.
155
157
  # @todo Need to add nil check here
156
- if new_return_type.defined?
157
- type = with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, *pin.gates)
158
- end
158
+ type = if new_return_type.defined?
159
+ with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, *pin.gates)
160
+ else
161
+ inferr_from_factory_parameters(api_map, pin)
162
+ end
159
163
  type ||= ComplexType::UNDEFINED
160
164
  [type, new_signature_pin]
161
165
  end
@@ -219,6 +223,27 @@ module Solargraph
219
223
  end
220
224
  end
221
225
 
226
+ # @param api_map [ApiMap]
227
+ # @param method_pin [Pin::Method]
228
+ # @return [ComplexType, nil]
229
+ def inferr_from_factory_parameters api_map, method_pin
230
+ factory_parameter = api_map.factory_parameters_for_method(method_pin).find do |factory_param|
231
+ method_pin.parameters.each_with_index.find do |param, index|
232
+ current_argument = arguments[index]
233
+ next unless current_argument&.literal?
234
+ # @type [Solargraph::Source::Chain::Literal]
235
+ last_link = current_argument.links.last
236
+ argument_value = last_link.value
237
+
238
+ param.name == factory_param.param_name && argument_value == factory_param.value
239
+ end
240
+ end
241
+
242
+ return nil if factory_parameter.nil?
243
+
244
+ factory_parameter.return_type.qualify(api_map, method_pin.namespace)
245
+ end
246
+
222
247
  # @param pin [Pin::Base]
223
248
  # @param api_map [ApiMap]
224
249
  # @param context [ComplexType, ComplexType::UniqueType]
@@ -6,29 +6,29 @@ module Solargraph
6
6
  class Source
7
7
  class Chain
8
8
  class Literal < Link
9
- attr_reader :word, :value
9
+ attr_reader :word
10
+
11
+ # @return [::String, ::Symbol]
12
+ attr_reader :value
13
+
14
+ # @return [Parser::AST::Node]
15
+ attr_reader :node
10
16
 
11
17
  # @param type [String]
12
18
  # @param node [Parser::AST::Node, Object]
13
19
  def initialize type, node
14
20
  super("<#{type}>")
21
+ @node = node
15
22
 
16
- # @todo We might be able to do some light inference from literals and
17
- # tuples as long as literal values are intransitive.
18
-
19
- # if node.is_a?(::Parser::AST::Node)
20
- # # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
21
- # if node.type == :true
22
- # @value = true
23
- # # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
24
- # elsif node.type == :false
25
- # @value = false
26
- # # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
27
- # elsif %i[int sym].include?(node.type)
28
- # # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
29
- # @value = node.children.first
30
- # end
31
- # end
23
+ if node.is_a?(::Parser::AST::Node)
24
+ if node.type == :true
25
+ @value = true
26
+ elsif node.type == :false
27
+ @value = false
28
+ elsif %i[int sym].include?(node.type)
29
+ @value = node.children.first
30
+ end
31
+ end
32
32
  @type = type
33
33
  # @literal_type = ComplexType.try_parse(@value.inspect)
34
34
  @literal_type = ComplexType::UNDEFINED
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class Source
5
+ class Chain
6
+ # A method parameter represented as a link in a method call chain.
7
+ #
8
+ # @example
9
+ # some_method('literal_value')
10
+ # ^^^^^^^^^^^^^
11
+ class Parameter < Link
12
+ # @param literal [Chain::Literal] - literal argument
13
+ # @param method_call_chain [Chain] method call that contains the argument
14
+ def initialize literal, method_call_chain # rubocop:disable Lint/MissingSuper
15
+ @literal = literal
16
+ @method_call_chain = method_call_chain
17
+ end
18
+
19
+ def word
20
+ @word ||= "#{method_name}(.., #{@literal.word}, ..)"
21
+ end
22
+
23
+ # @param api_map [ApiMap]
24
+ # @param name_pin [Pin::Base]
25
+ # @param locals [::Array<Pin::Parameter, Pin::LocalVariable>]
26
+ # @return [::Array<Pin::Base>]
27
+ def resolve api_map, name_pin, locals
28
+ # @type [Pin::Method]
29
+ method_pin = method_call_chain.define(api_map, name_pin, locals)&.first
30
+ return [] unless method_pin
31
+
32
+ # Use indexed lookup for better performance
33
+ api_map.factory_parameters_for_method(method_pin).select do |fp|
34
+ param_index = method_pin.parameters.find_index { |param| param.name == fp.param_name }
35
+ next if param_index.nil?
36
+ next false unless fp.value == literal_value
37
+
38
+ # a splat takes every argument from its position on
39
+ method_pin.parameters[param_index].restarg? ? current_index >= param_index : current_index == param_index
40
+ end
41
+ end
42
+
43
+ # @return [Boolean] true if this is a parameter of Kernel#require
44
+ def require_parameter?
45
+ method_call_chain.links.last.word == 'require'
46
+ end
47
+
48
+ private
49
+
50
+ # @return [Chain::Literal]
51
+ attr_reader :literal
52
+ # @ return [Chain]
53
+ attr_reader :method_call_chain
54
+
55
+ # @return [String] The name of the method that this parameter belongs to
56
+ def method_name
57
+ @method_name ||= method_call.word
58
+ end
59
+
60
+ # @return [Chain::Call]
61
+ def method_call
62
+ @method_call_chain.links.last
63
+ end
64
+
65
+ # The index of the literal in the method call chain.
66
+ # @return [Integer]
67
+ def current_index
68
+ @current_index ||= method_call_chain.node.children[2..].index(literal.node)
69
+ end
70
+
71
+ # @return [::String, ::Symbol] The literal value of the parameter
72
+ # @sg-ignore false "return type could not be inferred"
73
+ def literal_value
74
+ literal.node.children.first
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -34,6 +34,7 @@ module Solargraph
34
34
  autoload :ZSuper, 'solargraph/source/chain/z_super'
35
35
  autoload :Hash, 'solargraph/source/chain/hash'
36
36
  autoload :Array, 'solargraph/source/chain/array'
37
+ autoload :Parameter, 'solargraph/source/chain/parameter'
37
38
 
38
39
  @@inference_stack = []
39
40
  @@inference_depth = 0
@@ -182,6 +183,13 @@ module Solargraph
182
183
  links.last.is_a?(Chain::Literal)
183
184
  end
184
185
 
186
+ # @return [Boolean]
187
+ # @sg-ignore false "return type could not be inferred"
188
+ def require_parameter?
189
+ # @sg-ignore false "Unresolved call to require_parameter? on Solargraph::Source::Chain::Link"
190
+ links.last.is_a?(Chain::Parameter) && links.last.require_parameter?
191
+ end
192
+
185
193
  def undefined?
186
194
  links.any?(&:undefined?)
187
195
  end
@@ -31,55 +31,18 @@ module Solargraph
31
31
 
32
32
  # @return [Source::Chain]
33
33
  def chain
34
- # Special handling for files that end with an integer and a period
35
- # if phrase =~ /^[0-9]+\.$/
36
- # return Chain.new([Chain::Literal.new('Integer', Integer(phrase[0..-2])),
37
- # Chain::UNDEFINED_CALL])
38
- # end
39
- if phrase.start_with?(':') && !phrase.start_with?('::')
40
- return Chain.new([Chain::Literal.new('Symbol',
41
- # @sg-ignore Need to add nil check here
42
- phrase[1..].to_sym)])
43
- end
44
- if end_of_phrase.strip == '::' && source.code[Position.to_offset(
45
- source.code, position
46
- )].to_s.match?(/[a-z]/i)
47
- return SourceChainer.chain(source,
48
- Position.new(position.line,
49
- position.character + 1))
50
- end
34
+ return SourceChainer.chain(source, Position.new(position.line, position.character + 1)) if end_of_phrase.strip == '::' && source.code[Position.to_offset(source.code, position)].to_s.match?(/[a-z]/i)
35
+
51
36
  begin
52
- return Chain.new([]) if phrase.end_with?('..')
53
- # @type [::Parser::AST::Node, nil]
54
- node = nil
55
- # @type [::Parser::AST::Node, nil]
56
- parent = nil
57
- if !source.repaired? && source.parsed? && source.synchronized?
58
- tree = source.tree_at(position.line, position.column)
59
- node, parent = tree[0..2]
60
- elsif source.parsed? && source.repaired? && end_of_phrase == '.'
61
- node, parent = source.tree_at(fixed_position.line, fixed_position.column)[0..2]
62
- # provide filename and line so that we can look up local variables there later
63
- node = Parser.parse(fixed_phrase, source.filename, fixed_position.line) if node.nil?
64
- elsif source.repaired?
65
- node = Parser.parse(fixed_phrase, source.filename, fixed_position.line)
66
- else
67
- unless source.error_ranges.any? do |r|
68
- r.nil? || r.include?(fixed_position)
69
- end
70
- node, parent = source.tree_at(fixed_position.line,
71
- fixed_position.column)[0..2]
72
- end
73
- # Exception for positions that chain literal nodes in unsynchronized sources
74
- node = nil unless source.synchronized? || !Parser.infer_literal_node_type(node).nil?
75
- node = Parser.parse(fixed_phrase, source.filename, fixed_position.line) if node.nil?
76
- end
37
+ node, parent, tree = node_from_position
77
38
  rescue Parser::SyntaxError
78
39
  return Chain.new([Chain::UNDEFINED_CALL])
79
40
  end
41
+
80
42
  return Chain.new([Chain::UNDEFINED_CALL]) if node.nil? || (node.type == :sym && !phrase.start_with?(':'))
81
- # chain = NodeChainer.chain(node, source.filename, parent && parent.type == :block)
82
- chain = Parser.chain(node, source.filename, parent)
43
+
44
+ chain = Parser.chain(node, source.filename, parent, tree)
45
+
83
46
  if source.repaired? || !source.parsed? || !source.synchronized?
84
47
  if end_of_phrase.strip == '.'
85
48
  chain.links.push Chain::UNDEFINED_CALL
@@ -94,6 +57,39 @@ module Solargraph
94
57
 
95
58
  private
96
59
 
60
+ # @return [Array(Parser::AST::Node, Parser::AST::Node, Array<Parser::AST::Node>)]
61
+ # parent node, child node, and the tree at the position
62
+ def node_from_position
63
+ node = nil
64
+ parent = nil
65
+ tree = nil
66
+
67
+ if source.parsed? && !source.repaired? && source.synchronized?
68
+ tree = source.tree_at(position.line, position.column)
69
+ node, parent = tree[0..2]
70
+ elsif source.parsed? && source.repaired? && end_of_phrase == '.'
71
+ tree = source.tree_at(fixed_position.line, fixed_position.column)
72
+ node, parent = tree[0..2]
73
+ # provide filename and line so that we can look up local variables there later
74
+ node = Parser.parse(fixed_phrase, source.filename, fixed_position.line) if node.nil?
75
+ tree = source.tree_at(fixed_position.line, fixed_position.column) if node
76
+ elsif source.repaired?
77
+ node = Parser.parse(fixed_phrase, source.filename, fixed_position.line)
78
+ tree = source.tree_at(fixed_position.line, fixed_position.column) if node
79
+ else
80
+ unless source.error_ranges.any? { |r| r.nil? || r.include?(fixed_position) }
81
+ tree = source.tree_at(fixed_position.line, fixed_position.column)
82
+ node, parent = tree[0..2]
83
+ end
84
+ # Exception for positions that chain literal nodes in unsynchronized sources
85
+ node = nil unless source.synchronized? || !Parser.infer_literal_node_type(node).nil?
86
+ node = Parser.parse(fixed_phrase, source.filename, fixed_position.line) if node.nil?
87
+ tree = source.tree_at(fixed_position.line, fixed_position.column) if node
88
+ end
89
+
90
+ [node, parent, tree]
91
+ end
92
+
97
93
  # @return [Position]
98
94
  attr_reader :position
99
95
 
@@ -13,14 +13,14 @@ module Solargraph
13
13
  @cursor = cursor
14
14
  closure_pin = closure
15
15
  # @sg-ignore Need to add nil check here
16
- if closure_pin.is_a?(Pin::Block) && !Solargraph::Range.from_node(closure_pin.receiver).contain?(cursor.range.start)
16
+ if closure_pin.is_a?(Pin::Block) && !Solargraph::Range.from_node(closure_pin.receiver)&.contain?(cursor.range.start)
17
17
  closure_pin.rebind(api_map)
18
18
  end
19
19
  end
20
20
 
21
21
  # @return [Array<Pin::Base>] Relevant pins for infering the type of the Cursor's position
22
22
  def define
23
- return [] if cursor.comment? || cursor.chain.literal?
23
+ return [] if cursor.comment? || cursor.chain.literal? || cursor.chain.require_parameter?
24
24
  result = cursor.chain.define(api_map, closure, locals)
25
25
  result.concat file_global_methods
26
26
  if result.empty?
@@ -40,7 +40,8 @@ module Solargraph
40
40
  # @return [Completion]
41
41
  def complete
42
42
  return package_completions([]) if !source_map.source.parsed? || cursor.string?
43
- if cursor.chain.literal? && cursor.chain.links.last.word == '<Symbol>'
43
+ # TODO: Improve magic word comparsion == '<::Symbol>', too fragile
44
+ if cursor.chain.literal? && cursor.chain.links.last.word == '<::Symbol>'
44
45
  return package_completions(api_map.get_symbols)
45
46
  end
46
47
  return Completion.new([], cursor.range) if cursor.chain.literal?
@@ -211,6 +212,7 @@ module Solargraph
211
212
  result.concat api_map.get_constants('', cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates) # .select { |pin| pin.name.start_with?(full) }
212
213
  end
213
214
  else
215
+ type = type.qualify(api_map, *gates)
214
216
  result.concat api_map.get_constants(type.namespace,
215
217
  cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates)
216
218
  end
@@ -12,7 +12,7 @@ module Solargraph
12
12
 
13
13
  private_class_method :new
14
14
 
15
- DIRECTIVE_REGEXP = /(@!method|@!attribute|@!visibility|@!domain|@!macro|@!parse|@!override)/
15
+ DIRECTIVE_REGEXP = /(@!method|@!attribute|@!visibility|@!scope|@!domain|@!macro|@!parse|@!override)/
16
16
 
17
17
  # Generate the data.
18
18
  #
@@ -104,9 +104,13 @@ module Solargraph
104
104
  directive_processor = YardMap::Directives.for(directive)
105
105
  return unless directive_processor
106
106
 
107
- @pins += directive_processor.process_directive(
107
+ new_pins = directive_processor.process_directive(
108
108
  @source, @pins, source_position, comment_position, directive
109
109
  )
110
+ # A @!scope directive in the indented text of another directive applies
111
+ # only to the object that directive defines.
112
+ YardMap::Directives::ScopeDirective.apply_nested directive, new_pins
113
+ @pins += new_pins
110
114
  end
111
115
 
112
116
  # @param comment [String]
@@ -190,10 +190,12 @@ module Solargraph
190
190
  end
191
191
  end
192
192
 
193
- private
194
-
195
193
  # @return [Array<Pin::Base>]
196
- attr_writer :convention_pins
194
+ def convention_pins
195
+ @convention_pins || []
196
+ end
197
+
198
+ private
197
199
 
198
200
  # @return [Hash{Class<Pin::Base> => Array<Pin::Base>}]
199
201
  def pin_class_hash
@@ -206,9 +208,12 @@ module Solargraph
206
208
  @data ||= Data.new(source)
207
209
  end
208
210
 
211
+ # @param pins [Array<Pin::Base>]
209
212
  # @return [Array<Pin::Base>]
210
- def convention_pins
211
- @convention_pins || []
213
+ def convention_pins= pins
214
+ # unmemoizing the document_symbols in case it was called from any of conventions
215
+ @document_symbols = nil
216
+ @convention_pins = pins
212
217
  end
213
218
 
214
219
  # @generic T
@@ -75,7 +75,7 @@ module Solargraph
75
75
  # @todo 22: Translate to something flow sensitive typing understands
76
76
  # @todo 3: Need a downcast here
77
77
  #
78
- # flow sensitive typing could handle (96):
78
+ # flow sensitive typing could handle (97):
79
79
  #
80
80
  # @todo 36: flow sensitive typing needs to handle attrs
81
81
  # @todo 29: flow sensitive typing should be able to handle redefinition
@@ -113,6 +113,7 @@ module Solargraph
113
113
  # @todo 1: flow sensitive typing needs to create separate ranges for postfix if
114
114
  # @todo 1: flow sensitive typing needs to handle constants
115
115
  # @todo 1: flow sensitive typing needs to eliminate literal from union with return if foo == :bar
116
+ # @todo 1: Array#flatten returns a bare Array
116
117
  def require_all_unique_types_match_expected?
117
118
  report?(:require_all_unique_types_match_expected, :strong)
118
119
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = ENV.fetch('SOLARGRAPH_FORCE_VERSION', '0.60.4')
4
+ VERSION = ENV.fetch('SOLARGRAPH_FORCE_VERSION', '0.61.0')
5
5
  end
@@ -10,7 +10,6 @@ module Solargraph
10
10
  #
11
11
  class Workspace
12
12
  autoload :Config, 'solargraph/workspace/config'
13
- autoload :Gemspecs, 'solargraph/workspace/gemspecs'
14
13
  autoload :RequirePaths, 'solargraph/workspace/require_paths'
15
14
 
16
15
  # @return [String]
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  class Cache
6
6
  def initialize
7
7
  # @type [Hash{String => Array<Solargraph::Pin::Base>}]
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  module Directives
6
6
  module AttributeDirective
7
7
  module_function
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  module Directives
6
6
  module DomainDirective
7
7
  module_function