solargraph 0.21.1 → 0.22.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph.rb +0 -1
  3. data/lib/solargraph/api_map.rb +40 -23
  4. data/lib/solargraph/api_map/completion.rb +3 -0
  5. data/lib/solargraph/api_map/probe.rb +16 -19
  6. data/lib/solargraph/api_map/store.rb +6 -2
  7. data/lib/solargraph/diagnostics/rubocop.rb +6 -0
  8. data/lib/solargraph/language_server/host.rb +9 -1
  9. data/lib/solargraph/language_server/message.rb +3 -0
  10. data/lib/solargraph/language_server/message/extended.rb +1 -0
  11. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +1 -1
  12. data/lib/solargraph/language_server/message/extended/download_core.rb +25 -0
  13. data/lib/solargraph/language_server/message/initialize.rb +3 -1
  14. data/lib/solargraph/language_server/message/text_document.rb +13 -11
  15. data/lib/solargraph/language_server/message/text_document/definition.rb +1 -1
  16. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  17. data/lib/solargraph/language_server/message/text_document/references.rb +14 -0
  18. data/lib/solargraph/language_server/message/text_document/rename.rb +17 -0
  19. data/lib/solargraph/language_server/uri_helpers.rb +2 -0
  20. data/lib/solargraph/library.rb +37 -0
  21. data/lib/solargraph/live_map.rb +8 -2
  22. data/lib/solargraph/live_map/cache.rb +7 -0
  23. data/lib/solargraph/pin.rb +1 -0
  24. data/lib/solargraph/pin/attribute.rb +5 -1
  25. data/lib/solargraph/pin/namespace.rb +0 -2
  26. data/lib/solargraph/pin/proxy_method.rb +31 -0
  27. data/lib/solargraph/plugin/process.rb +1 -1
  28. data/lib/solargraph/plugin/runtime.rb +0 -1
  29. data/lib/solargraph/shell.rb +0 -1
  30. data/lib/solargraph/source.rb +31 -1
  31. data/lib/solargraph/source/fragment.rb +12 -15
  32. data/lib/solargraph/source/location.rb +3 -0
  33. data/lib/solargraph/source/mapper.rb +20 -10
  34. data/lib/solargraph/source/node_methods.rb +111 -0
  35. data/lib/solargraph/version.rb +1 -1
  36. data/lib/solargraph/workspace.rb +7 -1
  37. data/lib/solargraph/workspace/config.rb +6 -2
  38. data/lib/solargraph/yard_map.rb +12 -1
  39. metadata +25 -7
  40. data/lib/solargraph/node_methods.rb +0 -101
@@ -38,6 +38,6 @@ module Solargraph::LanguageServer::Message::TextDocument
38
38
  def link_documentation path
39
39
  uri = "solargraph:/document?query=" + URI.encode(path)
40
40
  "[#{path}](#{uri})"
41
- end
41
+ end
42
42
  end
43
43
  end
@@ -0,0 +1,14 @@
1
+ module Solargraph::LanguageServer::Message::TextDocument
2
+ class References < Base
3
+ def process
4
+ locs = host.references_from(uri_to_file(params['textDocument']['uri']), params['position']['line'], params['position']['character'])
5
+ result = locs.map do |loc|
6
+ {
7
+ uri: file_to_uri(loc.filename),
8
+ range: loc.range.to_hash
9
+ }
10
+ end
11
+ set_result result
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ module Solargraph::LanguageServer::Message::TextDocument
2
+ class Rename < Base
3
+ def process
4
+ locs = host.references_from(uri_to_file(params['textDocument']['uri']), params['position']['line'], params['position']['character'])
5
+ changes = {}
6
+ locs.each do |loc|
7
+ uri = file_to_uri(loc.filename)
8
+ changes[uri] ||= []
9
+ changes[uri].push({
10
+ range: loc.range.to_hash,
11
+ newText: params['newName']
12
+ })
13
+ end
14
+ set_result changes: changes
15
+ end
16
+ end
17
+ end
@@ -1,6 +1,8 @@
1
1
  module Solargraph
2
2
  module LanguageServer
3
3
  module UriHelpers
4
+ module_function
5
+
4
6
  # Convert a file URI to a path.
5
7
  #
6
8
  # @param uri [String]
@@ -141,6 +141,30 @@ module Solargraph
141
141
  api_map.signify(fragment)
142
142
  end
143
143
 
