solargraph 0.21.1 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
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)