solargraph 0.25.1 → 0.26.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph.rb +18 -16
  3. data/lib/solargraph/api_map.rb +100 -161
  4. data/lib/solargraph/api_map/source_to_yard.rb +9 -9
  5. data/lib/solargraph/api_map/store.rb +50 -13
  6. data/lib/solargraph/basic_type.rb +33 -0
  7. data/lib/solargraph/basic_type_methods.rb +111 -0
  8. data/lib/solargraph/complex_type.rb +51 -89
  9. data/lib/solargraph/core_fills.rb +12 -8
  10. data/lib/solargraph/diagnostics/type_not_defined.rb +2 -2
  11. data/lib/solargraph/language_server.rb +3 -0
  12. data/lib/solargraph/language_server/completion_item_kinds.rb +2 -0
  13. data/lib/solargraph/language_server/error_codes.rb +2 -0
  14. data/lib/solargraph/language_server/host.rb +53 -6
  15. data/lib/solargraph/language_server/message.rb +13 -0
  16. data/lib/solargraph/language_server/message/text_document/definition.rb +4 -6
  17. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +2 -1
  18. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -1
  19. data/lib/solargraph/language_server/message_types.rb +2 -0
  20. data/lib/solargraph/language_server/request.rb +4 -0
  21. data/lib/solargraph/language_server/symbol_kinds.rb +28 -26
  22. data/lib/solargraph/language_server/transport.rb +3 -0
  23. data/lib/solargraph/language_server/uri_helpers.rb +2 -0
  24. data/lib/solargraph/library.rb +12 -7
  25. data/lib/solargraph/pin.rb +1 -1
  26. data/lib/solargraph/pin/attribute.rb +5 -5
  27. data/lib/solargraph/pin/base.rb +51 -16
  28. data/lib/solargraph/pin/base_variable.rb +25 -7
  29. data/lib/solargraph/pin/block.rb +18 -1
  30. data/lib/solargraph/pin/block_parameter.rb +42 -5
  31. data/lib/solargraph/pin/conversions.rb +4 -2
  32. data/lib/solargraph/pin/method.rb +6 -6
  33. data/lib/solargraph/pin/method_parameter.rb +6 -6
  34. data/lib/solargraph/pin/namespace.rb +7 -2
  35. data/lib/solargraph/pin/proxy_type.rb +39 -0
  36. data/lib/solargraph/pin/symbol.rb +20 -12
  37. data/lib/solargraph/pin/yard_pin/method.rb +2 -2
  38. data/lib/solargraph/source.rb +89 -38
  39. data/lib/solargraph/source/call_chainer.rb +273 -0
  40. data/lib/solargraph/source/chain.rb +104 -0
  41. data/lib/solargraph/source/chain/call.rb +72 -0
  42. data/lib/solargraph/source/chain/class_variable.rb +11 -0
  43. data/lib/solargraph/source/chain/constant.rb +17 -0
  44. data/lib/solargraph/source/chain/definition.rb +16 -0
  45. data/lib/solargraph/source/chain/global_variable.rb +11 -0
  46. data/lib/solargraph/source/chain/head.rb +20 -0
  47. data/lib/solargraph/source/chain/instance_variable.rb +11 -0
  48. data/lib/solargraph/source/chain/link.rb +33 -0
  49. data/lib/solargraph/source/chain/literal.rb +21 -0
  50. data/lib/solargraph/source/chain/variable.rb +11 -0
  51. data/lib/solargraph/source/change.rb +3 -1
  52. data/lib/solargraph/{api_map → source}/completion.rb +3 -1
  53. data/lib/solargraph/source/encoding_fixes.rb +21 -0
  54. data/lib/solargraph/source/fragment.rb +139 -284
  55. data/lib/solargraph/source/mapper.rb +27 -16
  56. data/lib/solargraph/source/node_chainer.rb +94 -0
  57. data/lib/solargraph/source/node_methods.rb +2 -2
  58. data/lib/solargraph/source/position.rb +4 -0
  59. data/lib/solargraph/source/range.rb +10 -2
  60. data/lib/solargraph/version.rb +1 -1
  61. data/lib/solargraph/yard_map.rb +13 -2
  62. metadata +20 -6
  63. data/lib/solargraph/api_map/probe.rb +0 -251
  64. data/lib/solargraph/api_map/type_methods.rb +0 -40
  65. data/lib/solargraph/pin/proxy_method.rb +0 -30
@@ -20,11 +20,15 @@ module Solargraph
20
20
  @node = node
21
21
  @node_stack = []
22
22
  @directives = {}
