solargraph 0.51.2 → 0.53.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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/plugins.yml +40 -0
  3. data/.github/workflows/rspec.yml +1 -3
  4. data/.github/workflows/typecheck.yml +34 -0
  5. data/.yardopts +2 -2
  6. data/CHANGELOG.md +55 -5
  7. data/README.md +13 -16
  8. data/SPONSORS.md +1 -7
  9. data/lib/solargraph/api_map/cache.rb +60 -20
  10. data/lib/solargraph/api_map/store.rb +47 -11
  11. data/lib/solargraph/api_map.rb +161 -95
  12. data/lib/solargraph/bench.rb +2 -2
  13. data/lib/solargraph/cache.rb +29 -5
  14. data/lib/solargraph/complex_type/type_methods.rb +54 -9
  15. data/lib/solargraph/complex_type/unique_type.rb +155 -58
  16. data/lib/solargraph/complex_type.rb +73 -16
  17. data/lib/solargraph/convention.rb +0 -1
  18. data/lib/solargraph/converters/dd.rb +5 -0
  19. data/lib/solargraph/converters/dl.rb +3 -0
  20. data/lib/solargraph/converters/dt.rb +3 -0
  21. data/lib/solargraph/diagnostics/rubocop.rb +8 -7
  22. data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -0
  23. data/lib/solargraph/diagnostics/type_check.rb +1 -0
  24. data/lib/solargraph/diagnostics.rb +2 -2
  25. data/lib/solargraph/doc_map.rb +146 -0
  26. data/lib/solargraph/gem_pins.rb +64 -0
  27. data/lib/solargraph/language_server/host/cataloger.rb +1 -0
  28. data/lib/solargraph/language_server/host/diagnoser.rb +2 -2
  29. data/lib/solargraph/language_server/host/dispatch.rb +10 -4
  30. data/lib/solargraph/language_server/host/message_worker.rb +4 -0
  31. data/lib/solargraph/language_server/host/sources.rb +7 -4
  32. data/lib/solargraph/language_server/host.rb +15 -6
  33. data/lib/solargraph/language_server/message/completion_item/resolve.rb +3 -1
  34. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +13 -1
  35. data/lib/solargraph/language_server/message/initialize.rb +5 -2
  36. data/lib/solargraph/language_server/message/text_document/hover.rb +2 -0
  37. data/lib/solargraph/language_server/message/text_document.rb +0 -1
  38. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +5 -0
  39. data/lib/solargraph/language_server/transport/adapter.rb +16 -1
  40. data/lib/solargraph/language_server/transport/data_reader.rb +2 -0
  41. data/lib/solargraph/library.rb +70 -16
  42. data/lib/solargraph/location.rb +1 -0
  43. data/lib/solargraph/parser/comment_ripper.rb +4 -0
  44. data/lib/solargraph/parser/node_methods.rb +47 -7
  45. data/lib/solargraph/parser/node_processor/base.rb +9 -0
  46. data/lib/solargraph/parser/{legacy → parser_gem}/class_methods.rb +31 -5
  47. data/lib/solargraph/parser/{legacy → parser_gem}/flawed_builder.rb +3 -1
  48. data/lib/solargraph/parser/{legacy → parser_gem}/node_chainer.rb +49 -36
  49. data/lib/solargraph/parser/parser_gem/node_methods.rb +499 -0
  50. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/alias_node.rb +1 -1
  51. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/args_node.rb +4 -1
  52. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/begin_node.rb +1 -1
  53. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/block_node.rb +3 -2
  54. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/casgn_node.rb +2 -2
  55. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/cvasgn_node.rb +1 -1
  56. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/def_node.rb +1 -1
  57. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/defs_node.rb +2 -2
  58. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/gvasgn_node.rb +1 -1
  59. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/ivasgn_node.rb +2 -2
  60. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/lvasgn_node.rb +2 -2
  61. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/namespace_node.rb +2 -2
  62. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/orasgn_node.rb +1 -1
  63. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/resbody_node.rb +3 -3
  64. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/sclass_node.rb +1 -1
  65. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/send_node.rb +2 -2
  66. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/sym_node.rb +1 -1
  67. data/lib/solargraph/parser/parser_gem/node_processors.rb +54 -0
  68. data/lib/solargraph/parser/parser_gem.rb +12 -0
  69. data/lib/solargraph/parser/region.rb +1 -1
  70. data/lib/solargraph/parser/snippet.rb +2 -0
  71. data/lib/solargraph/parser.rb +8 -9
  72. data/lib/solargraph/pin/base.rb +64 -9
  73. data/lib/solargraph/pin/base_variable.rb +6 -2
  74. data/lib/solargraph/pin/block.rb +13 -8
  75. data/lib/solargraph/pin/closure.rb +17 -2
  76. data/lib/solargraph/pin/common.rb +7 -3
  77. data/lib/solargraph/pin/conversions.rb +33 -3
  78. data/lib/solargraph/pin/delegated_method.rb +1 -1
  79. data/lib/solargraph/pin/documenting.rb +25 -34
  80. data/lib/solargraph/pin/instance_variable.rb +4 -0
  81. data/lib/solargraph/pin/local_variable.rb +13 -1
  82. data/lib/solargraph/pin/method.rb +169 -18
  83. data/lib/solargraph/pin/namespace.rb +18 -5
  84. data/lib/solargraph/pin/parameter.rb +44 -14
  85. data/lib/solargraph/pin/reference/override.rb +2 -2
  86. data/lib/solargraph/pin/reference.rb +8 -0
  87. data/lib/solargraph/pin/search.rb +3 -3
  88. data/lib/solargraph/pin/signature.rb +123 -3
  89. data/lib/solargraph/pin.rb +0 -1
  90. data/lib/solargraph/range.rb +2 -2
  91. data/lib/solargraph/rbs_map/conversions.rb +287 -45
  92. data/lib/solargraph/rbs_map/core_fills.rb +6 -29
  93. data/lib/solargraph/rbs_map/core_map.rb +2 -1
  94. data/lib/solargraph/rbs_map/core_signs.rb +2 -0
  95. data/lib/solargraph/rbs_map/stdlib_map.rb +2 -8
  96. data/lib/solargraph/rbs_map.rb +20 -11
  97. data/lib/solargraph/shell.rb +62 -59
  98. data/lib/solargraph/source/chain/array.rb +32 -0
  99. data/lib/solargraph/source/chain/block_symbol.rb +13 -0
  100. data/lib/solargraph/source/chain/call.rb +99 -46
  101. data/lib/solargraph/source/chain/constant.rb +15 -1
  102. data/lib/solargraph/source/chain/if.rb +23 -0
  103. data/lib/solargraph/source/chain/link.rb +8 -2
  104. data/lib/solargraph/source/chain/or.rb +1 -1
  105. data/lib/solargraph/source/chain/z_super.rb +3 -3
  106. data/lib/solargraph/source/chain.rb +29 -14
  107. data/lib/solargraph/source/change.rb +3 -0
  108. data/lib/solargraph/source/cursor.rb +2 -0
  109. data/lib/solargraph/source/source_chainer.rb +8 -5
  110. data/lib/solargraph/source.rb +18 -19
  111. data/lib/solargraph/source_map/clip.rb +11 -23
  112. data/lib/solargraph/source_map/mapper.rb +12 -1
  113. data/lib/solargraph/source_map.rb +15 -5
  114. data/lib/solargraph/type_checker/checks.rb +10 -2
  115. data/lib/solargraph/type_checker.rb +92 -26
  116. data/lib/solargraph/version.rb +1 -1
  117. data/lib/solargraph/workspace/config.rb +8 -6
  118. data/lib/solargraph/workspace.rb +3 -2
  119. data/lib/solargraph/yard_map/cache.rb +6 -0
  120. data/lib/solargraph/yard_map/helpers.rb +1 -1
  121. data/lib/solargraph/yard_map/mapper/to_method.rb +11 -1
  122. data/lib/solargraph/yard_map/mapper.rb +1 -1
  123. data/lib/solargraph/yard_map/to_method.rb +11 -4
  124. data/lib/solargraph/yard_map.rb +1 -292
  125. data/lib/solargraph/yard_tags.rb +20 -0
  126. data/lib/solargraph/yardoc.rb +52 -0
  127. data/lib/solargraph.rb +6 -4
  128. data/solargraph.gemspec +3 -2
  129. metadata +51 -58
  130. data/lib/solargraph/api_map/bundler_methods.rb +0 -22
  131. data/lib/solargraph/documentor.rb +0 -76
  132. data/lib/solargraph/parser/legacy/node_methods.rb +0 -325
  133. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +0 -23
  134. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +0 -15
  135. data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +0 -18
  136. data/lib/solargraph/parser/legacy/node_processors.rb +0 -55
  137. data/lib/solargraph/parser/legacy.rb +0 -12
  138. data/lib/solargraph/parser/rubyvm/class_methods.rb +0 -153
  139. data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -160
  140. data/lib/solargraph/parser/rubyvm/node_methods.rb +0 -317
  141. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +0 -85
  142. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +0 -42
  143. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +0 -33
  144. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +0 -23
  145. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +0 -75
  146. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +0 -68
  147. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +0 -23
  148. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +0 -38
  149. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +0 -39
  150. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +0 -20
  151. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +0 -27
  152. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +0 -39
  153. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +0 -26
  154. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +0 -15
  155. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +0 -51
  156. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +0 -32
  157. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +0 -15
  158. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +0 -279
  159. data/lib/solargraph/parser/rubyvm/node_processors.rb +0 -64
  160. data/lib/solargraph/parser/rubyvm/node_wrapper.rb +0 -47
  161. data/lib/solargraph/parser/rubyvm.rb +0 -40
  162. data/lib/yard-solargraph.rb +0 -33
