solargraph 0.18.3 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/solargraph/api_map/probe.rb +222 -0
- data/lib/solargraph/api_map/source_to_yard.rb +3 -3
- data/lib/solargraph/api_map/store.rb +135 -0
- data/lib/solargraph/api_map.rb +169 -609
- data/lib/solargraph/diagnostics/rubocop.rb +4 -4
- data/lib/solargraph/language_server/host.rb +53 -19
- data/lib/solargraph/language_server/message/extended/document.rb +1 -1
- data/lib/solargraph/language_server/message/extended/search.rb +1 -1
- data/lib/solargraph/language_server/message/method_not_found.rb +1 -1
- data/lib/solargraph/language_server/message/text_document/definition.rb +2 -15
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +2 -15
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +3 -15
- data/lib/solargraph/language_server/message_types.rb +10 -0
- data/lib/solargraph/language_server.rb +1 -0
- data/lib/solargraph/library.rb +8 -0
- data/lib/solargraph/node_methods.rb +6 -1
- data/lib/solargraph/page.rb +2 -1
- data/lib/solargraph/pin/attribute.rb +8 -12
- data/lib/solargraph/pin/base.rb +20 -95
- data/lib/solargraph/pin/base_variable.rb +15 -74
- data/lib/solargraph/pin/block.rb +21 -0
- data/lib/solargraph/pin/block_parameter.rb +30 -44
- data/lib/solargraph/pin/class_variable.rb +3 -0
- data/lib/solargraph/pin/constant.rb +4 -8
- data/lib/solargraph/pin/conversions.rb +4 -3
- data/lib/solargraph/pin/documenting.rb +27 -0
- data/lib/solargraph/pin/global_variable.rb +3 -0
- data/lib/solargraph/pin/instance_variable.rb +5 -4
- data/lib/solargraph/pin/local_variable.rb +8 -15
- data/lib/solargraph/pin/localized.rb +12 -0
- data/lib/solargraph/pin/method.rb +6 -67
- data/lib/solargraph/pin/method_parameter.rb +24 -11
- data/lib/solargraph/pin/namespace.rb +26 -35
- data/lib/solargraph/pin/reference.rb +15 -8
- data/lib/solargraph/pin/symbol.rb +34 -3
- data/lib/solargraph/pin/yard_object.rb +11 -4
- data/lib/solargraph/pin.rb +16 -2
- data/lib/solargraph/server.rb +2 -2
- data/lib/solargraph/source/change.rb +10 -13
- data/lib/solargraph/source/fragment.rb +42 -94
- data/lib/solargraph/source/location.rb +13 -0
- data/lib/solargraph/source/mapper.rb +426 -0
- data/lib/solargraph/source/position.rb +1 -0
- data/lib/solargraph/source/range.rb +11 -3
- data/lib/solargraph/source.rb +93 -284
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/views/_method.erb +59 -60
- data/lib/solargraph/views/_name_type_tag.erb +10 -0
- data/lib/solargraph/views/_namespace.erb +26 -26
- data/lib/solargraph/views/document.erb +23 -16
- data/lib/solargraph/views/layout.erb +38 -10
- data/lib/solargraph/views/search.erb +12 -11
- data/lib/solargraph/workspace/config.rb +27 -6
- data/lib/solargraph/workspace.rb +10 -2
- data/lib/solargraph.rb +10 -2
- data/lib/yard-solargraph.rb +3 -0
- metadata +25 -20
- data/lib/solargraph/pin/directed/attribute.rb +0 -20
- data/lib/solargraph/pin/directed/method.rb +0 -22
- data/lib/solargraph/pin/directed.rb +0 -9
- data/lib/solargraph/pin/parameter.rb +0 -23
@@ -25,6 +25,14 @@ module Solargraph
|
|
25
25
|
# @return [String] The updated text.
|
26
26
|
def write text, nullable = false
|
27
27
|
if nullable and !range.nil? and new_text.match(/[\.\[\{\(@\$:]$/)
|
28
|
+
if new_text == ':'
|
29
|
+
offset = Position.to_offset(text, range.start)
|
30
|
+
if text[offset - 1] == ':'
|
31
|
+
p = Position.from_offset(text, offset - 1)
|
32
|
+
r = Change.new(Range.new(p, range.start), ' ')
|
33
|
+
text = r.write(text)
|
34
|
+
end
|
35
|
+
end
|
28
36
|
commit text, "#{new_text[0..-2]} "
|
29
37
|
elsif range.nil?
|
30
38
|
new_text
|
@@ -50,21 +58,10 @@ module Solargraph
|
|
50
58
|
private
|
51
59
|
|
52
60
|
def commit text, insert
|
53
|
-
start_offset =
|
54
|
-
end_offset =
|
61
|
+
start_offset = Position.to_offset(text, range.start)
|
62
|
+
end_offset = Position.to_offset(text, range.ending)
|
55
63
|
(start_offset == 0 ? '' : text[0..start_offset-1].to_s) + insert.force_encoding('utf-8') + text[end_offset..-1].to_s
|
56
64
|
end
|
57
|
-
|
58
|
-
def get_offset text, line, column
|
59
|
-
offset = 0
|
60
|
-
feed = 0
|
61
|
-
text.lines.each do |l|
|
62
|
-
break if line == feed
|
63
|
-
offset += l.length
|
64
|
-
feed += 1
|
65
|
-
end
|
66
|
-
offset + column
|
67
|
-
end
|
68
65
|
end
|
69
66
|
end
|
70
67
|
end
|
@@ -12,21 +12,20 @@ module Solargraph
|
|
12
12
|
# @param source [Solargraph::Source]
|
13
13
|
# @param line [Integer]
|
14
14
|
# @param column [Integer]
|
15
|
-
def initialize source, line, column
|
16
|
-
# @todo Split this object from the source. The source can change; if
|
17
|
-
# it does, this object's data should not.
|
15
|
+
def initialize source, line, column
|
18
16
|
@source = source
|
19
17
|
@code = source.code
|
20
18
|
@line = line
|
21
19
|
@column = column
|
22
|
-
@
|
20
|
+
@calculated_literal = false
|
23
21
|
end
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
def character
|
24
|
+
@column
|
25
|
+
end
|
26
|
+
|
27
|
+
def position
|
28
|
+
@position ||= Position.new(line, column)
|
30
29
|
end
|
31
30
|
|
32
31
|
# Get the fully qualified namespace at the current offset.
|
@@ -34,14 +33,8 @@ module Solargraph
|
|
34
33
|
# @return [String]
|
35
34
|
def namespace
|
36
35
|
if @namespace.nil?
|
37
|
-
|
38
|
-
@
|
39
|
-
next unless n.kind_of?(AST::Node)
|
40
|
-
if n.type == :class or n.type == :module
|
41
|
-
parts.unshift unpack_name(n.children[0])
|
42
|
-
end
|
43
|
-
end
|
44
|
-
@namespace = parts.join('::')
|
36
|
+
pin = @source.locate_namespace_pin(line, character)
|
37
|
+
@namespace = (pin.nil? ? '' : pin.path)
|
45
38
|
end
|
46
39
|
@namespace
|
47
40
|
end
|
@@ -74,16 +67,7 @@ module Solargraph
|
|
74
67
|
def scope
|
75
68
|
if @scope.nil?
|
76
69
|
@scope = :class
|
77
|
-
|
78
|
-
until tree.empty?
|
79
|
-
cursor = tree.shift
|
80
|
-
break if cursor.type == :class or cursor.type == :module
|
81
|
-
if cursor.type == :def
|
82
|
-
pin = @source.method_pins.select{|pin| pin.contain?(offset)}.first
|
83
|
-
# @todo The pin should never be nil here, but we're guarding it just in case
|
84
|
-
@scope = (pin.nil? ? :instance : pin.scope)
|
85
|
-
end
|
86
|
-
end
|
70
|
+
@scope = :instance if named_path.kind == Pin::METHOD and named_path.scope == :instance
|
87
71
|
end
|
88
72
|
@scope
|
89
73
|
end
|
@@ -193,7 +177,8 @@ module Solargraph
|
|
193
177
|
#
|
194
178
|
# @return [Boolean]
|
195
179
|
def string?
|
196
|
-
@string ||= (node.type == :str or node.type == :dstr)
|
180
|
+
# @string ||= (node.type == :str or node.type == :dstr)
|
181
|
+
@string ||= @source.string_at?(line, character)
|
197
182
|
end
|
198
183
|
|
199
184
|
# True if the current offset is inside a comment.
|
@@ -219,83 +204,35 @@ module Solargraph
|
|
219
204
|
@whole_word_range ||= word_range_at(offset, true)
|
220
205
|
end
|
221
206
|
|
222
|
-
|
223
|
-
|
224
|
-
#
|
225
|
-
# @return [Array<Solargraph::Pin::LocalVariable>]
|
226
|
-
def local_variable_pins name = nil
|
227
|
-
@local_variable_pins ||= prefer_non_nil_variables(@source.local_variable_pins.select{|pin| pin.visible_from?(node)})
|
228
|
-
return @local_variable_pins if name.nil?
|
229
|
-
@local_variable_pins.select{|pin| pin.name == name}
|
207
|
+
def block
|
208
|
+
@block ||= @source.locate_block_pin(line, character)
|
230
209
|
end
|
231
210
|
|
232
|
-
def
|
233
|
-
@
|
211
|
+
def named_path
|
212
|
+
@named_path ||= @source.locate_named_path_pin(line, character)
|
234
213
|
end
|
235
214
|
|
236
|
-
def
|
237
|
-
@
|
215
|
+
def locals
|
216
|
+
@locals ||= @source.locals.select{|pin| pin.visible_from?(block, position)}
|
238
217
|
end
|
239
218
|
|
240
|
-
def
|
241
|
-
|
242
|
-
@calculated_base = calculated_signature[0..-2] if calculated_signature.end_with?('.')
|
243
|
-
@calculated_base ||= calculated_signature.split('.')[0..-2].join('.')
|
244
|
-
end
|
245
|
-
@calculated_base
|
219
|
+
def base_literal?
|
220
|
+
!base_literal.nil?
|
246
221
|
end
|
247
222
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
# @todo Smelly exceptional case for arrays
|
255
|
-
return signature.sub(/^\.\[\]/, 'Array.new') if signature.start_with?('.[].')
|
256
|
-
pn = @source.node_at(line, column - 2)
|
257
|
-
unless pn.nil?
|
258
|
-
literal = infer_literal_node_type(pn)
|
259
|
-
unless literal.nil?
|
260
|
-
return "#{literal}.new#{signature}"
|
261
|
-
end
|
262
|
-
end
|
263
|
-
return signature
|
264
|
-
end
|
265
|
-
# @todo Smelly exceptional case for integers
|
266
|
-
base, rest = signature.split('.', 2)
|
267
|
-
base.sub!(/^[0-9]+?$/, 'Integer.new')
|
268
|
-
var = local_variable_pins(base).first
|
269
|
-
unless var.nil?
|
270
|
-
done = []
|
271
|
-
until var.nil?
|
272
|
-
break if done.include?(var)
|
273
|
-
done.push var
|
274
|
-
type = var.calculated_signature
|
275
|
-
break if type.nil?
|
276
|
-
base = type
|
277
|
-
var = local_variable_pins(base).first
|
278
|
-
end
|
279
|
-
end
|
280
|
-
base = @source.qualify(base, namespace)
|
281
|
-
base + (rest.nil? ? '' : ".#{rest}")
|
282
|
-
end
|
283
|
-
|
284
|
-
# @todo DRY this method. It exists in ApiMap.
|
285
|
-
# @return [Array<Solargraph::Pin::Base>]
|
286
|
-
def prefer_non_nil_variables pins
|
287
|
-
result = []
|
288
|
-
nil_pins = []
|
289
|
-
pins.each do |pin|
|
290
|
-
if pin.nil_assignment? and pin.return_type.nil?
|
291
|
-
nil_pins.push pin
|
292
|
-
else
|
293
|
-
result.push pin
|
223
|
+
def base_literal
|
224
|
+
if @base_literal.nil? and !@calculated_literal
|
225
|
+
@calculated_literal = true
|
226
|
+
if signature.start_with?('.')
|
227
|
+
pn = @source.node_at(line, column - 2)
|
228
|
+
@base_literal = infer_literal_node_type(pn) unless pn.nil?
|
294
229
|
end
|
295
230
|
end
|
296
|
-
|
231
|
+
@base_literal
|
297
232
|
end
|
298
233
|
|
234
|
+
private
|
235
|
+
|
299
236
|
# @return [Integer]
|
300
237
|
def offset
|
301
238
|
@offset ||= get_offset(line, column)
|
@@ -366,7 +303,18 @@ module Solargraph
|
|
366
303
|
end
|
367
304
|
index -= 1
|
368
305
|
end
|
369
|
-
# @todo Smelly exceptional case for
|
306
|
+
# @todo Smelly exceptional case for integer literals
|
307
|
+
match = signature.match(/^[0-9]+/)
|
308
|
+
if match
|
309
|
+
index += match[0].length
|
310
|
+
signature = signature[match[0].length..-1].to_s
|
311
|
+
@base_literal = 'Integer'
|
312
|
+
# @todo Smelly exceptional case for array literals
|
313
|
+
elsif signature.start_with?('.[]')
|
314
|
+
index += 3
|
315
|
+
signature = signature[index+3..-1].to_s
|
316
|
+
@base_literal = 'Array'
|
317
|
+
end
|
370
318
|
[index + 1, signature]
|
371
319
|
end
|
372
320
|
|
@@ -0,0 +1,426 @@
|
|
1
|
+
module Solargraph
|
2
|
+
class Source
|
3
|
+
class Mapper
|
4
|
+
include NodeMethods
|
5
|
+
|
6
|
+
private_class_method :new
|
7
|
+
|
8
|
+
# @return [Array<Solargraph::Pin::Base>]
|
9
|
+
def map filename, code, node, comments
|
10
|
+
@filename = filename
|
11
|
+
@code = code
|
12
|
+
@node = node
|
13
|
+
@comments = comments
|
14
|
+
@node_stack = []
|
15
|
+
# @node_tree = []
|
16
|
+
@comment_hash = {}
|
17
|
+
@directives = {}
|
18
|
+
@docstring_hash = associate_comments(node, comments)
|
19
|
+
# @todo Stuff that needs to be resolved
|
20
|
+
@variables = []
|
21
|
+
@path_macros = {}
|
22
|
+
|
23
|
+
@pins = []
|
24
|
+
@requires = []
|
25
|
+
@symbols = []
|
26
|
+
@locals = []
|
27
|
+
|
28
|
+
# HACK make sure the first node gets processed
|
29
|
+
root = AST::Node.new(:source, [filename])
|
30
|
+
root = root.append node
|
31
|
+
# @todo Is the root namespace a class or a module? Assuming class for now.
|
32
|
+
@pins.push Pin::Namespace.new(get_node_location(nil), '', '', nil, :class, :public, nil)
|
33
|
+
process root
|
34
|
+
process_directives
|
35
|
+
[@pins, @locals, @requires, @symbols, @path_macros]
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
# @return [Array<Solargraph::Pin::Base>]
|
40
|
+
def map filename, code, node, comments
|
41
|
+
new.map filename, code, node, comments
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param node [Parser::AST::Node]
|
46
|
+
# @return [String]
|
47
|
+
def code_for node
|
48
|
+
b = node.location.expression.begin.begin_pos
|
49
|
+
e = node.location.expression.end.end_pos
|
50
|
+
frag = @code[b..e-1].to_s
|
51
|
+
frag.strip.gsub(/,$/, '')
|
52
|
+
end
|
53
|
+
|
54
|
+
def comment_hash
|
55
|
+
@comment_hash
|
56
|
+
end
|
57
|
+
|
58
|
+
def filename
|
59
|
+
@filename
|
60
|
+
end
|
61
|
+
|
62
|
+
def comments
|
63
|
+
@comments
|
64
|
+
end
|
65
|
+
|
66
|
+
def pins
|
67
|
+
@pins ||= []
|
68
|
+
end
|
69
|
+
|
70
|
+
def process node, tree = [], visibility = :public, scope = :instance, fqn = '', stack = []
|
71
|
+
stack.push node
|
72
|
+
if node.kind_of?(AST::Node)
|
73
|
+
@node_stack.unshift node
|
74
|
+
if node.type == :class or node.type == :module
|
75
|
+
visibility = :public
|
76
|
+
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
|
77
|
+
tree = pack_name(node.children[0])
|
78
|
+
else
|
79
|
+
tree = tree + pack_name(node.children[0])
|
80
|
+
end
|
81
|
+
fqn = tree.join('::')
|
82
|
+
sc = nil
|
83
|
+
if node.type == :class and !node.children[1].nil?
|
84
|
+
sc = unpack_name(node.children[1])
|
85
|
+
end
|
86
|
+
pins.push Solargraph::Pin::Namespace.new(get_node_location(node), tree[0..-2].join('::') || '', pack_name(node.children[0]).last.to_s, docstring_for(node), node.type, visibility, sc)
|
87
|
+
end
|
88
|
+
file = filename
|
89
|
+
node.children.each do |c|
|
90
|
+
if c.kind_of?(AST::Node)
|
91
|
+
if c.type == :ivasgn
|
92
|
+
here = get_node_start_position(c)
|
93
|
+
context = get_named_path_pin(here)
|
94
|
+
if c.children[1].nil?
|
95
|
+
ora = find_parent(stack, :or_asgn)
|
96
|
+
unless ora.nil?
|
97
|
+
u = c.updated(:ivasgn, c.children + ora.children[1..-1], nil)
|
98
|
+
@docstring_hash[u.loc] = docstring_for(ora)
|
99
|
+
pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, docstring_for(u), resolve_node_signature(u.children[1]), infer_literal_node_type(u.children[1]), context)
|
100
|
+
end
|
101
|
+
else
|
102
|
+
pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(c), fqn || '',c.children[0].to_s, docstring_for(c), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), context)
|
103
|
+
end
|
104
|
+
elsif c.type == :cvasgn
|
105
|
+
here = get_node_start_position(c)
|
106
|
+
context = get_named_path_pin(here)
|
107
|
+
if c.children[1].nil?
|
108
|
+
ora = find_parent(stack, :or_asgn)
|
109
|
+
unless ora.nil?
|
110
|
+
u = c.updated(:cvasgn, c.children + ora.children[1..-1], nil)
|
111
|
+
@docstring_hash[u.loc] = docstring_for(ora)
|
112
|
+
pins.push Solargraph::Pin::ClassVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, docstring_for(u), resolve_node_signature(u.children[1]), infer_literal_node_type(u.children[1]), context)
|
113
|
+
end
|
114
|
+
else
|
115
|
+
pins.push Solargraph::Pin::ClassVariable.new(get_node_location(c), fqn || '', c.children[0].to_s, docstring_for(c), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), context)
|
116
|
+
end
|
117
|
+
elsif c.type == :lvasgn
|
118
|
+
here = get_node_start_position(c)
|
119
|
+
context = get_named_path_pin(here)
|
120
|
+
block = get_block_pin(here)
|
121
|
+
presence = Source::Range.new(here, block.location.range.ending)
|
122
|
+
if c.children[1].nil?
|
123
|
+
ora = find_parent(stack, :or_asgn)
|
124
|
+
unless ora.nil?
|
125
|
+
u = c.updated(:lvasgn, c.children + ora.children[1..-1], nil)
|
126
|
+
@docstring_hash[u.loc] = docstring_for(ora)
|
127
|
+
@locals.push Solargraph::Pin::LocalVariable.new(get_node_location(u), fqn, u.children[0].to_s, docstring_for(u), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), context, block, presence)
|
128
|
+
end
|
129
|
+
else
|
130
|
+
@locals.push Solargraph::Pin::LocalVariable.new(get_node_location(c), fqn, c.children[0].to_s, docstring_for(c), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), context, block, presence)
|
131
|
+
end
|
132
|
+
elsif c.type == :gvasgn
|
133
|
+
pins.push Solargraph::Pin::GlobalVariable.new(get_node_location(c), fqn, c.children[0].to_s, docstring_for(c), resolve_node_signature(c.children[1]), infer_literal_node_type(c.children[1]), @pins.first)
|
134
|
+
elsif c.type == :sym
|
135
|
+
@symbols.push Solargraph::Pin::Symbol.new(get_node_location(c), ":#{c.children[0]}")
|
136
|
+
|
137
|
+
elsif c.type == :casgn
|
138
|
+
here = get_node_start_position(c)
|
139
|
+
block = get_block_pin(here)
|
140
|
+
pins.push Solargraph::Pin::Constant.new(get_node_location(c), fqn, c.children[1].to_s, docstring_for(c), resolve_node_signature(c.children[2]), infer_literal_node_type(c.children[2]), block, :public)
|
141
|
+
elsif c.type == :def and c.children[0].to_s[0].match(/[a-z]/i)
|
142
|
+
methpin = Solargraph::Pin::Method.new(get_node_location(c), fqn || '', c.children[(c.type == :def ? 0 : 1)].to_s, docstring_for(c), scope, visibility, get_method_args(c))
|
143
|
+
if methpin.name == 'initialize' and methpin.scope == :instance
|
144
|
+
pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, 'new', methpin.docstring, :class, :public, methpin.parameters)
|
145
|
+
# @todo Smelly instance variable access.
|
146
|
+
pins.last.instance_variable_set(:@return_type, methpin.namespace)
|
147
|
+
pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, methpin.name, methpin.docstring, methpin.scope, :private, methpin.parameters)
|
148
|
+
else
|
149
|
+
pins.push methpin
|
150
|
+
end
|
151
|
+
elsif c.type == :defs
|
152
|
+
s_visi = visibility
|
153
|
+
s_visi = :public if scope != :class
|
154
|
+
if c.children[0].is_a?(AST::Node) and c.children[0].type == :self
|
155
|
+
dfqn = fqn || ''
|
156
|
+
else
|
157
|
+
dfqn = unpack_name(c.children[0])
|
158
|
+
end
|
159
|
+
unless dfqn.nil?
|
160
|
+
pins.push Solargraph::Pin::Method.new(get_node_location(c), dfqn, "#{c.children[(node.type == :def ? 0 : 1)]}", docstring_for(c), :class, s_visi, get_method_args(c))
|
161
|
+
process c, tree, scope, :class, dfqn, stack
|
162
|
+
end
|
163
|
+
next
|
164
|
+
elsif c.type == :send and [:public, :protected, :private].include?(c.children[1])
|
165
|
+
visibility = c.children[1]
|
166
|
+
elsif c.type == :send and [:private_class_method].include?(c.children[1]) and c.children[2].kind_of?(AST::Node)
|
167
|
+
if c.children[2].type == :sym or c.children[2].type == :str
|
168
|
+
ref = pins.select{|p| p.namespace == (fqn || '') and p.name == c.children[2].children[0].to_s}.first
|
169
|
+
unless ref.nil?
|
170
|
+
pins.delete ref
|
171
|
+
pins.push Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.docstring, ref.scope, :private, ref.parameters)
|
172
|
+
end
|
173
|
+
else
|
174
|
+
process c, tree, :private, :class, fqn, stack
|
175
|
+
next
|
176
|
+
end
|
177
|
+
elsif c.type == :send and [:private_constant].include?(c.children[1]) and c.children[2].kind_of?(AST::Node)
|
178
|
+
if c.children[2].type == :sym or c.children[2].type == :str
|
179
|
+
# @todo What to do about references?
|
180
|
+
cn = c.children[2].children[0].to_s
|
181
|
+
ref = pins.select{|p| p.namespace == (fqn || '') and p.name == cn}.first
|
182
|
+
unless ref.nil?
|
183
|
+
pins.delete ref
|
184
|
+
# Might be either a namespace or constant
|
185
|
+
if ref.kind == Pin::CONSTANT
|
186
|
+
pins.push ref.class.new(ref.location, ref.namespace, ref.name, ref.docstring, ref.signature, ref.return_type, ref.context, :private)
|
187
|
+
else
|
188
|
+
pins.push ref.class.new(ref.location, ref.namespace, ref.name, ref.docstring, ref.type, :private, (ref.superclass_reference.nil? ? nil : ref.superclass_reference.name))
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
next
|
193
|
+
elsif c.type == :send and c.children[1] == :include and c.children[0].nil?
|
194
|
+
last_node = get_last_in_stack_not_begin(stack)
|
195
|
+
if last_node.nil? or last_node.type == :class or last_node.type == :module or last_node.type == :source
|
196
|
+
if c.children[2].kind_of?(AST::Node) and c.children[2].type == :const
|
197
|
+
c.children[2..-1].each do |i|
|
198
|
+
nspin = @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.path == fqn}.last
|
199
|
+
unless nspin.nil?
|
200
|
+
iref = Pin::Reference.new(get_node_location(c), nspin.path, unpack_name(i))
|
201
|
+
nspin.include_references.push(iref)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
elsif c.type == :send and c.children[1] == :extend and c.children[0].nil?
|
207
|
+
last_node = get_last_in_stack_not_begin(stack)
|
208
|
+
if last_node.nil? or last_node.type == :class or last_node.type == :module or last_node.type == :source
|
209
|
+
if c.children[2].kind_of?(AST::Node) and c.children[2].type == :const
|
210
|
+
# namespace_extends[fqn || ''] ||= []
|
211
|
+
c.children[2..-1].each do |i|
|
212
|
+
nspin = @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.path == fqn}.last
|
213
|
+
unless nspin.nil?
|
214
|
+
ref = Pin::Reference.new(get_node_location(c), nspin.path, unpack_name(i))
|
215
|
+
nspin.extend_references.push(ref)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
elsif c.type == :send and [:attr_reader, :attr_writer, :attr_accessor].include?(c.children[1])
|
221
|
+
c.children[2..-1].each do |a|
|
222
|
+
if c.children[1] == :attr_reader or c.children[1] == :attr_accessor
|
223
|
+
pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}", docstring_for(c), :reader) #AttrPin.new(c)
|
224
|
+
end
|
225
|
+
if c.children[1] == :attr_writer or c.children[1] == :attr_accessor
|
226
|
+
pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}=", docstring_for(c), :writer) #AttrPin.new(c)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
elsif c.type == :sclass and c.children[0].type == :self
|
230
|
+
process c, tree, :public, :class, fqn || '', stack
|
231
|
+
next
|
232
|
+
elsif c.type == :send and c.children[1] == :require
|
233
|
+
if c.children[2].kind_of?(AST::Node) and c.children[2].type == :str
|
234
|
+
@requires.push c.children[2].children[0].to_s
|
235
|
+
end
|
236
|
+
elsif c.type == :args
|
237
|
+
if @node_stack.first.type == :block
|
238
|
+
pi = 0
|
239
|
+
c.children.each do |u|
|
240
|
+
# @todo Fix this
|
241
|
+
# pins.push Solargraph::Pin::BlockParameter.new(self, u, fqn || '', @node_stack.clone, pi)
|
242
|
+
here = get_node_start_position(c)
|
243
|
+
blk = get_block_pin(here)
|
244
|
+
@locals.push Solargraph::Pin::BlockParameter.new(get_node_location(u), fqn || '', "#{u.children[0]}", docstring_for(c), blk)
|
245
|
+
blk.parameters.push @locals.push.last
|
246
|
+
pi += 1
|
247
|
+
end
|
248
|
+
else
|
249
|
+
c.children.each do |u|
|
250
|
+
here = get_node_start_position(c)
|
251
|
+
blk = get_block_pin(here)
|
252
|
+
@locals.push Solargraph::Pin::MethodParameter.new(get_node_location(u), fqn || '', "#{u.children[0]}", docstring_for(c), blk)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
elsif c.type == :block
|
256
|
+
@pins.push Solargraph::Pin::Block.new(get_node_location(c), fqn || '', '', docstring_for(c), resolve_node_signature(c.children[0]))
|
257
|
+
end
|
258
|
+
process c, tree, visibility, scope, fqn, stack
|
259
|
+
end
|
260
|
+
end
|
261
|
+
@node_stack.shift
|
262
|
+
end
|
263
|
+
stack.pop
|
264
|
+
end
|
265
|
+
|
266
|
+
# def path_for node
|
267
|
+
# path = namespace_for(node) || ''
|
268
|
+
# mp = (method_pins + attribute_pins).select{|p| p.node == node}.first
|
269
|
+
# unless mp.nil?
|
270
|
+
# path += (mp.scope == :instance ? '#' : '.') + mp.name
|
271
|
+
# end
|
272
|
+
# path
|
273
|
+
# end
|
274
|
+
|
275
|
+
def get_last_in_stack_not_begin stack
|
276
|
+
index = stack.length - 1
|
277
|
+
last = stack[index]
|
278
|
+
while !last.nil? and last.type == :begin
|
279
|
+
index -= 1
|
280
|
+
last = stack[index]
|
281
|
+
end
|
282
|
+
last
|
283
|
+
end
|
284
|
+
|
285
|
+
def get_block_pin position
|
286
|
+
@pins.select{|pin| [Pin::BLOCK, Pin::NAMESPACE, Pin::METHOD].include?(pin.kind) and pin.location.range.contain?(position)}.last
|
287
|
+
end
|
288
|
+
|
289
|
+
def get_named_path_pin position
|
290
|
+
@pins.select{|pin| [Pin::NAMESPACE, Pin::METHOD].include?(pin.kind) and pin.location.range.contain?(position)}.last
|
291
|
+
end
|
292
|
+
|
293
|
+
def get_namespace_pin position
|
294
|
+
@pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.location.range.contain?(position)}.last
|
295
|
+
end
|
296
|
+
|
297
|
+
# @return [YARD::Docstring]
|
298
|
+
def docstring_for node
|
299
|
+
return @docstring_hash[node.loc] if node.respond_to?(:loc)
|
300
|
+
nil
|
301
|
+
end
|
302
|
+
|
303
|
+
def get_node_start_position(node)
|
304
|
+
Position.new(node.loc.line - 1, node.loc.column)
|
305
|
+
end
|
306
|
+
|
307
|
+
def get_node_location(node)
|
308
|
+
if node.nil?
|
309
|
+
st = Position.new(0, 0)
|
310
|
+
en = Position.from_offset(@code, @code.length)
|
311
|
+
else
|
312
|
+
st = Position.new(node.loc.line - 1, node.loc.column)
|
313
|
+
en = Position.new(node.loc.last_line - 1, node.loc.last_column)
|
314
|
+
end
|
315
|
+
range = Range.new(st, en)
|
316
|
+
Location.new(filename, range)
|
317
|
+
end
|
318
|
+
|
319
|
+
def associate_comments node, comments
|
320
|
+
return nil if comments.nil?
|
321
|
+
comment_hash = Parser::Source::Comment.associate_locations(node, comments)
|
322
|
+
yard_hash = {}
|
323
|
+
comment_hash.each_pair { |k, v|
|
324
|
+
ctxt = ''
|
325
|
+
num = nil
|
326
|
+
started = false
|
327
|
+
v.each { |l|
|
328
|
+
# Trim the comment and minimum leading whitespace
|
329
|
+
p = l.text.gsub(/^#/, '')
|
330
|
+
if num.nil? and !p.strip.empty?
|
331
|
+
num = p.index(/[^ ]/)
|
332
|
+
started = true
|
333
|
+
elsif started and !p.strip.empty?
|
334
|
+
cur = p.index(/[^ ]/)
|
335
|
+
num = cur if cur < num
|
336
|
+
end
|
337
|
+
if started
|
338
|
+
ctxt += "#{p[num..-1]}\n"
|
339
|
+
end
|
340
|
+
}
|
341
|
+
parse = YARD::Docstring.parser.parse(ctxt)
|
342
|
+
unless parse.directives.empty?
|
343
|
+
@directives[k] ||= []
|
344
|
+
@directives[k].concat parse.directives
|
345
|
+
end
|
346
|
+
yard_hash[k] = parse.to_docstring
|
347
|
+
}
|
348
|
+
yard_hash
|
349
|
+
end
|
350
|
+
|
351
|
+
def namespace_for(node)
|
352
|
+
position = Source::Position.new(node.loc.line - 1, node.loc.column)
|
353
|
+
@pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.location.range.contain?(position)}.last
|
354
|
+
end
|
355
|
+
|
356
|
+
def process_directives
|
357
|
+
@directives.each_pair do |k, v|
|
358
|
+
v.each do |d|
|
359
|
+
ns = namespace_for(k.node)
|
360
|
+
docstring = YARD::Docstring.parser.parse(d.tag.text).to_docstring
|
361
|
+
if d.tag.tag_name == 'attribute'
|
362
|
+
t = (d.tag.types.nil? || d.tag.types.empty?) ? nil : d.tag.types.flatten.join('')
|
363
|
+
if t.nil? or t.include?('r')
|
364
|
+
# location, namespace, name, docstring, access
|
365
|
+
pins.push Solargraph::Pin::Attribute.new(get_node_location(k.node), namespace_for(k.node).path, d.tag.name, docstring, :reader)
|
366
|
+
end
|
367
|
+
if t.nil? or t.include?('w')
|
368
|
+
pins.push Solargraph::Pin::Attribute.new(get_node_location(k.node), namespace_for(k.node).path, "#{d.tag.name}=", docstring, :reader)
|
369
|
+
end
|
370
|
+
elsif d.tag.tag_name == 'method'
|
371
|
+
gen_src = Source.new("def #{d.tag.name};end", filename)
|
372
|
+
gen_pin = gen_src.pins.last # Method is last pin after root namespace
|
373
|
+
nsp = namespace_for(k.node)
|
374
|
+
next if nsp.nil? # @todo Add methods to global namespace?
|
375
|
+
@pins.push Solargraph::Pin::Method.new(get_node_location(k.node), nsp.path, gen_pin.name, docstring, :instance, :public, [])
|
376
|
+
elsif d.tag.tag_name == 'macro'
|
377
|
+
# @todo Handle various types of macros (attach, new, whatever)
|
378
|
+
# path = path_for(k.node)
|
379
|
+
here = get_node_start_position(k.node)
|
380
|
+
pin = @pins.select{|pin| [Pin::NAMESPACE, Pin::METHOD].include?(pin.kind) and pin.location.range.contain?(here)}.first
|
381
|
+
@path_macros[pin.path] = v
|
382
|
+
else
|
383
|
+
# STDERR.puts "Nothing to do for directive: #{d}"
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
def find_parent(stack, *types)
|
390
|
+
stack.reverse.each { |p|
|
391
|
+
return p if types.include?(p.type)
|
392
|
+
}
|
393
|
+
nil
|
394
|
+
end
|
395
|
+
|
396
|
+
def get_method_args node
|
397
|
+
return [] if node.nil?
|
398
|
+
list = nil
|
399
|
+
args = []
|
400
|
+
node.children.each { |c|
|
401
|
+
if c.kind_of?(AST::Node) and c.type == :args
|
402
|
+
list = c
|
403
|
+
break
|
404
|
+
end
|
405
|
+
}
|
406
|
+
return args if list.nil?
|
407
|
+
list.children.each { |c|
|
408
|
+
if c.type == :arg
|
409
|
+
args.push c.children[0].to_s
|
410
|
+
elsif c.type == :restarg
|
411
|
+
args.push "*#{c.children[0]}"
|
412
|
+
elsif c.type == :optarg
|
413
|
+
args.push "#{c.children[0]} = #{code_for(c.children[1])}"
|
414
|
+
elsif c.type == :kwarg
|
415
|
+
args.push "#{c.children[0]}:"
|
416
|
+
elsif c.type == :kwoptarg
|
417
|
+
args.push "#{c.children[0]}: #{code_for(c.children[1])}"
|
418
|
+
elsif c.type == :blockarg
|
419
|
+
args.push "&#{c.children[0]}"
|
420
|
+
end
|
421
|
+
}
|
422
|
+
args
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|