solargraph 0.28.4 → 0.29.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph/api_map.rb +43 -10
  3. data/lib/solargraph/api_map/store.rb +13 -0
  4. data/lib/solargraph/bundle.rb +3 -0
  5. data/lib/solargraph/complex_type.rb +5 -4
  6. data/lib/solargraph/core_fills.rb +3 -2
  7. data/lib/solargraph/language_server/host.rb +0 -1
  8. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +4 -3
  9. data/lib/solargraph/language_server/message/extended/document_gems.rb +7 -0
  10. data/lib/solargraph/language_server/transport/socket.rb +7 -1
  11. data/lib/solargraph/library.rb +1 -1
  12. data/lib/solargraph/page.rb +1 -2
  13. data/lib/solargraph/pin.rb +1 -0
  14. data/lib/solargraph/pin/base.rb +8 -1
  15. data/lib/solargraph/pin/base_variable.rb +1 -1
  16. data/lib/solargraph/pin/block_parameter.rb +5 -0
  17. data/lib/solargraph/pin/conversions.rb +1 -1
  18. data/lib/solargraph/pin/method.rb +59 -1
  19. data/lib/solargraph/pin/method_alias.rb +19 -0
  20. data/lib/solargraph/pin/yard_pin/method.rb +2 -2
  21. data/lib/solargraph/source.rb +63 -1
  22. data/lib/solargraph/source/chain/call.rb +39 -20
  23. data/lib/solargraph/source/node_chainer.rb +1 -1
  24. data/lib/solargraph/source/node_methods.rb +81 -0
  25. data/lib/solargraph/source_map.rb +15 -0
  26. data/lib/solargraph/source_map/clip.rb +1 -1
  27. data/lib/solargraph/source_map/mapper.rb +9 -421
  28. data/lib/solargraph/source_map/node_processor.rb +75 -0
  29. data/lib/solargraph/source_map/node_processor/alias_node.rb +21 -0
  30. data/lib/solargraph/source_map/node_processor/args_node.rb +28 -0
  31. data/lib/solargraph/source_map/node_processor/base.rb +68 -0
  32. data/lib/solargraph/source_map/node_processor/begin_node.rb +11 -0
  33. data/lib/solargraph/source_map/node_processor/block_node.rb +14 -0
  34. data/lib/solargraph/source_map/node_processor/casgn_node.rb +14 -0
  35. data/lib/solargraph/source_map/node_processor/cvasgn_node.rb +14 -0
  36. data/lib/solargraph/source_map/node_processor/def_node.rb +54 -0
  37. data/lib/solargraph/source_map/node_processor/defs_node.rb +21 -0
  38. data/lib/solargraph/source_map/node_processor/gvasgn_node.rb +12 -0
  39. data/lib/solargraph/source_map/node_processor/ivasgn_node.rb +18 -0
  40. data/lib/solargraph/source_map/node_processor/lvasgn_node.rb +16 -0
  41. data/lib/solargraph/source_map/node_processor/namespace_node.rb +26 -0
  42. data/lib/solargraph/source_map/node_processor/orasgn_node.rb +12 -0
  43. data/lib/solargraph/source_map/node_processor/sclass_node.rb +11 -0
  44. data/lib/solargraph/source_map/node_processor/send_node.rb +150 -0
  45. data/lib/solargraph/source_map/node_processor/sym_node.rb +11 -0
  46. data/lib/solargraph/source_map/region.rb +58 -0
  47. data/lib/solargraph/version.rb +1 -1
  48. data/lib/solargraph/yard_map.rb +17 -10
  49. data/lib/yard-solargraph.rb +0 -2
  50. metadata +24 -18
@@ -0,0 +1,19 @@
1
+ module Solargraph
2
+ module Pin
3
+ # Use this class to track method aliases for later remapping. Common
4
+ # examples are aliases for superclass methods or methods from included
5
+ # modules.
6
+ #
7
+ class MethodAlias < Method
8
+ attr_reader :original
9
+
10
+ def initialize location, namespace, name, scope, original
11
+ # @todo Determine how to handle these parameters. Among other things,
12
+ # determine if the visibility is defined by the location of the
13
+ # alias call or the original method.
14
+ super(location, namespace, name, '', scope, :public, [])
15
+ @original = original
16
+ end
17
+ end
18
+ end
19
+ end
@@ -6,7 +6,7 @@ module Solargraph
6
6
 
7
7
  def initialize code_object, location, name = nil, scope = nil, visibility = nil
8
8
  comments = (code_object.docstring ? code_object.docstring.all : nil)
9
- super(location, code_object.namespace.to_s, name || code_object.name.to_s, comments, scope || code_object.scope, visibility || code_object.visibility, get_parameters(code_object))
9
+ super(location, code_object.namespace.to_s, name || code_object.name.to_s, comments, scope || code_object.scope, visibility || code_object.visibility, get_parameters(code_object), nil)
10
10
  end
11
11
 
12
12
  def return_complex_type
@@ -28,7 +28,7 @@ module Solargraph
28
28
  args.push p
29
29
  end
30
30
  args
31
- end
31
+ end
32
32
  end
33
33
  end
34
34
  end
@@ -46,7 +46,12 @@ module Solargraph
46
46
  @node, @comments = Source.parse_with_comments(@code, filename)
47
47
  @parsed = true
48
48
  rescue Parser::SyntaxError, EncodingError => e
