solargraph 0.51.2 → 0.54.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 (183) 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 +127 -5
  7. data/README.md +13 -16
  8. data/SPONSORS.md +1 -7
  9. data/lib/solargraph/api_map/cache.rb +50 -20
  10. data/lib/solargraph/api_map/source_to_yard.rb +17 -10
  11. data/lib/solargraph/api_map/store.rb +60 -15
  12. data/lib/solargraph/api_map.rb +282 -123
  13. data/lib/solargraph/bench.rb +3 -2
  14. data/lib/solargraph/cache.rb +29 -5
  15. data/lib/solargraph/complex_type/type_methods.rb +122 -39
  16. data/lib/solargraph/complex_type/unique_type.rb +310 -76
  17. data/lib/solargraph/complex_type.rb +166 -44
  18. data/lib/solargraph/convention.rb +0 -1
  19. data/lib/solargraph/converters/dd.rb +5 -0
  20. data/lib/solargraph/converters/dl.rb +3 -0
  21. data/lib/solargraph/converters/dt.rb +3 -0
  22. data/lib/solargraph/diagnostics/rubocop.rb +8 -7
  23. data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -0
  24. data/lib/solargraph/diagnostics/type_check.rb +1 -0
  25. data/lib/solargraph/diagnostics.rb +2 -2
  26. data/lib/solargraph/doc_map.rb +187 -0
  27. data/lib/solargraph/gem_pins.rb +72 -0
  28. data/lib/solargraph/language_server/host/diagnoser.rb +2 -2
  29. data/lib/solargraph/language_server/host/dispatch.rb +22 -5
  30. data/lib/solargraph/language_server/host/message_worker.rb +49 -5
  31. data/lib/solargraph/language_server/host/sources.rb +8 -65
  32. data/lib/solargraph/language_server/host.rb +65 -84
  33. data/lib/solargraph/language_server/message/base.rb +19 -12
  34. data/lib/solargraph/language_server/message/completion_item/resolve.rb +3 -1
  35. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +13 -1
  36. data/lib/solargraph/language_server/message/initialize.rb +19 -2
  37. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  38. data/lib/solargraph/language_server/message/text_document/definition.rb +3 -3
  39. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
  40. data/lib/solargraph/language_server/message/text_document/formatting.rb +1 -0
  41. data/lib/solargraph/language_server/message/text_document/hover.rb +3 -1
  42. data/lib/solargraph/language_server/message/text_document/type_definition.rb +3 -3
  43. data/lib/solargraph/language_server/message/text_document.rb +0 -1
  44. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +5 -0
  45. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
  46. data/lib/solargraph/language_server/progress.rb +135 -0
  47. data/lib/solargraph/language_server/transport/adapter.rb +16 -1
  48. data/lib/solargraph/language_server/transport/data_reader.rb +2 -0
  49. data/lib/solargraph/language_server.rb +1 -0
  50. data/lib/solargraph/library.rb +207 -111
  51. data/lib/solargraph/location.rb +15 -1
  52. data/lib/solargraph/page.rb +6 -0
  53. data/lib/solargraph/parser/comment_ripper.rb +4 -0
  54. data/lib/solargraph/parser/node_methods.rb +47 -7
  55. data/lib/solargraph/parser/node_processor/base.rb +11 -1
  56. data/lib/solargraph/parser/node_processor.rb +1 -0
  57. data/lib/solargraph/parser/{legacy → parser_gem}/class_methods.rb +31 -9
  58. data/lib/solargraph/parser/{legacy → parser_gem}/flawed_builder.rb +3 -1
  59. data/lib/solargraph/parser/{legacy → parser_gem}/node_chainer.rb +62 -43
  60. data/lib/solargraph/parser/parser_gem/node_methods.rb +495 -0
  61. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/alias_node.rb +1 -1
  62. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +57 -0
  63. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/begin_node.rb +1 -1
  64. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/block_node.rb +3 -2
  65. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/casgn_node.rb +2 -2
  66. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/cvasgn_node.rb +1 -1
  67. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/def_node.rb +7 -20
  68. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/defs_node.rb +2 -2
  69. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/gvasgn_node.rb +1 -1
  70. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/ivasgn_node.rb +2 -2
  71. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/lvasgn_node.rb +4 -4
  72. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +53 -0
  73. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/namespace_node.rb +2 -2
  74. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/orasgn_node.rb +1 -1
  75. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/resbody_node.rb +3 -3
  76. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/sclass_node.rb +1 -1
  77. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/send_node.rb +8 -6
  78. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/sym_node.rb +1 -1
  79. data/lib/solargraph/parser/parser_gem/node_processors.rb +56 -0
  80. data/lib/solargraph/parser/parser_gem.rb +12 -0
  81. data/lib/solargraph/parser/region.rb +1 -1
  82. data/lib/solargraph/parser/snippet.rb +2 -0
  83. data/lib/solargraph/parser.rb +8 -12
  84. data/lib/solargraph/pin/base.rb +78 -10
  85. data/lib/solargraph/pin/base_variable.rb +40 -7
  86. data/lib/solargraph/pin/block.rb +69 -46
  87. data/lib/solargraph/pin/callable.rb +147 -0
  88. data/lib/solargraph/pin/closure.rb +23 -3
  89. data/lib/solargraph/pin/common.rb +6 -6
  90. data/lib/solargraph/pin/conversions.rb +36 -5
  91. data/lib/solargraph/pin/delegated_method.rb +6 -2
  92. data/lib/solargraph/pin/documenting.rb +25 -32
  93. data/lib/solargraph/pin/instance_variable.rb +6 -2
  94. data/lib/solargraph/pin/local_variable.rb +13 -1
  95. data/lib/solargraph/pin/method.rb +205 -32
  96. data/lib/solargraph/pin/namespace.rb +20 -7
  97. data/lib/solargraph/pin/parameter.rb +41 -36
  98. data/lib/solargraph/pin/proxy_type.rb +1 -1
  99. data/lib/solargraph/pin/reference/override.rb +2 -2
  100. data/lib/solargraph/pin/reference.rb +8 -0
  101. data/lib/solargraph/pin/search.rb +3 -3
  102. data/lib/solargraph/pin/signature.rb +8 -14
  103. data/lib/solargraph/pin.rb +4 -2
  104. data/lib/solargraph/range.rb +4 -6
  105. data/lib/solargraph/rbs_map/conversions.rb +326 -76
  106. data/lib/solargraph/rbs_map/core_fills.rb +16 -33
  107. data/lib/solargraph/rbs_map/core_map.rb +3 -13
  108. data/lib/solargraph/rbs_map/stdlib_map.rb +2 -8
  109. data/lib/solargraph/rbs_map.rb +32 -13
  110. data/lib/solargraph/shell.rb +95 -72
  111. data/lib/solargraph/source/chain/array.rb +33 -0
  112. data/lib/solargraph/source/chain/block_symbol.rb +13 -0
  113. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  114. data/lib/solargraph/source/chain/call.rb +152 -69
  115. data/lib/solargraph/source/chain/constant.rb +15 -1
  116. data/lib/solargraph/source/chain/if.rb +23 -0
  117. data/lib/solargraph/source/chain/link.rb +17 -2
  118. data/lib/solargraph/source/chain/or.rb +2 -2
  119. data/lib/solargraph/source/chain/z_super.rb +3 -3
  120. data/lib/solargraph/source/chain.rb +85 -26
  121. data/lib/solargraph/source/change.rb +3 -0
  122. data/lib/solargraph/source/cursor.rb +16 -2
  123. data/lib/solargraph/source/source_chainer.rb +8 -5
  124. data/lib/solargraph/source/updater.rb +1 -0
  125. data/lib/solargraph/source.rb +120 -148
  126. data/lib/solargraph/source_map/clip.rb +16 -27
  127. data/lib/solargraph/source_map/data.rb +30 -0
  128. data/lib/solargraph/source_map/mapper.rb +15 -3
  129. data/lib/solargraph/source_map.rb +48 -24
  130. data/lib/solargraph/type_checker/checks.rb +10 -2
  131. data/lib/solargraph/type_checker/rules.rb +6 -1
  132. data/lib/solargraph/type_checker.rb +150 -39
  133. data/lib/solargraph/version.rb +1 -1
  134. data/lib/solargraph/views/environment.erb +3 -5
  135. data/lib/solargraph/workspace/config.rb +9 -6
  136. data/lib/solargraph/workspace.rb +30 -3
  137. data/lib/solargraph/yard_map/cache.rb +6 -0
  138. data/lib/solargraph/yard_map/helpers.rb +1 -1
  139. data/lib/solargraph/yard_map/mapper/to_method.rb +16 -3
  140. data/lib/solargraph/yard_map/mapper.rb +1 -1
  141. data/lib/solargraph/yard_map/to_method.rb +11 -4
  142. data/lib/solargraph/yard_map.rb +1 -292
  143. data/lib/solargraph/yard_tags.rb +20 -0
  144. data/lib/solargraph/yardoc.rb +52 -0
  145. data/lib/solargraph.rb +6 -4
  146. data/solargraph.gemspec +7 -6
  147. metadata +71 -82
  148. data/lib/solargraph/api_map/bundler_methods.rb +0 -22
  149. data/lib/solargraph/documentor.rb +0 -76
  150. data/lib/solargraph/language_server/host/cataloger.rb +0 -56
  151. data/lib/solargraph/parser/legacy/node_methods.rb +0 -325
  152. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +0 -23
  153. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +0 -50
  154. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +0 -15
  155. data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +0 -18
  156. data/lib/solargraph/parser/legacy/node_processors.rb +0 -55
  157. data/lib/solargraph/parser/legacy.rb +0 -12
  158. data/lib/solargraph/parser/rubyvm/class_methods.rb +0 -153
  159. data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -160
  160. data/lib/solargraph/parser/rubyvm/node_methods.rb +0 -317
  161. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +0 -85
  162. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +0 -42
  163. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +0 -33
  164. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +0 -23
  165. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +0 -75
  166. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +0 -68
  167. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +0 -23
  168. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +0 -38
  169. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +0 -39
  170. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +0 -20
  171. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +0 -27
  172. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +0 -39
  173. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +0 -26
  174. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +0 -15
  175. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +0 -51
  176. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +0 -32
  177. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +0 -15
  178. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +0 -279
  179. data/lib/solargraph/parser/rubyvm/node_processors.rb +0 -64
  180. data/lib/solargraph/parser/rubyvm/node_wrapper.rb +0 -47
  181. data/lib/solargraph/parser/rubyvm.rb +0 -40
  182. data/lib/solargraph/rbs_map/core_signs.rb +0 -33
  183. data/lib/yard-solargraph.rb +0 -33