@@ -159,7 +159,7 @@ module Solargraph
159
159
  # @param filename [String] The file to analyze
160
160
  # @param line [Integer] The zero-based line number
161
161
  # @param column [Integer] The zero-based column number
162
- # @return [SourceMap::Completion]
162
+ # @return [SourceMap::Completion, nil]
163
163
  # @todo Take a Location instead of filename/line/column
164
164
  def completions_at filename, line, column
165
165
  position = Position.new(line, column)
@@ -175,7 +175,7 @@ module Solargraph
175
175
  # @param filename [String] The file to analyze
176
176
  # @param line [Integer] The zero-based line number
177
177
  # @param column [Integer] The zero-based column number
178
- # @return [Array<Solargraph::Pin::Base>]
178
+ # @return [Array<Solargraph::Pin::Base>, nil]
179
179
  # @todo Take filename/position instead of filename/line/column
180
180
  def definitions_at filename, line, column
181
181
  position = Position.new(line, column)
@@ -205,7 +205,7 @@ module Solargraph
205
205
  # @param filename [String] The file to analyze
206
206
  # @param line [Integer] The zero-based line number
207
207
  # @param column [Integer] The zero-based column number
208
- # @return [Array<Solargraph::Pin::Base>]
208
+ # @return [Array<Solargraph::Pin::Base>, nil]
209
209
  # @todo Take filename/position instead of filename/line/column
