solargraph 0.18.3 → 0.19.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.
- 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
|