49
- @node, @comments = Source.parse_with_comments(@code.gsub(/[^s]/, ' '), filename)
49
+ # @todo 100% whitespace results in a nil node, so there's no reason to parse it.
50
+ # We still need to determine whether the resulting node should be nil or a dummy
51
+ # node with a location that encompasses the range.
52
+ # @node, @comments = Source.parse_with_comments(@code.gsub(/[^\s]/, ' '), filename)
53
+ @node = nil
54
+ @comments = []
50
55
  @parsed = false
51
56
  rescue Exception => e
52
57
  STDERR.puts e.message
@@ -171,8 +176,65 @@ module Solargraph
171
176
  @error_ranges ||= []
172
177
  end
173
178
 
179
+ def code_for(node)
180
+ # @todo Using node locations on code with converted EOLs seems
181
+ # slightly more efficient than calculating offsets.
182
+ # b = node.location.expression.begin.begin_pos
183
+ # e = node.location.expression.end.end_pos
184
+ b = Position.line_char_to_offset(@code, node.location.line, node.location.column)
185
+ e = Position.line_char_to_offset(@code, node.location.last_line, node.location.last_column)
186
+ frag = code[b..e-1].to_s
187
+ frag.strip.gsub(/,$/, '')
188
+ end
189
+
190
+ # @param node [Parser::AST::Node]
191
+ # @return [String]
192
+ def comments_for node
193
+ arr = associated_comments[node.loc.line]
194
+ arr ? stringify_comment_array(arr) : nil
195
+ end
196
+
197
+ def location
198
+ st = Position.new(0, 0)
199
+ en = Position.from_offset(code, code.length)
200
+ range = Range.new(st, en)
201
+ Location.new(filename, range)
202
+ end
203
+
174
204
  private
175
205
 