210
210
  def type_definitions_at filename, line, column
211
211
  position = Position.new(line, column)
@@ -283,10 +283,16 @@ module Solargraph
283
283
  return if map.nil?
284
284
  pin = map.requires.select { |p| p.location.range.contain?(location.range.start) }.first
285
285
  return nil if pin.nil?
286
+ # @param full [String]
287
+ return_if_match = proc do |full|
288
+ if source_map_hash.key?(full)
289
+ return Location.new(full, Solargraph::Range.from_to(0, 0, 0, 0))
290
+ end
291
+ end
286
292
  workspace.require_paths.each do |path|
287
- full = Pathname.new(path).join("#{pin.name}.rb").to_s
288
- next unless source_map_hash.key?(full)
289
- return Location.new(full, Solargraph::Range.from_to(0, 0, 0, 0))
293
+ full = File.join path, pin.name
294
+ return_if_match.(full)
295
+ return_if_match.(full << ".rb")
290
296
  end
291
297
  nil
292
298
  rescue FileNotFoundError
@@ -296,13 +302,13 @@ module Solargraph
296
302
  # Get an array of pins that match a path.
297
303
  #
298
304
  # @param path [String]
299
- # @return [Array<Solargraph::Pin::Base>]
305
+ # @return [Enumerable<Solargraph::Pin::Base>]
300
306
  def get_path_pins path
301
307
  api_map.get_path_suggestions(path)
302
308
  end
303
309
 
304
310
  # @param query [String]
305
- # @return [Array<YARD::CodeObjects::Base>]
311
+ # @return [Enumerable<YARD::CodeObjects::Base>]
306
312
  def document query
307
313
  api_map.document query
308
314
  end
@@ -334,11 +340,12 @@ module Solargraph
334
340
  end
335
341
 
336
342
  # @param path [String]
337
- # @return [Array<Solargraph::Pin::Base>]
343
+ # @return [Enumerable<Solargraph::Pin::Base>]
338
344
  def path_pins path
339
345
  api_map.get_path_suggestions(path)
340
346
  end
341
347
 
348
+ # @return [Array<SourceMap>]
342
349
  def source_maps
343
350
  source_map_hash.values
344
351
  end
@@ -395,14 +402,19 @@ module Solargraph
395
402
  end
396
403
  end
397
404
 
405
+ # @return [void]
398
406
  private def catalog_inlock
399
- return if synchronized?
400
- logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
401
- api_map.catalog bench
402
- @synchronized = true
403
- logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)" if logger.info?
407
+ return if synchronized?
408
+
409
+ logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
410
+ api_map.catalog bench
411
+ @synchronized = true
412
+ logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)"
413
+ logger.info "#{api_map.uncached_gemspecs.length} uncached gemspecs"
414
+ cache_next_gemspec
404
415
  end