23
+ @comment_ranges = comments.map do |c|
24
+ Source::Range.from_to(c.loc.expression.line, c.loc.expression.column, c.loc.expression.last_line, c.loc.expression.last_column)
25
+ end
23
26
  @node_comments = associate_comments(node, comments)
24
27
  @pins = []
25
28
  @requires = []
26
29
  @symbols = []
27
30
  @locals = []
31
+ @strings = []
28
32
 
29
33
  @used_comment_locs = []
30
34
 
@@ -37,7 +41,8 @@ module Solargraph
37
41
  @node_comments.reject{|k, v| @used_comment_locs.include?(k)}.each do |k, v|
38
42
  @pins.first.comments.concat v
39
43
  end
40
- [@pins, @locals, @requires, @symbols]
44
+
45
+ [@pins, @locals, @requires, @symbols, @strings, @comment_ranges]
41
46
  end
42
47
 
43
48
  class << self
@@ -70,7 +75,13 @@ module Solargraph
70
75
  @pins ||= []
71
76
  end
72
77
 
78
+ # @param node [Parser::AST::Node]
73
79
  def process node, tree = [], visibility = :public, scope = :instance, fqn = '', stack = []
80
+ return unless node.is_a?(AST::Node)
81
+ if node.type == :str
82
+ @strings.push Source::Range.from_to(node.loc.expression.line, node.loc.expression.column, node.loc.expression.last_line, node.loc.expression.last_column)
83
+ return
84
+ end
74
85
  stack.push node
75
86
  if node.kind_of?(AST::Node)
76
87
  @node_stack.unshift node
@@ -98,17 +109,17 @@ module Solargraph
98
109
  ora = find_parent(stack, :or_asgn)
99
110
  unless ora.nil?
100
111
  u = c.updated(:ivasgn, c.children + ora.children[1..-1], nil)
101
- pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, comments_for(u), resolve_node_signature(u.children[1]), infer_literal_node_type(u.children[1]), context)
112
+ pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, comments_for(u), u.children[1], infer_literal_node_type(u.children[1]), context)
102
113
  if visibility == :module_function and context.kind == Pin::METHOD
103
114
  other = pins.select{|pin| pin.path == "#{context.namespace}.#{context.name}"}.first
104
- pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, comments_for(u), resolve_node_signature(u.children[1]), infer_literal_node_type(u.children[1]), other) unless other.nil?
115
+ pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, comments_for(u), u.children[1], infer_literal_node_type(u.children[1]), other) unless other.nil?
105
116
  end
106
117
  end
107
118
  else
108
- pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(c), fqn || '',c.children[0].to_s, comments_for(c), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), context)
119
+ pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(c), fqn || '',c.children[0].to_s, comments_for(c), c.children[1], infer_literal_node_type(c.children[1]), context)
109
120
  if visibility == :module_function and context.kind == Pin::METHOD
110
121
  other = pins.select{|pin| pin.path == "#{context.namespace}.#{context.name}"}.first
111
- pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(c), fqn || '',c.children[0].to_s, comments_for(c), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), other)
122
+ pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(c), fqn || '',c.children[0].to_s, comments_for(c), c.children[1], infer_literal_node_type(c.children[1]), other)
112
123
  end
113
124
  end
114
125
  elsif c.type == :cvasgn
@@ -118,10 +129,10 @@ module Solargraph
118
129
  ora = find_parent(stack, :or_asgn)
119
130
  unless ora.nil?
120
131
  u = c.updated(:cvasgn, c.children + ora.children[1..-1], nil)
121
- pins.push Solargraph::Pin::ClassVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, comments_for(u), resolve_node_signature(u.children[1]), infer_literal_node_type(u.children[1]), context)
132
+ pins.push Solargraph::Pin::ClassVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, comments_for(u), u.children[1], infer_literal_node_type(u.children[1]), context)
122
133
  end
123
134
  else
124
- pins.push Solargraph::Pin::ClassVariable.new(get_node_location(c), fqn || '', c.children[0].to_s, comments_for(c), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), context)
135
+ pins.push Solargraph::Pin::ClassVariable.new(get_node_location(c), fqn || '', c.children[0].to_s, comments_for(c), c.children[1], infer_literal_node_type(c.children[1]), context)
125
136
  end
126
137
  elsif c.type == :lvasgn
127
138
  here = get_node_start_position(c)
@@ -132,25 +143,25 @@ module Solargraph
132
143
  ora = find_parent(stack, :or_asgn)
133
144
  unless ora.nil?
134
145
  u = c.updated(:lvasgn, c.children + ora.children[1..-1], nil)