206
+ def associated_comments
207
+ @associated_comments ||= begin
208
+ result = {}
209
+ Parser::Source::Comment.associate_locations(node, comments).each_pair do |loc, all|
210
+ block = all.select{ |l| l.document? || code.lines[l.loc.line].strip.start_with?('#')}
211
+ next if block.empty?
212
+ result[loc.line] ||= []
213
+ result[loc.line].concat block
214
+ end
215
+ result
216
+ end
217
+ end
218
+
219
+ def stringify_comment_array comments
220
+ ctxt = ''
221
+ num = nil
222
+ started = false
223
+ comments.each { |l|
224
+ # Trim the comment and minimum leading whitespace
225
+ p = l.text.gsub(/^#/, '')
226
+ if num.nil? and !p.strip.empty?
227
+ num = p.index(/[^ ]/)
228
+ started = true
229
+ elsif started and !p.strip.empty?
230
+ cur = p.index(/[^ ]/)
231
+ num = cur if cur < num
232
+ end
233
+ ctxt += "#{p[num..-1]}\n" if started
234
+ }
235
+ ctxt
236
+ end
237
+
176
238
  # @return [Array<Range>]
177
239
  def string_ranges
178
240
  @string_ranges ||= string_ranges_in(@node)
@@ -42,16 +42,18 @@ module Solargraph
42
42
  def inferred_pins pins, api_map, context, locals
43
43
  result = pins.map do |p|
44
44
  if CoreFills::METHODS_RETURNING_SELF.include?(p.path)
45
- next Solargraph::Pin::Method.new(p.location, p.namespace, p.name, "@return [#{context.tag}]", p.scope, p.visibility, p.parameters)
45
+ next Solargraph::Pin::Method.new(p.location, p.namespace, p.name, "@return [#{context.tag}]", p.scope, p.visibility, p.parameters, p.node)
46
46
  end
47
47
  if CoreFills::METHODS_RETURNING_SUBTYPES.include?(p.path) && !context.subtypes.empty?
48
- next Solargraph::Pin::Method.new(p.location, p.namespace, p.name, "@return [#{context.subtypes.first.tag}]", p.scope, p.visibility, p.parameters)
48
+ next Solargraph::Pin::Method.new(p.location, p.namespace, p.name, "@return [#{context.subtypes.first.tag}]", p.scope, p.visibility, p.parameters, p.node)
49
+ end
50
+ if p.kind == Pin::METHOD && !p.macros.empty?
51
+ result = process_macro(p, api_map, context, locals)
52
+ next result unless result.return_type.undefined?
53
+ elsif !p.directives.empty?
54
+ result = process_directive(p, api_map, context, locals)
55
+ next result unless result.return_type.undefined?
49
56
  end
50
- # @todo Temporarily disabled macros
51
- # if p.kind == Pin::METHOD && !p.macros.empty?
52
- # result = process_macro(p, api_map, context, locals)
53
- # next result unless result.return_type.undefined?
54
- # end
55
57
  next p if p.kind == Pin::METHOD || p.kind == Pin::ATTRIBUTE || p.kind == Pin::NAMESPACE
56
58
  type = p.infer(api_map)
57
59
  next p if p.return_complex_type == type
@@ -67,22 +69,39 @@ module Solargraph
67
69
  # @param pin [Pin::Method]
68
70
  # @param api_map [ApiMap]
69
71
  # @param context [ComplexType]
72
+ # @return [Pin::Base]
70
73
  def process_macro pin, api_map, context, locals
71
74
  pin.macros.each do |macro|
72
- vals = arguments.map{ |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals)) }
73
- txt = macro.tag.text.clone
74
- i = 1
75
- vals.each do |v|
76
- txt.gsub!(/\$#{i}/, v.context.namespace)
77
- i += 1
78
- end
79
- docstring = YARD::Docstring.parser.parse(txt).to_docstring
80
- tag = docstring.tag(:return)
81
- unless tag.nil? || tag.types.nil?
82
- return Pin::ProxyType.anonymous(ComplexType.parse(*tag.types))
83
- end
75
+ result = inner_process_macro(pin, macro, api_map, context, locals)
76
+ return result unless result.return_type.undefined?
77
+ end
78
+ Pin::ProxyType.new(nil, nil, nil, ComplexType::UNDEFINED)
79
+ end
80
+
81
+ def process_directive pin, api_map, context, locals
82
+ pin.directives.each do |dir|
83
+ macro = api_map.named_macro(dir.tag.name)
84
+ next if macro.nil?
85
+ result = inner_process_macro(pin, macro, api_map, context, locals)
86
+ return result unless result.return_type.undefined?
87
+ end
88
+ Pin::ProxyType.new(nil, nil, nil, ComplexType::UNDEFINED)
89
+ end
90
+
91
+ def inner_process_macro pin, macro, api_map, context, locals
92
+ vals = arguments.map{ |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals)) }
93
+ txt = macro.tag.text.clone
94
+ i = 1
95
+ vals.each do |v|
96
+ txt.gsub!(/\$#{i}/, v.context.namespace)
97
+ i += 1
98
+ end
99
+ docstring = YARD::Docstring.parser.parse(txt).to_docstring
100
+ tag = docstring.tag(:return)
101
+ unless tag.nil? || tag.types.nil?
102
+ return Pin::ProxyType.anonymous(ComplexType.parse(*tag.types))
84
103
  end
85
- return Pin::ProxyType.new(nil, nil, nil, ComplexType::UNDEFINED)
104
+ Pin::ProxyType.new(nil, nil, nil, ComplexType::UNDEFINED)
86
105
  end
87
106
  end
88
107
  end
@@ -41,7 +41,7 @@ module Solargraph
41
41
  # @param n [Parser::AST::Node]
42
42
  # @return [Array<Chain::Link>]
43
43
  def generate_links n
44
- return [] if n.nil?
44
+ return [] unless n.is_a?(Parser::AST::Node)
45
45
  return generate_links(n.children[0]) if n.type == :begin
46
46
  # @todo This might not be right. It's weird either way.
47
47
  # return generate_links(n.children[2] || n.children[0]) if n.type == :block
@@ -60,6 +60,8 @@ module Solargraph
60
60
  return 'Float'
61
61
  elsif node.type == :sym
62
62
  return 'Symbol'
63
+ elsif node.type == :regexp
64
+ return 'Regexp'
63
65
  # @todo Maybe ignore nils
64
66
  # elsif node.type == :nil
65
67
  # return 'NilClass'
@@ -106,6 +108,85 @@ module Solargraph
106
108
  end
107
109
  signature
108
110
  end
111
+
112
+ def returns_from node
113
+ DeepInference.get_return_nodes(node)
114
+ end
115
+
116
+ module DeepInference
117
+ class << self
118
+ CONDITIONAL = [:if, :unless]
119
+ REDUCEABLE = [:begin, :kwbegin]
120
+ SKIPPABLE = [:def, :defs, :class, :sclass, :module]
121
+
122
+ def get_return_nodes node
123
+ result = []
124
+ if REDUCEABLE.include?(node.type)
125
+ result.concat get_return_nodes_from_children(node)
126
+ elsif CONDITIONAL.include?(node.type)
127
+ result.concat reduce_to_value_nodes(node.children[1..-1])
128
+ else
129
+ result.push node
130
+ end
131
+ result
132
+ end
133
+
134
+ private
135
+
136
+ def get_return_nodes_from_children parent
137
+ result = []
138
+ nodes = parent.children.select{|n| n.is_a?(AST::Node)}
139
+ nodes[0..-2].each do |node|
140
+ next if SKIPPABLE.include?(node.type)
141
+ if node.type == :return
142
+ result.concat reduce_to_value_nodes([node.children[0]])
143
+ # @todo Maybe return the result here because the rest of the code is
144
+ # unreachable
145
+ return result
146
+ else
147
+ result.concat get_return_nodes_only(node)
148
+ end
149
+ end
150
+ result.concat reduce_to_value_nodes([nodes.last]) unless nodes.last.nil?
151
+ result
152
+ end
153
+
154
+ def get_return_nodes_only parent
155
+ result = []
156
+ nodes = parent.children.select{|n| n.is_a?(AST::Node)}
157
+ nodes.each do |node|
158
+ next if SKIPPABLE.include?(node.type)
159
+ if node.type == :return
160
+ result.concat reduce_to_value_nodes([node.children[0]])
161
+ # @todo Maybe return the result here because the rest of the code is
162
+ # unreachable
163
+ else
164
+ result.concat get_return_nodes_only(node)
165
+ end
166
+ end
167
+ result
168
+ end
169
+
170
+ def reduce_to_value_nodes nodes
171
+ result = []
172
+ nodes.each do |node|
173
+ if REDUCEABLE.include?(node.type)
174
+ result.concat get_return_nodes_from_children(node)
175
+ # node.children.each do |child|
176
+ # result.concat reduce_to_value_nodes(child)
177
+ # end
178
+ elsif CONDITIONAL.include?(node.type)
179
+ result.concat reduce_to_value_nodes(node.children[1..-1])
180
+ elsif node.type == :return
181
+ result.concat get_return_nodes(node.children[0])
182
+ else
183
+ result.push node
184
+ end
185
+ end
186
+ result
187
+ end
188
+ end
189
+ end
109
190
  end
110
191
  end
111
192
  end
@@ -2,9 +2,11 @@ module Solargraph
2
2
  # An index of pins and other ApiMap-related data for a Source.
3
3
  #
4
4
  class SourceMap
5
+ autoload :NodeProcessor, 'solargraph/source_map/node_processor'
5
6
  autoload :Mapper, 'solargraph/source_map/mapper'
6
7
  autoload :Clip, 'solargraph/source_map/clip'
7
8
  autoload :Completion, 'solargraph/source_map/completion'
9
+ autoload :Region, 'solargraph/source_map/region'
8
10
 
9
11
  # @return [Source]
10
12
  attr_reader :source
@@ -15,6 +17,9 @@ module Solargraph
15
17
  # @return [Array<Pin::Base>]
16
18
  attr_reader :locals
17
19
 
20
+ # @param source [Source]
21
+ # @param pins [Array<Pin::Base>]
22
+ # @param locals [Array<Pin::Base>]
18
23
  def initialize source, pins, locals
19
24
  # HACK: Keep the library from changing this
20
25
  @source = source.dup
@@ -22,14 +27,17 @@ module Solargraph
22
27
  @locals = locals
23
28
  end
24
29
 
30
+ # @return [String]
25
31
  def filename
26
32
  source.filename
27
33
  end
28
34
 
35
+ # @return [String]
29
36
  def code
30
37
  source.code
31
38
  end
32
39
 
40
+ # @return [Array<Pin::Reference::Require>]
33
41
  def requires
34
42
  @requires ||= pins.select{|p| p.kind == Pin::REQUIRE_REFERENCE}
35
43
  end
@@ -46,12 +54,15 @@ module Solargraph
46
54
  @source.comment_at?(position)
47
55
  end
48
56
 
57
+ # @return [Array<Pin::Base>]
49
58
  def document_symbols
50
59
  @document_symbols ||= pins.select { |pin|
51
60
  [Pin::ATTRIBUTE, Pin::CONSTANT, Pin::METHOD, Pin::NAMESPACE].include?(pin.kind) and !pin.path.empty?
52
61
  }
53
62
  end
54
63
 
64
+ # @param query [String]
65
+ # @return [Array<Pin::Base>]
55
66
  def query_symbols query
56
67
  document_symbols.select{|pin| pin.path.include?(query)}
57
68
  end
@@ -82,6 +93,7 @@ module Solargraph
82
93
  end
83
94
 
84
95
  # @param other_map [SourceMap]
96
+ # @return Boolean
85
97
  def try_merge! other_map
86
98
  return false if pins.length != other_map.pins.length || locals.length != other_map.locals.length || requires.map(&:name).uniq.sort != other_map.requires.map(&:name).uniq.sort
87
99
  pins.each_index do |i|
@@ -101,12 +113,15 @@ module Solargraph
101
113
  end
102
114
 
103
115
  class << self
116
+ # @param filename [String]
104
117
  # @return [SourceMap]
105
118
  def load filename
106
119
  source = Solargraph::Source.load(filename)
107
120
  SourceMap.map(source)
108
121
  end
109
122
 
123
+ # @param code [String]
124
+ # @param filename [String, nil]
110
125
  # @return [SourceMap]
111
126
  def load_string code, filename = nil
112
127
  source = Solargraph::Source.load_string(code, filename)
@@ -29,7 +29,7 @@ module Solargraph
29
29
  type = cursor.chain.base.infer(api_map, context_pin, locals)
30
30
  else
31
31
  full = cursor.chain.links.first.word
32
- if full.include?('::')
32
+ if full.include?('::') && cursor.chain.links.length == 1
33
33
  type = ComplexType.parse(full.split('::')[0..-2].join('::'))
34
34
  elsif cursor.chain.links.length > 1
35
35
  type = ComplexType.parse(full)
@@ -13,29 +13,14 @@ module Solargraph
13
13
  # Generate the data.
14
14
  #
15
15
  # @return [Array]
16
- def map filename, code, node, comments
17
- @filename = filename
18
- @code = code
19
- @node = node
20
- @comments = comments
21
- @node_stack = []
22
- @directives = {}
23
- @comment_ranges = comments.map do |c|
24
- Range.from_to(c.loc.expression.line, c.loc.expression.column, c.loc.expression.last_line, c.loc.expression.last_column)
25
- end
26
- @node_comments = associate_comments(node, comments)
27
- @pins = []
28
- @locals = []
29
- @strings = []
30
-
31
- # HACK make sure the first node gets processed
32
- root = AST::Node.new(:source, [filename])
33
- root = root.append node
34
- # @todo Is the root namespace a class or a module? Assuming class for now.
35
- @pins.push Pin::Namespace.new(get_node_location(nil), '', '', nil, :class, :public)
36
- process root
16
+ def map source
17
+ @filename = source.filename
18
+ @code = source.code
19
+ @comments = source.comments
20
+ @pins = Solargraph::SourceMap::NodeProcessor.process(source.node, Solargraph::SourceMap::Region.new(source: source), [])
37
21
  process_comment_directives
38
- [@pins, @locals]
22
+ locals = @pins.select{|p| [Pin::LocalVariable, Pin::MethodParameter, Pin::BlockParameter].include?(p.class)}
23
+ [@pins - locals, locals]
39
24
  end
40
25
 
41
26
  def unmap filename, code
@@ -50,23 +35,10 @@ module Solargraph
50
35
  # @return [Array]
51
36
  def map source
52
37
  return new.unmap(source.filename, source.code) unless source.parsed?
53
- new.map source.filename, source.code, source.node, source.comments
38
+ new.map source
54
39
  end
55
40
  end
56
41
 
57
- # @param node [Parser::AST::Node]
58
- # @return [String]
59
- def code_for node
60
- # @todo Using node locations on code with converted EOLs seems
61
- # slightly more efficient than calculating offsets.
62
- b = node.location.expression.begin.begin_pos
63
- e = node.location.expression.end.end_pos
64
- # b = Position.line_char_to_offset(@code, node.location.line - 1, node.location.column)
65
- # e = Position.line_char_to_offset(@code, node.location.last_line - 1, node.location.last_column)
66
- frag = source_from_parser[b..e-1].to_s
67
- frag.strip.gsub(/,$/, '')
68
- end
69
-
70
42
  # @return [String]
71
43
  def filename
72
44
  @filename
@@ -77,346 +49,6 @@ module Solargraph
77
49
  @pins ||= []
78
50
  end
79
51
 
80
- # @param node [Parser::AST::Node]
81
- def process node, tree = [], visibility = :public, scope = :instance, fqn = '', stack = []
82
- return unless node.is_a?(AST::Node)
83
- return if node.type == :str
84
- stack.push node
85
- if node.kind_of?(AST::Node)
86
- @node_stack.unshift node
87
- if node.type == :class or node.type == :module
88
- visibility = :public
89
- if node.children[0].kind_of?(AST::Node) and node.children[0].children[0].kind_of?(AST::Node) and node.children[0].children[0].type == :cbase
90
- tree = pack_name(node.children[0])
91
- tree.shift if tree.first.empty?
92
- else
93
- tree = tree + pack_name(node.children[0])
94
- end
95
- fqn = tree.join('::')
96
- sc = nil
97
- if node.type == :class and !node.children[1].nil?
98
- sc = unpack_name(node.children[1])
99
- end
100
- pins.push Solargraph::Pin::Namespace.new(get_node_location(node), tree[0..-2].join('::') || '', pack_name(node.children[0]).last.to_s, comments_for(node), node.type, visibility)
101
- pins.push Pin::Reference::Superclass.new(pins.last.location, pins.last.path, sc) unless sc.nil?
102
- end
103
- file = filename
104
- node.children.each do |c|
105
- if c.kind_of?(AST::Node)
106
- if c.type == :ivasgn
107
- here = get_node_start_position(c)
108
- named_path = get_named_path_pin(here)
109
- if c.children[1].nil?
110
- ora = find_parent(stack, :or_asgn)
111
- unless ora.nil?
112
- u = c.updated(:ivasgn, c.children + ora.children[1..-1], nil)
113
- 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]), named_path.context)
114
- if visibility == :module_function and named_path.kind == Pin::METHOD
115
- other = ComplexType.parse("Module<#{named_path.context.namespace}>")
116
- 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?
117
- end
118
- end
119
- else
120
- 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]), named_path.context)
121
- if visibility == :module_function and named_path.kind == Pin::METHOD
122
- other = ComplexType.parse("Module<#{named_path.context.namespace}>")
123
- 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)
124
- end
125
- end
126
- elsif c.type == :cvasgn
127
- here = get_node_start_position(c)
128
- context = get_named_path_pin(here)
129
- if c.children[1].nil?
130
- ora = find_parent(stack, :or_asgn)
131
- unless ora.nil?
132
- u = c.updated(:cvasgn, c.children + ora.children[1..-1], nil)
133
- 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.context)
134
- end
135
- else
136
- 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.context)
137
- end
138
- elsif c.type == :lvasgn
139
- here = get_node_start_position(c)
140
- context = get_named_path_pin(here)
141
- block = get_block_pin(here)
142
- presence = Range.new(here, block.location.range.ending)
143
- if c.children[1].nil?
144
- ora = find_parent(stack, :or_asgn)
145
- unless ora.nil?
146
- u = c.updated(:lvasgn, c.children + ora.children[1..-1], nil)
147
- @locals.push Solargraph::Pin::LocalVariable.new(get_node_location(u), fqn, u.children[0].to_s, comments_for(ora), u.children[1], infer_literal_node_type(c.children[1]), context.context, block, presence)
148
- end
149
- else
150
- @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.context, block, presence)
151
- end
152
- elsif c.type == :gvasgn
153
- if c.children[1].nil?
154
- ora = find_parent(stack, :or_asgn)
155
- unless ora.nil?
156
- u = c.updated(:gvasgn, c.children + ora.children[1..-1], nil)
157
- pins.push Solargraph::Pin::GlobalVariable.new(get_node_location(c), fqn, u.children[0].to_s, comments_for(c), u.children[1], infer_literal_node_type(c.children[1]), @pins.first.context)
158
- end
159
- else
160
- 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.context)
161
- end
162
- elsif c.type == :sym
163
- pins.push Solargraph::Pin::Symbol.new(get_node_location(c), ":#{c.children[0]}")
164
- elsif c.type == :casgn
165
- here = get_node_start_position(c)
166
- block = get_block_pin(here)
167
- 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.context, :public)
168
- elsif c.type == :def
169
- 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))
170
- if methpin.name == 'initialize' and methpin.scope == :instance
171
- pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, 'new', methpin.comments, :class, :public, methpin.parameters)
172
- # @todo Smelly instance variable access.
173
- pins.last.instance_variable_set(:@return_complex_type, ComplexType.parse(methpin.namespace))
174
- pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, methpin.name, methpin.comments, methpin.scope, :private, methpin.parameters)
175
- elsif visibility == :module_function
176
- pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, methpin.name, methpin.comments, :class, :public, methpin.parameters)
177
- pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, methpin.name, methpin.comments, :instance, :private, methpin.parameters)
178
- else
179
- pins.push methpin
180
- end
181
- elsif c.type == :defs
182
- s_visi = visibility
183
- s_visi = :public if s_visi == :module_function or scope != :class
184
- if c.children[0].is_a?(AST::Node) and c.children[0].type == :self
185
- dfqn = fqn || ''
186
- else
187
- dfqn = unpack_name(c.children[0])
188
- end
189
- unless dfqn.nil?
190
- pins.push Solargraph::Pin::Method.new(get_node_location(c), dfqn, "#{c.children[(node.type == :def ? 0 : 1)]}", comments_for(c), :class, s_visi, get_method_args(c))
191
- process c, tree, scope, :class, dfqn, stack
192
- end
193
- next
194
- elsif c.type == :send and [:public, :protected, :private].include?(c.children[1])
195
- visibility = c.children[1]
196
- elsif c.type == :send and [:private_class_method].include?(c.children[1]) and c.children[2].kind_of?(AST::Node)
197
- if c.children[2].type == :sym or c.children[2].type == :str
198
- ref = pins.select{|p| p.namespace == (fqn || '') and p.name == c.children[2].children[0].to_s}.first
199
- unless ref.nil?
200
- pins.delete ref
201
- pins.push Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.comments, ref.scope, :private, ref.parameters)
202
- end
203
- else
204
- process c, tree, :private, :class, fqn, stack
205
- next
206
- end
207
- elsif c.type == :send and [:private_constant].include?(c.children[1]) and c.children[2].kind_of?(AST::Node)
208
- if c.children[2].type == :sym or c.children[2].type == :str
209
- # @todo What to do about references?
210
- cn = c.children[2].children[0].to_s
211
- ref = pins.select{|p| p.namespace == (fqn || '') and p.name == cn}.first
212
- unless ref.nil?
213
- pins.delete ref
214
- # Might be either a namespace or constant
215
- if ref.kind == Pin::CONSTANT
216
- pins.push ref.class.new(ref.location, ref.namespace, ref.name, ref.comments, ref.signature, ref.return_type, ref.context, :private)
217
- else
218
- # pins.push ref.class.new(ref.location, ref.namespace, ref.name, ref.comments, ref.type, :private, (ref.superclass_reference.nil? ? nil : ref.superclass_reference.name))
219
- pins.push ref.class.new(ref.location, ref.namespace, ref.name, ref.comments, ref.type, :private)
220
- end
221
- end
222
- end
223
- next
224
- elsif c.type == :send and c.children[1] == :module_function
225
- if c.children[2].nil?
226
- visibility = :module_function
227
- elsif c.children[2].type == :sym or c.children[2].type == :str
228
- # @todo What to do about references?
229
- c.children[2..-1].each do |x|
230
- cn = x.children[0].to_s
231
- ref = pins.select{|p| p.namespace == (fqn || '') and p.name == cn}.first
232
- unless ref.nil?
233
- pins.delete ref
234
- mm = Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.comments, :class, :public, ref.parameters)
235
- cm = Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.comments, :instance, :private, ref.parameters)
236
- pins.push mm, cm
237
- pins.select{|pin| pin.kind == Pin::INSTANCE_VARIABLE and pin.context == ref.context}.each do |ivar|
238
- pins.delete ivar
239
- pins.push Solargraph::Pin::InstanceVariable.new(ivar.location, ivar.namespace, ivar.name, ivar.comments, ivar.signature, ivar.instance_variable_get(:@literal), mm)
240
- pins.push Solargraph::Pin::InstanceVariable.new(ivar.location, ivar.namespace, ivar.name, ivar.comments, ivar.signature, ivar.instance_variable_get(:@literal), cm)
241
- end
242
- end
243
- end
244
- elsif c.children[2].type == :def
245
- # @todo A single function
246
- process c, tree, :module_function, :class, fqn, stack
247
- next
248
- end
249
- elsif c.type == :send and c.children[1] == :include and c.children[0].nil?
250
- last_node = get_last_in_stack_not_begin(stack)
251
- if last_node.nil? or last_node.type == :class or last_node.type == :module or last_node.type == :source
252
- if c.children[2].kind_of?(AST::Node) and c.children[2].type == :const
253
- c.children[2..-1].each do |i|
254
- nspin = @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.path == fqn}.last
255
- unless nspin.nil?
256
- # iref = Pin::Reference.new(get_node_location(c), nspin.path, unpack_name(i))
257
- # nspin.include_references.push(iref)
258
- pins.push Pin::Reference::Include.new(get_node_location(c), nspin.path, unpack_name(i))
259
- end
260
- end
261
- end
262
- end
263
- elsif c.type == :send and c.children[1] == :extend and c.children[0].nil?
264
- last_node = get_last_in_stack_not_begin(stack)
265
- if last_node.nil? or last_node.type == :class or last_node.type == :module or last_node.type == :source
266
- c.children[2..-1].each do |i|
267
- nspin = @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.path == fqn}.last
268
- unless nspin.nil?
269
- ref = nil
270
- if i.type == :self
271
- # ref = Pin::Reference.new(get_node_location(c), nspin.path, nspin.path)
272
- ref = Pin::Reference::Extend.new(get_node_location(c), nspin.path, nspin.path)
273
- elsif i.type == :const
274
- # ref = Pin::Reference.new(get_node_location(c), nspin.path, unpack_name(i))
275
- ref = Pin::Reference::Extend.new(get_node_location(c), nspin.path, unpack_name(i))
276
- end
277
- # nspin.extend_references.push(ref) unless ref.nil?
278
- pins.push ref unless ref.nil?
279
- end
280
- end
281
- end
282
- elsif c.type == :send && [:attr_reader, :attr_writer, :attr_accessor].include?(c.children[1])
283
- c.children[2..-1].each do |a|
284
- if c.children[1] == :attr_reader or c.children[1] == :attr_accessor
285
- pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}", comments_for(c), :reader, scope, visibility)
286
- end
287
- if c.children[1] == :attr_writer or c.children[1] == :attr_accessor
288
- pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}=", comments_for(c), :writer, scope, visibility)
289
- end
290
- end
291
- elsif c.type == :alias
292
- pin = pins.select{|p| p.name == c.children[1].children[0].to_s && p.namespace == fqn && p.scope == scope}.first
293
- unless pin.nil?
294
- if pin.is_a?(Solargraph::Pin::Method)
295
- pins.push Solargraph::Pin::Method.new(get_node_location(c), pin.namespace, c.children[0].children[0].to_s, comments_for(c) || pin.comments, pin.scope, pin.visibility, pin.parameters)
296
- elsif pin.is_a?(Solargraph::Pin::Attribute)
297
- pins.push Solargraph::Pin::Attribute.new(get_node_location(c), pin.namespace, c.children[0].children[0].to_s, comments_for(c) || pin.comments, pin.access, pin.scope, pin.visibility)
298
- end
299
- end
300
- elsif c.type == :send && c.children[1] == :alias_method && c.children[2] && c.children[2] && c.children[2].type == :sym && c.children[3] && c.children[3].type == :sym
301
- pin = pins.select{|p| p.name == c.children[3].children[0].to_s && p.namespace == fqn && p.scope == scope}.first
302
- unless pin.nil?
303
- if pin.is_a?(Solargraph::Pin::Method)
304
- pins.push Solargraph::Pin::Method.new(get_node_location(c), pin.namespace, c.children[2].children[0].to_s, comments_for(c) || pin.comments, pin.scope, pin.visibility, pin.parameters)
305
- elsif pin.is_a?(Solargraph::Pin::Attribute)
306
- pins.push Solargraph::Pin::Attribute.new(get_node_location(c), pin.namespace, c.children[2].children[0].to_s, comments_for(c) || pin.comments, pin.access, pin.scope, pin.visibility)
307
- end
308
- end
309
- elsif c.type == :sclass && c.children[0].type == :self
310
- process c, tree, :public, :class, fqn || '', stack
311
- next
312
- elsif c.type == :send && c.children[1] == :require
313
- if c.children[2].kind_of?(AST::Node) and c.children[2].type == :str
314
- # @requires.push Solargraph::Pin::Reference.new(get_node_location(c), fqn, c.children[2].children[0].to_s)
315
- pins.push Pin::Reference::Require.new(get_node_location(c), c.children[2].children[0].to_s)
316
- end
317
- elsif c.type == :args
318
- if @node_stack.first.type == :block
319
- pi = 0
320
- c.children.each do |u|
321
- here = get_node_start_position(c)
322
- blk = get_block_pin(here)
323
- @locals.push Solargraph::Pin::BlockParameter.new(get_node_location(u), fqn || '', "#{u.children[0]}", comments_for(c), blk)
324
- blk.parameters.push @locals.push.last
325
- pi += 1
326
- end
327
- else
328
- c.children.each do |u|
329
- here = get_node_start_position(u)
330
- context = get_named_path_pin(here)
331
- block = get_block_pin(here)
332
- presence = Range.new(here, block.location.range.ending)
333
- @locals.push Solargraph::Pin::MethodParameter.new(get_node_location(u), fqn, u.children[0].to_s, comments_for(c), resolve_node_signature(u.children[1]), infer_literal_node_type(u.children[1]), context, block, presence)
334
- end
335
- end
336
- elsif c.type == :block
337
- here = get_node_start_position(c)
338
- named_path = get_named_path_pin(here)
339
- @pins.push Solargraph::Pin::Block.new(get_node_location(c), fqn || '', '', comments_for(c), c.children[0], named_path.context)
340
- end
341
- process c, tree, visibility, scope, fqn, stack
342
- end
343
- end
344
- @node_stack.shift
345
- end
346
- stack.pop
347
- end
348
-
349
- def get_last_in_stack_not_begin stack
350
- index = stack.length - 1
351
- last = stack[index]
352
- while !last.nil? and last.type == :begin
353
- index -= 1
354
- last = stack[index]
355
- end
356
- last
357
- end
358
-
359
- def get_block_pin position
360
- @pins.select{|pin| [Pin::BLOCK, Pin::NAMESPACE, Pin::METHOD].include?(pin.kind) and pin.location.range.contain?(position)}.last
361
- end
362
-
363
- def get_named_path_pin position
364
- @pins.select{|pin| [Pin::NAMESPACE, Pin::METHOD].include?(pin.kind) and pin.location.range.contain?(position)}.last
365
- end
366
-
367
- def get_namespace_pin position
368
- @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.location.range.contain?(position)}.last
369
- end
370
-
371
- # @return [String]
372
- def comments_for node
373
- result = @node_comments[node.loc]
374
- return nil if result.nil?
375
- result
376
- end
377
-
378
- # @param node [Parser::AST::Node]
379
- # @return [Solargraph::Location]
380
- def get_node_location(node)
381
- if node.nil?
382
- st = Position.new(0, 0)
383
- en = Position.from_offset(@code, @code.length)
384
- else
385
- st = Position.new(node.loc.line, node.loc.column)
386
- en = Position.new(node.loc.last_line, node.loc.last_column)
387
- end
388
- range = Range.new(st, en)
389
- Location.new(filename, range)
390
- end
391
-
392
- # @param node [Parser::AST::Node]
393
- # @param comments [Array]
394
- # @return [Hash]
395
- def associate_comments node, comments
396
- return nil if comments.nil?
397
- comment_hash = Parser::Source::Comment.associate_locations(node, comments)
398
- result_hash = {}
399
- comment_hash.each_pair { |k, v|
400
- ctxt = ''
401
- num = nil
402
- started = false
403
- v.each { |l|
404
- # Trim the comment and minimum leading whitespace
405
- p = l.text.gsub(/^#/, '')
406
- if num.nil? and !p.strip.empty?
407
- num = p.index(/[^ ]/)
408
- started = true
409
- elsif started and !p.strip.empty?
410
- cur = p.index(/[^ ]/)
411
- num = cur if cur < num
412
- end
413
- ctxt += "#{p[num..-1]}\n" if started
414
- }
415
- result_hash[k] = ctxt
416
- }
417
- result_hash
418
- end
419
-
420
52
  # @param node [Parser::AST::Node]
421
53
  # @return [Solargraph::Pin::Namespace]
422
54
  def namespace_for(node)
@@ -428,46 +60,6 @@ module Solargraph
428
60
  @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.location.range.contain?(position)}.last