405
416
 
417
+ # @return [Bench]
406
418
  def bench
407
419
  Bench.new(
408
420
  source_maps: source_map_hash.values,
@@ -447,6 +459,7 @@ module Solargraph
447
459
  result
448
460
  end
449
461
 
462
+ # @return [Hash{String => SourceMap}]
450
463
  def source_map_hash
451
464
  @source_map_hash ||= {}
452
465
  end
@@ -455,6 +468,7 @@ module Solargraph
455
468
  (workspace.filenames - source_map_hash.keys).empty?
456
469
  end
457
470
 
471
+ # @return [SourceMap, Boolean]
458
472
  def next_map
459
473
  return false if mapped?
460
474
  mutex.synchronize do
@@ -471,6 +485,7 @@ module Solargraph
471
485
  end
472
486
  end
473
487
 
488
+ # @return [self]
474
489
  def map!
475
490
  workspace.sources.each do |src|
476
491
  source_map_hash[src.filename] = Solargraph::SourceMap.map(src)
@@ -479,28 +494,34 @@ module Solargraph
479
494
  self
480
495
  end
481
496
 
497
+ # @return [Array<Solargraph::Pin::Base>]
482
498
  def pins
483
499
  @pins ||= []
484
500
  end
485
501
 
502
+ # @return [Set<String>]
486
503
  def external_requires
487
504
  @external_requires ||= source_map_external_require_hash.values.flatten.to_set
488
505
  end
489
506
 
490
507
  private
491
508
 
509
+ # @return [Hash{String => Set<String>}]
492
510
  def source_map_external_require_hash
493
511
  @source_map_external_require_hash ||= {}
494
512
  end
495
513
 
496
514
  # @param source_map [SourceMap]
515
+ # @return [void]
497
516
  def find_external_requires source_map
498
517
  new_set = source_map.requires.map(&:name).to_set
499
518
  # return if new_set == source_map_external_require_hash[source_map.filename]
519
+ _filenames = nil
520
+ filenames = ->{ _filenames ||= workspace.filenames.to_set }
500
521
  source_map_external_require_hash[source_map.filename] = new_set.reject do |path|
501
522
  workspace.require_paths.any? do |base|
502
- full = Pathname.new(base).join("#{path}.rb").to_s
503
- workspace.filenames.include?(full)
523
+ full = File.join(base, path)
524
+ filenames[].include?(full) or filenames[].include?(full << ".rb")
504
525
  end
505
526
  end
506
527
  @external_requires = nil
@@ -530,6 +551,9 @@ module Solargraph
530
551
  workspace.source(filename)
531
552
  end
532
553
 
554
+ # @param filename [String]
555
+ # @param error [FileNotFoundError]
556
+ # @return [nil]
533
557
  def handle_file_not_found filename, error
534
558
  if workspace.source(filename)
535
559
  Solargraph.logger.debug "#{filename} is not cataloged in the ApiMap"
@@ -539,6 +563,8 @@ module Solargraph
539
563
  end
540
564
  end
541
565
 
566
+ # @param source [Source]
567
+ # @return [void]
542
568
  def maybe_map source
543
569
  return unless source
544
570
  return unless @current == source || workspace.has_file?(source.filename)
@@ -563,5 +589,33 @@ module Solargraph
563
589
  @synchronized = false
564
590
  end
565
591
  end
592
+
593
+ # @return [Set<Gem::Specification>]
594
+ def cache_errors
595
+ @cache_errors ||= Set.new
596
+ end
597
+
598
+ # @return [void]
599
+ def cache_next_gemspec
600
+ return if @cache_pid
601
+ spec = api_map.uncached_gemspecs.find { |spec| !cache_errors.include?(spec)}
602
+ return unless spec
603
+
604
+ logger.info "Caching #{spec.name} #{spec.version}"
605
+ Thread.new do
606
+ @cache_pid = Process.spawn('solargraph', 'cache', spec.name, spec.version.to_s)
607
+ Process.wait(@cache_pid)
608
+ logger.info "Cached #{spec.name} #{spec.version}"
609
+ @synchronized = false
610
+ rescue Errno::EINVAL => e
611
+ logger.info "Cached #{spec.name} #{spec.version} with EINVAL"
612
+ @synchronized = false
613
+ rescue StandardError => e
614
+ cache_errors.add spec
615
+ Solargraph.logger.warn "Error caching gemspec #{spec.name} #{spec.version}: [#{e.class}] #{e.message}"
616
+ ensure
617
+ @cache_pid = nil
618
+ end
619
+ end
566
620
  end
567
621
  end
@@ -25,6 +25,7 @@ module Solargraph
25
25
  }
