solargraph 0.17.4 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph.rb +16 -12
  3. data/lib/solargraph/api_map.rb +516 -588
  4. data/lib/solargraph/api_map/completion.rb +16 -0
  5. data/lib/solargraph/api_map/source_to_yard.rb +2 -2
  6. data/lib/solargraph/language_server.rb +12 -0
  7. data/lib/solargraph/language_server/completion_item_kinds.rb +31 -0
  8. data/lib/solargraph/language_server/error_codes.rb +16 -0
  9. data/lib/solargraph/language_server/host.rb +305 -0
  10. data/lib/solargraph/language_server/message.rb +70 -0
  11. data/lib/solargraph/language_server/message/base.rb +64 -0
  12. data/lib/solargraph/language_server/message/cancel_request.rb +11 -0
  13. data/lib/solargraph/language_server/message/client.rb +5 -0
  14. data/lib/solargraph/language_server/message/client/register_capability.rb +13 -0
  15. data/lib/solargraph/language_server/message/completion_item.rb +9 -0
  16. data/lib/solargraph/language_server/message/completion_item/resolve.rb +23 -0
  17. data/lib/solargraph/language_server/message/exit_notification.rb +12 -0
  18. data/lib/solargraph/language_server/message/extended.rb +15 -0
  19. data/lib/solargraph/language_server/message/extended/document.rb +18 -0
  20. data/lib/solargraph/language_server/message/extended/search.rb +18 -0
  21. data/lib/solargraph/language_server/message/initialize.rb +39 -0
  22. data/lib/solargraph/language_server/message/initialized.rb +10 -0
  23. data/lib/solargraph/language_server/message/method_not_found.rb +14 -0
  24. data/lib/solargraph/language_server/message/method_not_implemented.rb +12 -0
  25. data/lib/solargraph/language_server/message/shutdown.rb +11 -0
  26. data/lib/solargraph/language_server/message/text_document.rb +21 -0
  27. data/lib/solargraph/language_server/message/text_document/base.rb +17 -0
  28. data/lib/solargraph/language_server/message/text_document/completion.rb +69 -0
  29. data/lib/solargraph/language_server/message/text_document/definition.rb +38 -0
  30. data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -0
  31. data/lib/solargraph/language_server/message/text_document/did_close.rb +12 -0
  32. data/lib/solargraph/language_server/message/text_document/did_open.rb +13 -0
  33. data/lib/solargraph/language_server/message/text_document/did_save.rb +15 -0
  34. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +31 -0
  35. data/lib/solargraph/language_server/message/text_document/formatting.rb +36 -0
  36. data/lib/solargraph/language_server/message/text_document/hover.rb +19 -0
  37. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +29 -0
  38. data/lib/solargraph/language_server/message/text_document/signature_help.rb +23 -0
  39. data/lib/solargraph/language_server/message/workspace.rb +11 -0
  40. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +9 -0
  41. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +30 -0
  42. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +31 -0
  43. data/lib/solargraph/language_server/symbol_kinds.rb +32 -0
  44. data/lib/solargraph/language_server/transport.rb +7 -0
  45. data/lib/solargraph/language_server/transport/socket.rb +66 -0
  46. data/lib/solargraph/language_server/uri_helpers.rb +21 -0
  47. data/lib/solargraph/library.rb +225 -0
  48. data/lib/solargraph/live_map.rb +1 -1
  49. data/lib/solargraph/page.rb +61 -0
  50. data/lib/solargraph/pin.rb +7 -0
  51. data/lib/solargraph/pin/attribute.rb +9 -0
  52. data/lib/solargraph/pin/base.rb +76 -6
  53. data/lib/solargraph/pin/base_variable.rb +29 -7
  54. data/lib/solargraph/pin/block_parameter.rb +53 -0
  55. data/lib/solargraph/pin/constant.rb +6 -2
  56. data/lib/solargraph/pin/conversions.rb +65 -0
  57. data/lib/solargraph/pin/directed/attribute.rb +4 -0
  58. data/lib/solargraph/pin/directed/method.rb +6 -1
  59. data/lib/solargraph/pin/helper.rb +35 -0
  60. data/lib/solargraph/pin/keyword.rb +22 -0
  61. data/lib/solargraph/pin/local_variable.rb +0 -1
  62. data/lib/solargraph/pin/method.rb +55 -2
  63. data/lib/solargraph/pin/method_parameter.rb +19 -0
  64. data/lib/solargraph/pin/namespace.rb +7 -2
  65. data/lib/solargraph/pin/parameter.rb +23 -0
  66. data/lib/solargraph/pin/plugin/method.rb +3 -2
  67. data/lib/solargraph/pin/yard_object.rb +101 -0
  68. data/lib/solargraph/server.rb +82 -135
  69. data/lib/solargraph/shell.rb +20 -1
  70. data/lib/solargraph/source.rb +709 -0
  71. data/lib/solargraph/source/flawed_builder.rb +10 -0
  72. data/lib/solargraph/source/fragment.rb +319 -0
  73. data/lib/solargraph/source/position.rb +26 -0
  74. data/lib/solargraph/source/range.rb +39 -0
  75. data/lib/solargraph/suggestion.rb +29 -4
  76. data/lib/solargraph/version.rb +1 -1
  77. data/lib/solargraph/workspace.rb +105 -0
  78. data/lib/solargraph/{api_map → workspace}/config.rb +1 -1
  79. data/lib/solargraph/yard_map.rb +59 -37
  80. metadata +168 -5
  81. data/lib/solargraph/api_map/source.rb +0 -470
  82. data/lib/solargraph/code_map.rb +0 -868
