steep 1.10.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -1
  3. data/CLAUDE.md +114 -0
  4. data/README.md +1 -1
  5. data/Rakefile +15 -3
  6. data/Steepfile +13 -13
  7. data/lib/steep/annotation_parser.rb +5 -1
  8. data/lib/steep/annotations_helper.rb +12 -2
  9. data/lib/steep/ast/node/type_application.rb +22 -16
  10. data/lib/steep/ast/node/type_assertion.rb +7 -4
  11. data/lib/steep/ast/types/factory.rb +3 -2
  12. data/lib/steep/cli.rb +246 -2
  13. data/lib/steep/daemon/configuration.rb +19 -0
  14. data/lib/steep/daemon/server.rb +476 -0
  15. data/lib/steep/daemon.rb +201 -0
  16. data/lib/steep/diagnostic/ruby.rb +50 -8
  17. data/lib/steep/diagnostic/signature.rb +31 -8
  18. data/lib/steep/drivers/check.rb +301 -140
  19. data/lib/steep/drivers/print_project.rb +9 -10
  20. data/lib/steep/drivers/query.rb +102 -0
  21. data/lib/steep/drivers/start_server.rb +19 -0
  22. data/lib/steep/drivers/stop_server.rb +20 -0
  23. data/lib/steep/drivers/watch.rb +2 -2
  24. data/lib/steep/index/rbs_index.rb +38 -13
  25. data/lib/steep/index/signature_symbol_provider.rb +24 -3
  26. data/lib/steep/interface/builder.rb +48 -15
  27. data/lib/steep/interface/shape.rb +13 -5
  28. data/lib/steep/locator.rb +377 -0
  29. data/lib/steep/project/dsl.rb +26 -5
  30. data/lib/steep/project/group.rb +8 -2
  31. data/lib/steep/project/target.rb +16 -2
  32. data/lib/steep/project.rb +21 -2
  33. data/lib/steep/server/base_worker.rb +2 -2
  34. data/lib/steep/server/change_buffer.rb +2 -1
  35. data/lib/steep/server/custom_methods.rb +12 -0
  36. data/lib/steep/server/inline_source_change_detector.rb +94 -0
  37. data/lib/steep/server/interaction_worker.rb +51 -74
  38. data/lib/steep/server/lsp_formatter.rb +48 -12
  39. data/lib/steep/server/master.rb +100 -18
  40. data/lib/steep/server/target_group_files.rb +124 -151
  41. data/lib/steep/server/type_check_controller.rb +276 -123
  42. data/lib/steep/server/type_check_worker.rb +104 -3
  43. data/lib/steep/services/completion_provider/rbs.rb +74 -0
  44. data/lib/steep/services/completion_provider/ruby.rb +652 -0
  45. data/lib/steep/services/completion_provider/type_name.rb +243 -0
  46. data/lib/steep/services/completion_provider.rb +39 -662
  47. data/lib/steep/services/content_change.rb +14 -1
  48. data/lib/steep/services/file_loader.rb +4 -2
  49. data/lib/steep/services/goto_service.rb +271 -68
  50. data/lib/steep/services/hover_provider/content.rb +67 -0
  51. data/lib/steep/services/hover_provider/rbs.rb +8 -9
  52. data/lib/steep/services/hover_provider/ruby.rb +123 -64
  53. data/lib/steep/services/hover_provider/singleton_methods.rb +4 -0
  54. data/lib/steep/services/signature_service.rb +129 -54
  55. data/lib/steep/services/type_check_service.rb +72 -27
  56. data/lib/steep/signature/validator.rb +30 -18
  57. data/lib/steep/source/ignore_ranges.rb +14 -4
  58. data/lib/steep/source.rb +16 -2
  59. data/lib/steep/tagged_logging.rb +39 -0
  60. data/lib/steep/type_construction.rb +94 -21
  61. data/lib/steep/type_inference/block_params.rb +7 -7
  62. data/lib/steep/type_inference/context.rb +4 -2
  63. data/lib/steep/type_inference/logic_type_interpreter.rb +21 -3
  64. data/lib/steep/type_inference/method_call.rb +4 -0
  65. data/lib/steep/type_inference/type_env.rb +1 -1
  66. data/lib/steep/typing.rb +0 -2
  67. data/lib/steep/version.rb +1 -1
  68. data/lib/steep.rb +42 -32
  69. data/manual/ruby-diagnostics.md +67 -0
  70. data/sample/Steepfile +1 -0
  71. data/sample/lib/conference.rb +1 -0
  72. data/sample/lib/deprecated.rb +6 -0
  73. data/sample/lib/inline.rb +43 -0
  74. data/sample/sig/generics.rbs +3 -0
  75. data/steep.gemspec +4 -5
  76. metadata +26 -26
  77. data/lib/steep/services/type_name_completion.rb +0 -236