429
61
  end
430
62
 
431
- def find_parent(stack, *types)
432
- stack.reverse.each { |p|
433
- return p if types.include?(p.type)
434
- }
435
- nil
436
- end
437
-
438
- def get_method_args node
439
- return [] if node.nil?
440
- list = nil
441
- args = []
442
- node.children.each { |c|
443
- if c.kind_of?(AST::Node) and c.type == :args
444
- list = c
445
- break
446
- end
447
- }
448
- return args if list.nil?
449
- list.children.each { |c|
450
- if c.type == :arg
451
- args.push c.children[0].to_s
452
- elsif c.type == :restarg
453
- args.push "*#{c.children[0]}"
454
- elsif c.type == :optarg
455
- args.push "#{c.children[0]} = #{code_for(c.children[1])}"
456
- elsif c.type == :kwarg
457
- args.push "#{c.children[0]}:"
458
- elsif c.type == :kwoptarg
459
- args.push "#{c.children[0]}: #{code_for(c.children[1])}"
460
- elsif c.type == :blockarg
461
- args.push "&#{c.children[0]}"
462
- end
463
- }
464
- args
465
- end
466
-
467
- def source_from_parser
468
- @source_from_parser ||= @code.gsub(/\r\n/, "\n")
469
- end
470
-
471
63
  def process_comment position, comment