26
26
  end
27
27
 
28
+ # @param other [BasicObject]
28
29
  def == other
29
30
  return false unless other.is_a?(Location)
30
31
  filename == other.filename and range == other.range
@@ -3,6 +3,9 @@ require 'ripper'
3
3
  module Solargraph
4
4
  module Parser
5
5
  class CommentRipper < Ripper::SexpBuilderPP
6
+ # @param src [String]
7
+ # @param filename [String]
8
+ # @param lineno [Integer]
6
9
  def initialize src, filename = '(ripper)', lineno = 0
7
10
  super
8
11
  @buffer = src
@@ -42,6 +45,7 @@ module Solargraph
42
45
  result
43
46
  end
44
47
 
48
+ # @return [Hash{Integer => String}]
45
49
  def parse
46
50
  @comments = {}
47
51
  super
@@ -3,38 +3,78 @@ module Solargraph
3
3
  class NodeMethods
4
4
  module_function
5
5
 
6
+ # @abstract
7
+ # @param node [Parser::AST::Node]
8
+ # @return [String]
6
9
  def unpack_name node
7
10
  raise NotImplementedError
8
11
  end
9
12
 
10
- def infer_literal_type node
13
+ # @abstract
14
+ # @todo Temporarily here for testing. Move to Solargraph::Parser.
15
+ # @param node [Parser::AST::Node]
16
+ # @return [Array<Parser::AST::Node>]
17
+ def call_nodes_from node
11
18
  raise NotImplementedError
12
19
  end
13
20
 
14
- def calls_from node
21
+ # Find all the nodes within the provided node that potentially return a
22
+ # value.
23
+ #
24
+ # The node parameter typically represents a method's logic, e.g., the
25
+ # second child (after the :args node) of a :def node. A simple one-line
26
+ # method would typically return itself, while a node with conditions
27
+ # would return the resulting node from each conditional branch. Nodes
28
+ # that follow a :return node are assumed to be unreachable. Nil values
29
+ # are converted to nil node types.
30
+ #
31
+ # @abstract
32
+ # @param node [Parser::AST::Node]
33
+ # @return [Array<Parser::AST::Node>]
34
+ def returns_from_method_body node
15
35
  raise NotImplementedError
16
36
  end
17
37
 
18
- def returns_from node
38
+ # @abstract
39
+ # @param node [Parser::AST::Node]
40
+ #
41
+ # @return [Array<Parser::AST::Node>]
42
+ def const_nodes_from node
19
43
  raise NotImplementedError
20
44
  end
21
45
 
22
- def process node
46
+ # @abstract
47
+ # @param cursor [Solargraph::Source::Cursor]
48
+ # @return [Parser::AST::Node, nil]
49
+ def find_recipient_node cursor
23
50
  raise NotImplementedError
24
51
  end
25
52
 
26
- def references node
53
+ # @abstract
54
+ # @param node [Parser::AST::Node]
55
+ # @return [Array<AST::Node>] low-level value nodes in
56
+ # value position. Does not include explicit return
57
+ # statements
58
+ def value_position_nodes_only(node)
27
59
  raise NotImplementedError
28
60
  end
29
61
 
30
- def chain node, filename = nil, in_block = false
62
+ # @abstract
63
+ # @param nodes [Enumerable<Parser::AST::Node>]
64
+ def any_splatted_call?(nodes)
31
65
  raise NotImplementedError
32
66
  end
33
67
 
34
- def node? node
68
+ # @abstract
69
+ # @param node [Parser::AST::Node]
70
+ # @return [void]
71
+ def process node
35
72
  raise NotImplementedError
36
73
  end
37
74
 
75
+ # @abstract
76
+ # @param node [Parser::AST::Node]
77
+ # @return [Hash{Parser::AST::Node => Chain}]
38
78
  def convert_hash node
39
79
  raise NotImplementedError
40
80
  end
@@ -19,6 +19,7 @@ module Solargraph
19
19
  # @param node [Parser::AST::Node]
20
20
  # @param region [Region]
21
21
  # @param pins [Array<Pin::Base>]
22
+ # @param locals [Array<Pin::LocalVariable>]
22
23
  def initialize node, region, pins, locals
23
24
  @node = node
24
25
  @region = region
@@ -54,20 +55,28 @@ module Solargraph
54
55
  Location.new(region.filename, range)
55
56
  end
56
57
 