@@ -0,0 +1,652 @@
1
+ module Steep
2
+ module Services
3
+ module CompletionProvider
4
+ class Ruby
5
+ include NodeHelper
6
+
7
+ attr_reader :source_text
8
+ attr_reader :path
9
+ attr_reader :subtyping
10
+ attr_reader :modified_text
11
+ attr_reader :source
12
+ attr_reader :typing
13
+
14
+ def initialize(source_text:, path:, subtyping:)
15
+ @source_text = source_text
16
+ @path = path
17
+ @subtyping = subtyping
18
+ end
19
+
20
+ def type_check!(text, line:, column:)
21
+ @modified_text = text
22
+
23
+ Steep.measure "parsing" do
24
+ @source = Source
25
+ .parse(text, path: path, factory: subtyping.factory)
26
+ .without_unrelated_defs(line: line, column: column)
27
+ end
28
+
29
+ Steep.measure "typechecking" do
30
+ location = source.buffer.loc_to_pos([line, column])
31
+ resolver = ::RBS::Resolver::ConstantResolver.new(builder: subtyping.factory.definition_builder)
32
+ @typing = TypeCheckService.type_check(source: source, subtyping: subtyping, constant_resolver: resolver, cursor: location)
33
+ end
34
+ end
35
+
36
+ def env
37
+ subtyping.factory.env
38
+ end
39
+
40
+ def run(line:, column:)
41
+ source_text = self.source_text.dup
42
+ index = index_for(source_text, line:line, column: column)
43
+ possible_trigger = source_text[index-1]
44
+
45
+ Steep.logger.debug "possible_trigger: #{possible_trigger.inspect}"
46
+
47
+ position = Position.new(line: line, column: column)
48
+
49
+ begin
50
+ Steep.logger.tagged "completion_provider#run(line: #{line}, column: #{column})" do
51
+ Steep.measure "type_check!" do
52
+ type_check!(source_text, line: line, column: column)
53
+ end
54
+ end
55
+
56
+ Steep.measure "completion item collection" do
57
+ items_for_trigger(position: position)
58
+ end
59
+
60
+ rescue Parser::SyntaxError => exn
61
+ Steep.logger.info "recovering syntax error: #{exn.inspect}"
62
+
63
+ @source_text = source_text.dup
64
+
65
+ case possible_trigger
66
+ when "."
67
+ if source_text[index-2] == "&"
68
+ source_text[index-1] = " "
69
+ source_text[index-2] = " "
70
+ type_check!(source_text, line: line, column: column)
71
+ items_for_qcall(position: position)
72
+ else
73
+ source_text[index-1] = " "
74
+ type_check!(source_text, line: line, column: column)
75
+ items_for_dot(position: position)
76
+ end
77
+ when "@"
78
+ source_text[index-1] = " "
79
+ type_check!(source_text, line: line, column: column)
80
+ items_for_atmark(position: position)
81
+ when ":"
82
+ if source_text[index-2] == ":"
83
+ source_text[index-1] = " "
84
+ source_text[index-2] = " "
85
+ type_check!(source_text, line: line, column: column)
86
+ items_for_colon2(position: position)
87
+ else
88
+ []
89
+ end
90
+ else
91
+ items = [] #: Array[item]
92
+ items_for_following_keyword_arguments(source_text, index: index, line: line, column: column, items: items)
93
+ items
94
+ end
95
+ end
96
+ end
97
+
98
+ def run_at_comment(line:, column:)
99
+ source_text = self.source_text.dup
100
+ index = index_for(source_text, line:line, column: column)
101
+ possible_trigger = source_text[index-1]
102
+
103
+ Steep.logger.debug "possible_trigger: #{possible_trigger.inspect}"
104
+
105
+ position = Position.new(line: line, column: column)
106
+
107
+ begin
108
+ Steep.logger.tagged "completion_provider#run(line: #{line}, column: #{column})" do
109
+ Steep.measure "type_check!" do
110
+ type_check!(source_text, line: line, column: column)
111
+ rescue Parser::SyntaxError
112
+ return
113
+ end
114
+ end
115
+
116
+ locator = Locator::Ruby.new(source)
117
+ case result = locator.find(line, column)
118
+ when Locator::CommentResult
119
+ buffer = typing.source.buffer
120
+
121
+ items = [] #: Array[item]
122
+
123
+ prefix_size, rbs_items = items_for_rbs(position: position, buffer: buffer)
124
+ items.concat rbs_items
125
+
126
+ comment_content = result.comment.text[1..] #: String
127
+
128
+ range = Range.new(
129
+ start: Position.new(line: line, column: column),
130
+ end: Position.new(line: line, column: column)
131
+ )
132
+
133
+ annotation_items = [
134
+ TextItem.new(label: "steep:ignore:start", text: "steep:ignore:start", help_text: "Open ignore block", range: range),
135
+ TextItem.new(label: "steep:ignore:end", text: "steep:ignore:end", help_text: "Close ignore block", range: range),
136
+ TextItem.new(label: "steep:ignore", text: "steep:ignore ${1:optional diagnostics}", help_text: "Ignore line", range: range),
137
+ TextItem.new(label: "@type var x: T", text: "@type var ${1:variable}: ${2:var type}", help_text: "Type of local variable", range: range),
138
+ TextItem.new(label: "@type self: T", text: "@type self: ${1:self type}", help_text: "Type of `self`", range: range),
139
+ TextItem.new(label: "@type block: T", text: "@type block: ${1:block type}", help_text: "Type of `block`", range: range),
140
+ TextItem.new(label: "@type break: T", text: "@type break: ${1:break type}", help_text: "Type of `block`", range: range),
141
+ ]
142
+
143
+ annotation_items.each do |item|
144
+ if item.text.start_with?(comment_content)
145
+ items << TextItem.new(
146
+ label: item.label,
147
+ text: item.text,
148
+ help_text: item.help_text,
149
+ range: Range.new(
150
+ start: Position.new(line: item.range.start.line, column: item.range.start.column - comment_content.size),
151
+ end: item.range.end
152
+ )
153
+ )
154
+ end
155
+ end
156
+
157
+ [prefix_size, items]
158
+ when Locator::TypeApplicationResult, Locator::TypeAssertionResult, Locator::AnnotationResult
159
+ buffer = typing.source.buffer
160
+ items_for_rbs(position: position, buffer: buffer)
161
+ end
162
+ end
163
+ end
164
+
165
+ def range_from_loc(loc)
166
+ Range.new(
167
+ start: Position.new(line: loc.line, column: loc.column),
168
+ end: Position.new(line: loc.last_line, column: loc.last_line)
169
+ )
170
+ end
171
+
172
+ def at_end?(pos, of:)
173
+ if of
174
+ of.last_line == pos.line && of.last_column == pos.column
175
+ end
176
+ end
177
+
178
+ def at_comment?(position)
179
+ source.find_comment(line: position.line, column: position.column)
180
+ end
181
+
182
+ def range_for(position, prefix: "")
183
+ if prefix.empty?
184
+ Range.new(start: position, end: position)
185
+ else
186
+ Range.new(start: position - prefix.size, end: position)
187
+ end
188
+ end
189
+
190
+ def items_for_trigger(position:)
191
+ node, *parents = source.find_nodes(line: position.line, column: position.column)
192
+ node ||= source.node
193
+
194
+ return [] unless node && parents
195
+
196
+ items = [] #: Array[item]
197
+
198
+ context = typing.cursor_context.context or raise
199
+
200
+ case
201
+ when node.type == :send && node.children[0] == nil && at_end?(position, of: (_ = node.loc).selector)
202
+ # foo ←
203
+ prefix = node.children[1].to_s
204
+
205
+ method_items_for_receiver_type(context.self_type, include_private: true, prefix: prefix, position: position, items: items)
206
+ local_variable_items_for_context(context, position: position, prefix: prefix, items: items)
207
+
208
+ if (send_node, block_node = deconstruct_sendish_and_block_nodes(*parents))
209
+ keyword_argument_items_for_method(
210
+ call_node: block_node || send_node,
211
+ send_node: send_node,
212
+ position: position,
213
+ prefix: prefix,
214
+ items: items
215
+ )
216
+ end
217
+
218
+ when node.type == :lvar && at_end?(position, of: node.loc)
219
+ # foo ← (lvar)
220
+ local_variable_items_for_context(context, position: position, prefix: node.children[0].to_s, items: items)
221
+
222
+ when node.type == :send && node.children[0] && at_end?(position, of: (_ = node.loc).selector)
223
+ # foo.ba ←
224
+ receiver_type =
225
+ case (type = typing.type_of(node: node.children[0]))
226
+ when AST::Types::Self
227
+ context.self_type
228
+ else
229
+ type
230
+ end
231
+ prefix = node.children[1].to_s
232
+
233
+ method_items_for_receiver_type(receiver_type, include_private: false, prefix: prefix, position: position, items: items)
234
+
235
+ when node.type == :csend && node.children[0] && at_end?(position, of: (_ = node.loc).selector)
236
+ # foo&.ba ←
237
+ receiver_type =
238
+ case (type = typing.type_of(node: node.children[0]))
239
+ when AST::Types::Self
240
+ context.self_type
241
+ else
242
+ unwrap_optional(type)
243
+ end
244
+ prefix = node.children[1].to_s
245
+
246
+ method_items_for_receiver_type(receiver_type, include_private: false, prefix: prefix, position: position, items: items)
247
+
248
+ when node.type == :const && node.children[0] == nil && at_end?(position, of: node.loc)
249
+ # Foo ← (const)
250
+ prefix = node.children[1].to_s
251
+
252
+ method_items_for_receiver_type(context.self_type, include_private: false, prefix: prefix, position: position, items: items)
253
+ constant_items_for_context(context, prefix: prefix, position: position, items: items)
254
+
255
+ when node.type == :const && node.children[0] && at_end?(position, of: node.loc)
256
+ # Foo::Ba ← (const)
257
+ parent_node = node.children[0]
258
+ parent_type = typing.type_of(node: parent_node)
259
+
260
+ if parent_type
261
+ prefix = node.children[1].to_s
262
+
263
+ method_items_for_receiver_type(parent_type, include_private: false, prefix: prefix, position: position, items: items)
264
+ constant_items_for_context(context, parent: parent_node, prefix: prefix, position: position, items: items)
265
+ end
266
+
267
+ when node.type == :send && at_end?(position, of: (_ = node.loc).dot) && (_ = node.loc).dot.source == "."
268
+ # foo.← ba
269
+ receiver_type =
270
+ case (type = typing.type_of(node: node.children[0]))
271
+ when AST::Types::Self
272
+ context.self_type
273
+ else
274
+ type
275
+ end
276
+
277
+ method_items_for_receiver_type(receiver_type, include_private: false, prefix: "", position: position, items: items)
278
+
279
+ when node.type == :send && at_end?(position, of: (_ = node.loc).dot) && (_ = node.loc).dot.source == "::"
280
+ # foo::← ba
281
+ items.push(*items_for_colon2(position: position))
282
+
283
+ when node.type == :csend && at_end?(position, of: (_ = node.loc).dot)
284
+ # foo&.← ba
285
+ receiver_type =
286
+ case (type = typing.type_of(node: node.children[0]))
287
+ when AST::Types::Self
288
+ context.self_type
289
+ else
290
+ unwrap_optional(type)
291
+ end
292
+
293
+ method_items_for_receiver_type(receiver_type, include_private: false, prefix: "", position: position, items: items)
294
+
295
+ when node.type == :ivar && at_end?(position, of: node.loc)
296
+ # @fo ←
297
+ instance_variable_items_for_context(context, position: position, prefix: node.children[0].to_s, items: items)
298
+
299
+ else
300
+ method_items_for_receiver_type(context.self_type, include_private: true, prefix: "", position: position, items: items)
301
+ local_variable_items_for_context(context, position: position, prefix: "", items: items)
302
+ instance_variable_items_for_context(context, position: position, prefix: "", items: items)
303
+ constant_items_for_context(context, position: position, prefix: "", items: items)
304
+ end
305
+
306
+ items
307
+ end
308
+
309
+ def items_for_dot(position:)
310
+ # foo. ←
311
+ shift_pos = position-1
312
+ node, *_parents = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
313
+ node ||= source.node
314
+
315
+ return [] unless node
316
+
317
+ if at_end?(shift_pos, of: node.loc)
318
+ begin
319
+ context = typing.cursor_context.context or raise
320
+ receiver_type =
321
+ case (type = typing.type_of(node: node))
322
+ when AST::Types::Self
323
+ context.self_type
324
+ else
325
+ type
326
+ end
327
+
328
+ items = [] #: Array[item]
329
+ method_items_for_receiver_type(receiver_type, include_private: false, prefix: "", position: position, items: items)
330
+ items
331
+ rescue Typing::UnknownNodeError
332
+ []
333
+ end
334
+ else
335
+ []
336
+ end
337
+ end
338
+
339
+ def items_for_qcall(position:)
340
+ # foo&. ←
341
+ shift_pos = position-2
342
+ node, *_parents = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
343
+ node ||= source.node
344
+
345
+ return [] unless node
346
+
347
+ if at_end?(shift_pos, of: node.loc)
348
+ begin
349
+ context = typing.cursor_context.context or raise
350
+ receiver_type =
351
+ case (type = typing.type_of(node: node))
352
+ when AST::Types::Self
353
+ context.self_type
354
+ else
355
+ unwrap_optional(type)
356
+ end
357
+
358
+ items = [] #: Array[item]
359
+ method_items_for_receiver_type(receiver_type, include_private: false, prefix: "", position: position, items: items)
360
+ items
361
+ rescue Typing::UnknownNodeError
362
+ []
363
+ end
364
+ else
365
+ []
366
+ end
367
+ end
368
+
369
+ def items_for_colon2(position:)
370
+ # :: ←
371
+ shift_pos = position-2
372
+ node, *_ = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
373
+ node ||= source.node
374
+
375
+ items = [] #: Array[item]
376
+ case node&.type
377
+ when :const
378
+ # Constant:: ←
379
+ context = typing.cursor_context.context or raise
380
+ constant_items_for_context(context, parent: node, position: position, items: items, prefix: "")
381
+ when nil
382
+ # :: ←
383
+ context = typing.cursor_context.context or raise
384
+ constant_items_for_context(context, parent: nil, position: position, items: items, prefix: "")
385
+ end
386
+
387
+ if node
388
+ items.push(*items_for_dot(position: position - 1))
389
+ end
390
+
391
+ items
392
+ end
393
+
394
+ def items_for_atmark(position:)
395
+ # @ ←
396
+ shift_pos = position-1
397
+ node, *_ = source.find_nodes(line: shift_pos.line, column: shift_pos.column)
398
+ node ||= source.node
399
+
400
+ return [] unless node
401
+
402
+ context = typing.cursor_context.context or raise
403
+ items = [] #: Array[item]
404
+ instance_variable_items_for_context(context, prefix: "@", position: position, items: items)
405
+ items
406
+ end
407
+
408
+ def items_for_rbs(position:, buffer:)
409
+ items = [] #: Array[item]
410
+
411
+ context = typing.cursor_context.context or raise
412
+ completion = TypeName.new(env: context.env, context: context.module_context.nesting, dirs: [])
413
+ prefix = TypeName::Prefix.parse(buffer, line: position.line, column: position.column)
414
+
415
+ size = prefix&.size || 0
416
+ range = Range.new(start: position - size, end: position)
417
+
418
+ completion.find_type_names(prefix).each do |name|
419
+ if (absolute, relative = completion.resolve_name_in_context(name))
420
+ items << TypeNameItem.new(relative_type_name: relative, absolute_type_name: absolute, env: context.env, range: range)
421
+ end
422
+ end
423
+
424
+ [size, items]
425
+ end
426
+
427
+ def items_for_following_keyword_arguments(text, index:, line:, column:, items:)
428
+ return if text[index - 1] !~ /[a-zA-Z0-9]/
429
+
430
+ text = text.dup
431
+ argname = [] #: Array[String]
432
+ while text[index - 1] =~ /[a-zA-Z0-9]/
433
+ argname.unshift(text[index - 1] || '')
434
+ source_text[index - 1] = " "
435
+ index -= 1
436
+ end
437
+
438
+ begin
439
+ type_check!(source_text, line: line, column: column)
440
+ rescue Parser::SyntaxError
441
+ return
442
+ end
443
+
444
+ if nodes = source.find_nodes(line: line, column: column)
445
+ if (send_node, block_node = deconstruct_sendish_and_block_nodes(*nodes))
446
+ position = Position.new(line: line, column: column)
447
+ keyword_argument_items_for_method(
448
+ call_node: block_node || send_node,
449
+ send_node: send_node,
450
+ position: position,
451
+ prefix: argname.join,
452
+ items: items
453
+ )
454
+ end
455
+ end
456
+ end
457
+
458
+ def method_items_for_receiver_type(type, include_private:, prefix:, position:, items:)
459
+ range = range_for(position, prefix: prefix)
460
+ context = typing.cursor_context.context or raise
461
+
462
+ config =
463
+ if (module_type = context.module_context&.module_type) && (instance_type = context.module_context&.instance_type)
464
+ Interface::Builder::Config.new(
465
+ self_type: context.self_type,
466
+ class_type: module_type,
467
+ instance_type: instance_type,
468
+ variable_bounds: context.variable_context.upper_bounds
469
+ )
470
+ else
471
+ Interface::Builder::Config.new(self_type: context.self_type, variable_bounds: context.variable_context.upper_bounds)
472
+ end
473
+
474
+ if shape = subtyping.builder.shape(type, config)
475
+ shape = shape.public_shape unless include_private
476
+
477
+ shape.methods.each do |name, method_entry|
478
+ next if disallowed_method?(name)
479
+
480
+ if name.to_s.start_with?(prefix)
481
+ if word_name?(name.to_s)
482
+ case type
483
+ when AST::Types::Name::Instance, AST::Types::Name::Interface, AST::Types::Name::Singleton
484
+ # Simple method type
485
+ all_decls = Set.new(method_entry.overloads.flat_map {|overload| overload.method_decls(name) }).sort_by {|decl| decl.method_name.to_s }
486
+ all_members = Set.new(all_decls.flat_map {|decl| decl.method_def.member })
487
+ all_members.each do |member|
488
+ associated_decl = all_decls.find {|decl| decl.method_def.member == member } or next
489
+ overloads = method_entry.overloads.select {|overload| overload.method_defs.any? {|defn| defn.member == member }}
490
+ annotations = associated_decl.method_def.member_annotations
491
+ items << SimpleMethodNameItem.new(
492
+ identifier: name,
493
+ range: range,
494
+ receiver_type: type,
495
+ method_name: associated_decl.method_name,
496
+ method_types: overloads.map {|overload| subtyping.factory.method_type_1(overload.method_type) },
497
+ method_member: member,
498
+ deprecated: AnnotationsHelper.deprecated_annotation?(annotations) ? true : false
499
+ )
500
+ end
501
+ else
502
+ generated_overloads, defined_overloads =
503
+ method_entry.overloads.partition {|overload| overload.method_defs.empty? }
504
+
505
+ unless defined_overloads.empty?
506
+ items << ComplexMethodNameItem.new(
507
+ identifier: name,
508
+ range: range,
509
+ receiver_type: type,
510
+ method_types: defined_overloads.map { subtyping.factory.method_type_1(_1.method_type) },
511
+ method_decls: defined_overloads.flat_map { _1.method_decls(name).to_a }.sort_by {|decl| decl.method_name.to_s }
512
+ )
513
+ end
514
+
515
+ unless generated_overloads.empty?
516
+ items << GeneratedMethodNameItem.new(
517
+ identifier: name,
518
+ range: range,
519
+ receiver_type: type,
520
+ method_types: generated_overloads.map { subtyping.factory.method_type_1(_1.method_type) }
521
+ )
522
+ end
523
+ end
524
+ end
525
+ end
526
+ end
527
+ end
528
+ end
529
+
530
+ def word_name?(name)
531
+ name =~ /\w/ ? true : false
532
+ end
533
+
534
+ def local_variable_items_for_context(context, position:, prefix:, items:)
535
+ range = range_for(position, prefix: prefix)
536
+ context.type_env.local_variable_types.each do |name, pair|
537
+ type, _ = pair
538
+
539
+ if name.to_s.start_with?(prefix)
540
+ items << LocalVariableItem.new(identifier: name, range: range, type: type)
541
+ end
542
+ end
543
+ end
544
+
545
+ def constant_items_for_context(context, parent: nil, position:, prefix:, items:)
546
+ range = range_for(position, prefix: prefix)
547
+
548
+ if parent
549
+ case parent.type
550
+ when :const
551
+ const_name = typing.source_index.reference(constant_node: parent) or raise "Unknown node in source_index: #{parent}"
552
+ consts = context.type_env.constant_env.children(const_name)
553
+ end
554
+ else
555
+ consts = context.type_env.constant_env.constants
556
+ end
557
+
558
+ if consts
559
+ consts.each do |name, tuple|
560
+ type, full_name, _ = tuple
561
+
562
+ if name.to_s.start_with?(prefix)
563
+ items << ConstantItem.new(env: env, identifier: name, range: range, type: type, full_name: full_name)
564
+ end
565
+ end
566
+ end
567
+ end
568
+
569
+ def instance_variable_items_for_context(context, position:, prefix:, items:)
570
+ range = range_for(position, prefix: prefix)
571
+ context.type_env.instance_variable_types.each do |name, type|
572
+ if name.to_s.start_with?(prefix)
573
+ items << InstanceVariableItem.new(identifier: name, range: range, type: type)
574
+ end
575
+ end
576
+ end
577
+
578
+ def keyword_argument_items_for_method(call_node:, send_node:, position:, prefix:, items:)
579
+ _receiver_node, _method_name, argument_nodes = deconstruct_send_node!(send_node)
580
+
581
+ call = typing.call_of(node: call_node)
582
+
583
+ case call
584
+ when TypeInference::MethodCall::Typed, TypeInference::MethodCall::Error
585
+ context = typing.cursor_context.context or raise
586
+ type = call.receiver_type
587
+ type = type.subst(Interface::Substitution.build([], self_type: context.self_type, module_type: context.module_context&.module_type, instance_type: context.module_context&.instance_type))
588
+
589
+ config = Interface::Builder::Config.new(self_type: type, variable_bounds: context.variable_context.upper_bounds)
590
+ if shape = subtyping.builder.shape(type, config)
591
+ shape = shape.public_shape if private_send?(call_node)
592
+ if method = shape.methods[call.method_name]
593
+ method.overloads.each.with_index do |overload, i|
594
+ defn = overload.method_decls(call.method_name).to_a[0]&.method_def
595
+ if defn && defn.type.type
596
+ range = range_for(position, prefix: prefix)
597
+ kwargs = argument_nodes.find { |arg| arg.type == :kwargs }&.children || []
598
+ used_kwargs = kwargs.filter_map { |arg| arg.type == :pair && arg.children.first.children.first }
599
+
600
+ if defn.type.type.is_a?(::RBS::Types::UntypedFunction)
601
+ kwargs = [] #: Array[Symbol]
602
+ else
603
+ kwargs = defn.type.type.required_keywords.keys + defn.type.type.optional_keywords.keys
604
+ end
605
+
606
+ kwargs.each do |name|
607
+ if name.to_s.start_with?(prefix) && !used_kwargs.include?(name)
608
+ items << KeywordArgumentItem.new(identifier: "#{name}:", range: range)
609
+ end
610
+ end
611
+ end
612
+ end
613
+ end
614
+ end
615
+ end
616
+ end
617
+
618
+
619
+ def index_for(string, line:, column:)
620
+ index = 0
621
+
622
+ string.each_line.with_index do |s, i|
623
+ if i+1 == line
624
+ index += column
625
+ break
626
+ else
627
+ index += s.size
628
+ end
629
+ end
630
+
631
+ index
632
+ end
633
+
634
+ def disallowed_method?(name)
635
+ # initialize isn't invoked by developers when creating
636
+ # instances of new classes, so don't show it as
637
+ # an LSP option
638
+ name == :initialize
639
+ end
640
+
641
+ def unwrap_optional(type)
642
+ if type.is_a?(AST::Types::Union) && type.types.include?(AST::Builtin.nil_type)
643
+ types = type.types.reject { |t| t == AST::Builtin.nil_type }
644
+ AST::Types::Union.new(types: types)
645
+ else
646
+ type
647
+ end
648
+ end
649
+ end
650
+ end
651
+ end
652
+ end