144
+ def references_from filename, line, column
145
+ source = read(filename)
146
+ api_map.virtualize source
147
+ fragment = source.fragment_at(line, column)
148
+ pins = api_map.define(fragment)
149
+ return [] if pins.empty?
150
+ result = []
151
+ # @param pin [Solargraph::Pin::Base]
152
+ pins.uniq.each do |pin|
153
+ if pin.kind != Solargraph::Pin::NAMESPACE and !pin.location.nil?
154
+ mn_loc = get_symbol_name_location(pin)
155
+ result.push mn_loc unless mn_loc.nil?
156
+ end
157
+ (workspace.sources + source_hash.values).uniq(&:filename).each do |source|
158
+ found = source.references(pin.name).select do |loc|
159
+ referenced = definitions_at(loc.filename, loc.range.ending.line, loc.range.ending.character).first
160
+ !referenced.nil? and referenced.path == pin.path
161
+ end
162
+ result.concat found.sort{|a, b| a.range.start.line <=> b.range.start.line}
163
+ end
164
+ end
165
+ result
166
+ end
167
+
144
168
  # Get the pin at the specified location or nil if the pin does not exist.
145
169
  #
146
170
  # @return [Solargraph::Pin::Base]
@@ -270,5 +294,18 @@ module Solargraph
270
294
  raise FileNotFoundError, "File not found: #{filename}" unless File.file?(filename)
271
295
  Solargraph::Source.load(filename)
272
296
  end
297
+
298
+ def get_symbol_name_location pin
299
+ decsrc = read(pin.location.filename)
300
+ offset = Solargraph::Source::Position.to_offset(decsrc.code, pin.location.range.start)
301
+ soff = decsrc.code.index(pin.name, offset)
302
+ eoff = soff + pin.name.length
303
+ Solargraph::Source::Location.new(
304
+ pin.location.filename, Solargraph::Source::Range.new(
305
+ Solargraph::Source::Position.from_offset(decsrc.code, soff),
306
+ Solargraph::Source::Position.from_offset(decsrc.code, eoff)
307
+ )
308
+ )
309
+ end
273
310
  end
274
311
  end
@@ -9,11 +9,17 @@ module Solargraph
9
9
  # @return [Solargraph::ApiMap]
10
10
  attr_reader :api_map
11
11
 
12
+ # @param api_map [Solargraph::ApiMap]
12
13
  def initialize api_map
13
14
  @api_map = api_map
14
15
  runners
15
16
  end
16
17
 
18
+ def get_path_pin path
19
+ cache.get_path_pin(path)
20
+ end
21
+
22
+ # @return [Array<Solargraph::Pin::Base>]
17
23
  def get_methods(namespace, root = '', scope = 'instance', with_private = false)
18
24
  fqns = api_map.find_fully_qualified_namespace(namespace, root)