58
+ # @param node [Parser::AST::Node]
59
+ # @return [String, nil]
57
60
  def comments_for(node)
58
61
  region.source.comments_for(node)
59
62
  end
60
63
 
64
+ # @param position [Solargraph::Position]
65
+ # @return [Pin::Base, nil]
61
66
  def named_path_pin position
62
67
  pins.select{|pin| pin.is_a?(Pin::Closure) && pin.path && !pin.path.empty? && pin.location.range.contain?(position)}.last
63
68
  end
64
69
 
65
70
  # @todo Candidate for deprecation
71
+ # @param position [Solargraph::Position]
72
+ # @return [Pin::Closure, nil]
66
73
  def block_pin position
67
74
  pins.select{|pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position)}.last
68
75
  end
69
76
 
70
77
  # @todo Candidate for deprecation
78
+ # @param position [Solargraph::Position]
79
+ # @return [Pin::Closure, nil]
71
80
  def closure_pin position
72
81
  pins.select{|pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position)}.last
73
82
  end
@@ -1,12 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'parser/current'
4
+ require 'parser/source/buffer'
2
5
 
3
6
  module Solargraph
4
7
  module Parser
5
- module Legacy
8
+ module ParserGem
6
9
  module ClassMethods
7
10
  # @param code [String]
8
- # @param filename [String]
9
- # @return [Array(Parser::AST::Node, Array<Parser::Source::Comment>)]
11
+ # @param filename [String, nil]
12
+ # @return [Array(Parser::AST::Node, Hash{Integer => String})]
10
13
  def parse_with_comments code, filename = nil
11
14
  buffer = ::Parser::Source::Buffer.new(filename, 0)
12
15
  buffer.source = code
@@ -29,7 +32,7 @@ module Solargraph
29
32
  raise Parser::SyntaxError, e.message
30
33
  end
31
34
 
32
- # @return [Parser::Base]
35
+ # @return [::Parser::Base]
33
36
  def parser
34
37
  # @todo Consider setting an instance variable. We might not need to
35
38
  # recreate the parser every time we use it.
@@ -39,19 +42,30 @@ module Solargraph
39
42
  parser
40
43
  end
41
44
 
45
+ # @param source [Source]
46
+ # @return [Array(Array<Pin::Base>, Array<Pin::Base>)]
42
47
  def map source
43
48
  NodeProcessor.process(source.node, Region.new(source: source))
44
49
  end
45
50
 
51
+ # @param node [Parser::AST::Node]
52
+ # @return [Array<Parser::AST::Node>]
46
53
  def returns_from node
47
54
  NodeMethods.returns_from(node)
48
55
  end
49
56
 
57
+ # @param source [Source]
58
+ # @param name [String]
59
+ # @return [Array<Location>]
50
60
  def references source, name
51
61
  if name.end_with?("=")
52
62
  reg = /#{Regexp.escape name[0..-2]}\s*=/
63
+ # @param code [String]
64
+ # @param offset [Integer]
53
65
  extract_offset = ->(code, offset) { reg.match(code, offset).offset(0) }
54
66
  else
67
+ # @param code [String]
68
+ # @param offset [Integer]
55
69
  extract_offset = ->(code, offset) { [soff = code.index(name, offset), soff + name.length] }
56
70
  end
57
71
  inner_node_references(name, source.node).map do |n|
@@ -80,36 +94,48 @@ module Solargraph
80
94
  result
81
95
  end
82
96
 
97
+ # @return [Source::Chain]
83
98
  def chain *args
84
99
  NodeChainer.chain *args
85
100
  end
86
101
 
102
+ # @return [Source::Chain]
87
103
  def chain_string *args
88
104
  NodeChainer.load_string *args
89
105
  end
90
106
 
107
+ # @return [Array(Array<Pin::Base>, Array<Pin::Base>)]
91
108
  def process_node *args
92
109
  Solargraph::Parser::NodeProcessor.process *args
93
110
  end
94
111
 
112
+ # @param node [Parser::AST::Node]
113
+ # @return [String, nil]
95
114
  def infer_literal_node_type node
96
115
  NodeMethods.infer_literal_node_type node
97
116
  end
98
117
 
118
+ # @return [void]
99
119
  def version
100
120
  parser.version
101
121
  end
102
122
 
123
+ # @param node [BasicObject]
124
+ # @return [Boolean]
103
125
  def is_ast_node? node
104
126
  node.is_a?(::Parser::AST::Node)
105
127
  end
106
128
 
129
+ # @param node [Parser::AST::Node]
130
+ # @return [Range]
107
131
  def node_range node
108
132
  st = Position.new(node.loc.line, node.loc.column)