@@ -4,15 +4,31 @@ module Solargraph
4
4
  # A container for type data based on YARD type tags.
5
5
  #
6
6
  class ComplexType
7
+ GENERIC_TAG_NAME = 'generic'.freeze
7
8
  # @!parse
8
9
  # include TypeMethods
9
10
 
10
11
  autoload :TypeMethods, 'solargraph/complex_type/type_methods'
11
12
  autoload :UniqueType, 'solargraph/complex_type/unique_type'
12
13
 
13
- # @param types [Array<UniqueType>]
14
+ # @param types [Array<UniqueType, ComplexType>]
14
15
  def initialize types = [UniqueType::UNDEFINED]
15
- @items = types.uniq(&:to_s)
16
+ # @todo @items here should not need an annotation
17
+ # @type [Array<UniqueType>]
18
+ @items = types.flat_map(&:items).uniq(&:to_s)
19
+ end
20
+
21
+ def eql?(other)
22
+ self.class == other.class &&
23
+ @items == other.items
24
+ end
25
+
26
+ def ==(other)
27
+ self.eql?(other)
28
+ end
29
+
30
+ def hash
31
+ [self.class, @items].hash
16
32
  end
17
33
 
18
34
  # @param api_map [ApiMap]
@@ -21,34 +37,61 @@ module Solargraph
21
37
  def qualify api_map, context = ''