19
25
  params = {
@@ -26,7 +32,7 @@ module Solargraph
26
32
  runners.each do |p|
27
33
  next if did_runtime and p.runtime?
28
34
  p.get_methods(namespace: namespace, root: root, scope: scope, with_private: with_private).each do |m|
29
- result.push Suggestion.new(m['name'], kind: Suggestion::METHOD, docstring: YARD::Docstring.new('(defined at runtime)'), path: "#{fqns}.#{m['name']}", arguments: m['parameters'])
35
+ result.push Solargraph::Pin::Method.new(nil, namespace, m['name'], YARD::Docstring.new('(defined at runtime)'), scope.to_sym, nil, [])
30
36
  end
31
37
  did_runtime = true if p.runtime?
32
38
  end
@@ -34,7 +40,7 @@ module Solargraph
34
40
  result
35
41
  end
36
42
 
37
- # @return [Array<Solargraph::Suggestion>]
43
+ # @return [Array<Solargraph::Pin::Base>]
38
44
  def get_constants(namespace, root = '')
39
45
  cached = cache.get_constants(namespace, root)
40
46
  return cached unless cached.nil?
@@ -4,6 +4,7 @@ module Solargraph
4
4
  def initialize
5
5
  @method_cache = {}
6
6
  @constant_cache = {}
7
+ @path_cache = {}
7
8
  end
8
9
 
9
10
  def get_methods options
@@ -12,6 +13,7 @@ module Solargraph
12
13
 
13
14
  def set_methods options, values
14
15
  @method_cache[options] = values
16
+ values.each { |pin| @path_cache[pin.path] = pin }
15
17
  end
16
18
 
17
19
  def get_constants namespace, root
@@ -22,9 +24,14 @@ module Solargraph
22
24
  @constant_cache[[namespace, root]] = values
23
25
  end
24
26
 
27
+ def get_path_pin path
28
+ @path_cache[path]
29
+ end
30
+
25
31
  def clear
26
32
  @method_cache.clear
27
33
  @constant_cache.clear
34
+ @path_cache.clear
28
35
  end
29
36
  end
30
37
  end
@@ -21,6 +21,7 @@ module Solargraph
21
21
  autoload :Documenting, 'solargraph/pin/documenting'
22
22
  autoload :Block, 'solargraph/pin/block'
23
23
  autoload :Localized, 'solargraph/pin/localized'
24
+ autoload :ProxyMethod, 'solargraph/pin/proxy_method'
24
25
 
25
26
  ATTRIBUTE = 1
26
27
  CLASS_VARIABLE = 2
@@ -4,10 +4,14 @@ module Solargraph
4
4
  # @return [Symbol] :reader or :writer
5
5
  attr_reader :access
6
6
 
7
- def initialize location, namespace, name, docstring, access
7
+ # @return [Symbol] :class or :instance
8
+ attr_reader :scope
9
+
10
+ def initialize location, namespace, name, docstring, access, scope
8
11
  super(location, namespace, name, docstring)
9
12
  @access = access
10
13
  @docstring = docstring
14
+ @scope = scope
11
15
  end
12
16
 
13
17
  def kind
@@ -1,8 +1,6 @@
1
1
  module Solargraph
2
2
  module Pin
3
3
  class Namespace < Pin::Base
4
- include Solargraph::NodeMethods
5
-
6
4
  attr_reader :visibility
7
5
 
8
6
  attr_reader :type
@@ -0,0 +1,31 @@
1
+ module Solargraph
2
+ module Pin
3
+ # ProxyMethod serves as a quick, disposable shortcut for providing context
4
+ # to type inference methods. ApiMap::Probe can treat it as an anonymous
5
+ # method while analyzing signatures.
6
+ #
7
+ class ProxyMethod
8
+ # @return [String]
9
+ attr_reader :return_type
10
+
11
+ def initialize return_type
12
+ @return_type = return_type
13
+ end
14
+
15
+ # @return [String]
16
+ def namespace
17
+ @namespace ||= ApiMap::TypeMethods.extract_namespace(@return_type)
18
+ end
19
+
20
+ # @return [Integer]
21
+ def kind
22
+ Pin::METHOD
23
+ end
24
+
25
+ # @return [Symbol]
26
+ def scope
27
+ :instance
28
+ end
29
+ end
30
+ end
31
+ end
@@ -33,7 +33,7 @@ module Solargraph
33
33
  rescue JSON::ParserError => e
34
34
  STDOUT.puts respond_err "Error parsing input: #{e.message}"
35
35
  rescue Exception => e
36
- STDOUT.puts respond_err "Error processing input: #{e.message}"
36
+ STDOUT.puts respond_err "Error processing input: #{e.message}\n#{e.backtrace}"
37
37
  end
38
38
  STDOUT.flush
39
39
  end
@@ -61,7 +61,6 @@ module Solargraph
61
61
 
62
62
  def load_environment
63
63
  return if api_map.nil?
64
- STDERR.puts "Required paths given to Runtime: #{api_map.required}"
65
64
  send_require api_map.required
66
65
  @current_required = api_map.required.clone
67
66
  end
@@ -69,7 +69,6 @@ module Solargraph
69
69
  matches = []
70
70
  if options[:extensions]
71
71
  Gem::Specification.each do |g|
72
- puts g.name
73
72
  if g.name.match(/^solargraph\-[A-Za-z0-9_\-]*?\-ext/)
74
73
  require g.name
75
74
  matches.push g.name
@@ -11,6 +11,7 @@ module Solargraph
11
11
  autoload :Updater, 'solargraph/source/updater'
12
12
  autoload :Change, 'solargraph/source/change'
13
13
  autoload :Mapper, 'solargraph/source/mapper'
14
+ autoload :NodeMethods, 'solargraph/source/node_methods'
14
15
 
15
16
  # @return [String]
16
17
  attr_reader :code
@@ -40,6 +41,7 @@ module Solargraph
40
41
  # @return [Time]
41
42
  attr_reader :stime
42
43
 
44
+ # @return [Array<Solargraph::Pin::Base>]
43
45
  attr_reader :pins
44
46
 
45
47
  attr_reader :requires
@@ -132,6 +134,22 @@ module Solargraph
132
134
  symbol_pins
133
135
  end
134
136
 
137
+ # @return [Array<Source::Location>]
138
+ def references name
139
+ inner_node_references(name, node).map do |n|
140
+ offset = Position.to_offset(code, get_node_start_position(n))
141
+ soff = code.index(name, offset)
142
+ eoff = soff + name.length
143
+ Location.new(
144
+ filename,
145
+ Solargraph::Source::Range.new(
146
+ Position.from_offset(code, soff),
147
+ Position.from_offset(code, eoff)
148
+ )
149
+ )
150
+ end
151
+ end
152
+
135
153
  def locate_named_path_pin line, character
136
154
  _locate_pin line, character, Pin::NAMESPACE, Pin::METHOD
137
155
  end
@@ -157,7 +175,8 @@ module Solargraph
157
175
 
158
176
  # Get the nearest node that contains the specified index.
159
177
  #
160
- # @param index [Integer]
178
+ # @param line [Integer]
179
+ # @param column [Integer]
161
180
  # @return [AST::Node]
162
181
  def node_at(line, column)
163
182
  tree_at(line, column).first
@@ -246,6 +265,17 @@ module Solargraph
246
265
 
247
266
  private
248
267
 
268
+ def inner_node_references name, top
269
+ result = []
270
+ if top.kind_of?(AST::Node)
271
+ if (top.type == :const and top.children[1].to_s == name) or (top.type == :send and top.children[1].to_s == name)
272
+ result.push top
273
+ end
274
+ top.children.each { |c| result.concat inner_node_references(name, c) }
275
+ end
276
+ result
277
+ end
278
+
249
279
  def parse
250
280
  node, comments = inner_parse(@fixed, filename)
251
281
  @node = node
@@ -3,12 +3,11 @@ module Solargraph
3
3
  class Fragment
4
4
  include NodeMethods
5
5
 
6
- attr_reader :tree
7
-
8
6
  attr_reader :line
9
7
 
10
8
  attr_reader :column
11
9
 
10
+ # @return [Solargraph::Source]
12
11
  attr_reader :source
13
12
 
14
13
  # @param source [Solargraph::Source]
@@ -46,17 +45,6 @@ module Solargraph
46
45
  @argument ||= !signature_position.nil?
47
46
  end
48
47
 
49
- def chained?
50
- if @chained.nil?
51
- @chained = false
52
- @tree.each do |n|
53
- @chained = true if n.type == :send
54
- break
55
- end
56
- end
57
- @chained
58
- end
59
-
60
48
  # @return [Fragment]
61
49
  def recipient
62
50
  return nil if signature_position.nil?
@@ -319,9 +307,18 @@ module Solargraph
319
307
  @base_literal = 'Integer'
320
308
  # @todo Smelly exceptional case for array literals
321
309
  elsif signature.start_with?('.[]')
322
- index += 3
323
- signature = signature[index+3..-1].to_s
310
+ index += 2
311
+ signature = signature[3..-1].to_s
324
312
  @base_literal = 'Array'
313
+ elsif signature.start_with?('.')
314
+ pos = Position.from_offset(source.code, index)
315
+ node = source.node_at(pos.line, pos.character)
316
+ lit = infer_literal_node_type(node)
317
+ unless lit.nil?
318
+ signature = signature[1..-1].to_s
319
+ index += 1
320
+ @base_literal = lit
321
+ end
325
322
  end
326
323
  [index + 1, signature]
327
324
  end
@@ -1,7 +1,10 @@
1
1
  module Solargraph
2
2
  class Source
3
3
  class Location
4
+ # @return [String]
4
5
  attr_reader :filename
6
+
7
+ # @return [Solargraph::Source::Range]
5
8
  attr_reader :range
6
9
 
7
10
  def initialize filename, range
@@ -106,9 +106,17 @@ module Solargraph
106
106
  u = c.updated(:ivasgn, c.children + ora.children[1..-1], nil)
107
107
  @docstring_hash[u.loc] = docstring_for(ora)
108
108
  pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, docstring_for(u), resolve_node_signature(u.children[1]), infer_literal_node_type(u.children[1]), context)