109
133
  en = Position.new(node.loc.last_line, node.loc.last_column)
110
134
  Range.new(st, en)
111
135
  end
112
136
 
137
+ # @param node [Parser::AST::Node]
138
+ # @return [Array<Range>]
113
139
  def string_ranges node
114
140
  return [] unless is_ast_node?(node)
115
141
  result = []
@@ -128,7 +154,7 @@ module Solargraph
128
154
  end
129
155
  end
130
156
  result
131
- end
157
+ end
132
158
  end
133
159
  end
134
160
  end
@@ -2,11 +2,13 @@
2
2
 
3
3
  module Solargraph
4
4
  module Parser
5
- module Legacy
5
+ module ParserGem
6
6
  # A custom builder for source parsers that ignores character encoding
7
7
  # issues in literal strings.
8
8
  #
9
9
  class FlawedBuilder < ::Parser::Builders::Default
10
+ # @param token [::Parser::AST::Node]
11
+ # @return [String]
10
12
  def string_value(token)
11
13
  value(token)
12
14
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Solargraph
4
4
  module Parser
5
- module Legacy
5
+ module ParserGem
6
6
  # A factory for generating chains from nodes.
7
7
  #
8
8
  class NodeChainer
@@ -10,11 +10,12 @@ module Solargraph
10
10
  Chain = Source::Chain
11
11
 
12
12
  # @param node [Parser::AST::Node]
13
- # @param filename [String]
14
- def initialize node, filename = nil, in_block = false
13
+ # @param filename [String, nil]
14
+ # @param parent [Parser::AST::Node, nil]
15
+ def initialize node, filename = nil, parent = nil
15
16
  @node = node
16
17
  @filename = filename
17
- @in_block = in_block ? 1 : 0
18
+ @parent = parent
18
19
  end
19
20
 
20
21
  # @return [Source::Chain]
@@ -25,10 +26,11 @@ module Solargraph
25
26
 
26
27
  class << self
27
28
  # @param node [Parser::AST::Node]
28
- # @param filename [String]
29
+ # @param filename [String, nil]
30
+ # @param parent [Parser::AST::Node, nil]
29
31
  # @return [Source::Chain]
30
- def chain node, filename = nil, in_block = false
31
- NodeChainer.new(node, filename, in_block).chain
32
+ def chain node, filename = nil, parent = nil
33
+ NodeChainer.new(node, filename, parent).chain
32
34
  end
33
35
 
34
36
  # @param code [String]
@@ -47,54 +49,43 @@ module Solargraph
47
49
  # @return [Array<Chain::Link>]
48
50
  def generate_links n
49
51
  return [] unless n.is_a?(::Parser::AST::Node)
50
- return generate_links(n.children[0]) if n.type == :begin
51
52
  return generate_links(n.children[0]) if n.type == :splat
53
+ # @type [Array<Chain::Link>]
52
54
  result = []
53
55
  if n.type == :block
54
- @in_block += 1
55
- result.concat generate_links(n.children[0])
56
- @in_block -= 1
56
+ result.concat NodeChainer.chain(n.children[0], @filename, n).links
57
57
  elsif n.type == :send
58
58
  if n.children[0].is_a?(::Parser::AST::Node)
59
59
  result.concat generate_links(n.children[0])
60
- args = []
61
- n.children[2..-1].each do |c|
62
- args.push NodeChainer.chain(c)
63
- end
64
- result.push Chain::Call.new(n.children[1].to_s, args, @in_block > 0 || block_passed?(n))
60
+ result.push Chain::Call.new(n.children[1].to_s, node_args(n), passed_block(n))
65
61
  elsif n.children[0].nil?
66
62
  args = []
67
63
  n.children[2..-1].each do |c|
68
- args.push NodeChainer.chain(c)
64
+ args.push NodeChainer.chain(c, @filename, n)
69
65
  end
70
- result.push Chain::Call.new(n.children[1].to_s, args, @in_block > 0 || block_passed?(n))
66
+ result.push Chain::Call.new(n.children[1].to_s, node_args(n), passed_block(n))
71
67
  else
72
68
  raise "No idea what to do with #{n}"
73
69
  end
74
70
  elsif n.type == :csend
75
71
  if n.children[0].is_a?(::Parser::AST::Node)
76
72
  result.concat generate_links(n.children[0])
77
- args = []
78
- n.children[2..-1].each do |c|
79
- args.push NodeChainer.chain(c)
80
- end
81
- result.push Chain::QCall.new(n.children[1].to_s, args, @in_block > 0 || block_passed?(n))
73
+ result.push Chain::QCall.new(n.children[1].to_s, node_args(n))
82
74
  elsif n.children[0].nil?
