solargraph 0.54.4 → 0.56.2
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/plugins.yml +2 -0
- data/.github/workflows/typecheck.yml +3 -1
- data/.gitignore +2 -0
- data/CHANGELOG.md +62 -0
- data/README.md +13 -3
- data/lib/solargraph/api_map/index.rb +24 -16
- data/lib/solargraph/api_map/store.rb +48 -23
- data/lib/solargraph/api_map.rb +175 -77
- data/lib/solargraph/bench.rb +17 -1
- data/lib/solargraph/complex_type/type_methods.rb +6 -1
- data/lib/solargraph/complex_type/unique_type.rb +98 -9
- data/lib/solargraph/complex_type.rb +35 -6
- data/lib/solargraph/convention/base.rb +3 -3
- data/lib/solargraph/convention/data_definition/data_assignment_node.rb +60 -0
- data/lib/solargraph/convention/data_definition/data_definition_node.rb +89 -0
- data/lib/solargraph/convention/data_definition.rb +104 -0
- data/lib/solargraph/convention/gemspec.rb +2 -1
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -0
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
- data/lib/solargraph/convention/struct_definition.rb +141 -0
- data/lib/solargraph/convention.rb +5 -3
- data/lib/solargraph/doc_map.rb +277 -57
- data/lib/solargraph/gem_pins.rb +53 -37
- data/lib/solargraph/language_server/host/message_worker.rb +10 -7
- data/lib/solargraph/language_server/host.rb +12 -2
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
- data/lib/solargraph/language_server/message/extended/document.rb +5 -2
- data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
- data/lib/solargraph/library.rb +45 -17
- data/lib/solargraph/location.rb +21 -0
- data/lib/solargraph/logging.rb +1 -0
- data/lib/solargraph/parser/comment_ripper.rb +12 -6
- data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
- data/lib/solargraph/parser/node_methods.rb +14 -0
- data/lib/solargraph/parser/node_processor/base.rb +9 -4
- data/lib/solargraph/parser/node_processor.rb +21 -8
- data/lib/solargraph/parser/parser_gem/class_methods.rb +16 -14
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
- data/lib/solargraph/parser/parser_gem/node_methods.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
- data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +4 -1
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +42 -0
- data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
- data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
- data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
- data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
- data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
- data/lib/solargraph/parser/region.rb +1 -1
- data/lib/solargraph/parser.rb +1 -0
- data/lib/solargraph/pin/base.rb +316 -28
- data/lib/solargraph/pin/base_variable.rb +16 -9
- data/lib/solargraph/pin/block.rb +2 -0
- data/lib/solargraph/pin/breakable.rb +9 -0
- data/lib/solargraph/pin/callable.rb +74 -3
- data/lib/solargraph/pin/closure.rb +18 -1
- data/lib/solargraph/pin/common.rb +5 -0
- data/lib/solargraph/pin/delegated_method.rb +20 -1
- data/lib/solargraph/pin/documenting.rb +16 -0
- data/lib/solargraph/pin/keyword.rb +7 -2
- data/lib/solargraph/pin/local_variable.rb +15 -6
- data/lib/solargraph/pin/method.rb +169 -43
- data/lib/solargraph/pin/namespace.rb +17 -9
- data/lib/solargraph/pin/parameter.rb +60 -11
- data/lib/solargraph/pin/proxy_type.rb +12 -6
- data/lib/solargraph/pin/reference/override.rb +10 -6
- data/lib/solargraph/pin/reference/require.rb +2 -2
- data/lib/solargraph/pin/signature.rb +42 -0
- data/lib/solargraph/pin/singleton.rb +1 -1
- data/lib/solargraph/pin/symbol.rb +3 -2
- data/lib/solargraph/pin/until.rb +18 -0
- data/lib/solargraph/pin/while.rb +18 -0
- data/lib/solargraph/pin.rb +4 -1
- data/lib/solargraph/pin_cache.rb +185 -0
- data/lib/solargraph/position.rb +9 -0
- data/lib/solargraph/range.rb +9 -0
- data/lib/solargraph/rbs_map/conversions.rb +221 -67
- data/lib/solargraph/rbs_map/core_fills.rb +32 -16
- data/lib/solargraph/rbs_map/core_map.rb +34 -11
- data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
- data/lib/solargraph/rbs_map.rb +74 -17
- data/lib/solargraph/shell.rb +17 -18
- data/lib/solargraph/source/chain/array.rb +11 -7
- data/lib/solargraph/source/chain/block_symbol.rb +1 -1
- data/lib/solargraph/source/chain/block_variable.rb +1 -1
- data/lib/solargraph/source/chain/call.rb +53 -23
- data/lib/solargraph/source/chain/constant.rb +1 -1
- data/lib/solargraph/source/chain/hash.rb +4 -3
- data/lib/solargraph/source/chain/head.rb +1 -1
- data/lib/solargraph/source/chain/if.rb +1 -1
- data/lib/solargraph/source/chain/link.rb +2 -0
- data/lib/solargraph/source/chain/literal.rb +22 -2
- data/lib/solargraph/source/chain/or.rb +1 -1
- data/lib/solargraph/source/chain/z_super.rb +1 -1
- data/lib/solargraph/source/chain.rb +78 -48
- data/lib/solargraph/source/source_chainer.rb +2 -2
- data/lib/solargraph/source_map/clip.rb +3 -1
- data/lib/solargraph/source_map/mapper.rb +9 -5
- data/lib/solargraph/source_map.rb +0 -17
- data/lib/solargraph/type_checker/checks.rb +4 -0
- data/lib/solargraph/type_checker.rb +35 -8
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/views/_method.erb +10 -10
- data/lib/solargraph/views/_namespace.erb +3 -3
- data/lib/solargraph/views/document.erb +10 -10
- data/lib/solargraph/workspace/config.rb +1 -1
- data/lib/solargraph/workspace.rb +23 -5
- data/lib/solargraph/yard_map/helpers.rb +29 -1
- data/lib/solargraph/yard_map/mapper/to_constant.rb +7 -5
- data/lib/solargraph/yard_map/mapper/to_method.rb +53 -18
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +9 -7
- data/lib/solargraph/yard_map/mapper.rb +4 -3
- data/lib/solargraph/yard_map/to_method.rb +4 -2
- data/lib/solargraph/yardoc.rb +7 -8
- data/lib/solargraph.rb +32 -1
- data/rbs/fills/tuple.rbs +150 -0
- data/rbs_collection.yaml +19 -0
- data/solargraph.gemspec +2 -1
- metadata +37 -9
- data/lib/solargraph/cache.rb +0 -77
@@ -16,7 +16,13 @@ module Solargraph
|
|
16
16
|
def initialize types = [UniqueType::UNDEFINED]
|
17
17
|
# @todo @items here should not need an annotation
|
18
18
|
# @type [Array<UniqueType>]
|
19
|
-
|
19
|
+
items = types.flat_map(&:items).uniq(&:to_s)
|
20
|
+
if items.any? { |i| i.name == 'false' } && items.any? { |i| i.name == 'true' }
|
21
|
+
items.delete_if { |i| i.name == 'false' || i.name == 'true' }
|
22
|
+
items.unshift(ComplexType::BOOLEAN)
|
23
|
+
end
|
24
|
+
items = [UniqueType::UNDEFINED] if items.any?(&:undefined?)
|
25
|
+
@items = items
|
20
26
|
end
|
21
27
|
|
22
28
|
# @sg-ignore Fix "Not enough arguments to Module#protected"
|
@@ -70,7 +76,7 @@ module Solargraph
|
|
70
76
|
end
|
71
77
|
|
72
78
|
# @yieldparam [UniqueType]
|
73
|
-
# @return [Array]
|
79
|
+
# @return [Array<UniqueType>]
|
74
80
|
def map &block
|
75
81
|
@items.map &block
|
76
82
|
end
|
@@ -93,6 +99,12 @@ module Solargraph
|
|
93
99
|
end
|
94
100
|
end
|
95
101
|
|
102
|
+
# @param atype [ComplexType] type which may be assigned to this type
|
103
|
+
# @param api_map [ApiMap] The ApiMap that performs qualification
|
104
|
+
def can_assign?(api_map, atype)
|
105
|
+
any? { |ut| ut.can_assign?(api_map, atype) }
|
106
|
+
end
|
107
|
+
|
96
108
|
# @return [Integer]
|
97
109
|
def length
|
98
110
|
@items.length
|
@@ -103,10 +115,6 @@ module Solargraph
|
|
103
115
|
@items
|
104
116
|
end
|
105
117
|
|
106
|
-
def tags
|
107
|
-
@items.map(&:tag).join(', ')
|
108
|
-
end
|
109
|
-
|
110
118
|
# @param index [Integer]
|
111
119
|
# @return [UniqueType]
|
112
120
|
def [](index)
|
@@ -147,6 +155,23 @@ module Solargraph
|
|
147
155
|
map(&:tag).join(', ')
|
148
156
|
end
|
149
157
|
|
158
|
+
def tags
|
159
|
+
map(&:tag).join(', ')
|
160
|
+
end
|
161
|
+
|
162
|
+
def simple_tags
|
163
|
+
simplify_literals.tags
|
164
|
+
end
|
165
|
+
|
166
|
+
def literal?
|
167
|
+
@items.any?(&:literal?)
|
168
|
+
end
|
169
|
+
|
170
|
+
# @return [ComplexType]
|
171
|
+
def downcast_to_literal_if_possible
|
172
|
+
ComplexType.new(items.map(&:downcast_to_literal_if_possible))
|
173
|
+
end
|
174
|
+
|
150
175
|
def desc
|
151
176
|
rooted_tags
|
152
177
|
end
|
@@ -175,6 +200,10 @@ module Solargraph
|
|
175
200
|
any?(&:generic?)
|
176
201
|
end
|
177
202
|
|
203
|
+
def simplify_literals
|
204
|
+
ComplexType.new(map(&:simplify_literals))
|
205
|
+
end
|
206
|
+
|
178
207
|
# @param new_name [String, nil]
|
179
208
|
# @yieldparam t [UniqueType]
|
180
209
|
# @yieldreturn [UniqueType]
|
@@ -20,12 +20,12 @@ module Solargraph
|
|
20
20
|
EMPTY_ENVIRON
|
21
21
|
end
|
22
22
|
|
23
|
-
# The Environ for a
|
23
|
+
# The Environ for a DocMap.
|
24
24
|
# Subclasses can override this method.
|
25
25
|
#
|
26
|
-
# @param
|
26
|
+
# @param doc_map [DocMap]
|
27
27
|
# @return [Environ]
|
28
|
-
def global
|
28
|
+
def global doc_map
|
29
29
|
EMPTY_ENVIRON
|
30
30
|
end
|
31
31
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
module Convention
|
5
|
+
module DataDefinition
|
6
|
+
# A node wrapper for a Data definition via const assignment.
|
7
|
+
# @example
|
8
|
+
# MyData = Data.new(:bar, :baz) do
|
9
|
+
# def foo
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
class DataAssignmentNode < DataDefintionNode
|
13
|
+
class << self
|
14
|
+
# @example
|
15
|
+
# s(:casgn, nil, :Foo,
|
16
|
+
# s(:block,
|
17
|
+
# s(:send,
|
18
|
+
# s(:const, nil, :Data), :define,
|
19
|
+
# s(:sym, :bar),
|
20
|
+
# s(:sym, :baz)),
|
21
|
+
# s(:args),
|
22
|
+
# s(:def, :foo,
|
23
|
+
# s(:args),
|
24
|
+
# s(:send, nil, :bar))))
|
25
|
+
def match?(node)
|
26
|
+
return false unless node&.type == :casgn
|
27
|
+
return false if node.children[2].nil?
|
28
|
+
|
29
|
+
data_node = if node.children[2].type == :block
|
30
|
+
node.children[2].children[0]
|
31
|
+
else
|
32
|
+
node.children[2]
|
33
|
+
end
|
34
|
+
|
35
|
+
data_definition_node?(data_node)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def class_name
|
40
|
+
if node.children[0]
|
41
|
+
Parser::NodeMethods.unpack_name(node.children[0]) + "::#{node.children[1]}"
|
42
|
+
else
|
43
|
+
node.children[1].to_s
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# @return [Parser::AST::Node]
|
50
|
+
def data_node
|
51
|
+
if node.children[2].type == :block
|
52
|
+
node.children[2].children[0]
|
53
|
+
else
|
54
|
+
node.children[2]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
module Convention
|
5
|
+
module DataDefinition
|
6
|
+
# A node wrapper for a Data definition via inheritance.
|
7
|
+
# @example
|
8
|
+
# class MyData < Data.new(:bar, :baz)
|
9
|
+
# def foo
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
class DataDefintionNode
|
13
|
+
class << self
|
14
|
+
# @example
|
15
|
+
# s(:class,
|
16
|
+
# s(:const, nil, :Foo),
|
17
|
+
# s(:send,
|
18
|
+
# s(:const, nil, :Data), :define,
|
19
|
+
# s(:sym, :bar),
|
20
|
+
# s(:sym, :baz)),
|
21
|
+
# s(:hash,
|
22
|
+
# s(:pair,
|
23
|
+
# s(:sym, :keyword_init),
|
24
|
+
# s(:true)))),
|
25
|
+
# s(:def, :foo,
|
26
|
+
# s(:args),
|
27
|
+
# s(:send, nil, :bar)))
|
28
|
+
def match?(node)
|
29
|
+
return false unless node&.type == :class
|
30
|
+
|
31
|
+
data_definition_node?(node.children[1])
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# @param data_node [Parser::AST::Node]
|
37
|
+
# @return [Boolean]
|
38
|
+
def data_definition_node?(data_node)
|
39
|
+
return false unless data_node.is_a?(::Parser::AST::Node)
|
40
|
+
return false unless data_node&.type == :send
|
41
|
+
return false unless data_node.children[0]&.type == :const
|
42
|
+
return false unless data_node.children[0].children[1] == :Data
|
43
|
+
return false unless data_node.children[1] == :define
|
44
|
+
|
45
|
+
true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Parser::AST::Node]
|
50
|
+
def initialize(node)
|
51
|
+
@node = node
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [String]
|
55
|
+
def class_name
|
56
|
+
Parser::NodeMethods.unpack_name(node)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Array<Array(Parser::AST::Node, String)>]
|
60
|
+
def attributes
|
61
|
+
data_attribute_nodes.map do |data_def_param|
|
62
|
+
next unless data_def_param.type == :sym
|
63
|
+
[data_def_param, data_def_param.children[0].to_s]
|
64
|
+
end.compact
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [Parser::AST::Node]
|
68
|
+
def body_node
|
69
|
+
node.children[2]
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# @return [Parser::AST::Node]
|
75
|
+
attr_reader :node
|
76
|
+
|
77
|
+
# @return [Parser::AST::Node]
|
78
|
+
def data_node
|
79
|
+
node.children[1]
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Array<Parser::AST::Node>]
|
83
|
+
def data_attribute_nodes
|
84
|
+
data_node.children[2..-1]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
module Convention
|
5
|
+
module DataDefinition
|
6
|
+
autoload :DataDefintionNode, 'solargraph/convention/data_definition/data_definition_node'
|
7
|
+
autoload :DataAssignmentNode, 'solargraph/convention/data_definition/data_assignment_node'
|
8
|
+
|
9
|
+
module NodeProcessors
|
10
|
+
class DataNode < Parser::NodeProcessor::Base
|
11
|
+
# @return [Boolean] continue processing the next processor of the same node.
|
12
|
+
def process
|
13
|
+
return true if data_definition_node.nil?
|
14
|
+
|
15
|
+
loc = get_node_location(node)
|
16
|
+
nspin = Solargraph::Pin::Namespace.new(
|
17
|
+
type: :class,
|
18
|
+
location: loc,
|
19
|
+
closure: region.closure,
|
20
|
+
name: data_definition_node.class_name,
|
21
|
+
comments: comments_for(node),
|
22
|
+
visibility: :public,
|
23
|
+
gates: region.closure.gates.freeze
|
24
|
+
)
|
25
|
+
pins.push nspin
|
26
|
+
|
27
|
+
# define initialize method
|
28
|
+
initialize_method_pin = Pin::Method.new(
|
29
|
+
name: 'initialize',
|
30
|
+
parameters: [],
|
31
|
+
scope: :instance,
|
32
|
+
location: get_node_location(node),
|
33
|
+
closure: nspin,
|
34
|
+
visibility: :private,
|
35
|
+
comments: comments_for(node)
|
36
|
+
)
|
37
|
+
|
38
|
+
# TODO: Support both arg and kwarg initializers for Data.define
|
39
|
+
# Solargraph::SourceMap::Clip#complete_keyword_parameters does not seem to currently take into account [Pin::Method#signatures] hence we only one for :kwarg
|
40
|
+
pins.push initialize_method_pin
|
41
|
+
|
42
|
+
data_definition_node.attributes.map do |attribute_node, attribute_name|
|
43
|
+
initialize_method_pin.parameters.push(
|
44
|
+
Pin::Parameter.new(
|
45
|
+
name: attribute_name,
|
46
|
+
decl: :kwarg,
|
47
|
+
location: get_node_location(attribute_node),
|
48
|
+
closure: initialize_method_pin
|
49
|
+
)
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
# define attribute readers and instance variables
|
54
|
+
data_definition_node.attributes.each do |attribute_node, attribute_name|
|
55
|
+
name = attribute_name.to_s
|
56
|
+
method_pin = Pin::Method.new(
|
57
|
+
name: name,
|
58
|
+
parameters: [],
|
59
|
+
scope: :instance,
|
60
|
+
location: get_node_location(attribute_node),
|
61
|
+
closure: nspin,
|
62
|
+
comments: attribute_comments(attribute_node, attribute_name),
|
63
|
+
visibility: :public
|
64
|
+
)
|
65
|
+
|
66
|
+
pins.push method_pin
|
67
|
+
|
68
|
+
pins.push Pin::InstanceVariable.new(name: "@#{attribute_name}",
|
69
|
+
closure: method_pin,
|
70
|
+
location: get_node_location(attribute_node),
|
71
|
+
comments: attribute_comments(attribute_node, attribute_name))
|
72
|
+
end
|
73
|
+
|
74
|
+
process_children region.update(closure: nspin, visibility: :public)
|
75
|
+
|
76
|
+
false
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# @return [DataDefintionNode, nil]
|
82
|
+
def data_definition_node
|
83
|
+
@data_definition_node ||= if DataDefintionNode.match?(node)
|
84
|
+
DataDefintionNode.new(node)
|
85
|
+
elsif DataAssignmentNode.match?(node)
|
86
|
+
DataAssignmentNode.new(node)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# @param attribute_node [Parser::AST::Node]
|
91
|
+
# @return [String, nil]
|
92
|
+
def attribute_comments(attribute_node, attribute_name)
|
93
|
+
data_comments = comments_for(attribute_node)
|
94
|
+
return if data_comments.nil? || data_comments.empty?
|
95
|
+
|
96
|
+
data_comments.split("\n").find do |row|
|
97
|
+
row.include?(attribute_name)
|
98
|
+
end&.gsub('@param', '@return')&.gsub(attribute_name, '')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
module Convention
|
5
|
+
module StructDefinition
|
6
|
+
# A node wrapper for a Struct definition via const assignment.
|
7
|
+
# @example
|
8
|
+
# MyStruct = Struct.new(:bar, :baz) do
|
9
|
+
# def foo
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
class StructAssignmentNode < StructDefintionNode
|
13
|
+
class << self
|
14
|
+
# @example
|
15
|
+
# s(:casgn, nil, :Foo,
|
16
|
+
# s(:block,
|
17
|
+
# s(:send,
|
18
|
+
# s(:const, nil, :Struct), :new,
|
19
|
+
# s(:sym, :bar),
|
20
|
+
# s(:sym, :baz)),
|
21
|
+
# s(:args),
|
22
|
+
# s(:def, :foo,
|
23
|
+
# s(:args),
|
24
|
+
# s(:send, nil, :bar))))
|
25
|
+
def match?(node)
|
26
|
+
return false unless node&.type == :casgn
|
27
|
+
return false if node.children[2].nil?
|
28
|
+
|
29
|
+
struct_node = if node.children[2].type == :block
|
30
|
+
node.children[2].children[0]
|
31
|
+
else
|
32
|
+
node.children[2]
|
33
|
+
end
|
34
|
+
|
35
|
+
struct_definition_node?(struct_node)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def class_name
|
40
|
+
if node.children[0]
|
41
|
+
Parser::NodeMethods.unpack_name(node.children[0]) + "::#{node.children[1]}"
|
42
|
+
else
|
43
|
+
node.children[1].to_s
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# @return [Parser::AST::Node]
|
50
|
+
def struct_node
|
51
|
+
if node.children[2].type == :block
|
52
|
+
node.children[2].children[0]
|
53
|
+
else
|
54
|
+
node.children[2]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
module Convention
|
5
|
+
module StructDefinition
|
6
|
+
# A node wrapper for a Struct definition via inheritance.
|
7
|
+
# @example
|
8
|
+
# class MyStruct < Struct.new(:bar, :baz)
|
9
|
+
# def foo
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
class StructDefintionNode
|
13
|
+
class << self
|
14
|
+
# @example
|
15
|
+
# s(:class,
|
16
|
+
# s(:const, nil, :Foo),
|
17
|
+
# s(:send,
|
18
|
+
# s(:const, nil, :Struct), :new,
|
19
|
+
# s(:sym, :bar),
|
20
|
+
# s(:sym, :baz)),
|
21
|
+
# s(:hash,
|
22
|
+
# s(:pair,
|
23
|
+
# s(:sym, :keyword_init),
|
24
|
+
# s(:true)))),
|
25
|
+
# s(:def, :foo,
|
26
|
+
# s(:args),
|
27
|
+
# s(:send, nil, :bar)))
|
28
|
+
def match?(node)
|
29
|
+
return false unless node&.type == :class
|
30
|
+
|
31
|
+
struct_definition_node?(node.children[1])
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# @param struct_node [Parser::AST::Node]
|
37
|
+
# @return [Boolean]
|
38
|
+
def struct_definition_node?(struct_node)
|
39
|
+
return false unless struct_node.is_a?(::Parser::AST::Node)
|
40
|
+
return false unless struct_node&.type == :send
|
41
|
+
return false unless struct_node.children[0]&.type == :const
|
42
|
+
return false unless struct_node.children[0].children[1] == :Struct
|
43
|
+
return false unless struct_node.children[1] == :new
|
44
|
+
|
45
|
+
true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Parser::AST::Node]
|
50
|
+
def initialize(node)
|
51
|
+
@node = node
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [String]
|
55
|
+
def class_name
|
56
|
+
Parser::NodeMethods.unpack_name(node)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Array<Array(Parser::AST::Node, String)>]
|
60
|
+
def attributes
|
61
|
+
struct_attribute_nodes.map do |struct_def_param|
|
62
|
+
next unless struct_def_param.type == :sym
|
63
|
+
[struct_def_param, struct_def_param.children[0].to_s]
|
64
|
+
end.compact
|
65
|
+
end
|
66
|
+
|
67
|
+
def keyword_init?
|
68
|
+
keyword_init_param = struct_attribute_nodes.find do |struct_def_param|
|
69
|
+
struct_def_param.type == :hash && struct_def_param.children[0].type == :pair &&
|
70
|
+
struct_def_param.children[0].children[0].children[0] == :keyword_init
|
71
|
+
end
|
72
|
+
|
73
|
+
return false if keyword_init_param.nil?
|
74
|
+
|
75
|
+
keyword_init_param.children[0].children[1].type == :true
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [Parser::AST::Node]
|
79
|
+
def body_node
|
80
|
+
node.children[2]
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# @return [Parser::AST::Node]
|
86
|
+
attr_reader :node
|
87
|
+
|
88
|
+
# @return [Parser::AST::Node]
|
89
|
+
def struct_node
|
90
|
+
node.children[1]
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [Array<Parser::AST::Node>]
|
94
|
+
def struct_attribute_nodes
|
95
|
+
struct_node.children[2..-1]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
module Convention
|
5
|
+
module StructDefinition
|
6
|
+
autoload :StructDefintionNode, 'solargraph/convention/struct_definition/struct_definition_node'
|
7
|
+
autoload :StructAssignmentNode, 'solargraph/convention/struct_definition/struct_assignment_node'
|
8
|
+
|
9
|
+
module NodeProcessors
|
10
|
+
class StructNode < Parser::NodeProcessor::Base
|
11
|
+
# @return [Boolean] continue processing the next processor of the same node.
|
12
|
+
def process
|
13
|
+
return true if struct_definition_node.nil?
|
14
|
+
|
15
|
+
loc = get_node_location(node)
|
16
|
+
nspin = Solargraph::Pin::Namespace.new(
|
17
|
+
type: :class,
|
18
|
+
location: loc,
|
19
|
+
closure: region.closure,
|
20
|
+
name: struct_definition_node.class_name,
|
21
|
+
docstring: docstring,
|
22
|
+
visibility: :public,
|
23
|
+
gates: region.closure.gates.freeze
|
24
|
+
)
|
25
|
+
pins.push nspin
|
26
|
+
|
27
|
+
# define initialize method
|
28
|
+
initialize_method_pin = Pin::Method.new(
|
29
|
+
name: 'initialize',
|
30
|
+
parameters: [],
|
31
|
+
scope: :instance,
|
32
|
+
location: get_node_location(node),
|
33
|
+
closure: nspin,
|
34
|
+
visibility: :private,
|
35
|
+
docstring: docstring
|
36
|
+
)
|
37
|
+
|
38
|
+
pins.push initialize_method_pin
|
39
|
+
|
40
|
+
struct_definition_node.attributes.map do |attribute_node, attribute_name|
|
41
|
+
initialize_method_pin.parameters.push(
|
42
|
+
Pin::Parameter.new(
|
43
|
+
name: attribute_name,
|
44
|
+
decl: struct_definition_node.keyword_init? ? :kwarg : :arg,
|
45
|
+
location: get_node_location(attribute_node),
|
46
|
+
closure: initialize_method_pin
|
47
|
+
)
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
# define attribute accessors and instance variables
|
52
|
+
struct_definition_node.attributes.each do |attribute_node, attribute_name|
|
53
|
+
[attribute_name, "#{attribute_name}="].each do |name|
|
54
|
+
docs = docstring.tags.find { |t| t.tag_name == 'param' && t.name == attribute_name }
|
55
|
+
|
56
|
+
method_pin = Pin::Method.new(
|
57
|
+
name: name,
|
58
|
+
parameters: [],
|
59
|
+
scope: :instance,
|
60
|
+
location: get_node_location(attribute_node),
|
61
|
+
closure: nspin,
|
62
|
+
# even assignments return the value
|
63
|
+
comments: attribute_comment(docs, false),
|
64
|
+
visibility: :public
|
65
|
+
)
|
66
|
+
|
67
|
+
if name.end_with?('=')
|
68
|
+
method_pin.parameters << Pin::Parameter.new(
|
69
|
+
name: attribute_name,
|
70
|
+
location: get_node_location(attribute_node),
|
71
|
+
closure: nspin,
|
72
|
+
comments: attribute_comment(docs, true)
|
73
|
+
)
|
74
|
+
|
75
|
+
pins.push Pin::InstanceVariable.new(name: "@#{attribute_name}",
|
76
|
+
closure: method_pin,
|
77
|
+
location: get_node_location(attribute_node),
|
78
|
+
comments: attribute_comment(docs, false))
|
79
|
+
end
|
80
|
+
|
81
|
+
pins.push method_pin
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
process_children region.update(closure: nspin, visibility: :public)
|
86
|
+
false
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# @return [StructDefintionNode, nil]
|
92
|
+
def struct_definition_node
|
93
|
+
@struct_definition_node ||= if StructDefintionNode.match?(node)
|
94
|
+
StructDefintionNode.new(node)
|
95
|
+
elsif StructAssignmentNode.match?(node)
|
96
|
+
StructAssignmentNode.new(node)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Gets/generates the relevant docstring for this struct & it's attributes
|
101
|
+
# @return [YARD::Docstring]
|
102
|
+
def docstring
|
103
|
+
@docstring ||= parse_comments
|
104
|
+
end
|
105
|
+
|
106
|
+
# Parses any relevant comments for a struct int a yard docstring
|
107
|
+
# @return [YARD::Docstring]
|
108
|
+
def parse_comments
|
109
|
+
struct_comments = comments_for(node) || ''
|
110
|
+
struct_definition_node.attributes.each do |attr_node, attr_name|
|
111
|
+
comment = comments_for(attr_node)
|
112
|
+
next if comment.nil?
|
113
|
+
|
114
|
+
# We should support specific comments for an attribute, and that can be either a @return on an @param
|
115
|
+
# But since we merge into the struct_comments, then we should interpret either as a param
|
116
|
+
comment = '@param ' + attr_name + comment[7..] if comment.start_with?('@return')
|
117
|
+
|
118
|
+
struct_comments += "\n#{comment}"
|
119
|
+
end
|
120
|
+
|
121
|
+
Solargraph::Source.parse_docstring(struct_comments).to_docstring
|
122
|
+
end
|
123
|
+
|
124
|
+
# @param tag [YARD::Tags::Tag, nil] The param tag for this attribute. If nil, this method is a no-op
|
125
|
+
# @param for_setter [Boolean] If true, will return a @param tag instead of a @return tag
|
126
|
+
def attribute_comment(tag, for_setter)
|
127
|
+
return "" if tag.nil?
|
128
|
+
|
129
|
+
suffix = "[#{tag.types&.join(',') || 'undefined'}] #{tag.text}"
|
130
|
+
|
131
|
+
if for_setter
|
132
|
+
"@param #{tag.name} #{suffix}"
|
133
|
+
else
|
134
|
+
"@return #{suffix}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -10,6 +10,8 @@ module Solargraph
|
|
10
10
|
autoload :Gemfile, 'solargraph/convention/gemfile'
|
11
11
|
autoload :Gemspec, 'solargraph/convention/gemspec'
|
12
12
|
autoload :Rakefile, 'solargraph/convention/rakefile'
|
13
|
+
autoload :StructDefinition, 'solargraph/convention/struct_definition'
|
14
|
+
autoload :DataDefinition, 'solargraph/convention/data_definition'
|
13
15
|
|
14
16
|
# @type [Set<Convention::Base>]
|
15
17
|
@@conventions = Set.new
|
@@ -30,12 +32,12 @@ module Solargraph
|
|
30
32
|
result
|
31
33
|
end
|
32
34
|
|
33
|
-
# @param yard_map [
|
35
|
+
# @param yard_map [DocMap]
|
34
36
|
# @return [Environ]
|
35
|
-
def self.for_global(
|
37
|
+
def self.for_global(doc_map)
|
36
38
|
result = Environ.new
|
37
39
|
@@conventions.each do |conv|
|
38
|
-
result.merge conv.global(
|
40
|
+
result.merge conv.global(doc_map)
|
39
41
|
end
|
40
42
|
result
|
41
43
|
end
|