135
- @locals.push Solargraph::Pin::LocalVariable.new(get_node_location(u), fqn, u.children[0].to_s, comments_for(ora), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), context, block, presence)
146
+ @locals.push Solargraph::Pin::LocalVariable.new(get_node_location(u), fqn, u.children[0].to_s, comments_for(ora), c.children[1], infer_literal_node_type(c.children[1]), context, block, presence)
136
147
  end
137
148
  else
138
- @locals.push Solargraph::Pin::LocalVariable.new(get_node_location(c), fqn, c.children[0].to_s, comments_for(c), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), context, block, presence)
149
+ @locals.push Solargraph::Pin::LocalVariable.new(get_node_location(c), fqn, c.children[0].to_s, comments_for(c), c.children[1], infer_literal_node_type(c.children[1]), context, block, presence)
139
150
  end
140
151
  elsif c.type == :gvasgn
141
- pins.push Solargraph::Pin::GlobalVariable.new(get_node_location(c), fqn, c.children[0].to_s, comments_for(c), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), @pins.first)
152
+ pins.push Solargraph::Pin::GlobalVariable.new(get_node_location(c), fqn, c.children[0].to_s, comments_for(c), c.children[1], infer_literal_node_type(c.children[1]), @pins.first)
142
153
  elsif c.type == :sym
143
154
  @symbols.push Solargraph::Pin::Symbol.new(get_node_location(c), ":#{c.children[0]}")
144
155
  elsif c.type == :casgn
145
156
  here = get_node_start_position(c)
146
157
  block = get_block_pin(here)
147
- pins.push Solargraph::Pin::Constant.new(get_node_location(c), fqn, c.children[1].to_s, comments_for(c), resolve_node_signature(c.children[2]), infer_literal_node_type(c.children[2]), block, :public)
158
+ pins.push Solargraph::Pin::Constant.new(get_node_location(c), fqn, c.children[1].to_s, comments_for(c), c.children[2], infer_literal_node_type(c.children[2]), block, :public)
148
159
  elsif c.type == :def
149
160
  methpin = Solargraph::Pin::Method.new(get_node_location(c), fqn || '', c.children[(c.type == :def ? 0 : 1)].to_s, comments_for(c), scope, visibility, get_method_args(c))
150
161
  if methpin.name == 'initialize' and methpin.scope == :instance
151
162
  pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, 'new', methpin.comments, :class, :public, methpin.parameters)
152
163
  # @todo Smelly instance variable access.
153
- pins.last.instance_variable_set(:@return_complex_types, ComplexType.parse(methpin.namespace))
164
+ pins.last.instance_variable_set(:@return_complex_type, ComplexType.parse(methpin.namespace))
154
165
  pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, methpin.name, methpin.comments, methpin.scope, :private, methpin.parameters)
155
166
  elsif visibility == :module_function
156
167
  pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, methpin.name, methpin.comments, :class, :public, methpin.parameters)
@@ -290,7 +301,7 @@ module Solargraph
290
301
  end
291
302
  end
292
303
  elsif c.type == :block
293
- @pins.push Solargraph::Pin::Block.new(get_node_location(c), fqn || '', '', comments_for(c), resolve_node_signature(c.children[0]))
304
+ @pins.push Solargraph::Pin::Block.new(get_node_location(c), fqn || '', '', comments_for(c), c.children[0], scope)
294
305
  end
295
306
  process c, tree, visibility, scope, fqn, stack
296
307
  end
@@ -333,8 +344,8 @@ module Solargraph
333
344
  st = Position.new(0, 0)
334
345
  en = Position.from_offset(@code, @code.length)
335
346
  else
336
- st = Position.new(node.loc.line - 1, node.loc.column)
337
- en = Position.new(node.loc.last_line - 1, node.loc.last_column)
347
+ st = Position.new(node.loc.line, node.loc.column)
348
+ en = Position.new(node.loc.last_line, node.loc.last_column)
338
349
  end
339
350
  range = Range.new(st, en)
340
351
  Location.new(filename, range)
@@ -371,7 +382,7 @@ module Solargraph
371
382
  # @param node [Parser::AST::Node]
372
383
  # @return [Solargraph::Pin::Namespace]
373
384
  def namespace_for(node)
374
- position = Source::Position.new(node.loc.line - 1, node.loc.column)
385
+ position = Source::Position.new(node.loc.line, node.loc.column)
375
386
  @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.location.range.contain?(position)}.last
376
387
  end
377
388
 
