solargraph 0.61.0 → 0.62.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/.github/workflows/linting.yml +24 -2
- data/.github/workflows/plugins.yml +18 -0
- data/.github/workflows/rspec.yml +14 -1
- data/.github/workflows/typecheck.yml +6 -0
- data/CHANGELOG.md +38 -0
- data/lib/solargraph/api_map/index.rb +14 -3
- data/lib/solargraph/api_map/store.rb +6 -0
- data/lib/solargraph/api_map.rb +34 -2
- data/lib/solargraph/collection/gem.rb +12 -10
- data/lib/solargraph/collection.rb +0 -1
- data/lib/solargraph/complex_type/unique_type.rb +13 -0
- data/lib/solargraph/complex_type.rb +23 -3
- data/lib/solargraph/convention.rb +0 -4
- data/lib/solargraph/diagnostics/type_check.rb +0 -1
- data/lib/solargraph/external.rb +56 -28
- data/lib/solargraph/language_server/host/diagnoser.rb +1 -1
- data/lib/solargraph/language_server/host/dispatch.rb +4 -2
- data/lib/solargraph/language_server/host.rb +10 -2
- data/lib/solargraph/language_server/message/completion_item/resolve.rb +10 -3
- data/lib/solargraph/language_server/message/text_document/definition.rb +1 -1
- data/lib/solargraph/library.rb +19 -33
- data/lib/solargraph/parser/node_processor/base.rb +6 -0
- data/lib/solargraph/parser/parser_gem/node_methods.rb +29 -6
- data/lib/solargraph/parser/parser_gem/node_processors/class_definition/class_assignment_node.rb +98 -0
- data/lib/solargraph/parser/parser_gem/node_processors/class_definition.rb +54 -0
- data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/data_definition/data_assignment_node.rb +65 -0
- data/lib/solargraph/parser/parser_gem/node_processors/data_definition/data_definition_node.rb +97 -0
- data/lib/solargraph/parser/parser_gem/node_processors/data_definition.rb +110 -0
- data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +2 -0
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +185 -0
- data/lib/solargraph/parser/parser_gem/node_processors/struct_definition/struct_assignment_node.rb +66 -0
- data/lib/solargraph/parser/parser_gem/node_processors/struct_definition/struct_definition_node.rb +108 -0
- data/lib/solargraph/parser/parser_gem/node_processors/struct_definition.rb +170 -0
- data/lib/solargraph/parser/parser_gem/node_processors.rb +33 -29
- data/lib/solargraph/pin/base.rb +25 -1
- data/lib/solargraph/pin/base_variable.rb +31 -1
- data/lib/solargraph/pin/callable.rb +11 -2
- data/lib/solargraph/pin/delegated_method.rb +5 -0
- data/lib/solargraph/pin/duck_method.rb +36 -16
- data/lib/solargraph/pin/instance_variable.rb +45 -0
- data/lib/solargraph/pin/method.rb +10 -0
- data/lib/solargraph/pin/method_alias.rb +4 -0
- data/lib/solargraph/pin/parameter.rb +1 -1
- data/lib/solargraph/pin/reference/included_mixin.rb +22 -0
- data/lib/solargraph/pin/reference.rb +1 -0
- data/lib/solargraph/plugin/active_support.rb +100 -0
- data/lib/solargraph/plugin/addon.rb +16 -0
- data/lib/solargraph/plugin/base.rb +25 -0
- data/lib/solargraph/plugin/panel.rb +37 -0
- data/lib/solargraph/plugin.rb +26 -0
- data/lib/solargraph/rbs_collection.rb +119 -0
- data/lib/solargraph/rbs_map/conversions.rb +106 -47
- data/lib/solargraph/rbs_map/gem.rb +4 -1
- data/lib/solargraph/rbs_map/helpers.rb +7 -6
- data/lib/solargraph/rbs_translator.rb +105 -70
- data/lib/solargraph/repo.rb +1 -1
- data/lib/solargraph/source/chain/call.rb +32 -13
- data/lib/solargraph/source/node_map.rb +27 -0
- data/lib/solargraph/source.rb +17 -0
- data/lib/solargraph/source_map/call.rb +18 -0
- data/lib/solargraph/source_map/mapper.rb +1 -1
- data/lib/solargraph/source_map.rb +18 -4
- data/lib/solargraph/type_checker/rules.rb +2 -2
- data/lib/solargraph/type_checker.rb +4 -1
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/yard_map/macro.rb +51 -1
- data/lib/solargraph.rb +2 -0
- data/rbs/fills/boolean/0/boolean.rbs +12 -0
- data/solargraph.gemspec +1 -1
- metadata +21 -11
- data/lib/solargraph/collection/rbs.rb +0 -49
- data/lib/solargraph/convention/active_support_concern.rb +0 -111
- data/lib/solargraph/convention/data_definition/data_assignment_node.rb +0 -61
- data/lib/solargraph/convention/data_definition/data_definition_node.rb +0 -93
- data/lib/solargraph/convention/data_definition.rb +0 -108
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +0 -62
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +0 -103
- data/lib/solargraph/convention/struct_definition.rb +0 -168
|
@@ -118,11 +118,13 @@ module Solargraph
|
|
|
118
118
|
.tap { |lib| lib.add_observer self }
|
|
119
119
|
end
|
|
120
120
|
|
|
121
|
-
#
|
|
121
|
+
# The Library observer callback that #generic_library registers.
|
|
122
122
|
#
|
|
123
|
+
# @abstract
|
|
124
|
+
# @param progress [Solargraph::LanguageServer::Progress, nil]
|
|
123
125
|
# @return [void]
|
|
124
126
|
def update progress
|
|
125
|
-
|
|
127
|
+
raise NotImplementedError, 'Host::Dispatch requires an update method'
|
|
126
128
|
end
|
|
127
129
|
end
|
|
128
130
|
end
|
|
@@ -495,7 +495,7 @@ module Solargraph
|
|
|
495
495
|
)
|
|
496
496
|
result.concat(library.locate_pins(location).select { |pin| pin.name == params['label'] })
|
|
497
497
|
end
|
|
498
|
-
if params['data']['path']
|
|
498
|
+
if params['data']['path'] && !params['data']['path'].empty?
|
|
499
499
|
result.concat library.path_pins(params['data']['path'])
|
|
500
500
|
# @todo This exception is necessary because `Library#path_pins` does
|
|
501
501
|
# not perform a namespace method query, so the implicit `.new` pin
|
|
@@ -706,6 +706,15 @@ module Solargraph
|
|
|
706
706
|
client_capabilities['window'] && client_capabilities['window']['workDoneProgress']
|
|
707
707
|
end
|
|
708
708
|
|
|
709
|
+
# The Library observer callback that sends a library's progress to
|
|
710
|
+
# the client.
|
|
711
|
+
#
|
|
712
|
+
# @param progress [Solargraph::LanguageServer::Progress, nil]
|
|
713
|
+
# @return [void]
|
|
714
|
+
def update progress
|
|
715
|
+
progress&.send(self)
|
|
716
|
+
end
|
|
717
|
+
|
|
709
718
|
private
|
|
710
719
|
|
|
711
720
|
# @return [Array<Integer>]
|
|
@@ -736,7 +745,6 @@ module Solargraph
|
|
|
736
745
|
# @return [String]
|
|
737
746
|
def normalize_separators path
|
|
738
747
|
return path if File::ALT_SEPARATOR.nil?
|
|
739
|
-
# @sg-ignore flow sensitive typing needs to handle constants
|
|
740
748
|
path.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
|
|
741
749
|
end
|
|
742
750
|
|
|
@@ -7,6 +7,12 @@ module Solargraph
|
|
|
7
7
|
# completionItem/resolve message handler
|
|
8
8
|
#
|
|
9
9
|
class Resolve < Base
|
|
10
|
+
# Properties that must not be changed during completionItem/resolve
|
|
11
|
+
# according to the LSP specification.
|
|
12
|
+
#
|
|
13
|
+
# @see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion
|
|
14
|
+
IMMUTABLE_FIELDS = %i[label kind sortText filterText insertText textEdit data].freeze
|
|
15
|
+
|
|
10
16
|
def process
|
|
11
17
|
pins = host.locate_pins(params)
|
|
12
18
|
set_result merge(pins)
|
|
@@ -17,14 +23,15 @@ module Solargraph
|
|
|
17
23
|
# @param pins [Array<Pin::Base>]
|
|
18
24
|
# @return [Hash]
|
|
19
25
|
def merge pins
|
|
20
|
-
|
|
26
|
+
original = params.transform_keys(&:to_sym)
|
|
27
|
+
return original if pins.empty?
|
|
21
28
|
docs = pins
|
|
22
29
|
.reject { |pin| pin.documentation.empty? && pin.return_type.undefined? }
|
|
23
|
-
result =
|
|
24
|
-
.transform_keys(&:to_sym)
|
|
30
|
+
result = original
|
|
25
31
|
.merge(pins.first.resolve_completion_item)
|
|
26
32
|
.merge(documentation: markup_content(join_docs(docs)))
|
|
27
33
|
result[:detail] = pins.first.detail
|
|
34
|
+
IMMUTABLE_FIELDS.each { |field| result[field] = original[field] }
|
|
28
35
|
result
|
|
29
36
|
end
|
|
30
37
|
|
|
@@ -15,7 +15,7 @@ module Solargraph
|
|
|
15
15
|
|
|
16
16
|
# @return [Array<Hash>, nil]
|
|
17
17
|
def code_location
|
|
18
|
-
suggestions = host.definitions_at(params['textDocument']['uri'], @line, @column)
|
|
18
|
+
suggestions = host.definitions_at(params['textDocument']['uri'], @line, @column) || []
|
|
19
19
|
# @sg-ignore Need to add nil check here
|
|
20
20
|
return nil if suggestions.empty?
|
|
21
21
|
# @sg-ignore Need to add nil check here
|
data/lib/solargraph/library.rb
CHANGED
|
@@ -589,49 +589,35 @@ module Solargraph
|
|
|
589
589
|
def cache_next_gemspec
|
|
590
590
|
return if @cache_progress
|
|
591
591
|
|
|
592
|
-
spec = api_map.unloaded_gems.first
|
|
592
|
+
spec = (api_map.unloaded_gems - cache_errors).first
|
|
593
593
|
return end_cache_progress unless spec
|
|
594
594
|
|
|
595
595
|
pending = api_map.unloaded_gems.length - cache_errors.length - 1
|
|
596
596
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
return if pending - queued_gemspec_cache.length < 1
|
|
597
|
+
logger.info "Caching #{spec.name} #{spec.version}"
|
|
598
|
+
Thread.new do
|
|
599
|
+
report_cache_progress spec.name, pending
|
|
601
600
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
# would try to re-lock a mutex this thread already holds and
|
|
606
|
-
# deadlock (https://github.com/castwide/solargraph/issues/1111).
|
|
607
|
-
cache_next_gemspec
|
|
608
|
-
else
|
|
609
|
-
logger.info "Caching #{spec.name} #{spec.version}"
|
|
610
|
-
Thread.new do
|
|
611
|
-
report_cache_progress spec.name, pending
|
|
612
|
-
|
|
613
|
-
cmd = [workspace.command_path, 'cache', spec.name]
|
|
614
|
-
cmd.push('--directory', workspace.directory) if workspace.directory
|
|
601
|
+
cmd = [workspace.command_path, 'cache', spec.name]
|
|
602
|
+
cmd.push('--directory', workspace.directory) if workspace.directory
|
|
603
|
+
begin
|
|
615
604
|
_o, e, s = Open3.capture3(*cmd)
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
else
|
|
619
|
-
cache_errors.add spec
|
|
620
|
-
logger.warn "Error caching gemspec #{spec.name} #{spec.version}"
|
|
621
|
-
logger.warn e
|
|
622
|
-
end
|
|
623
|
-
end_cache_progress
|
|
624
|
-
catalog
|
|
625
|
-
sync_catalog
|
|
605
|
+
rescue SystemCallError => e
|
|
606
|
+
# The command could not start, e.g. Errno::ENOENT - handled below
|
|
626
607
|
end
|
|
608
|
+
if s&.success?
|
|
609
|
+
logger.info "Cached #{spec.name} #{spec.version}"
|
|
610
|
+
else
|
|
611
|
+
cache_errors.add spec
|
|
612
|
+
logger.warn "Error caching gemspec #{spec.name} #{spec.version}"
|
|
613
|
+
logger.warn e
|
|
614
|
+
end
|
|
615
|
+
end_cache_progress
|
|
616
|
+
catalog
|
|
617
|
+
sync_catalog
|
|
627
618
|
end
|
|
628
619
|
end
|
|
629
620
|
|
|
630
|
-
# @return [Array<Gem::Specification>]
|
|
631
|
-
def queued_gemspec_cache
|
|
632
|
-
@queued_gemspec_cache ||= []
|
|
633
|
-
end
|
|
634
|
-
|
|
635
621
|
# @param gem_name [String]
|
|
636
622
|
# @param pending [Integer]
|
|
637
623
|
# @return [void]
|
|
@@ -91,6 +91,12 @@ module Solargraph
|
|
|
91
91
|
region.source.comments_for(node)
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
+
# @param node [Parser::AST::Node]
|
|
95
|
+
# @return [String, nil]
|
|
96
|
+
def type_assertion_for node
|
|
97
|
+
region.source.type_assertion_for(node)
|
|
98
|
+
end
|
|
99
|
+
|
|
94
100
|
# @param position [Solargraph::Position]
|
|
95
101
|
# @return [Pin::Closure, nil]
|
|
96
102
|
def named_path_pin position
|
|
@@ -197,6 +197,18 @@ module Solargraph
|
|
|
197
197
|
nodes.any? { |n| splatted_call?(n) }
|
|
198
198
|
end
|
|
199
199
|
|
|
200
|
+
# Anonymous `*`, `**` and `...` arguments pass on whatever the
|
|
201
|
+
# enclosing method received, so neither count nor types are known.
|
|
202
|
+
#
|
|
203
|
+
# @param nodes [Enumerable<Parser::AST::Node>]
|
|
204
|
+
def any_forwarded_args? nodes
|
|
205
|
+
nodes.any? do |n|
|
|
206
|
+
next false unless Parser.is_ast_node?(n)
|
|
207
|
+
next true if %i[forwarded_args forwarded_restarg].include?(n.type)
|
|
208
|
+
n.type == :hash && n.children.any? { |c| Parser.is_ast_node?(c) && c.type == :forwarded_kwrestarg }
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
200
212
|
# @todo Temporarily here for testing. Move to Solargraph::Parser.
|
|
201
213
|
# @param node [Parser::AST::Node]
|
|
202
214
|
# @return [Array<Parser::AST::Node>]
|
|
@@ -442,6 +454,11 @@ module Solargraph
|
|
|
442
454
|
CONDITIONAL_ALL_BUT_FIRST = %i[if unless].freeze
|
|
443
455
|
ONLY_ONE_CHILD = [:return].freeze
|
|
444
456
|
FIRST_TWO_CHILDREN = [:rescue].freeze
|
|
457
|
+
# :ensure's value is always its body's value (first
|
|
458
|
+
# child); the ensure clause itself (second child) never
|
|
459
|
+
# contributes to the method's return value unless it
|
|
460
|
+
# explicitly returns.
|
|
461
|
+
ENSURE = [:ensure].freeze
|
|
445
462
|
COMPOUND_STATEMENTS = %i[begin kwbegin].freeze
|
|
446
463
|
SKIPPABLE = %i[def defs class sclass module].freeze
|
|
447
464
|
FUNCTION_VALUE = [:block].freeze
|
|
@@ -488,6 +505,13 @@ module Solargraph
|
|
|
488
505
|
result.concat reduce_to_value_nodes([node.children[0]])
|
|
489
506
|
elsif FIRST_TWO_CHILDREN.include?(node.type)
|
|
490
507
|
result.concat reduce_to_value_nodes([node.children[0], node.children[1]])
|
|
508
|
+
elsif ENSURE.include?(node.type)
|
|
509
|
+
result.concat reduce_to_value_nodes([node.children[0]])
|
|
510
|
+
ensure_body = node.children[1]
|
|
511
|
+
if include_explicit_returns && ensure_body.is_a?(::Parser::AST::Node)
|
|
512
|
+
# @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
|
|
513
|
+
result.concat explicit_return_values_from_compound_statement(ensure_body)
|
|
514
|
+
end
|
|
491
515
|
elsif FUNCTION_VALUE.include?(node.type)
|
|
492
516
|
# the block itself is a first class value that could be returned
|
|
493
517
|
result.push node
|
|
@@ -605,14 +629,13 @@ module Solargraph
|
|
|
605
629
|
elsif CONDITIONAL_ALL_BUT_FIRST.include?(node.type)
|
|
606
630
|
# @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
|
|
607
631
|
result.concat reduce_to_value_nodes(node.children[1..])
|
|
608
|
-
|
|
609
|
-
elsif node.type == :return
|
|
632
|
+
elsif ONLY_ONE_CHILD.include?(node.type) || ENSURE.include?(node.type)
|
|
610
633
|
# @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
|
|
611
634
|
result.concat reduce_to_value_nodes([node.children[0]])
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
#
|
|
615
|
-
result.concat reduce_to_value_nodes(node.children)
|
|
635
|
+
elsif FIRST_TWO_CHILDREN.include?(node.type)
|
|
636
|
+
# a :rescue node's value is its body's value or the
|
|
637
|
+
# value of whichever resbody handled the exception
|
|
638
|
+
result.concat reduce_to_value_nodes([node.children[0], node.children[1]])
|
|
616
639
|
# @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
|
|
617
640
|
elsif node.type == :block
|
|
618
641
|
# @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
|
data/lib/solargraph/parser/parser_gem/node_processors/class_definition/class_assignment_node.rb
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Parser
|
|
5
|
+
module ParserGem
|
|
6
|
+
module NodeProcessors
|
|
7
|
+
module ClassDefinition
|
|
8
|
+
# A node wrapper for a class definition via const assignment.
|
|
9
|
+
# @example
|
|
10
|
+
# MyError = Class.new(StandardError) do
|
|
11
|
+
# def retry_after; 5; end
|
|
12
|
+
# end
|
|
13
|
+
class ClassAssignmentNode
|
|
14
|
+
class << self
|
|
15
|
+
# @example
|
|
16
|
+
# s(:casgn, nil, :Foo,
|
|
17
|
+
# s(:block,
|
|
18
|
+
# s(:send,
|
|
19
|
+
# s(:const, nil, :Class), :new,
|
|
20
|
+
# s(:const, nil, :StandardError)),
|
|
21
|
+
# s(:args),
|
|
22
|
+
# s(:def, :retry_after, s(:args), s(:int, 5))))
|
|
23
|
+
#
|
|
24
|
+
# @param node [Parser::AST::Node]
|
|
25
|
+
# @return [Boolean]
|
|
26
|
+
def match? node
|
|
27
|
+
return false unless node&.type == :casgn
|
|
28
|
+
|
|
29
|
+
class_new_node?(new_node(node))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# The `Class.new(...)` send node, whether or not a block is attached.
|
|
33
|
+
#
|
|
34
|
+
# @param node [Parser::AST::Node]
|
|
35
|
+
# @return [Parser::AST::Node, nil]
|
|
36
|
+
def new_node node
|
|
37
|
+
value = node.children[2]
|
|
38
|
+
return nil if value.nil?
|
|
39
|
+
return value unless value.type == :block
|
|
40
|
+
|
|
41
|
+
value.children[0]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# @param send_node [Parser::AST::Node, nil]
|
|
47
|
+
# @return [Boolean]
|
|
48
|
+
def class_new_node? send_node
|
|
49
|
+
return false if send_node.nil?
|
|
50
|
+
return false unless send_node.type == :send
|
|
51
|
+
return false unless send_node.children[1] == :new
|
|
52
|
+
|
|
53
|
+
receiver = send_node.children[0]
|
|
54
|
+
return false if receiver.nil?
|
|
55
|
+
return false unless receiver.type == :const
|
|
56
|
+
|
|
57
|
+
receiver.children[1] == :Class
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @param node [Parser::AST::Node]
|
|
62
|
+
def initialize node
|
|
63
|
+
@node = node
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @return [String]
|
|
67
|
+
def class_name
|
|
68
|
+
namespace = node.children[0]
|
|
69
|
+
return node.children[1].to_s if namespace.nil?
|
|
70
|
+
|
|
71
|
+
"#{Parser::NodeMethods.unpack_name(namespace)}::#{node.children[1]}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# The superclass, when it is written as a constant. `Class.new(expr)`
|
|
75
|
+
# with a non-constant argument yields nil (superclass stays Object).
|
|
76
|
+
#
|
|
77
|
+
# @return [String, nil]
|
|
78
|
+
def superclass_name
|
|
79
|
+
send_node = self.class.new_node(node)
|
|
80
|
+
return nil if send_node.nil?
|
|
81
|
+
|
|
82
|
+
arg = send_node.children[2]
|
|
83
|
+
return nil if arg.nil?
|
|
84
|
+
return nil unless arg.type == :const
|
|
85
|
+
|
|
86
|
+
Parser::NodeMethods.unpack_name(arg)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# @return [Parser::AST::Node]
|
|
92
|
+
attr_reader :node
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Parser
|
|
5
|
+
module ParserGem
|
|
6
|
+
module NodeProcessors
|
|
7
|
+
module ClassDefinition
|
|
8
|
+
autoload :ClassAssignmentNode, 'solargraph/parser/parser_gem/node_processors/class_definition/class_assignment_node'
|
|
9
|
+
|
|
10
|
+
class ClassNode < Parser::NodeProcessor::Base
|
|
11
|
+
# @return [Boolean] continue processing the next processor of the same node.
|
|
12
|
+
def process
|
|
13
|
+
definition_node = class_definition_node
|
|
14
|
+
return true if definition_node.nil?
|
|
15
|
+
|
|
16
|
+
loc = get_node_location(node)
|
|
17
|
+
nspin = Solargraph::Pin::Namespace.new(
|
|
18
|
+
type: :class,
|
|
19
|
+
location: loc,
|
|
20
|
+
closure: region.closure,
|
|
21
|
+
name: definition_node.class_name,
|
|
22
|
+
comments: comments_for(node),
|
|
23
|
+
visibility: :public,
|
|
24
|
+
gates: region.closure.gates.freeze,
|
|
25
|
+
source: :class_definition
|
|
26
|
+
)
|
|
27
|
+
pins.push nspin
|
|
28
|
+
|
|
29
|
+
superclass_name = definition_node.superclass_name
|
|
30
|
+
if superclass_name
|
|
31
|
+
pins.push Pin::Reference::Superclass.new(
|
|
32
|
+
location: loc,
|
|
33
|
+
closure: nspin,
|
|
34
|
+
name: superclass_name,
|
|
35
|
+
source: :class_definition
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
process_children region.update(closure: nspin, visibility: :public)
|
|
40
|
+
false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# @return [ClassDefinition::ClassAssignmentNode, nil]
|
|
46
|
+
def class_definition_node
|
|
47
|
+
@class_definition_node ||= ClassAssignmentNode.new(node) if ClassAssignmentNode.match?(node)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Parser
|
|
5
|
+
module ParserGem
|
|
6
|
+
module NodeProcessors
|
|
7
|
+
module DataDefinition
|
|
8
|
+
# A node wrapper for a Data definition via const assignment.
|
|
9
|
+
# @example
|
|
10
|
+
# MyData = Data.new(:bar, :baz) do
|
|
11
|
+
# def foo
|
|
12
|
+
# end
|
|
13
|
+
# end
|
|
14
|
+
class DataAssignmentNode < DataDefintionNode
|
|
15
|
+
class << self
|
|
16
|
+
# @example
|
|
17
|
+
# s(:casgn, nil, :Foo,
|
|
18
|
+
# s(:block,
|
|
19
|
+
# s(:send,
|
|
20
|
+
# s(:const, nil, :Data), :define,
|
|
21
|
+
# s(:sym, :bar),
|
|
22
|
+
# s(:sym, :baz)),
|
|
23
|
+
# s(:args),
|
|
24
|
+
# s(:def, :foo,
|
|
25
|
+
# s(:args),
|
|
26
|
+
# s(:send, nil, :bar))))
|
|
27
|
+
# @param node [::Parser::AST::Node]
|
|
28
|
+
def match? node
|
|
29
|
+
return false unless node&.type == :casgn
|
|
30
|
+
return false if node.children[2].nil?
|
|
31
|
+
|
|
32
|
+
data_node = if node.children[2].type == :block
|
|
33
|
+
node.children[2].children[0]
|
|
34
|
+
else
|
|
35
|
+
node.children[2]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
data_definition_node?(data_node)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def class_name
|
|
43
|
+
if node.children[0]
|
|
44
|
+
Parser::NodeMethods.unpack_name(node.children[0]) + "::#{node.children[1]}"
|
|
45
|
+
else
|
|
46
|
+
node.children[1].to_s
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
# @return [Parser::AST::Node]
|
|
53
|
+
def data_node
|
|
54
|
+
if node.children[2].type == :block
|
|
55
|
+
node.children[2].children[0]
|
|
56
|
+
else
|
|
57
|
+
node.children[2]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Parser
|
|
5
|
+
module ParserGem
|
|
6
|
+
module NodeProcessors
|
|
7
|
+
module DataDefinition
|
|
8
|
+
# A node wrapper for a Data definition via inheritance.
|
|
9
|
+
# @example
|
|
10
|
+
# class MyData < Data.new(:bar, :baz)
|
|
11
|
+
# def foo
|
|
12
|
+
# end
|
|
13
|
+
# end
|
|
14
|
+
class DataDefintionNode
|
|
15
|
+
class << self
|
|
16
|
+
# @example
|
|
17
|
+
# s(:class,
|
|
18
|
+
# s(:const, nil, :Foo),
|
|
19
|
+
# s(:send,
|
|
20
|
+
# s(:const, nil, :Data), :define,
|
|
21
|
+
# s(:sym, :bar),
|
|
22
|
+
# s(:sym, :baz)),
|
|
23
|
+
# s(:hash,
|
|
24
|
+
# s(:pair,
|
|
25
|
+
# s(:sym, :keyword_init),
|
|
26
|
+
# s(:true)))),
|
|
27
|
+
# s(:def, :foo,
|
|
28
|
+
# s(:args),
|
|
29
|
+
# s(:send, nil, :bar)))
|
|
30
|
+
#
|
|
31
|
+
# @param node [Parser::AST::Node]
|
|
32
|
+
def match? node
|
|
33
|
+
return false unless node&.type == :class
|
|
34
|
+
|
|
35
|
+
data_definition_node?(node.children[1])
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# @param data_node [Parser::AST::Node]
|
|
41
|
+
# @return [Boolean]
|
|
42
|
+
def data_definition_node? data_node
|
|
43
|
+
return false unless data_node.is_a?(::Parser::AST::Node)
|
|
44
|
+
return false unless data_node&.type == :send
|
|
45
|
+
return false unless data_node.children[0]&.type == :const
|
|
46
|
+
return false unless data_node.children[0].children[1] == :Data
|
|
47
|
+
return false unless data_node.children[1] == :define
|
|
48
|
+
|
|
49
|
+
true
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param node [Parser::AST::Node]
|
|
54
|
+
def initialize node
|
|
55
|
+
@node = node
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @return [String]
|
|
59
|
+
def class_name
|
|
60
|
+
Parser::NodeMethods.unpack_name(node)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [Array<Array(Parser::AST::Node, String)>]
|
|
64
|
+
def attributes
|
|
65
|
+
data_attribute_nodes.map do |data_def_param|
|
|
66
|
+
next unless data_def_param.type == :sym
|
|
67
|
+
[data_def_param, data_def_param.children[0].to_s]
|
|
68
|
+
end.compact
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @return [Parser::AST::Node, nil]
|
|
72
|
+
def body_node
|
|
73
|
+
node.children[2]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
# @return [Parser::AST::Node]
|
|
79
|
+
attr_reader :node
|
|
80
|
+
|
|
81
|
+
# @return [Parser::AST::Node, nil]
|
|
82
|
+
def data_node
|
|
83
|
+
node.children[1]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @sg-ignore Need to add nil check here
|
|
87
|
+
# @return [Array<Parser::AST::Node>]
|
|
88
|
+
def data_attribute_nodes
|
|
89
|
+
# @sg-ignore Need to add nil check here
|
|
90
|
+
data_node.children[2..]
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Parser
|
|
5
|
+
module ParserGem
|
|
6
|
+
module NodeProcessors
|
|
7
|
+
module DataDefinition
|
|
8
|
+
autoload :DataDefintionNode, 'solargraph/parser/parser_gem/node_processors/data_definition/data_definition_node'
|
|
9
|
+
autoload :DataAssignmentNode, 'solargraph/parser/parser_gem/node_processors/data_definition/data_assignment_node'
|
|
10
|
+
|
|
11
|
+
class DataNode < Parser::NodeProcessor::Base
|
|
12
|
+
# @return [Boolean] continue processing the next processor of the same node.
|
|
13
|
+
def process
|
|
14
|
+
return true if data_definition_node.nil?
|
|
15
|
+
|
|
16
|
+
loc = get_node_location(node)
|
|
17
|
+
nspin = Solargraph::Pin::Namespace.new(
|
|
18
|
+
type: :class,
|
|
19
|
+
location: loc,
|
|
20
|
+
closure: region.closure,
|
|
21
|
+
# @sg-ignore flow sensitive typing needs to handle attrs
|
|
22
|
+
name: data_definition_node.class_name,
|
|
23
|
+
comments: comments_for(node),
|
|
24
|
+
visibility: :public,
|
|
25
|
+
gates: region.closure.gates.freeze
|
|
26
|
+
)
|
|
27
|
+
pins.push nspin
|
|
28
|
+
|
|
29
|
+
# define initialize method
|
|
30
|
+
initialize_method_pin = Pin::Method.new(
|
|
31
|
+
name: 'initialize',
|
|
32
|
+
parameters: [],
|
|
33
|
+
scope: :instance,
|
|
34
|
+
location: get_node_location(node),
|
|
35
|
+
closure: nspin,
|
|
36
|
+
visibility: :private,
|
|
37
|
+
comments: comments_for(node)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# @todo Support both arg and kwarg initializers for Data.define
|
|
41
|
+
# Solargraph::SourceMap::Clip#complete_keyword_parameters does not seem to currently take into account [Pin::Method#signatures] hence we only one for :kwarg
|
|
42
|
+
pins.push initialize_method_pin
|
|
43
|
+
|
|
44
|
+
# @sg-ignore flow sensitive typing needs to handle attrs
|
|
45
|
+
data_definition_node.attributes.map do |attribute_node, attribute_name|
|
|
46
|
+
initialize_method_pin.parameters.push(
|
|
47
|
+
Pin::Parameter.new(
|
|
48
|
+
name: attribute_name,
|
|
49
|
+
decl: :kwarg,
|
|
50
|
+
location: get_node_location(attribute_node),
|
|
51
|
+
closure: initialize_method_pin
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# define attribute readers and instance variables
|
|
57
|
+
# @sg-ignore flow sensitive typing needs to handle attrs
|
|
58
|
+
data_definition_node.attributes.each do |attribute_node, attribute_name|
|
|
59
|
+
name = attribute_name.to_s
|
|
60
|
+
method_pin = Pin::Method.new(
|
|
61
|
+
name: name,
|
|
62
|
+
parameters: [],
|
|
63
|
+
scope: :instance,
|
|
64
|
+
location: get_node_location(attribute_node),
|
|
65
|
+
closure: nspin,
|
|
66
|
+
comments: attribute_comments(attribute_node, attribute_name),
|
|
67
|
+
visibility: :public
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
pins.push method_pin
|
|
71
|
+
|
|
72
|
+
pins.push Pin::InstanceVariable.new(name: "@#{attribute_name}",
|
|
73
|
+
closure: method_pin,
|
|
74
|
+
location: get_node_location(attribute_node),
|
|
75
|
+
comments: attribute_comments(attribute_node, attribute_name))
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
process_children region.update(closure: nspin, visibility: :public)
|
|
79
|
+
|
|
80
|
+
false
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
# @return [DataDefinition::DataDefintionNode, DataDefinition::DataAssignmentNode, nil]
|
|
86
|
+
def data_definition_node
|
|
87
|
+
@data_definition_node ||= if DataDefintionNode.match?(node)
|
|
88
|
+
DataDefintionNode.new(node)
|
|
89
|
+
elsif DataAssignmentNode.match?(node)
|
|
90
|
+
DataAssignmentNode.new(node)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# @param attribute_node [Parser::AST::Node]
|
|
95
|
+
# @param attribute_name [String]
|
|
96
|
+
# @return [String, nil]
|
|
97
|
+
def attribute_comments attribute_node, attribute_name
|
|
98
|
+
data_comments = comments_for(attribute_node)
|
|
99
|
+
return if data_comments.nil? || data_comments.empty?
|
|
100
|
+
|
|
101
|
+
data_comments.split("\n").find do |row|
|
|
102
|
+
row.include?(attribute_name)
|
|
103
|
+
end&.gsub('@param', '@return')&.gsub(attribute_name, '')
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|