solargraph 0.29.4 → 0.29.5

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