@@ -0,0 +1,94 @@
1
+ module Solargraph
2
+ class Source
3
+ class NodeChainer
4
+ include NodeMethods
5
+
6
+ def initialize filename, node
7
+ @filename = filename
8
+ @node = node
9
+ # @source = source
10
+ # @line = line
11
+ # @column = column
12
+ end
13
+
14
+ # @return [Source::Chain]
15
+ def chain
16
+ links = generate_links(@node)
17
+ Chain.new(@filename, links)
18
+ end
19
+
20
+ class << self
21
+ # @param source [Source]
22
+ # @param line [Integer]
23
+ # @param column [Integer]
24
+ # @return [Source::Chain]
25
+ def chain filename, node
26
+ NodeChainer.new(filename, node).chain
27
+ end
28
+
29
+ # @param code [String]
30
+ # @return [Chain]
31
+ def load_string(filename, code)
32
+ node = Source.parse_node(code.sub(/\.$/, ''), filename)
33
+ chain = Chain.new(filename, node)
34
+ chain.links.push(Chain::Link.new) if code.end_with?('.')
35
+ chain
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ # @param n [Parser::AST::Node]
42
+ # @return [Array<Chain::Link>]
43
+ def generate_links n
44
+ return [] if n.nil?
45
+ return generate_links(n.children[0]) if n.type == :begin
46
+ # @todo This might not be right. It's weird either way.
47
+ # return generate_links(n.children[2] || n.children[0]) if n.type == :block
48
+ result = []
49
+ if n.type == :block
50
+ # result.concat generate_links(n.children[2])
51
+ result.concat generate_links(n.children[0])
52
+ elsif n.type == :send
53
+ if n.children[0].is_a?(Parser::AST::Node)
54
+ result.concat generate_links(n.children[0])
55
+ args = []
56
+ n.children[2..-1].each do |c|
57
+ # @todo Handle link parameters
58
+ # args.push Chain.new(source, c.loc.last_line - 1, c.loc.column)
59
+ end
60
+ result.push Chain::Call.new(n.children[1].to_s, args)
61
+ elsif n.children[0].nil?
62
+ args = []
63
+ n.children[2..-1].each do |c|
64
+ # @todo Handle link parameters
65
+ # args.push Chain.new(source, c.loc.last_line - 1, c.loc.column)
66
+ end
67
+ result.push Chain::Call.new(n.children[1].to_s, args)
68
+ else
69
+ raise "No idea what to do with #{n}"
70
+ end
71
+ elsif n.type == :self
72
+ result.push Chain::Call.new('self')
73
+ elsif n.type == :const
74
+ result.push Chain::Constant.new(unpack_name(n))
75
+ elsif [:lvar, :lvasgn].include?(n.type)
76
+ result.push Chain::Call.new(n.children[0].to_s)
77
+ elsif [:ivar, :ivasgn].include?(n.type)
78
+ result.push Chain::InstanceVariable.new(n.children[0].to_s)
79
+ elsif [:cvar, :cvasgn].include?(n.type)
80
+ result.push Chain::ClassVariable.new(n.children[0].to_s)
81
+ elsif [:gvar, :gvasgn].include?(n.type)
82
+ result.push Chain::GlobalVariable.new(n.children[0].to_s)
83
+ elsif [:class, :module, :def, :defs].include?(n.type)
84
+ location = Solargraph::Source::Location.new(@filename, Source::Range.from_to(n.loc.expression.line - 1, n.loc.expression.column, n.loc.expression.last_line - 1, n.loc.expression.last_column))
85
+ result.push Chain::Definition.new(location)
86
+ else
87
+ lit = infer_literal_node_type(n)
88
+ result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
89
+ end
90
+ result
91
+ end
92
+ end
93
+ end
94
+ end
@@ -79,11 +79,11 @@ module Solargraph
79
79
  end
80
80
 
81
81
  def get_node_start_position(node)
82
- Position.new(node.loc.line - 1, node.loc.column)
82
+ Position.new(node.loc.line, node.loc.column)
83
83
  end
84
84
 
85
85
  def get_node_end_position(node)
86
- Position.new(node.loc.last_line - 1, node.loc.last_column)
86
+ Position.new(node.loc.last_line, node.loc.last_column)
87
87
  end
88
88
 
89
89
  def drill_signature node, signature
@@ -13,6 +13,10 @@ module Solargraph
13
13
  @character = character
14
14
  end
15
15
 
16
+ def column
17
+ character
18
+ end
19
+
16
20
  # Get a hash of the position. This representation is suitable for use in
17
21
  # the language server protocol.
18
22
  #
@@ -27,15 +27,23 @@ module Solargraph
27
27
 
28
28
  # True if the specified position is inside the range.
29
29
  #
