solargraph 0.17.4 → 0.18.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 (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