@@ -1,470 +0,0 @@
1
- require 'parser/current'
2
-
3
- module Solargraph
4
- class ApiMap
5
- class Source
6
- class FlawedBuilder < Parser::Builders::Default
7
- def string_value(token)
8
- value(token)
9
- end
10
- end
11
-
12
- # @return [String]
13
- attr_reader :code
14
-
15
- # @return [Parser::AST::Node]
16
- attr_reader :node
17
-
18
- # @return [Array]
19
- attr_reader :comments
20
-
21
- # @return [String]
22
- attr_reader :filename
23
-
24
- attr_reader :mtime
25
-
26
- # @return [Array<Integer>]
27
- attr_reader :stubbed_lines
28
-
29
- attr_reader :directives
30
-
31
- attr_reader :path_macros
32
-
33
- include NodeMethods
34
-
35
- def initialize code, node, comments, filename, stubbed_lines = []
36
- @code = code
37
- root = AST::Node.new(:source, [filename])
38
- root = root.append node
39
- @node = root
40
- @comments = comments
41
- @directives = {}
42
- @path_macros = {}
43
- @docstring_hash = associate_comments(node, comments) || {}
44
- @filename = filename
45
- @mtime = (!filename.nil? and File.exist?(filename) ? File.mtime(filename) : nil)
46
- @namespace_nodes = {}
47
- @all_nodes = []
48
- @node_stack = []
49
- @node_tree = {}
50
- @stubbed_lines = stubbed_lines
51
- inner_map_node @node
52
- @directives.each_pair do |k, v|
53
- v.each do |d|
54
- ns = namespace_for(k.node)
55
- docstring = YARD::Docstring.parser.parse(d.tag.text).to_docstring
56
- if d.tag.tag_name == 'attribute'
57
- t = (d.tag.types.nil? || d.tag.types.empty?) ? nil : d.tag.types.flatten.join('')
58
- if t.nil? or t.include?('r')
59
- attribute_pins.push Solargraph::Pin::Directed::Attribute.new(self, k.node, ns, :reader, docstring, d.tag.name)
60
- end
61
- if t.nil? or t.include?('w')
62
- attribute_pins.push Solargraph::Pin::Directed::Attribute.new(self, k.node, ns, :writer, docstring, "#{d.tag.name}=")
63
- end
64
- elsif d.tag.tag_name == 'method'
65
- gen_src = Source.virtual("def #{d.tag.name};end", filename)
66
- gen_pin = gen_src.method_pins.first
67
- method_pins.push Solargraph::Pin::Directed::Method.new(gen_src, gen_pin.node, ns, :instance, :public, docstring, gen_pin.name)
68
- elsif d.tag.tag_name == 'macro'
69
- # @todo Handle various types of macros (attach, new, whatever)
70
- path = path_for(k.node)
71
- @path_macros[path] = v
72
- else
73
- STDERR.puts "Nothing to do for directive: #{d}"
74
- end
75
- end
76
- end
77
- end
78
-
79
- def macro path
80
- @path_macros[path]
81
- end
82
-
83
- def namespaces
84
- @namespace_nodes.keys
85
- end
86
-
87
- def namespace_nodes
88
- @namespace_nodes
89
- end
90
-
91
- def namespace_includes
92
- @namespace_includes ||= {}
93
- end
94
-
95
- def namespace_extends
96
- @namespaces_extends ||= {}
97
- end
98
-
99
- def superclasses
100
- @superclasses ||= {}
101
- end
102
-
103
- # @return [Array<Solargraph::Pin::Method>]
104
- def method_pins
105
- @method_pins ||= []
106
- end
107
-
108
- # @return [Array<Solargraph::Pin::Attribute>]
109
- def attribute_pins
110
- @attribute_pins ||= []
111
- end
112
-
113
- # @return [Array<Solargraph::Pin::InstanceVariable>]
114
- def instance_variable_pins
115
- @instance_variable_pins ||= []
116
- end
117
-
118
- # @return [Array<Solargraph::Pin::ClassVariable>]
119
- def class_variable_pins
120
- @class_variable_pins ||= []
121
- end
122
-
123
- # @return [Array<Solargraph::Pin::LocalVariable>]
124
- def local_variable_pins
125
- @local_variable_pins ||= []
126
- end
127
-
128
- # @return [Array<Solargraph::Pin::GlobalVariable>]
129
- def global_variable_pins
130
- @global_variable_pins ||= []
131
- end
132
-
133
- # @return [Array<Solargraph::Pin::Constant>]
134
- def constant_pins
135
- @constant_pins ||= []
136
- end
137
-
138
- # @return [Array<Solargraph::Pin::Symbol>]
139
- def symbol_pins
140
- @symbol_pins ||= []
141
- end
142
-
143
- # @return [Array<Solargraph::Pin::Namespace>]
144
- def namespace_pins
145
- @namespace_pins ||= []
146
- end
147
-
148
- # @return [Array<String>]
149
- def required
150
- @required ||= []
151
- end
152
-
153
- # @return [YARD::Docstring]
154
- def docstring_for node
155
- return @docstring_hash[node.loc] if node.respond_to?(:loc)
156
- nil
157
- end
158
-
159
- # @return [String]
160
- def code_for node
161
- b = node.location.expression.begin.begin_pos
162
- e = node.location.expression.end.end_pos
163
- frag = code[b..e-1].to_s
164
- frag.strip.gsub(/,$/, '')
165
- end
166
-
167
- def tree_for node
168
- @node_tree[node] || []
169
- end
170
-
171
- # @return [String]
172
- def namespace_for node
173
- parts = []
174
- ([node] + (@node_tree[node] || [])).each do |n|
175
- if n.type == :class or n.type == :module
176
- parts.unshift unpack_name(n.children[0])
177
- end
178
- end
179
- parts.join('::')
180
- end
181
-
182
- def path_for node
183
- path = namespace_for(node) || ''
184
- mp = (method_pins + attribute_pins).select{|p| p.node == node}.first
185
- unless mp.nil?
186
- path += (mp.scope == :instance ? '#' : '.') + mp.name
187
- end
188
- path
189
- end
190
-
191
- def include? node
192
- @all_nodes.include? node
193
- end
194
-
195
- private
196
-
197
- def associate_comments node, comments
198
- return nil if comments.nil?
199
- comment_hash = Parser::Source::Comment.associate_locations(node, comments)
200
- yard_hash = {}
201
- comment_hash.each_pair { |k, v|
202
- ctxt = ''
203
- num = nil
204
- started = false
205
- v.each { |l|
206
- # Trim the comment and minimum leading whitespace
207
- p = l.text.gsub(/^#/, '')
208
- if num.nil? and !p.strip.empty?
209
- num = p.index(/[^ ]/)
210
- started = true
211
- elsif started and !p.strip.empty?
212
- cur = p.index(/[^ ]/)
213
- num = cur if cur < num
214
- end
215
- if started
216
- ctxt += "#{p[num..-1]}\n"
217
- end
218
- }
219
- parse = YARD::Docstring.parser.parse(ctxt)
220
- unless parse.directives.empty?
221
- @directives[k] ||= []
222
- @directives[k].concat parse.directives
223
- end
224
- yard_hash[k] = parse.to_docstring
225
- }
226
- yard_hash
227
- end
228
-
229
- def inner_map_node node, tree = [], visibility = :public, scope = :instance, fqn = nil, stack = []
230
- stack.push node
231
- source = self
232
- if node.kind_of?(AST::Node)
233
- @all_nodes.push node
234
- if node.type == :str or node.type == :dstr
235
- stack.pop
236
- return
237
- end
238
- @node_stack.unshift node
239
- if node.type == :class or node.type == :module
240
- visibility = :public
241
- 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
242
- tree = pack_name(node.children[0])
243
- else
244
- tree = tree + pack_name(node.children[0])
245
- end
246
- fqn = tree.join('::')
247
- @namespace_nodes[fqn] ||= []
248
- @namespace_nodes[fqn].push node
249
- namespace_pins.push Solargraph::Pin::Namespace.new(self, node, tree[0..-2].join('::') || '', :public)
250
- if node.type == :class and !node.children[1].nil?
251
- sc = unpack_name(node.children[1])
252
- superclasses[fqn] = sc
253
- end
254
- end
255
- file = source.filename
256
- node.children.each do |c|
257
- if c.kind_of?(AST::Node)
258
- @node_tree[c] = @node_stack.clone
259
- if c.type == :ivasgn
260
- par = find_parent(stack, :class, :module, :def, :defs)
261
- local_scope = ( (par.kind_of?(AST::Node) and par.type == :def) ? :instance : :class )
262
- if c.children[1].nil?
263
- ora = find_parent(stack, :or_asgn)
264
- unless ora.nil?
265
- u = c.updated(:ivasgn, c.children + ora.children[1..-1], nil)
266
- @node_tree[u] = @node_stack.clone
267
- @docstring_hash[u.loc] = docstring_for(ora)
268
- instance_variable_pins.push Solargraph::Pin::InstanceVariable.new(self, u, fqn || '', local_scope)
269
- end
270
- else
271
- instance_variable_pins.push Solargraph::Pin::InstanceVariable.new(self, c, fqn || '', local_scope)
272
- end
273
- elsif c.type == :cvasgn
274
- if c.children[1].nil?
275
- ora = find_parent(stack, :or_asgn)
276
- unless ora.nil?
277
- u = c.updated(:cvasgn, c.children + ora.children[1..-1], nil)
278
- @node_tree[u] = @node_stack.clone
279
- @docstring_hash[u.loc] = docstring_for(ora)
280
- class_variable_pins.push Solargraph::Pin::ClassVariable.new(self, u, fqn || '')
281
- end
282
- else
283
- class_variable_pins.push Solargraph::Pin::ClassVariable.new(self, c, fqn || '')
284
- end
285
- elsif c.type == :lvasgn
286
- if c.children[1].nil?
287
- ora = find_parent(stack, :or_asgn)
288
- unless ora.nil?
289
- u = c.updated(:lvasgn, c.children + ora.children[1..-1], nil)
290
- @node_tree[u] = @node_stack.clone
291
- @docstring_hash[u.loc] = docstring_for(ora)
292
- local_variable_pins.push Solargraph::Pin::LocalVariable.new(self, u, fqn || '', @node_stack.clone)
293
- end
294
- else
295
- @node_tree[c] = @node_stack.clone
296
- local_variable_pins.push Solargraph::Pin::LocalVariable.new(self, c, fqn || '', @node_stack.clone)
297
- end
298
- elsif c.type == :gvasgn
299
- global_variable_pins.push Solargraph::Pin::GlobalVariable.new(self, c, fqn || '')
300
- elsif c.type == :sym
301
- symbol_pins.push Solargraph::Pin::Symbol.new(self, c, fqn)
302
- elsif c.type == :casgn
303
- constant_pins.push Solargraph::Pin::Constant.new(self, c, fqn, :public)
304
- else
305
- if c.kind_of?(AST::Node)
306
- if c.type == :def and c.children[0].to_s[0].match(/[a-z]/i)
307
- method_pins.push Solargraph::Pin::Method.new(source, c, fqn || '', scope, visibility)
308
- elsif c.type == :defs
309
- s_visi = visibility
310
- s_visi = :public if scope != :class
311
- method_pins.push Solargraph::Pin::Method.new(source, c, fqn || '', :class, s_visi)
312
- inner_map_node c, tree, scope, :class, fqn, stack
313
- next
314
- elsif c.type == :send and [:public, :protected, :private].include?(c.children[1])
315
- visibility = c.children[1]
316
- elsif c.type == :send and [:private_class_method].include?(c.children[1]) and c.children[2].kind_of?(AST::Node)
317
- if c.children[2].type == :sym or c.children[2].type == :str
318
- ref = method_pins.select{|p| p.name == c.children[2].children[0].to_s}.first
319
- unless ref.nil?
320
- source.method_pins.delete ref
321
- source.method_pins.push Solargraph::Pin::Method.new(ref.source, ref.node, ref.namespace, ref.scope, :private)
322
- end
323
- else
324
- inner_map_node c, tree, :private, :class, fqn, stack
325
- next
326
- end
327
- elsif c.type == :send and [:private_constant].include?(c.children[1]) and c.children[2].kind_of?(AST::Node)
328
- if c.children[2].type == :sym or c.children[2].type == :str
329
- cn = c.children[2].children[0].to_s
330
- ref = constant_pins.select{|p| p.name == cn}.first
331
- if ref.nil?
332
- ref = namespace_pins.select{|p| p.name == cn}.first
333
- unless ref.nil?
334
- source.namespace_pins.delete ref
335
- source.namespace_pins.push Solargraph::Pin::Namespace.new(ref.source, ref.node, ref.namespace, :private)
336
- end
337
- else
338
- source.constant_pins.delete ref
339
- source.constant_pins.push Solargraph::Pin::Constant.new(ref.source, ref.node, ref.namespace, :private)
340
- end
341
- end
342
- next
343
- elsif c.type == :send and c.children[1] == :include
344
- namespace_includes[fqn] ||= []
345
- c.children[2..-1].each do |i|
346
- namespace_includes[fqn].push unpack_name(i)
347
- end
348
- elsif c.type == :send and c.children[1] == :extend
349
- namespace_extends[fqn] ||= []
350
- c.children[2..-1].each do |i|
351
- namespace_extends[fqn].push unpack_name(i)
352
- end
353
- elsif c.type == :send and [:attr_reader, :attr_writer, :attr_accessor].include?(c.children[1])
354
- c.children[2..-1].each do |a|
355
- if c.children[1] == :attr_reader or c.children[1] == :attr_accessor
356
- attribute_pins.push Solargraph::Pin::Attribute.new(self, a, fqn || '', :reader, docstring_for(c)) #AttrPin.new(c)
357
- end
358
- if c.children[1] == :attr_writer or c.children[1] == :attr_accessor
359
- attribute_pins.push Solargraph::Pin::Attribute.new(self, a, fqn || '', :writer, docstring_for(c)) #AttrPin.new(c)
360
- end
361
- end
362
- elsif c.type == :sclass and c.children[0].type == :self
363
- inner_map_node c, tree, :public, :class, fqn || '', stack
364
- next
365
- end
366
- end
367
- if c.type == :send and c.children[1] == :require
368
- if c.children[2].kind_of?(AST::Node) and c.children[2].type == :str
369
- required.push c.children[2].children[0].to_s
370
- end
371
- end
372
- end
373
- inner_map_node c, tree, visibility, scope, fqn, stack
374
- end
375
- end
376
- @node_stack.shift
377
- end
378
- stack.pop
379
- end
380
-
381
- def find_parent(stack, *types)
382
- stack.reverse.each { |p|
383
- return p if types.include?(p.type)
384
- }
385
- nil
386
- end
387
-
388
- class << self
389
- # @return [Solargraph::ApiMap::Source]
390
- def load filename
391
- Source.virtual(File.read(filename), filename)
392
- end
393
-
394
- # @return [Solargraph::ApiMap::Source]
395
- def virtual code, filename = nil
396
- node, comments = Source.parse(code, filename)
397
- Source.new(code, node, comments, filename)
398
- end
399
-
400
- def get_position_at(code, offset)
401
- cursor = 0
402
- line = 0
403
- col = nil
404
- code.each_line do |l|
405
- if cursor + l.length >= offset
406
- col = offset - cursor
407
- break
408
- end
409
- cursor += l.length
410
- line += 1
411
- end
412
- raise "Invalid offset" if col.nil?
413
- [line, col]
414
- end
415
-
416
- def parse code, filename = nil
417
- parser = Parser::CurrentRuby.new(FlawedBuilder.new)
418
- parser.diagnostics.all_errors_are_fatal = true
419
- parser.diagnostics.ignore_warnings = true
420
- buffer = Parser::Source::Buffer.new(filename, 1)
421
- buffer.source = code
422
- parser.parse_with_comments(buffer)
423
- end
424
-
425
- def fix code, filename = nil, offset = nil
426
- tries = 0
427
- code.gsub!(/\r/, '')
428
- offset = CodeMap.get_offset(code, offset[0], offset[1]) if offset.kind_of?(Array)
429
- pos = nil
430
- pos = get_position_at(code, offset) unless offset.nil?
431
- stubs = []
432
- fixed_position = false
433
- tmp = code
434
- begin
435
- node, comments = parse(tmp, filename)
436
- Source.new(code, node, comments, filename, stubs)
437
- rescue Parser::SyntaxError => e
438
- if tries < 10
439
- tries += 1
440
- # Stub periods before the offset to retain the expected node tree
441
- if !offset.nil? and tmp[offset-1] == '.'
442
- tmp = tmp[0, offset-1] + ';' + tmp[offset..-1]
443
- elsif !fixed_position and !offset.nil?
444
- fixed_position = true
445
- beg = beginning_of_line_from(tmp, offset)
446
- tmp = "#{tmp[0, beg]}##{tmp[beg+1..-1]}"
447
- stubs.push(pos[0])
448
- elsif e.message.include?('token $end')
449
- tmp += "\nend"
450
- elsif e.message.include?("unexpected `@'")
451
- tmp = tmp[0, e.diagnostic.location.begin_pos] + '_' + tmp[e.diagnostic.location.begin_pos+1..-1]
452
- end
453
- retry
454
- end
455
- STDERR.puts "Unable to parse file #{filename.nil? ? 'undefined' : filename}: #{e.message}"
456
- node, comments = parse(code.gsub(/[^\s]/, ' '), filename)
457
- Source.new(code, node, comments, filename)
458
- end
459
- end
460
-
461
- def beginning_of_line_from str, i
462
- while i > 0 and str[i-1] != "\n"
463
- i -= 1
464
- end
465
- i
466
- end
467
- end
468
- end
469
- end
470
- end