30
+ # @param position [Solargraph::Source::Position]
30
31
  # @return [Boolean]
31
32
  def contain? position
32
- return false if position.line < start.line
33
+ return false if position.line < start.line or position.line > ending.line
33
34
  return false if position.line == start.line and position.character < start.character
34
- return false if position.line > ending.line
35
35
  return false if position.line == ending.line and position.character > ending.character
36
36
  true
37
37
  end
38
38
 
39
+ # True if the range contains the specified position and the position does not precede it.
40
+ #
41
+ # @param position [Source::Position]
42
+ # @return [Boolean]
43
+ def include? position
44
+ contain?(position) and !(position.line == start.line and position.character == start.character)
45
+ end
46
+
39
47
  # Create a range from a pair of lines and characters.
40
48
  #
41
49
  # @param l1 [Integer] Starting line
@@ -1,3 +1,3 @@
1
1
  module Solargraph
2
- VERSION = '0.25.1'
2
+ VERSION = '0.26.0'
3
3
  end
@@ -26,6 +26,8 @@ module Solargraph
26
26
  # @return [Array<String>]
27
27
  attr_reader :required
28
28
 
29
+ # @param required [Array<String>]
30
+ # @param workspace [Solargraph::Workspace, nil]
29
31
  def initialize(required: [], workspace: nil)
30
32
  @workspace = workspace
31
33
  # HACK: YardMap needs its own copy of this array
@@ -69,7 +71,7 @@ module Solargraph
69
71
  end
70
72
  end
71
73
 
72
- # @param paths [Array<String>]
74
+ # @param new_requires [Array<String>]
73
75
  # @return [Boolean]
74
76
  def change new_requires
75
77
  if new_requires.uniq.sort == required.uniq.sort
@@ -82,6 +84,7 @@ module Solargraph
82
84
  end
83
85
  end
84
86
 
87
+ # @return [Array<Solargraph::Pin::Base>]
85
88
  def core_pins
86
89
  @@core_pins ||= begin
87
90
  result = []
@@ -100,6 +103,8 @@ module Solargraph
100
103
  @cache ||= YardMap::Cache.new
101
104
  end
102
105
 
106
+ # @param ns [YARD::CodeObjects::Namespace]
107
+ # @return [Array<Solargraph::Pin::Base>]
103
108
  def recurse_namespace_object ns
104
109
  result = []
105
110
  ns.children.each do |c|
@@ -109,6 +114,8 @@ module Solargraph
109
114
  result
110
115
  end
111
116
 
117
+ # @param code_object [YARD::CodeObjects::Base]
118
+ # @return [Solargraph::Pin::Base]
112
119
  def generate_pin code_object
113
120
  location = object_location(code_object)
114
121
  if code_object.is_a?(YARD::CodeObjects::NamespaceObject)
@@ -122,6 +129,7 @@ module Solargraph
122
129
  end
123
130
  end
124
131
 
132
+ # @return [void]
125
133
  def process_requires
126
134
  pins.clear
127
135
  unresolved_requires.clear
@@ -177,6 +185,8 @@ module Solargraph
177
185
  pins.concat core_pins
178
186
  end
179
187
 
188
+ # @param required_namespaces [Array<YARD::CodeObjects::Namespace>]
189
+ # @return [Array<Solargraph::Pin::Base>]
180
190
  def process_stdlib required_namespaces
181
191
  pins = []
182
192
  unless required_namespaces.empty?
@@ -199,6 +209,7 @@ module Solargraph
199
209
  end
200
210
 
201
211
  # @param spec [Gem::Specification]
212
+ # @return [void]
202
213
  def add_gem_dependencies spec
203
214
  (spec.dependencies - spec.development_dependencies).each do |dep|
204
215
  depspec = Gem::Specification.find_by_name(dep.name)
@@ -218,7 +229,7 @@ module Solargraph
218
229
  return nil if obj.file.nil? or obj.line.nil?
219
230
  @gem_paths.values.each do |path|
220
231
  file = File.join(path, obj.file)
221
- return Solargraph::Source::Location.new(file, Solargraph::Source::Range.from_to(obj.line - 1, 0, obj.line - 1, 0)) if File.exist?(file)
232
+ return Solargraph::Source::Location.new(file, Solargraph::Source::Range.from_to(obj.line, 0, obj.line, 0)) if File.exist?(file)
222
233
  end
223
234
  nil
224
235
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.25.1
4
+ version: 0.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-20 00:00:00.000000000 Z
11
+ date: 2018-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -236,11 +236,10 @@ files:
236
236
  - lib/solargraph.rb
237
237
  - lib/solargraph/api_map.rb
