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.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/plugins.yml +2 -0
  3. data/.github/workflows/typecheck.yml +3 -1
  4. data/.gitignore +2 -0
  5. data/CHANGELOG.md +62 -0
  6. data/README.md +13 -3
  7. data/lib/solargraph/api_map/index.rb +24 -16
  8. data/lib/solargraph/api_map/store.rb +48 -23
  9. data/lib/solargraph/api_map.rb +175 -77
  10. data/lib/solargraph/bench.rb +17 -1
  11. data/lib/solargraph/complex_type/type_methods.rb +6 -1
  12. data/lib/solargraph/complex_type/unique_type.rb +98 -9
  13. data/lib/solargraph/complex_type.rb +35 -6
  14. data/lib/solargraph/convention/base.rb +3 -3
  15. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +60 -0
  16. data/lib/solargraph/convention/data_definition/data_definition_node.rb +89 -0
  17. data/lib/solargraph/convention/data_definition.rb +104 -0
  18. data/lib/solargraph/convention/gemspec.rb +2 -1
  19. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -0
  20. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
  21. data/lib/solargraph/convention/struct_definition.rb +141 -0
  22. data/lib/solargraph/convention.rb +5 -3
  23. data/lib/solargraph/doc_map.rb +277 -57
  24. data/lib/solargraph/gem_pins.rb +53 -37
  25. data/lib/solargraph/language_server/host/message_worker.rb +10 -7
  26. data/lib/solargraph/language_server/host.rb +12 -2
  27. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  28. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  29. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  30. data/lib/solargraph/library.rb +45 -17
  31. data/lib/solargraph/location.rb +21 -0
  32. data/lib/solargraph/logging.rb +1 -0
  33. data/lib/solargraph/parser/comment_ripper.rb +12 -6
  34. data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
  35. data/lib/solargraph/parser/node_methods.rb +14 -0
  36. data/lib/solargraph/parser/node_processor/base.rb +9 -4
  37. data/lib/solargraph/parser/node_processor.rb +21 -8
  38. data/lib/solargraph/parser/parser_gem/class_methods.rb +16 -14
  39. data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
  40. data/lib/solargraph/parser/parser_gem/node_methods.rb +4 -2
  41. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  42. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  43. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  44. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
  45. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
  46. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  47. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  48. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  49. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  50. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
  51. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  52. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  53. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +4 -1
  54. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
  55. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +42 -0
  56. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
  57. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
  58. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  59. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
  60. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
  61. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  62. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  63. data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
  64. data/lib/solargraph/parser/region.rb +1 -1
  65. data/lib/solargraph/parser.rb +1 -0
  66. data/lib/solargraph/pin/base.rb +316 -28
  67. data/lib/solargraph/pin/base_variable.rb +16 -9
  68. data/lib/solargraph/pin/block.rb +2 -0
  69. data/lib/solargraph/pin/breakable.rb +9 -0
  70. data/lib/solargraph/pin/callable.rb +74 -3
  71. data/lib/solargraph/pin/closure.rb +18 -1
  72. data/lib/solargraph/pin/common.rb +5 -0
  73. data/lib/solargraph/pin/delegated_method.rb +20 -1
  74. data/lib/solargraph/pin/documenting.rb +16 -0
  75. data/lib/solargraph/pin/keyword.rb +7 -2
  76. data/lib/solargraph/pin/local_variable.rb +15 -6
  77. data/lib/solargraph/pin/method.rb +169 -43
  78. data/lib/solargraph/pin/namespace.rb +17 -9
  79. data/lib/solargraph/pin/parameter.rb +60 -11
  80. data/lib/solargraph/pin/proxy_type.rb +12 -6
  81. data/lib/solargraph/pin/reference/override.rb +10 -6
  82. data/lib/solargraph/pin/reference/require.rb +2 -2
  83. data/lib/solargraph/pin/signature.rb +42 -0
  84. data/lib/solargraph/pin/singleton.rb +1 -1
  85. data/lib/solargraph/pin/symbol.rb +3 -2
  86. data/lib/solargraph/pin/until.rb +18 -0
  87. data/lib/solargraph/pin/while.rb +18 -0
  88. data/lib/solargraph/pin.rb +4 -1
  89. data/lib/solargraph/pin_cache.rb +185 -0
  90. data/lib/solargraph/position.rb +9 -0
  91. data/lib/solargraph/range.rb +9 -0
  92. data/lib/solargraph/rbs_map/conversions.rb +221 -67
  93. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  94. data/lib/solargraph/rbs_map/core_map.rb +34 -11
  95. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  96. data/lib/solargraph/rbs_map.rb +74 -17
  97. data/lib/solargraph/shell.rb +17 -18
  98. data/lib/solargraph/source/chain/array.rb +11 -7
  99. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  100. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  101. data/lib/solargraph/source/chain/call.rb +53 -23
  102. data/lib/solargraph/source/chain/constant.rb +1 -1
  103. data/lib/solargraph/source/chain/hash.rb +4 -3
  104. data/lib/solargraph/source/chain/head.rb +1 -1
  105. data/lib/solargraph/source/chain/if.rb +1 -1
  106. data/lib/solargraph/source/chain/link.rb +2 -0
  107. data/lib/solargraph/source/chain/literal.rb +22 -2
  108. data/lib/solargraph/source/chain/or.rb +1 -1
  109. data/lib/solargraph/source/chain/z_super.rb +1 -1
  110. data/lib/solargraph/source/chain.rb +78 -48
  111. data/lib/solargraph/source/source_chainer.rb +2 -2
  112. data/lib/solargraph/source_map/clip.rb +3 -1
  113. data/lib/solargraph/source_map/mapper.rb +9 -5
  114. data/lib/solargraph/source_map.rb +0 -17
  115. data/lib/solargraph/type_checker/checks.rb +4 -0
  116. data/lib/solargraph/type_checker.rb +35 -8
  117. data/lib/solargraph/version.rb +1 -1
  118. data/lib/solargraph/views/_method.erb +10 -10
  119. data/lib/solargraph/views/_namespace.erb +3 -3
  120. data/lib/solargraph/views/document.erb +10 -10
  121. data/lib/solargraph/workspace/config.rb +1 -1
  122. data/lib/solargraph/workspace.rb +23 -5
  123. data/lib/solargraph/yard_map/helpers.rb +29 -1
  124. data/lib/solargraph/yard_map/mapper/to_constant.rb +7 -5
  125. data/lib/solargraph/yard_map/mapper/to_method.rb +53 -18
  126. data/lib/solargraph/yard_map/mapper/to_namespace.rb +9 -7
  127. data/lib/solargraph/yard_map/mapper.rb +4 -3
  128. data/lib/solargraph/yard_map/to_method.rb +4 -2
  129. data/lib/solargraph/yardoc.rb +7 -8
  130. data/lib/solargraph.rb +32 -1
  131. data/rbs/fills/tuple.rbs +150 -0
  132. data/rbs_collection.yaml +19 -0
  133. data/solargraph.gemspec +2 -1
  134. metadata +37 -9
  135. 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
- @items = types.flat_map(&:items).uniq(&:to_s)
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 YARD map.
23
+ # The Environ for a DocMap.
24
24
  # Subclasses can override this method.
25
25
  #
26
- # @param yard_map [YardMap]
26
+ # @param doc_map [DocMap]
27
27
  # @return [Environ]
28
- def global yard_map
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
@@ -12,7 +12,8 @@ module Solargraph
12
12
  'Gem::Specification.new',
13
13
  %(
14
14
  @yieldparam [self]
15
- )
15
+ ),
16
+ source: :gemspec
16
17
  )
17
18
  ]
18
19
  )
@@ -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 [YardMap]
35
+ # @param yard_map [DocMap]
34
36
  # @return [Environ]
35
- def self.for_global(yard_map)
37
+ def self.for_global(doc_map)
36
38
  result = Environ.new
37
39
  @@conventions.each do |conv|
38
- result.merge conv.global(yard_map)
40
+ result.merge conv.global(doc_map)
39
41
  end
40
42
  result
41
43
  end