472
64
  cmnt = remove_inline_comment_hashes(comment)
473
65
  return unless cmnt =~ /(@\!method|@\!attribute|@\!domain|@\!macro)/
@@ -485,7 +77,7 @@ module Solargraph
485
77
  namespace = namespace_at(position)
486
78
  gen_src = Solargraph::SourceMap.load_string("def #{directive.tag.name};end")
487
79
  gen_pin = gen_src.pins.last # Method is last pin after root namespace
488
- @pins.push Solargraph::Pin::Method.new(location, namespace.path, gen_pin.name, docstring.all, :instance, :public, gen_pin.parameters)
80
+ @pins.push Solargraph::Pin::Method.new(location, namespace.path, gen_pin.name, docstring.all, :instance, :public, gen_pin.parameters, nil)
489
81
  when 'attribute'
490
82
  namespace = namespace_at(position)
491
83
  t = (directive.tag.types.nil? || directive.tag.types.empty?) ? nil : directive.tag.types.flatten.join('')
@@ -499,10 +91,6 @@ module Solargraph
499
91
  when 'domain'
500
92
  namespace = namespace_at(position)
501
93
  namespace.domains.push directive.tag.text
502
- when 'macro'
503
- nxt_pos = Position.new(position.line + 1, @code.lines[position.line + 1].length)
504
- path_pin = get_named_path_pin(nxt_pos)
505
- path_pin.macros.push directive
506
94
  end
507
95
  end
508
96