238
238
  - lib/solargraph/api_map/cache.rb
239
- - lib/solargraph/api_map/completion.rb
240
- - lib/solargraph/api_map/probe.rb
241
239
  - lib/solargraph/api_map/source_to_yard.rb
242
240
  - lib/solargraph/api_map/store.rb
243
- - lib/solargraph/api_map/type_methods.rb
241
+ - lib/solargraph/basic_type.rb
242
+ - lib/solargraph/basic_type_methods.rb
244
243
  - lib/solargraph/complex_type.rb
245
244
  - lib/solargraph/core_fills.rb
246
245
  - lib/solargraph/diagnostics.rb
@@ -324,7 +323,7 @@ files:
324
323
  - lib/solargraph/pin/method_parameter.rb
325
324
  - lib/solargraph/pin/namespace.rb
326
325
  - lib/solargraph/pin/plugin/method.rb
327
- - lib/solargraph/pin/proxy_method.rb
326
+ - lib/solargraph/pin/proxy_type.rb
328
327
  - lib/solargraph/pin/reference.rb
329
328
  - lib/solargraph/pin/symbol.rb
330
329
  - lib/solargraph/pin/yard_pin.rb
@@ -340,11 +339,26 @@ files:
340
339
  - lib/solargraph/server_methods.rb
341
340
  - lib/solargraph/shell.rb
342
341
  - lib/solargraph/source.rb
342
+ - lib/solargraph/source/call_chainer.rb
343
+ - lib/solargraph/source/chain.rb
344
+ - lib/solargraph/source/chain/call.rb
345
+ - lib/solargraph/source/chain/class_variable.rb
346
+ - lib/solargraph/source/chain/constant.rb
347
+ - lib/solargraph/source/chain/definition.rb
348
+ - lib/solargraph/source/chain/global_variable.rb
349
+ - lib/solargraph/source/chain/head.rb
350
+ - lib/solargraph/source/chain/instance_variable.rb
351
+ - lib/solargraph/source/chain/link.rb
352
+ - lib/solargraph/source/chain/literal.rb
353
+ - lib/solargraph/source/chain/variable.rb
343
354
  - lib/solargraph/source/change.rb
355
+ - lib/solargraph/source/completion.rb
356
+ - lib/solargraph/source/encoding_fixes.rb
344
357
  - lib/solargraph/source/flawed_builder.rb
345
358
  - lib/solargraph/source/fragment.rb
346
359
  - lib/solargraph/source/location.rb
347
360
  - lib/solargraph/source/mapper.rb
361
+ - lib/solargraph/source/node_chainer.rb
348
362
  - lib/solargraph/source/node_methods.rb
349
363
  - lib/solargraph/source/position.rb
350
364
  - lib/solargraph/source/range.rb