109
+ if visibility == :module_function and context.kind == Pin::METHOD
110
+ other = pins.select{|pin| pin.path == "#{context.namespace}.#{context.name}"}.first
111
+ pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, docstring_for(u), resolve_node_signature(u.children[1]), infer_literal_node_type(u.children[1]), other) unless other.nil?
112
+ end
109
113
  end
110
114
  else
111
115
  pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(c), fqn || '',c.children[0].to_s, docstring_for(c), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), context)
116
+ if visibility == :module_function and context.kind == Pin::METHOD
117
+ other = pins.select{|pin| pin.path == "#{context.namespace}.#{context.name}"}.first
118
+ pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(c), fqn || '',c.children[0].to_s, docstring_for(c), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), other)
119
+ end
112
120
  end
113
121
  elsif c.type == :cvasgn
114
122
  here = get_node_start_position(c)
@@ -212,8 +220,14 @@ module Solargraph
212
220
  ref = pins.select{|p| p.namespace == (fqn || '') and p.name == cn}.first
213
221
  unless ref.nil?
214
222
  pins.delete ref
215
- pins.push Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.docstring, :class, :public, ref.parameters)
216
- pins.push Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.docstring, :instance, :private, ref.parameters)
223
+ mm = Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.docstring, :class, :public, ref.parameters)
224
+ cm = Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.docstring, :instance, :private, ref.parameters)
225
+ pins.push mm, cm
226
+ pins.select{|pin| pin.kind == Pin::INSTANCE_VARIABLE and pin.context == ref}.each do |ivar|
227
+ pins.delete ivar
228
+ pins.push Solargraph::Pin::InstanceVariable.new(ivar.location, ivar.namespace, ivar.name, ivar.docstring, ivar.signature, ivar.instance_variable_get(:@literal), mm)
229
+ pins.push Solargraph::Pin::InstanceVariable.new(ivar.location, ivar.namespace, ivar.name, ivar.docstring, ivar.signature, ivar.instance_variable_get(:@literal), cm)
230
+ end
217
231
  end
