solargraph 0.29.4 → 0.29.5

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.
@@ -4,6 +4,7 @@ module Solargraph
4
4
  module Pin
5
5
  autoload :Conversions, 'solargraph/pin/conversions'
6
6
  autoload :Base, 'solargraph/pin/base'
7
+ autoload :BaseMethod, 'solargraph/pin/base_method'
7
8
  autoload :Method, 'solargraph/pin/method'
8
9
  autoload :MethodAlias, 'solargraph/pin/method_alias'
9
10
  autoload :Attribute, 'solargraph/pin/attribute'
@@ -1,15 +1,9 @@
1
1
  module Solargraph
2
2
  module Pin
3
- class Attribute < Base
3
+ class Attribute < BaseMethod
4
4
  # @return [Symbol] :reader or :writer
5
5
  attr_reader :access
6
6
 
7
- # @return [Symbol] :class or :instance
8
- attr_reader :scope
9
-
10
- # @return [Symbol] :public, :protected, or :private
11
- attr_reader :visibility
12
-
13
7
  def initialize location, namespace, name, comments, access, scope, visibility
14
8
  super(location, namespace, name, comments)
15
9
  @access = access
@@ -33,10 +27,6 @@ module Solargraph
33
27
  @path ||= namespace + (scope == :instance ? '#' : '.') + name
34
28
  end
35
29
 
36
- def return_complex_type
37
- @return_complex_type ||= generate_complex_type
38
- end
39
-
40
30
  def parameters
41
31
  # Since attributes are generally equivalent to methods, treat
42
32
  # them as methods without parameters
@@ -46,26 +36,6 @@ module Solargraph
46
36
  def parameter_names
47
37
  []
48
38
  end
49
-
50
- private
51
-
52
- # @todo DRY this method. It also exists in Pin::Method.
53
- #
54
- # @return [ComplexType]
55
- def generate_complex_type
56
- tag = docstring.tag(:return)
57
- if tag.nil?
58
- ol = docstring.tag(:overload)
59
- tag = ol.tag(:return) unless ol.nil?
60
- end
61
- return ComplexType::UNDEFINED if tag.nil? or tag.types.nil? or tag.types.empty?
62
- begin
63
- ComplexType.parse *tag.types
64
- rescue Solargraph::ComplexTypeError => e
65
- STDERR.puts e.message
66
- ComplexType::UNDEFINED
67
- end
68
- end
69
39
  end
70
40
  end
71
41
  end
@@ -85,14 +85,12 @@ module Solargraph
85
85
  # @param other [Solargraph::Pin::Base, Object]
86
86
  # @return [Boolean]
87
87
  def nearly? other