@@ -1,251 +0,0 @@
1
- module Solargraph
2
- class ApiMap
3
- # A deep analysis provider for ApiMaps.
4
- #
5
- class Probe
6
- include TypeMethods
7
-
8
- # @return [Solargraph::ApiMap]
9
- attr_reader :api_map
10
-
11
- # @param api_map [ApiMap]
12
- def initialize api_map
13
- @api_map = api_map
14
- end
15
-
16
- # Get all matching pins for the signature.
17
- #
18
- # @param signature [String]
19
- # @param context_pin [Pin::Base]
20
- # @param locals [Array<Pin::Base>]
21
- # @return [Array<Pin::Base>]
22
- def infer_signature_pins signature, context_pin, locals
23
- return [] if signature.nil? or signature.empty?
24
- base, rest = signature.split('.', 2)
25
- return infer_word_pins(base, context_pin, locals) if rest.nil?
26
- pins = infer_word_pins(base, context_pin, locals).map do |pin|
27
- next pin unless pin.return_type.nil?
28
- type = resolve_pin_type(pin, locals - [pin])
29
- Pin::ProxyMethod.new(type)
30
- end
31
- return [] if pins.empty?
32
- rest = rest.split('.')
33
- last = rest.pop
34
- rest.each do |meth|
35
- found = nil
36
- pins.each do |pin|
37
- found = infer_method_name_pins(meth, pin)
38
- next if found.empty?
39
- pins = found
40
- break
41
- end
42
- return [] if found.nil?
43
- end
44
- pins.each do |pin|
45
- found = infer_method_name_pins(last, pin)
46
- return found unless found.empty?
47
- end
48
- []
49
- end
50
-
51
- # Get the return type for the signature.
52
- #
53
- # @param signature [String]
54
- # @param context_pin [Pin::Base]
55
- # @param locals [Array<Pin::Base>]
56
- # @return [String]
57
- def infer_signature_type signature, context_pin, locals
58
- pins = infer_signature_pins(signature, context_pin, locals)
59
- pins.each do |pin|
60
- return qualify(pin.return_type, pin.named_context) unless pin.return_type.nil?
61
- type = resolve_pin_type(pin, locals - [pin])
62
- return qualify(type, pin.named_context) unless type.nil?
63
- end
64
- nil
65
- end
66
-
67
- private
68
-
69
- # Word search is ALWAYS internal
70
- def infer_word_pins word, context_pin, locals
71
- return [] if word.empty?
72
- return infer_self(context_pin) if word == 'self'
73
- lvars = locals.select{|pin| pin.name == word}
74
- return resolve_word_types(lvars, locals) unless lvars.empty?
75
- return api_map.get_global_variable_pins.select{|pin| pin.name == word} if word.start_with?('$')
76
- namespace, scope = extract_namespace_and_scope_from_pin(context_pin)
77
- return api_map.get_class_variable_pins(namespace).select{|pin| pin.name == word} if word.start_with?('@@')
78
- return api_map.get_instance_variable_pins(namespace, scope).select{|pin| pin.name == word} if word.start_with?('@')
79
- return api_map.pins.select{|pin| word_matches_context?(word, namespace, scope, pin)} if variable_name?(word)
80
- result = []
81
- cparts = namespace.split('::')
82
- until cparts.empty?
83
- break unless result.empty?
84
- cur = cparts.join('::')
85
- result.concat api_map.get_path_suggestions("#{cur}::#{word}")
86
- cparts.pop
87
- end
88
- result.concat api_map.get_path_suggestions(word) if result.empty?
89
- # result.concat api_map.get_methods(namespace, scope: scope, visibility: [:public, :private, :protected]).select{|pin| pin.name == word} unless word.include?('::')
90
- result.concat api_map.get_method_stack(namespace, word, scope: scope) unless word.include?('::')
91
- result.concat api_map.get_constants('', namespace).select{|pin| pin.name == word} if result.empty?
92
- resolve_word_types(result, locals)
93
- end
94
-
95
- def resolve_word_types(pins, locals)
96
- pins.each do |p|
97
- next unless p.return_type.nil?
98
- type = resolve_pin_type(p, locals)
99
- # @todo Smelly instance variable access
100
- p.instance_variable_set(:@return_complex_types, ComplexType.parse(type)) unless type.nil?
101
- end
102
- pins
103
- end
104
-
105
- def infer_self context_pin
106
- if context_pin.kind == Pin::METHOD
107
- if context_pin.scope == :instance
108
- return [Pin::ProxyMethod.new(context_pin.namespace)]
109
- else
110
- return api_map.get_path_suggestions(context_pin.namespace)
111
- end
112
- else
113
- return api_map.get_path_suggestions(context_pin.path)
114
- end
115
- end
116
-
117
- # Method name search is external by default
118
- # @param method_name [String]
119
- # @param context_pin [Solargraph::Pin::Base]
120
- def infer_method_name_pins method_name, context_pin, internal = false
121
- relname, scope = extract_namespace_and_scope(context_pin.return_type)
122
- namespace = api_map.qualify(relname, context_pin.namespace)
123
- visibility = [:public]
124
- visibility.push :protected, :private if internal
125
- # result = api_map.get_methods(namespace, scope: scope, visibility: visibility).select{|pin| pin.name == method_name}
126
- result = api_map.get_method_stack(namespace, method_name, scope: scope).select{|pin| visibility.include?(pin.visibility)}
127
- # @todo This needs more rules. Probably need to update YardObject for it.
128
- return result if result.empty?
129
- return result unless method_name == 'new' and result.first.path == 'Class#new'
130
- result.unshift virtual_new_pin(result.first, context_pin)
131
- result
132
- end
133
-
134
- # Word and context matching rules for ApiMap source pins.
135
- #
136
- # @return [Boolean]
137
- def word_matches_context? word, namespace, scope, pin
138
- return false unless word == pin.name
139
- return true if pin.kind == Pin::NAMESPACE and pin.path == namespace and scope == :class
140
- return true if pin.kind == Pin::METHOD and pin.namespace == namespace and pin.scope == scope
141
- # @todo Handle instance variables, class variables, etc. in various ways
142
- pin.namespace == namespace and pin.scope == scope
143
- end
144
-
145
- # Fully qualify the namespace in a type.
146
- #
147
- # @return [String]
148
- def qualify type, context
149
- rns, rsc = extract_namespace_and_scope(type)
150
- res = api_map.qualify(rns, context)
151
- return res if rsc == :instance
152
- type.sub(/<#{rns}>/, "<#{res}>")
153
- end
154
-
155
- # Extract a namespace and a scope from a pin. For now, the pin must
156
- # be either a namespace, a method, or a block.
157
- #
158
- # @return [Array] The namespace (String) and scope (Symbol).
159
- def extract_namespace_and_scope_from_pin pin
160
- return [pin.namespace, pin.scope] if pin.kind == Pin::METHOD
161
- return [pin.path, :class] if pin.kind == Pin::NAMESPACE
162
- # @todo Is :class appropriate for blocks?
163
- return [pin.namespace, :class] if pin.kind == Pin::BLOCK
164
- raise "Unable to extract namespace and scope from #{pin.path}"
165
- end
166
-
167
- # Determine whether or not the word represents a variable. This method
168
- # is used to keep the probe from performing unnecessary constant and
169
- # method searches.
170
- #
171
- # @return [Boolean]
172
- def variable_name? word
173
- word.start_with?('@') or word.start_with?('$')
174
- end
175
-
176
- # Create a `new` pin to facilitate type inference. This is necessary for
177
- # classes from YARD and classes in the namespace that do not have an
178
- # `initialize` method.
179
- #
180
- # @return [Pin::Method]
181
- def virtual_new_pin new_pin, context_pin
182
- pin = Pin::Method.new(new_pin.location, context_pin.path, new_pin.name, '', :class, new_pin.visibility, new_pin.parameters)
183
- # @todo Smelly instance variable access.
184
- pin.instance_variable_set(:@return_complex_types, ComplexType.parse(context_pin.path))
185
- pin
186
- end
187
-
188
- # @param pin [Solargraph::Pin::Base]
189
- # @param locals [Array<Solargraph::Pin::Base>]
190
- # @return [String]
191
- def resolve_pin_type pin, locals
192
- return pin.return_type unless pin.return_type.nil?
193
- return pin.namespace if pin.kind == Pin::METHOD and pin.name == 'new' and pin.scope == :class
194
- return resolve_block_parameter(pin, locals) if pin.kind == Pin::BLOCK_PARAMETER
195
- return resolve_method_parameter(pin) if pin.is_a?(Pin::MethodParameter)
196
- return resolve_variable(pin, locals) if pin.variable?
197
- nil
198
- end
199
-
200
- def resolve_block_parameter pin, locals
201
- return pin.return_type unless pin.return_type.nil?
202
- signature = pin.block.receiver
203
- # @todo Not sure if assuming the first pin is good here
204
- meth = infer_signature_pins(signature, pin.block, locals).first
205
- return nil if meth.nil?
206
- if (Solargraph::CoreFills::METHODS_WITH_YIELDPARAM_SUBTYPES.include?(meth.path))
207
- base = signature.split('.')[0..-2].join('.')
208
- return nil if base.nil? or base.empty?
209
- # @todo Not sure if assuming the first pin is good here
210
- bmeth = infer_signature_pins(base, pin.block, locals).first
211
- return nil if bmeth.nil?
212
- btype = bmeth.return_type
213
- btype = infer_signature_type(bmeth.signature, pin.block, locals) if btype.nil? and bmeth.variable?
214
- subtypes = get_subtypes(btype)
215
- return nil if subtypes.nil?
216
- return subtypes[0]
217
- else
218
- yps = meth.docstring.tags(:yieldparam)
219
- unless yps[pin.index].nil? or yps[pin.index].types.nil? or yps[pin.index].types.empty?
220
- return yps[pin.index].types[0]
221
- end
222
- end
223
- nil
224
- end
225
-
226
- def resolve_method_parameter pin
227
- matches = api_map.get_method_stack(pin.namespace, pin.context.name, scope: pin.scope)
228
- matches.each do |m|
229
- next unless pin.context.parameters == m.parameters
230
- tag = m.docstring.tags(:param).select{|t| t.name == pin.name}.first
231
- next if tag.nil? or tag.types.nil?
232
- return tag.types[0]
233
- end
234
- nil
235
- end
236
-
237
- def resolve_variable(pin, locals)
238
- return nil if pin.nil_assignment?
239
- # @todo Do we need the locals here?
240
- return infer_signature_type(pin.signature, pin.context, locals)
241
- end
242
-
243
- def get_subtypes type
244
- return nil if type.nil?
245
- match = type.match(/<([a-z0-9_:, ]*)>/i)
246
- return [] if match.nil?
247
- match[1].split(',').map(&:strip)
248
- end
249
- end
250
- end
251
- end