83
- args = []
84
- n.children[2..-1].each do |c|
85
- args.push NodeChainer.chain(c)
86
- end
87
- result.push Chain::QCall.new(n.children[1].to_s, args, @in_block > 0 || block_passed?(n))
75
+ result.push Chain::QCall.new(n.children[1].to_s, node_args(n))
88
76
  else
89
77
  raise "No idea what to do with #{n}"
90
78
  end
91
79
  elsif n.type == :self
92
80
  result.push Chain::Head.new('self')
93
81
  elsif n.type == :zsuper
94
- result.push Chain::ZSuper.new('super', @in_block > 0 || block_passed?(n))
82
+ result.push Chain::ZSuper.new('super')
95
83
  elsif n.type == :super
96
- args = n.children.map { |c| NodeChainer.chain(c) }
97
- result.push Chain::Call.new('super', args, @in_block > 0 || block_passed?(n))
84
+ args = n.children.map { |c| NodeChainer.chain(c, @filename, n) }
85
+ result.push Chain::Call.new('super', args)
86
+ elsif n.type == :yield
87
+ args = n.children.map { |c| NodeChainer.chain(c, @filename, n) }
88
+ result.push Chain::Call.new('yield', args)
98
89
  elsif n.type == :const
99
90
  const = unpack_name(n)
100
91
  result.push Chain::Constant.new(const)
@@ -114,9 +105,11 @@ module Solargraph
114
105
  elsif n.type == :and
115
106
  result.concat generate_links(n.children.last)
116
107
  elsif n.type == :or
117
- result.push Chain::Or.new([NodeChainer.chain(n.children[0], @filename), NodeChainer.chain(n.children[1], @filename)])
108
+ result.push Chain::Or.new([NodeChainer.chain(n.children[0], @filename), NodeChainer.chain(n.children[1], @filename, n)])
109
+ elsif n.type == :if
110
+ result.push Chain::If.new([NodeChainer.chain(n.children[1], @filename), NodeChainer.chain(n.children[2], @filename, n)])
118
111
  elsif [:begin, :kwbegin].include?(n.type)
119
- result.concat generate_links(n.children[0])
112
+ result.concat generate_links(n.children.last)
120
113
  elsif n.type == :block_pass
121
114
  block_variable_name_node = n.children[0]
122
115
  if block_variable_name_node.nil?
@@ -124,10 +117,17 @@ module Solargraph
124
117
  # added in Ruby 3.1 - https://bugs.ruby-lang.org/issues/11256
125
118
  result.push Chain::BlockVariable.new(nil)
126
119
  else
127
- result.push Chain::BlockVariable.new("&#{block_variable_name_node.children[0].to_s}")
120
+ if block_variable_name_node.type == :sym
121
+ result.push Chain::BlockSymbol.new("#{block_variable_name_node.children[0].to_s}")
122
+ else
123
+ result.push Chain::BlockVariable.new("&#{block_variable_name_node.children[0].to_s}")
124
+ end
128
125
  end
129
126
  elsif n.type == :hash
130
127
  result.push Chain::Hash.new('::Hash', hash_is_splatted?(n))
128
+ elsif n.type == :array
129
+ chained_children = n.children.map { |c| NodeChainer.chain(c) }
130
+ result.push Source::Chain::Array.new(chained_children)
131
131
  else
132
132
  lit = infer_literal_node_type(n)
133
133
  result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
@@ -135,6 +135,7 @@ module Solargraph
135
135
  result
136
136
  end
137
137
 
138
+ # @param node [Parser::AST::Node]
138
139
  def hash_is_splatted? node
139
140
  return false unless Parser.is_ast_node?(node) && node.type == :hash
140
141
  return false unless Parser.is_ast_node?(node.children.last) && node.children.last.type == :kwsplat
@@ -142,8 +143,20 @@ module Solargraph
142
143
  true
143
144
  end
144
145
 
145
- def block_passed? node
146
- node.children.last.is_a?(::Parser::AST::Node) && node.children.last.type == :block_pass
146
+ # @param node [Parser::AST::Node]
147
+ # @return [Source::Chain, nil]
148
+ def passed_block node
149
+ return unless node == @node && @parent&.type == :block
150
+
151
+ NodeChainer.chain(@parent.children[2], @filename)
152
+ end
153
+
154
+ # @param node [Parser::AST::Node]
155
+ # @return [Array<Source::Chain>]
156
+ def node_args node
157
+ node.children[2..-1].map do |child|
158
+ NodeChainer.chain(child, @filename, node)
159
+ end
147
160
  end
148
161
  end
149
162
  end