88
- # @todo The directives test needs to be a deep check similar to
89
- # compare_docstring_tags.
90
- self.class == other.class and
91
- namespace == other.namespace and
92
- name == other.name and
93
- (comments == other.comments or
94
- ( ((maybe_directives? == false and other.maybe_directives? == false) or compare_directives(directives, other.directives)) and
95
- compare_docstring_tags(docstring, other.docstring) )
88
+ self.class == other.class &&
89
+ namespace == other.namespace &&
90
+ name == other.name &&
91
+ (comments == other.comments ||
92
+ (((maybe_directives? == false && other.maybe_directives? == false) || compare_directives(directives, other.directives)) &&
93
+ compare_docstring_tags(docstring, other.docstring))
96
94
  )
97
95
  end
98
96
 
@@ -145,12 +143,36 @@ module Solargraph
145
143
  @deprecated ||= docstring.has_tag?('deprecated')
146
144
  end
147
145
 
146
+ # Get a fully qualified type from the pin's return type.
147
+ #
148
+ # The relative type is determined from YARD documentation (@return,
149
+ # @param, @type, etc.) and its namespaces are fully qualified using the
150
+ # provided ApiMap.
151
+ #
148
152
  # @param api_map [ApiMap]
149
153
  # @return [ComplexType]
150
- def infer api_map
154
+ def typify api_map
151
155
  return_complex_type.qualify(api_map, namespace)
152
156
  end
153
157
 
158
+ # Infer the pin's return type via static code analysis.
159
+ #
160
+ # @param api_map [ApiMap]
161
+ # @return [ComplexType]
162
+ def probe api_map
163
+ typify api_map
164
+ end
165
+
166
+ # @deprecated Use #typify and/or #probe instead
167
+ # @param api_map [ApiMap]
168
+ # @return [ComplexType]
169
+ def infer api_map
170
+ STDERR.puts "WARNING: Pin #infer methods are deprecated. Use #typify or #probe instead."
171
+ type = typify(api_map)
172
+ return type unless type.undefined?
173
+ probe api_map
174
+ end
175
+
154
176
  # Try to merge data from another pin. Merges are only possible if the
155
177
  # pins are near matches (see the #nearly? method). The changes should
156
178
  # not have any side effects on the API surface.
@@ -160,14 +182,13 @@ module Solargraph
160
182
  def try_merge! pin
161
183
  return false unless nearly?(pin)
162
184
  @location = pin.location
163
- if comments != pin.comments
164
- @comments = pin.comments
165
- @docstring = pin.docstring
166
- @return_complex_type = pin.return_complex_type
167
- @documentation = nil
168
- @deprecated = nil
169
- reset_conversions
170
- end
185
+ return true if comments == pin.comments
186
+ @comments = pin.comments
187
+ @docstring = pin.docstring
188
+ @return_complex_type = pin.return_complex_type
189
+ @documentation = nil
190
+ @deprecated = nil
191
+ reset_conversions
171
192
  true
172
193
  end
173
194
 
@@ -0,0 +1,64 @@
1
+ module Solargraph
2
+ module Pin
3
+ # The base class for method and attribute pins.
4
+ #
5
+ class BaseMethod < Base
6
+ # @return [Symbol] :instance or :class
7
+ attr_reader :scope
8
+
9
+ # @return [Symbol] :public, :private, or :protected
10
+ attr_reader :visibility
11
+
12
+ def return_complex_type
13
+ @return_complex_type ||= generate_complex_type
14
+ end
15
+
16
+ def typify api_map
17
+ decl = super
18
+ return decl unless decl.undefined?
19
+ type = see_reference(api_map)
20
+ return type unless type.nil?
21
+ ComplexType::UNDEFINED
22
+ end
23
+
24
+ private
25
+
26
+ # @return [ComplexType]
27
+ def generate_complex_type
28
+ tag = docstring.tag(:return)
29
+ if tag.nil?
30
+ ol = docstring.tag(:overload)
31
+ tag = ol.tag(:return) unless ol.nil?
32
+ end
33
+ return ComplexType::UNDEFINED if tag.nil? or tag.types.nil? or tag.types.empty?
34
+ begin
35
+ ComplexType.parse *tag.types
36
+ rescue Solargraph::ComplexTypeError => e
37
+ STDERR.puts e.message
38
+ ComplexType::UNDEFINED
39
+ end
40
+ end
41
+
42
+ # @param [ApiMap]
43
+ def see_reference api_map
44
+ docstring.ref_tags.each do |ref|
45
+ next unless ref.tag_name == 'return' && ref.owner
46
+ parts = ref.owner.to_s.split(/[\.#]/)
47
+ if parts.first.empty?
48
+ path = "#{namespace}#{ref.owner.to_s}"
49
+ else
50
+ fqns = api_map.qualify(parts.first, namespace)
51
+ return ComplexType::UNDEFINED if fqns.nil?
52
+ path = fqns + ref.owner.to_s[parts.first.length] + parts.last
53
+ end
54
+ pins = api_map.get_path_pins(path)
55
+ pins.each do |pin|
56
+ type = pin.typify(api_map)
57
+ return type unless type.undefined?
58
+ end
59
+ end
60
+ nil
61
+ end
62
+ end
63
+ end
64
+ end
@@ -39,10 +39,8 @@ module Solargraph
39
39
  true
40
40
  end
41
41
 
42
- # @param api_map [ApiMap]
43
- def infer api_map
44
- result = super
45
- return result if result.defined? or @assignment.nil?
42
+ def probe api_map
43
+ return ComplexType::UNDEFINED if @assignment.nil?
46
44
  chain = Source::NodeChainer.chain(@assignment, filename)
47
45
  clip = api_map.clip_at(location.filename, location.range.start)
48
46
  locals = clip.locals - [self]
@@ -6,9 +6,6 @@ module Solargraph
6
6
  # @return [Parser::AST::Node]
7
7
  attr_reader :receiver
8
8
 
9
- # @return [Array<String>]
10
- attr_reader :parameters
11
-
12
9
  def initialize location, namespace, name, comments, receiver, context
13
10
  super(location, namespace, name, comments)
14
11
  @receiver = receiver
@@ -19,6 +16,7 @@ module Solargraph
19
16
  Pin::BLOCK
20
17
  end
21
18
 
19
+ # @return [Array<String>]
22
20
  def parameters
23
21
  @parameters ||= []
24
22
  end
@@ -71,9 +71,10 @@ module Solargraph
71
71
  block
72
72
  end
73
73
 
74
- # @param api_map [ApiMap]
75
- def infer api_map
76
- return return_complex_type unless return_complex_type.undefined?
74
+ def typify api_map
75
+ # @todo Does anything need to be eliminated because it's more accurately a probe?
76
+ type = super
77
+ return type unless type.undefined?
77
78
  chain = Source::NodeChainer.chain(block.receiver, filename)
78
79
  clip = api_map.clip_at(location.filename, location.range.start)
79
80
  locals = clip.locals - [self]
@@ -12,10 +12,6 @@ module Solargraph
12
12
  def kind
13
13
  Solargraph::Pin::KEYWORD
14
14
  end
15
-
16
- def completion_item_kind
17
- Solargraph::LanguageServer::CompletionItemKinds::KEYWORD
18
- end
19
15
  end
20
16
  end
21
17
  end
@@ -7,9 +7,10 @@ module Solargraph
7
7
  attr_reader :presence
8
8
 
9
9
  # @param other [Pin::Base] The caller's block
10
- # @param position [Position] The caller's position
10
+ # @param position [Position, Array(Integer, Integer)] The caller's position
11
11
  # @return [Boolean]
12
12
  def visible_from?(other, position)
13
+ position = Position.normalize(position)
13
14
  other.filename == filename and
14
15
  ( other == block or
15
16
  (block.location.range.contain?(other.location.range.start) and block.location.range.contain?(other.location.range.ending))
@@ -1,14 +1,8 @@
1
1
  module Solargraph
2
2
  module Pin
3
- class Method < Base
3
+ class Method < BaseMethod
4
4
  include Source::NodeMethods
5
5
 
6
- # @return [Symbol] :instance or :class
7
- attr_reader :scope
8
-
9
- # @return [Symbol] :public, :private, or :protected
10
- attr_reader :visibility
11
-
12
6
  # @return [Array<String>]
13
7
  attr_reader :parameters
14
8
 
@@ -51,15 +45,10 @@ module Solargraph
51
45
  Solargraph::LanguageServer::CompletionItemKinds::METHOD
52
46
  end
53
47
 
54
- # @return [Integer]
55
48
  def symbol_kind
56
49
  LanguageServer::SymbolKinds::METHOD
57
50
  end
58
51
 
59
- def return_complex_type
60
- @return_complex_type ||= generate_complex_type
61
- end
62
-
63
52
  def documentation
64
53
  if @documentation.nil?
65
54
  @documentation ||= super || ''
@@ -87,14 +76,18 @@ module Solargraph
87
76
  visibility == other.visibility
88
77
  end
89
78
 
90
- def infer api_map
91
- decl = super
92
- return decl unless decl.undefined?
93
- type = see_reference(api_map)
94
- return type unless type.nil?
79
+ def probe api_map
95
80
  infer_from_return_nodes(api_map)
96
81
  end
97
82
 
83
+ # @deprecated Use #typify and/or #probe instead
84
+ def infer api_map
85
+ STDERR.puts 'WARNING: Pin #infer methods are deprecated. Use #typify or #probe instead.'
86
+ type = typify(api_map)
87
+ return type unless type.undefined?
88
+ probe api_map
89
+ end
90
+
98
91
  def try_merge! pin
99
92
  return false unless super
100
93
  @node = pin.node
@@ -103,59 +96,31 @@ module Solargraph
103
96
 
104
97
  private
105
98
 
106
- # @return [ComplexType]
107
- def generate_complex_type
108
- tag = docstring.tag(:return)
109
- if tag.nil?
110
- ol = docstring.tag(:overload)
111
- tag = ol.tag(:return) unless ol.nil?
112
- end
113
- return ComplexType::UNDEFINED if tag.nil? or tag.types.nil? or tag.types.empty?
114
- begin
115
- ComplexType.parse *tag.types
116
- rescue Solargraph::ComplexTypeError => e
117
- STDERR.puts e.message
118
- ComplexType::UNDEFINED
119
- end
99
+ # @return [Parser::AST:Node, nil]
100
+ def method_body_node
101
+ return nil if node.nil?
102
+ return node.children[2] if node.type == :def
103
+ return node.children[3] if node.type == :defs
104
+ nil
120
105
  end
121
106
 
122
107
  # @param api_map [ApiMap]
108
+ # @return [ComplexType]
123
109
  def infer_from_return_nodes api_map
124
- return ComplexType::UNDEFINED if node.nil? ||
125
- (node.type == :def && node.children[2].nil?) ||
126
- (node.type == :defs && node.children[3].nil?)
127
110
  result = []
128
- nodes = node.type == :def ? returns_from(node.children[2]) : returns_from(node.children[3])
129
- nodes.each do |n|
111
+ returns_from(method_body_node).each do |n|
130
112
  next if n.loc.nil?
131
- clip = api_map.clip_at(location.filename, Solargraph::Position.new(n.loc.expression.last_line, n.loc.expression.last_column))
132
- type = clip.infer
113
+ clip = api_map.clip_at(
114
+ location.filename,
115
+ [n.loc.expression.last_line, n.loc.expression.last_column]
116
+ )
117
+ chain = Solargraph::Source::NodeChainer.chain(n, location.filename)
118
+ type = chain.infer(api_map, self, clip.locals)
133
119
  result.push type unless type.undefined?
134
120
  end
135
121
  return ComplexType::UNDEFINED if result.empty?
136
122
  ComplexType.parse(*result.map(&:tag))
137
123
  end
138
-
139
- # @param [ApiMap]
140
- def see_reference api_map
141
- docstring.ref_tags.each do |ref|
142
- next unless ref.tag_name == 'return' && ref.owner
143
- parts = ref.owner.to_s.split(/[\.#]/)
144
- if parts.first.empty?
145
- path = "#{namespace}#{ref.owner.to_s}"
146
- else
147
- fqns = api_map.qualify(parts.first, namespace)
148
- return ComplexType::UNDEFINED if fqns.nil?
149
- path = fqns + ref.owner.to_s[parts.first.length] + parts.last
150
- end
151
- pins = api_map.get_path_pins(path)
152
- pins.each do |pin|
153
- type = pin.infer(api_map)
154
- return type unless type.undefined?
155
- end
156
- end
157
- nil
158
- end
159
124
  end
160
125
  end
161
126
  end
@@ -46,8 +46,7 @@ module Solargraph
46
46
  @domains ||= []
47
47
  end
48
48
 
49
- def infer api_map
50
- # Assuming that namespace pins are always fully qualified
49
+ def typify api_map
51
50
  return_complex_type
52
51
  end
53
52
  end
@@ -28,9 +28,10 @@ module Solargraph
28
28
 
29
29
  # True if the specified position is inside the range.
30
30
  #
31
- # @param position [Solargraph::Position]
31
+ # @param position [Position, Array(Integer, Integer)]
32
32
  # @return [Boolean]
33
33
  def contain? position
34
+ position = Position.normalize(position)
34
35
  return false if position.line < start.line || position.line > ending.line
35
36
  return false if position.line == start.line && position.character < start.character
36
37
  return false if position.line == ending.line && position.character > ending.character
@@ -39,9 +40,10 @@ module Solargraph
39
40
 
40
41
  # True if the range contains the specified position and the position does not precede it.
41
42
  #
42
- # @param position [Position]
43
+ # @param position [Position, Array(Integer, Integer)]
43
44
  # @return [Boolean]
44
45
  def include? position
46
+ position = Position.normalize(position)
45
47
  contain?(position) && !(position.line == start.line && position.character == start.character)
46
48
  end
47
49
 
@@ -24,6 +24,7 @@ module Solargraph
24
24
  # @return [Parser::AST::Node]
25
25
  attr_reader :node
26
26
 
27
+ # @return [Array<Parser::Source::Comment>]
27
28
  attr_reader :comments
28
29
 
29
30
  # @return [String]
@@ -194,6 +195,9 @@ module Solargraph
194
195
  arr ? stringify_comment_array(arr) : nil
195
196
  end
196
197
 
198
+ # A location representing the file in its entirety.
199
+ #
200
+ # @return [Location]
197
201
  def location
198
202
  st = Position.new(0, 0)
199
203
  en = Position.from_offset(code, code.length)
@@ -203,6 +207,9 @@ module Solargraph
203
207
 
204
208
  private
205
209
 
210
+ # Get a hash of comments grouped by the line numbers of the associated code.
211
+ #
212
+ # @return [Hash{Integer => Array<Parser::Source::Comment>}]
206
213
  def associated_comments
207
214
  @associated_comments ||= begin
208
215
  result = {}
@@ -216,6 +223,10 @@ module Solargraph
216
223
  end
217
224
  end
218
225
 
226
+ # Get a string representation of an array of comments.
227
+ #
228
+ # @param comments [Array<Parser::Source::Comment>]
229
+ # @return [String]
219
230
  def stringify_comment_array comments
220
231
  ctxt = ''
221
232
  num = nil
@@ -309,17 +320,24 @@ module Solargraph
309
320
 
310
321
  # @param code [String]
311
322
  # @param filename [String]
323
+ # @param version [Integer]
312
324
  # @return [Solargraph::Source]
313
325
  def load_string code, filename = nil, version = 0
314
326
  Source.new code, filename, version
315
327
  end
316
328
 
329
+ # @param code [String]
330
+ # @param filename [String]
331
+ # @return [Array(Parser::AST::Node, Array<Parser::Source::Comment>)]
317
332
  def parse_with_comments code, filename = nil
318
333
  buffer = Parser::Source::Buffer.new(filename, 0)
319
334
  buffer.source = code.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '_')
320
335
  parser.parse_with_comments(buffer)
321
336
  end
322
337
 
338
+ # @param code [String]
339
+ # @param filename [String]
340
+ # @return [Parser::AST::Node]
323
341
  def parse code, filename = nil
324
342
  buffer = Parser::Source::Buffer.new(filename, 0)
325
343
  buffer.source = code.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '_')