218
232
  end
219
233
  elsif c.children[2].type == :def
@@ -253,10 +267,10 @@ module Solargraph
253
267
  elsif c.type == :send and [:attr_reader, :attr_writer, :attr_accessor].include?(c.children[1])
254
268
  c.children[2..-1].each do |a|
255
269
  if c.children[1] == :attr_reader or c.children[1] == :attr_accessor
256
- pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}", docstring_for(c), :reader) #AttrPin.new(c)
270
+ pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}", docstring_for(c), :reader, scope)
257
271
  end
258
272
  if c.children[1] == :attr_writer or c.children[1] == :attr_accessor
259
- pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}=", docstring_for(c), :writer) #AttrPin.new(c)
273
+ pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}=", docstring_for(c), :writer, scope)
260
274
  end
261
275
  end
262
276
  elsif c.type == :sclass and c.children[0].type == :self
@@ -330,10 +344,6 @@ module Solargraph
330
344
  nil
331
345
  end
332
346
 
333
- def get_node_start_position(node)
334
- Position.new(node.loc.line - 1, node.loc.column)
335
- end
336
-
337
347
  def get_node_location(node)
338
348
  if node.nil?
339
349
  st = Position.new(0, 0)
@@ -392,10 +402,10 @@ module Solargraph
392
402
  t = (d.tag.types.nil? || d.tag.types.empty?) ? nil : d.tag.types.flatten.join('')
393
403
  if t.nil? or t.include?('r')
394
404
  # location, namespace, name, docstring, access
395
- pins.push Solargraph::Pin::Attribute.new(get_node_location(k.node), namespace_for(k.node).path, d.tag.name, docstring, :reader)
405
+ pins.push Solargraph::Pin::Attribute.new(get_node_location(k.node), namespace_for(k.node).path, d.tag.name, docstring, :reader, :instance)
396
406
  end
397
407
  if t.nil? or t.include?('w')
398
- pins.push Solargraph::Pin::Attribute.new(get_node_location(k.node), namespace_for(k.node).path, "#{d.tag.name}=", docstring, :reader)
408
+ pins.push Solargraph::Pin::Attribute.new(get_node_location(k.node), namespace_for(k.node).path, "#{d.tag.name}=", docstring, :writer, :instance)
399
409
  end
400
410
  elsif d.tag.tag_name == 'method'
401
411
  gen_src = Source.new("def #{d.tag.name};end", filename)