22
38
  red = reduce_object
23
39
  types = red.items.map do |t|
24
- next t if ['Boolean', 'nil', 'void', 'undefined'].include?(t.name)
40
+ next t if ['nil', 'void', 'undefined'].include?(t.name)
41
+ next t if ['::Boolean'].include?(t.rooted_name)
25
42
  t.qualify api_map, context
26
43
  end
27
44
  ComplexType.new(types).reduce_object
28
45
  end
29
46
 
47
+ # @param generics_to_resolve [Enumerable<String>]]
48
+ # @param context_type [UniqueType, nil]
49
+ # @param resolved_generic_values [Hash{String => ComplexType}] Added to as types are encountered or resolved
50
+ # @return [self]
51
+ def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
52
+ return self unless generic?
53
+
54
+ ComplexType.new(@items.map { |i| i.resolve_generics_from_context(generics_to_resolve, context_type, resolved_generic_values: resolved_generic_values) })
55
+ end
56
+
57
+ # @return [UniqueType]
30
58
  def first
31
59
  @items.first
32
60
  end
33
61
 
62
+ # @return [String]
34
63
  def to_rbs
35
- ((@items.length > 1 ? '(' : '') + @items.map do |item|
36
- "#{item.namespace}#{item.parameters? ? "[#{item.subtypes.map { |s| s.to_rbs }.join(', ')}]" : ''}"
37
- end.join(' | ') + (@items.length > 1 ? ')' : '')).gsub(/undefined/, 'untyped')
64
+ ((@items.length > 1 ? '(' : '') +
65
+ @items.map(&:to_rbs).join(' | ') +
66
+ (@items.length > 1 ? ')' : ''))
67
+ end
68
+
69
+ # @param dst [ComplexType]
70
+ # @return [ComplexType]
71
+ def self_to_type dst
72
+ object_type_dst = dst.reduce_class_type
73
+ transform do |t|
74
+ next t if t.name != 'self'
75
+ object_type_dst
76
+ end
38
77
  end
39
78
 
79
+ # @yieldparam [UniqueType]
80
+ # @return [Array]
40
81
  def map &block
41
82
  @items.map &block
42
83
  end
43
84
 
44
85
  # @yieldparam [UniqueType]
45
- # @return [Array]
86
+ # @return [Enumerable<UniqueType>]
46
87
  def each &block
47
88
  @items.each &block
48
89
  end
49
90
 
50
91
  # @yieldparam [UniqueType]
51
- # @return [Enumerator<UniqueType>]
92
+ # @return [void]
93
+ # @overload each_unique_type()
94
+ # @return [Enumerator<UniqueType>]
52
95
  def each_unique_type &block
53
96
  return enum_for(__method__) unless block_given?
54
97
 
@@ -57,14 +100,27 @@ module Solargraph
57
100
  end
58
101
  end
59
102
 
103
+ # @return [Integer]
60
104
  def length
61
105
  @items.length
62
106
  end
63
107
 
108
+ # @return [Array<UniqueType>]
109
+ def to_a
110
+ @items
111
+ end
112
+
113
+ def tags
114
+ @items.map(&:tag).join(', ')
115
+ end
116
+
117
+ # @param index [Integer]
118
+ # @return [UniqueType]
64
119
  def [](index)
65
120
  @items[index]
66
121
  end
67
122
 
123
+ # @return [Array<UniqueType>]
68
124
  def select &block
69
125
  @items.select &block
70
126
  end
@@ -80,12 +136,16 @@ module Solargraph
80
136
  @items.map(&:namespace)
81
137
  end
82
138
 
139
+ # @param name [Symbol]
140
+ # @return [Object, nil]
83
141
  def method_missing name, *args, &block
84
142
  return if @items.first.nil?
85
143
  return @items.first.send(name, *args, &block) if respond_to_missing?(name)
86
144
  super
87
145
  end
88
146
 
147
+ # @param name [Symbol]
148
+ # @param include_private [Boolean]
89
149
  def respond_to_missing?(name, include_private = false)
90
150
  TypeMethods.public_instance_methods.include?(name) || super
91
151
  end
@@ -94,6 +154,10 @@ module Solargraph
94
154
  map(&:tag).join(', ')
95
155
  end
96
156
 
157
+ def rooted_tags
158
+ map(&:rooted_tag).join(', ')
159
+ end
160
+
97
161
  def all? &block
98
162
  @items.all? &block
99
163
  end
@@ -106,54 +170,84 @@ module Solargraph
106
170
  @items.any?(&:selfy?)
107
171
  end
108
172
 
109
- def parameterized?
110
- any?(&:parameterized?)
173
+ def generic?
174
+ any?(&:generic?)
111
175
  end
112
176
 
113
- def resolve_parameters definitions, context
114
- result = @items.map { |i| i.resolve_parameters(definitions, context) }
115
- ComplexType.parse(*result.map(&:tag))
177
+ # @param new_name [String, nil]
178
+ # @yieldparam t [UniqueType]
179
+ # @yieldreturn [UniqueType]
180
+ # @return [ComplexType]
181
+ def transform(new_name = nil, &transform_type)
182
+ raise "Please remove leading :: and set rooted with recreate() instead - #{new_name}" if new_name&.start_with?('::')
183
+ ComplexType.new(map { |ut| ut.transform(new_name, &transform_type) })
116
184
  end
117
185
 
118
- # @param dst [String]
186
+ # @return [self]
187
+ def force_rooted
188
+ transform do |t|
189
+ t.recreate(make_rooted: true)
190
+ end
191
+ end
192
+
193
+ # @param definitions [Pin::Namespace, Pin::Method]
194
+ # @param context_type [ComplexType]
119
195
  # @return [ComplexType]
120
- def self_to dst
121
- return self unless selfy?
122
- red = reduce_class(dst)
123
- result = @items.map { |i| i.self_to red }
124
- ComplexType.parse(*result.map(&:tag))
196
+ def resolve_generics definitions, context_type
197
+ result = @items.map { |i| i.resolve_generics(definitions, context_type) }
198
+ ComplexType.new(result)
125
199
  end
126
200
 
127
201
  def nullable?
128
202
  @items.any?(&:nil_type?)
129
203
  end
130
204
 
205
+ # @return [Array<ComplexType>]
131
206
  def all_params
132
207
  @items.first.all_params || []
133
208
  end
134
209
 
135
- protected
210
+ # @return [ComplexType]
211
+ def reduce_class_type
212
+ new_items = items.flat_map do |type|
213
+ next type unless ['Module', 'Class'].include?(type.name)
214
+
215
+ type.all_params
216
+ end
217
+ ComplexType.new(new_items)
218
+ end
219
+
220
+ # every type and subtype in this union have been resolved to be
221
+ # fully qualified
222
+ def all_rooted?
223
+ all?(&:all_rooted?)
224
+ end
225
+
226
+ # every top-level type has resolved to be fully qualified; see
227
+ # #all_rooted? to check their subtypes as well
228
+ def rooted?
229
+ all?(&:rooted?)
230
+ end
136
231
 
137
232
  attr_reader :items
138
233
 
139
- def reduce_object
140
- return self if name != 'Object' || subtypes.empty?
141
- ComplexType.try_parse(reduce_class(subtypes.join(', ')))
234
+ def rooted?
235
+ @items.all?(&:rooted?)
142
236
  end
143
237
 
144
- private
238
+ protected
145
239
 
146
- # @todo This is a quick and dirty hack that forces `self` keywords
147
- # to reference an instance of their class and never the class itself.
148
- # This behavior may change depending on which result is expected
149
- # from YARD conventions. See https://github.com/lsegal/yard/issues/1257
150
- # @param dst [String]
151
- # @return [String]
152
- def reduce_class dst
153
- while dst =~ /^(Class|Module)\<(.*?)\>$/
154
- dst = dst.sub(/^(Class|Module)\</, '').sub(/\>$/, '')
240
+ # @return [ComplexType]
241
+ def reduce_object
242
+ new_items = items.flat_map do |ut|
243
+ next [ut] if ut.name != 'Object' || ut.subtypes.empty?
244
+ ut.subtypes
155
245
  end
156
- dst
246
+ ComplexType.new(new_items)
247
+ end
248
+
249
+ def bottom?
250
+ @items.all?(&:bot?)
157
251
  end
158
252
 
159
253
  class << self
@@ -170,9 +264,18 @@ module Solargraph
170
264
  # used internally.
171
265
  #
172
266
  # @param *strings [Array<String>] The type definitions to parse
173
- # @param partial [Boolean] True if the string is part of a another type
174
- # @return [ComplexType, Array, nil]
267
+ # @return [ComplexType]
268
+ # # @overload parse(*strings, partial: false)
269
+ # # @todo Need ability to use a literal true as a type below
270
+ # # @param partial [Boolean] True if the string is part of a another type
271
+ # # @return [Array<UniqueType>]
272
+ # @sg-ignore
273
+ # @todo To be able to select the right signature above,
274
+ # Chain::Call needs to know the decl type (:arg, :optarg,
275
+ # :kwarg, etc) of the arguments given, instead of just having
276
+ # an array of Chains as the arguments.
175
277
  def parse *strings, partial: false
278
+ # @type [Hash{Array<String> => ComplexType}]
176
279
  @cache ||= {}
177
280
  unless partial
178
281
  cached = @cache[strings]
@@ -197,7 +300,10 @@ module Solargraph
197
300
  elsif base.end_with?('=')
198
301
  raise ComplexTypeError, "Invalid hash thing" unless key_types.nil?
199
302
  # types.push ComplexType.new([UniqueType.new(base[0..-2].strip)])
200
- types.push UniqueType.new(base[0..-2].strip)
303
+ types.push UniqueType.parse(base[0..-2].strip, subtype_string)
304
+ # @todo this should either expand key_type's type
305
+ # automatically or complain about not being
306
+ # compatible with key_type's type in type checking
201
307
  key_types = types
202
308
  types = []
203
309
  base.clear
@@ -220,12 +326,12 @@ module Solargraph
220
326
  paren_stack += 1
221
327
  elsif char == ')'
222
328
  paren_stack -= 1
223
- subtype_string += char if paren_stack == 0
329
+ subtype_string += char
224
330
  raise ComplexTypeError, "Invalid close in type #{type_string}" if paren_stack < 0
225
331
  next
226
332
  elsif char == ',' && point_stack == 0 && curly_stack == 0 && paren_stack == 0
227
333
  # types.push ComplexType.new([UniqueType.new(base.strip, subtype_string.strip)])
228
- types.push UniqueType.new(base.strip, subtype_string.strip)
334
+ types.push UniqueType.parse(base.strip, subtype_string.strip)
229
335
  base.clear
230
336
  subtype_string.clear
231
337
  next
@@ -238,7 +344,7 @@ module Solargraph
238
344
  end
239
345
  raise ComplexTypeError, "Unclosed subtype in #{type_string}" if point_stack != 0 || curly_stack != 0 || paren_stack != 0
240
346
  # types.push ComplexType.new([UniqueType.new(base, subtype_string)])
241
- types.push UniqueType.new(base.strip, subtype_string.strip)
347
+ types.push UniqueType.parse(base.strip, subtype_string.strip)
242
348
  end
243
349
  unless key_types.nil?
244
350
  raise ComplexTypeError, "Invalid use of key/value parameters" unless partial
@@ -255,17 +361,33 @@ module Solargraph
255
361
  def try_parse *strings
256
362
  parse *strings
257
363
  rescue ComplexTypeError => e
258
- Solargraph.logger.info "Error parsing complex type: #{e.message}"
364
+ Solargraph.logger.info "Error parsing complex type `#{strings.join(', ')}`: #{e.message}"
259
365
  ComplexType::UNDEFINED
260
366
  end
261
367
  end
262
368
 
263
369
  VOID = ComplexType.parse('void')
264
370
  UNDEFINED = ComplexType.parse('undefined')
265
- SYMBOL = ComplexType.parse('Symbol')
266
- ROOT = ComplexType.parse('Class<>')
371
+ SYMBOL = ComplexType.parse('::Symbol')
372
+ ROOT = ComplexType.parse('::Class<>')
267
373
  NIL = ComplexType.parse('nil')
268
374
  SELF = ComplexType.parse('self')
269
- BOOLEAN = ComplexType.parse('Boolean')
375
+ BOOLEAN = ComplexType.parse('::Boolean')
376
+ BOT = ComplexType.parse('bot')
377
+
378
+ private
379
+
380
+ # @todo This is a quick and dirty hack that forces `self` keywords
381
+ # to reference an instance of their class and never the class itself.
382
+ # This behavior may change depending on which result is expected
383
+ # from YARD conventions. See https://github.com/lsegal/yard/issues/1257
384
+ # @param dst [String]
385
+ # @return [String]
386
+ def reduce_class dst
387
+ while dst =~ /^(Class|Module)\<(.*?)\>$/
388
+ dst = dst.sub(/^(Class|Module)\</, '').sub(/\>$/, '')
389
+ end
390
+ dst
391
+ end
270
392
  end
271
393
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
3
 
5
4
  module Solargraph
6
5
  # Conventions provide a way to modify an ApiMap based on expectations about
@@ -1,6 +1,11 @@
1
+ require 'nokogiri'
2
+
1
3
  module ReverseMarkdown
2
4
  module Converters
3
5
  class Dd < Base
6
+ # @return [String]
7
+ # @param node [Nokogiri::XML::Element]
8
+ # @param state [Hash]
4
9
  def convert node, state = {}
5
10
  content = treat_children(node, state)
6
11
  ": #{content.strip}\n"
@@ -1,6 +1,9 @@
1
1
  module ReverseMarkdown
2
2
  module Converters
3
3
  class Dl < Base
4
+ # @return [String]
5
+ # @param node [Nokogiri::XML::Element]
6
+ # @param state [Hash]
4
7
  def convert node, state = {}
5
8
  content = treat_children(node, state).strip
6
9
  "\n\n#{content}\n"
@@ -1,6 +1,9 @@
1
1
  module ReverseMarkdown
2
2
  module Converters
3
3
  class Dt < Base
4
+ # @return [String]
5
+ # @param node [Nokogiri::XML::Element]
6
+ # @param state [Hash]
4
7
  def convert node, state = {}
5
8
  content = treat_children(node, state)
6
9
  "\n#{content.strip}\n"
@@ -29,9 +29,9 @@ module Solargraph
29
29
  store = RuboCop::ConfigStore.new
30
30
  runner = RuboCop::Runner.new(options, store)
31
31
  result = redirect_stdout{ runner.run(paths) }
32
-
32
+
33
33
  return [] if result.empty?
34
-
34
+
35
35
  make_array JSON.parse(result)
36
36
  rescue RuboCop::ValidationError, RuboCop::ConfigNotFoundError => e
37
37
  raise DiagnosticsError, "Error in RuboCop configuration: #{e.message}"
@@ -48,7 +48,7 @@ module Solargraph
48
48
  args.find { |a| a =~ /version=/ }.to_s.split('=').last
49
49
  end
50
50
 
51
- # @param resp [Hash]
51
+ # @param resp [Hash{String => Array<Hash{String => Array<Hash{String => undefined}>}>}]
52
52
  # @return [Array<Hash>]
53
53
  def make_array resp
54
54
  diagnostics = []
@@ -62,8 +62,8 @@ module Solargraph
62
62
 
63
63
  # Convert a RuboCop offense to an LSP diagnostic
64
64
  #
65
- # @param off [Hash] Offense received from Rubocop
66
- # @return [Hash] LSP diagnostic
65
+ # @param off [Hash{String => unknown}] Offense received from Rubocop
66
+ # @return [Hash{Symbol => Hash, String, Integer}] LSP diagnostic
67
67
  def offense_to_diagnostic off
68
68
  {
69
69
  range: offense_range(off).to_hash,
@@ -81,19 +81,20 @@ module Solargraph
81
81
  Range.new(offense_start_position(off), offense_ending_position(off))
82
82
  end
83
83
 
84
- # @param off [Hash]
84
+ # @param off [Hash{String => Hash{String => Integer}}]
85
85
  # @return [Position]
86
86
  def offense_start_position off
87
87
  Position.new(off['location']['start_line'] - 1, off['location']['start_column'] - 1)
88
88
  end
89
89
 
90
- # @param off [Hash]
90
+ # @param off [Hash{String => Hash{String => Integer}}]
91
91
  # @return [Position]
92
92
  def offense_ending_position off
93
93
  if off['location']['start_line'] != off['location']['last_line']
94
94
  Position.new(off['location']['start_line'], 0)
95
95
  else
96
96
  start_line = off['location']['start_line'] - 1
97
+ # @type [Integer]
97
98
  last_column = off['location']['last_column']
98
99
  line = @source.code.lines[start_line]
99
100
  col_off = if line.nil? || line.empty?
@@ -12,6 +12,7 @@ module Solargraph
12
12
  #
13
13
  # @param version [String, nil]
14
14
  # @raise [InvalidRubocopVersionError] if _version_ is not installed
15
+ # @return [void]
15
16
  def require_rubocop(version = nil)
16
17
  begin
17
18
  gem_path = Gem::Specification.find_by_name('rubocop', version).full_gem_path
@@ -6,6 +6,7 @@ module Solargraph
6
6
  # parameters, and invalid param tags.
7
7
  #
8
8
  class TypeCheck < Base
9
+ # @return [Array<Hash>]
9
10
  def diagnose source, api_map
10
11
  # return [] unless args.include?('always') || api_map.workspaced?(source.filename)
11
12
  severity = Diagnostics::Severities::ERROR
@@ -33,14 +33,14 @@ module Solargraph
33
33
  # Find a reporter by name.
34
34
  #
35
35
  # @param name [String] The name with which the reporter was registered
36
- # @return [Class<Solargraph::Diagnostics::Base>]
36
+ # @return [Class<Solargraph::Diagnostics::Base>, nil]
37
37
  def reporter name
38
38
  reporter_hash[name]
39
39
  end
40
40
 
41
41
  private
42
42
 
43
- # @return [Hash]
43
+ # @return [Hash{String => Class<Solargraph::Diagnostics::Base>}]
44
44
  def reporter_hash
45
45
  @reporter_hash ||= {}
46
46
  end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ # A collection of pins generated from required gems.
5
+ #
6
+ class DocMap
7
+ # @return [Array<String>]
8
+ attr_reader :requires
9
+
10
+ # @return [Array<Gem::Specification>]
11
+ attr_reader :preferences
12
+
13
+ # @return [Array<Pin::Base>]
14
+ attr_reader :pins
15
+
16
+ # @return [Array<Gem::Specification>]
17
+ attr_reader :uncached_gemspecs
18
+
19
+ # @param requires [Array<String>]
20
+ # @param preferences [Array<Gem::Specification>]
21
+ # @param rbs_path [String, Pathname, nil]
22
+ def initialize(requires, preferences, rbs_path = nil)
23
+ @requires = requires.compact
24
+ @preferences = preferences.compact
25
+ @rbs_path = rbs_path
26
+ generate
27
+ end
28
+
29
+ # @return [Array<Gem::Specification>]
30
+ def gemspecs
31
+ @gemspecs ||= required_gem_map.values.compact
32
+ end
33
+
34
+ # @return [Array<String>]
35
+ def unresolved_requires
36
+ @unresolved_requires ||= required_gem_map.select { |_, gemspec| gemspec.nil? }.keys
37
+ end
38
+
39
+ # @return [Hash{Gem::Specification => Array[Pin::Base]}]
40
+ def self.gems_in_memory
41
+ @gems_in_memory ||= {}
42
+ end
43
+
44
+ # @return [Set<Gem::Specification>]
45
+ def dependencies
46
+ @dependencies ||= (gemspecs.flat_map { |spec| fetch_dependencies(spec) } - gemspecs).to_set
47
+ end
48
+
49
+ private
50
+
51
+ # @return [void]
52
+ def generate
53
+ @pins = []
54
+ @uncached_gemspecs = []
55
+ required_gem_map.each do |path, gemspec|
56
+ if gemspec
57
+ try_cache gemspec
58
+ else
59
+ try_stdlib_map path
60
+ end
61
+ end
62
+ dependencies.each { |dep| try_cache dep }
63
+ @uncached_gemspecs.uniq!
64
+ end
65
+
66
+ # @return [Hash{String => Gem::Specification, nil}]
67
+ def required_gem_map
68
+ @required_gem_map ||= requires.to_h { |path| [path, resolve_path_to_gemspec(path)] }
69
+ end
70
+
71
+ # @return [Hash{String => Gem::Specification}]
72
+ def preference_map
73
+ @preference_map ||= preferences.to_h { |gemspec| [gemspec.name, gemspec] }
74
+ end
75
+
76
+ # @param gemspec [Gem::Specification]
77
+ # @return [void]
78
+ def try_cache gemspec
79
+ return if try_gem_in_memory(gemspec)
80
+ cache_file = File.join('gems', "#{gemspec.name}-#{gemspec.version}.ser")
81
+ if Cache.exist?(cache_file)
82
+ cached = Cache.load(cache_file)
83
+ gempins = update_from_collection(gemspec, cached)
84
+ self.class.gems_in_memory[gemspec] = gempins
85
+ @pins.concat gempins
86
+ else
87
+ Solargraph.logger.debug "No pin cache for #{gemspec.name} #{gemspec.version}"
88
+ @uncached_gemspecs.push gemspec
89
+ end
90
+ end
91
+
92
+ # @param path [String] require path that might be in the RBS stdlib collection
93
+ # @return [void]
94
+ def try_stdlib_map path
95
+ map = RbsMap::StdlibMap.load(path)
96
+ if map.resolved?
97
+ Solargraph.logger.debug "Loading stdlib pins for #{path}"
98
+ @pins.concat map.pins
99
+ else
100
+ # @todo Temporarily ignoring unresolved `require 'set'`
101
+ Solargraph.logger.debug "Require path #{path} could not be resolved" unless path == 'set'
102
+ end
103
+ end
104
+
105
+ # @param gemspec [Gem::Specification]
106
+ # @return [Boolean]
107
+ def try_gem_in_memory gemspec
108
+ gempins = DocMap.gems_in_memory[gemspec]
109
+ return false unless gempins
110
+ Solargraph.logger.debug "Found #{gemspec.name} #{gemspec.version} in memory"
111
+ @pins.concat gempins
112
+ true
113
+ end
114
+
115
+ def update_from_collection gemspec, gempins
116
+ return gempins unless @rbs_path && File.directory?(@rbs_path)
117
+ return gempins if RbsMap.new(gemspec.name, gemspec.version).resolved?
118
+
119
+ rbs_map = RbsMap.new(gemspec.name, gemspec.version, directories: [@rbs_path])
120
+ return gempins unless rbs_map.resolved?
121
+
122
+ Solargraph.logger.info "Updating #{gemspec.name} #{gemspec.version} from collection"
123
+ GemPins.combine(gempins, rbs_map)
124
+ end
125
+
126
+ # @param path [String]
127
+ # @return [Gem::Specification, nil]
128
+ def resolve_path_to_gemspec path
129
+ return nil if path.empty?
130
+
131
+ gemspec = Gem::Specification.find_by_path(path)
132
+ if gemspec.nil?
133
+ gem_name_guess = path.split('/').first
134
+ begin
135
+ # this can happen when the gem is included via a local path in
136
+ # a Gemfile; Gem doesn't try to index the paths in that case.
137
+ #
138
+ # See if we can make a good guess:
139
+ potential_gemspec = Gem::Specification.find_by_name(gem_name_guess)
140
+ file = "lib/#{path}.rb"
141
+ gemspec = potential_gemspec if potential_gemspec.files.any? { |gemspec_file| file == gemspec_file }
142
+ rescue Gem::MissingSpecError
143
+ Solargraph.logger.debug "Require path #{path} could not be resolved to a gem via find_by_path or guess of #{gem_name_guess}"
144
+ nil
145
+ end
146
+ end
147
+ gemspec_or_preference gemspec
148
+ end
149
+
150
+ # @param gemspec [Gem::Specification, nil]
151
+ # @return [Gem::Specification, nil]
152
+ def gemspec_or_preference gemspec
153
+ return gemspec unless gemspec && preference_map.key?(gemspec.name)
154
+ return gemspec if gemspec.version == preference_map[gemspec.name].version
155
+
156
+ change_gemspec_version gemspec, preference_map[by_path.name].version
157
+ end
158
+
159
+ # @param gemspec [Gem::Specification]
160
+ # @param version [Gem::Version]
161
+ # @return [Gem::Specification]
162
+ def change_gemspec_version gemspec, version
163
+ Gem::Specification.find_by_name(gemspec.name, "= #{version}")
164
+ rescue Gem::MissingSpecError
165
+ Solargraph.logger.info "Gem #{gemspec.name} version #{version} not found. Using #{gemspec.version} instead"
166
+ gemspec
167
+ end
168
+
169
+ # @param gemspec [Gem::Specification]
170
+ # @return [Array<Gem::Specification>]
171
+ def fetch_dependencies gemspec
172
+ only_runtime_dependencies(gemspec).each_with_object(Set.new) do |spec, deps|
173
+ Solargraph.logger.info "Adding #{spec.name} dependency for #{gemspec.name}"
174
+ dep = Gem::Specification.find_by_name(spec.name, spec.requirement)
175
+ deps.merge fetch_dependencies(dep) if deps.add?(dep)
176
+ rescue Gem::MissingSpecError
177
+ Solargraph.logger.warn "Gem dependency #{spec.name} #{spec.requirements} for #{gemspec.name} not found."
178
+ end.to_a
179
+ end
180
+
181
+ # @param gemspec [Gem::Specification]
182
+ # @return [Array<Gem::Dependency>]
183
+ def only_runtime_dependencies gemspec
184
+ gemspec.dependencies - gemspec.development_dependencies
185
+ end
186
+ end
187
+ end