repl_type_completor 0.1.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.
@@ -0,0 +1,428 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'methods'
4
+
5
+ module ReplTypeCompletor
6
+ module Types
7
+ OBJECT_TO_TYPE_SAMPLE_SIZE = 50
8
+
9
+ singleton_class.attr_reader :rbs_builder, :rbs_load_error
10
+
11
+ def self.rbs_load_started?
12
+ !!@load_started
13
+ end
14
+
15
+ def self.preload_rbs_builder
16
+ return if rbs_load_started?
17
+ @load_started = true
18
+ Thread.new do
19
+ load_rbs_builder
20
+ end
21
+ end
22
+
23
+ def self.load_rbs_builder
24
+ @load_started = true
25
+ require 'rbs'
26
+ require 'rbs/cli'
27
+ loader = RBS::CLI::LibraryOptions.new.loader
28
+ loader.add path: Pathname('sig')
29
+ @rbs_builder = RBS::DefinitionBuilder.new env: RBS::Environment.from_loader(loader).resolve_type_names
30
+ rescue LoadError, StandardError => e
31
+ @rbs_load_error = e
32
+ nil
33
+ end
34
+
35
+ def self.class_name_of(klass)
36
+ klass = klass.superclass if klass.singleton_class?
37
+ Methods::MODULE_NAME_METHOD.bind_call klass
38
+ end
39
+
40
+ def self.rbs_search_method(klass, method_name, singleton)
41
+ klass.ancestors.each do |ancestor|
42
+ name = class_name_of ancestor
43
+ next unless name && rbs_builder
44
+ type_name = RBS::TypeName(name).absolute!
45
+ definition = (singleton ? rbs_builder.build_singleton(type_name) : rbs_builder.build_instance(type_name)) rescue nil
46
+ method = definition.methods[method_name] if definition
47
+ return method if method
48
+ end
49
+ nil
50
+ end
51
+
52
+ def self.method_return_type(type, method_name)
53
+ receivers = type.types.map do |t|
54
+ case t
55
+ in SingletonType
56
+ [t, t.module_or_class, true]
57
+ in InstanceType
58
+ [t, t.klass, false]
59
+ end
60
+ end
61
+ types = receivers.flat_map do |receiver_type, klass, singleton|
62
+ method = rbs_search_method klass, method_name, singleton
63
+ next [] unless method
64
+ method.method_types.map do |method|
65
+ from_rbs_type(method.type.return_type, receiver_type, {})
66
+ end
67
+ end
68
+ UnionType[*types]
69
+ end
70
+
71
+ def self.rbs_methods(type, method_name, args_types, kwargs_type, has_block)
72
+ return [] unless rbs_builder
73
+
74
+ receivers = type.types.map do |t|
75
+ case t
76
+ in SingletonType
77
+ [t, t.module_or_class, true]
78
+ in InstanceType
79
+ [t, t.klass, false]
80
+ end
81
+ end
82
+ has_splat = args_types.include?(nil)
83
+ methods_with_score = receivers.flat_map do |receiver_type, klass, singleton|
84
+ method = rbs_search_method klass, method_name, singleton
85
+ next [] unless method
86
+ method.method_types.map do |method_type|
87
+ score = 0
88
+ score += 2 if !!method_type.block == has_block
89
+ reqs = method_type.type.required_positionals
90
+ opts = method_type.type.optional_positionals
91
+ rest = method_type.type.rest_positionals
92
+ trailings = method_type.type.trailing_positionals
93
+ keyreqs = method_type.type.required_keywords
94
+ keyopts = method_type.type.optional_keywords
95
+ keyrest = method_type.type.rest_keywords
96
+ args = args_types
97
+ if kwargs_type&.any? && keyreqs.empty? && keyopts.empty? && keyrest.nil?
98
+ kw_value_type = UnionType[*kwargs_type.values]
99
+ args += [InstanceType.new(Hash, K: SYMBOL, V: kw_value_type)]
100
+ end
101
+ if has_splat
102
+ score += 1 if args.count(&:itself) <= reqs.size + opts.size + trailings.size
103
+ elsif reqs.size + trailings.size <= args.size && (rest || args.size <= reqs.size + opts.size + trailings.size)
104
+ score += 2
105
+ centers = args[reqs.size...-trailings.size]
106
+ given = args.first(reqs.size) + centers.take(opts.size) + args.last(trailings.size)
107
+ expected = (reqs + opts.take(centers.size) + trailings).map(&:type)
108
+ if rest
109
+ given << UnionType[*centers.drop(opts.size)]
110
+ expected << rest.type
111
+ end
112
+ if given.any?
113
+ score += given.zip(expected).count do |t, e|
114
+ e = from_rbs_type e, receiver_type
115
+ intersect?(t, e) || (intersect?(STRING, e) && t.methods.include?(:to_str)) || (intersect?(INTEGER, e) && t.methods.include?(:to_int)) || (intersect?(ARRAY, e) && t.methods.include?(:to_ary))
116
+ end.fdiv(given.size)
117
+ end
118
+ end
119
+ [[method_type, given || [], expected || []], score]
120
+ end
121
+ end
122
+ max_score = methods_with_score.map(&:last).max
123
+ methods_with_score.select { _2 == max_score }.map(&:first)
124
+ end
125
+
126
+ def self.intersect?(a, b)
127
+ atypes = a.types.group_by(&:class)
128
+ btypes = b.types.group_by(&:class)
129
+ if atypes[SingletonType] && btypes[SingletonType]
130
+ aa, bb = [atypes, btypes].map {|types| types[SingletonType].map(&:module_or_class) }
131
+ return true if (aa & bb).any?
132
+ end
133
+
134
+ aa, bb = [atypes, btypes].map {|types| (types[InstanceType] || []).map(&:klass) }
135
+ (aa.flat_map(&:ancestors) & bb).any?
136
+ end
137
+
138
+ def self.type_from_object(object)
139
+ case object
140
+ when Array
141
+ InstanceType.new Array, { Elem: union_type_from_objects(object) }
142
+ when Hash
143
+ InstanceType.new Hash, { K: union_type_from_objects(object.keys), V: union_type_from_objects(object.values) }
144
+ when Module
145
+ SingletonType.new object
146
+ else
147
+ klass = Methods::OBJECT_SINGLETON_CLASS_METHOD.bind_call(object) rescue Methods::OBJECT_CLASS_METHOD.bind_call(object)
148
+ InstanceType.new klass
149
+ end
150
+ end
151
+
152
+ def self.union_type_from_objects(objects)
153
+ values = objects.size <= OBJECT_TO_TYPE_SAMPLE_SIZE ? objects : objects.sample(OBJECT_TO_TYPE_SAMPLE_SIZE)
154
+ klasses = values.map { Methods::OBJECT_CLASS_METHOD.bind_call(_1) }
155
+ UnionType[*klasses.uniq.map { InstanceType.new _1 }]
156
+ end
157
+
158
+ class SingletonType
159
+ attr_reader :module_or_class
160
+ def initialize(module_or_class)
161
+ @module_or_class = module_or_class
162
+ end
163
+ def transform() = yield(self)
164
+ def methods() = @module_or_class.methods
165
+ def all_methods() = methods | Kernel.methods
166
+ def constants() = @module_or_class.constants
167
+ def types() = [self]
168
+ def nillable?() = false
169
+ def nonnillable() = self
170
+ def inspect
171
+ "#{module_or_class}.itself"
172
+ end
173
+ end
174
+
175
+ class InstanceType
176
+ attr_reader :klass, :params
177
+ def initialize(klass, params = {})
178
+ @klass = klass
179
+ @params = params
180
+ end
181
+ def transform() = yield(self)
182
+ def methods() = rbs_methods.select { _2.public? }.keys | @klass.instance_methods
183
+ def all_methods() = rbs_methods.keys | @klass.instance_methods | @klass.private_instance_methods
184
+ def constants() = []
185
+ def types() = [self]
186
+ def nillable?() = (@klass == NilClass)
187
+ def nonnillable() = self
188
+ def rbs_methods
189
+ name = Types.class_name_of(@klass)
190
+ return {} unless name && Types.rbs_builder
191
+
192
+ type_name = RBS::TypeName(name).absolute!
193
+ Types.rbs_builder.build_instance(type_name).methods rescue {}
194
+ end
195
+ def inspect
196
+ if params.empty?
197
+ inspect_without_params
198
+ else
199
+ params_string = "[#{params.map { "#{_1}: #{_2.inspect}" }.join(', ')}]"
200
+ "#{inspect_without_params}#{params_string}"
201
+ end
202
+ end
203
+ def inspect_without_params
204
+ if klass == NilClass
205
+ 'nil'
206
+ elsif klass == TrueClass
207
+ 'true'
208
+ elsif klass == FalseClass
209
+ 'false'
210
+ else
211
+ klass.singleton_class? ? klass.superclass.to_s : klass.to_s
212
+ end
213
+ end
214
+ end
215
+
216
+ NIL = InstanceType.new NilClass
217
+ OBJECT = InstanceType.new Object
218
+ TRUE = InstanceType.new TrueClass
219
+ FALSE = InstanceType.new FalseClass
220
+ SYMBOL = InstanceType.new Symbol
221
+ STRING = InstanceType.new String
222
+ INTEGER = InstanceType.new Integer
223
+ RANGE = InstanceType.new Range
224
+ REGEXP = InstanceType.new Regexp
225
+ FLOAT = InstanceType.new Float
226
+ RATIONAL = InstanceType.new Rational
227
+ COMPLEX = InstanceType.new Complex
228
+ ARRAY = InstanceType.new Array
229
+ HASH = InstanceType.new Hash
230
+ CLASS = InstanceType.new Class
231
+ MODULE = InstanceType.new Module
232
+ PROC = InstanceType.new Proc
233
+
234
+ class UnionType
235
+ attr_reader :types
236
+
237
+ def initialize(*types)
238
+ @types = []
239
+ singletons = []
240
+ instances = {}
241
+ collect = -> type do
242
+ case type
243
+ in UnionType
244
+ type.types.each(&collect)
245
+ in InstanceType
246
+ params = (instances[type.klass] ||= {})
247
+ type.params.each do |k, v|
248
+ (params[k] ||= []) << v
249
+ end
250
+ in SingletonType
251
+ singletons << type
252
+ end
253
+ end
254
+ types.each(&collect)
255
+ @types = singletons.uniq + instances.map do |klass, params|
256
+ InstanceType.new(klass, params.transform_values { |v| UnionType[*v] })
257
+ end
258
+ end
259
+
260
+ def transform(&block)
261
+ UnionType[*types.map(&block)]
262
+ end
263
+
264
+ def nillable?
265
+ types.any?(&:nillable?)
266
+ end
267
+
268
+ def nonnillable
269
+ UnionType[*types.reject { _1.is_a?(InstanceType) && _1.klass == NilClass }]
270
+ end
271
+
272
+ def self.[](*types)
273
+ type = new(*types)
274
+ if type.types.empty?
275
+ OBJECT
276
+ elsif type.types.size == 1
277
+ type.types.first
278
+ else
279
+ type
280
+ end
281
+ end
282
+
283
+ def methods() = @types.flat_map(&:methods).uniq
284
+ def all_methods() = @types.flat_map(&:all_methods).uniq
285
+ def constants() = @types.flat_map(&:constants).uniq
286
+ def inspect() = @types.map(&:inspect).join(' | ')
287
+ end
288
+
289
+ BOOLEAN = UnionType[TRUE, FALSE]
290
+
291
+ def self.array_of(*types)
292
+ type = types.size >= 2 ? UnionType[*types] : types.first || OBJECT
293
+ InstanceType.new Array, Elem: type
294
+ end
295
+
296
+ def self.from_rbs_type(return_type, self_type, extra_vars = {})
297
+ case return_type
298
+ when RBS::Types::Bases::Self
299
+ self_type
300
+ when RBS::Types::Bases::Bottom, RBS::Types::Bases::Nil
301
+ NIL
302
+ when RBS::Types::Bases::Any, RBS::Types::Bases::Void
303
+ OBJECT
304
+ when RBS::Types::Bases::Class
305
+ self_type.transform do |type|
306
+ case type
307
+ in SingletonType
308
+ InstanceType.new(self_type.module_or_class.is_a?(Class) ? Class : Module)
309
+ in InstanceType
310
+ SingletonType.new type.klass
311
+ end
312
+ end
313
+ UnionType[*types]
314
+ when RBS::Types::Bases::Bool
315
+ BOOLEAN
316
+ when RBS::Types::Bases::Instance
317
+ self_type.transform do |type|
318
+ if type.is_a?(SingletonType) && type.module_or_class.is_a?(Class)
319
+ InstanceType.new type.module_or_class
320
+ else
321
+ OBJECT
322
+ end
323
+ end
324
+ when RBS::Types::Union
325
+ UnionType[*return_type.types.map { from_rbs_type _1, self_type, extra_vars }]
326
+ when RBS::Types::Proc
327
+ PROC
328
+ when RBS::Types::Tuple
329
+ elem = UnionType[*return_type.types.map { from_rbs_type _1, self_type, extra_vars }]
330
+ InstanceType.new Array, Elem: elem
331
+ when RBS::Types::Record
332
+ InstanceType.new Hash, K: SYMBOL, V: OBJECT
333
+ when RBS::Types::Literal
334
+ InstanceType.new return_type.literal.class
335
+ when RBS::Types::Variable
336
+ if extra_vars.key? return_type.name
337
+ extra_vars[return_type.name]
338
+ elsif self_type.is_a? InstanceType
339
+ self_type.params[return_type.name] || OBJECT
340
+ elsif self_type.is_a? UnionType
341
+ types = self_type.types.filter_map do |t|
342
+ t.params[return_type.name] if t.is_a? InstanceType
343
+ end
344
+ UnionType[*types]
345
+ else
346
+ OBJECT
347
+ end
348
+ when RBS::Types::Optional
349
+ UnionType[from_rbs_type(return_type.type, self_type, extra_vars), NIL]
350
+ when RBS::Types::Alias
351
+ case return_type.name.name
352
+ when :int
353
+ INTEGER
354
+ when :boolish
355
+ BOOLEAN
356
+ when :string
357
+ STRING
358
+ else
359
+ # TODO: ???
360
+ OBJECT
361
+ end
362
+ when RBS::Types::Interface
363
+ # unimplemented
364
+ OBJECT
365
+ when RBS::Types::ClassInstance
366
+ klass = return_type.name.to_namespace.path.reduce(Object) { _1.const_get _2 }
367
+ if return_type.args
368
+ args = return_type.args.map { from_rbs_type _1, self_type, extra_vars }
369
+ names = rbs_builder.build_singleton(return_type.name).type_params
370
+ params = names.map.with_index { [_1, args[_2] || OBJECT] }.to_h
371
+ end
372
+ InstanceType.new klass, params || {}
373
+ end
374
+ end
375
+
376
+ def self.method_return_bottom?(method)
377
+ method.type.return_type.is_a? RBS::Types::Bases::Bottom
378
+ end
379
+
380
+ def self.match_free_variables(vars, types, values)
381
+ accumulator = {}
382
+ types.zip values do |t, v|
383
+ _match_free_variable(vars, t, v, accumulator) if v
384
+ end
385
+ accumulator.transform_values { UnionType[*_1] }
386
+ end
387
+
388
+ def self._match_free_variable(vars, rbs_type, value, accumulator)
389
+ case [rbs_type, value]
390
+ in [RBS::Types::Variable,]
391
+ (accumulator[rbs_type.name] ||= []) << value if vars.include? rbs_type.name
392
+ in [RBS::Types::ClassInstance, InstanceType]
393
+ names = rbs_builder.build_singleton(rbs_type.name).type_params
394
+ names.zip(rbs_type.args).each do |name, arg|
395
+ v = value.params[name]
396
+ _match_free_variable vars, arg, v, accumulator if v
397
+ end
398
+ in [RBS::Types::Tuple, InstanceType] if value.klass == Array
399
+ v = value.params[:Elem]
400
+ rbs_type.types.each do |t|
401
+ _match_free_variable vars, t, v, accumulator
402
+ end
403
+ in [RBS::Types::Record, InstanceType] if value.klass == Hash
404
+ # TODO
405
+ in [RBS::Types::Interface,]
406
+ definition = rbs_builder.build_interface rbs_type.name
407
+ convert = {}
408
+ definition.type_params.zip(rbs_type.args).each do |from, arg|
409
+ convert[from] = arg.name if arg.is_a? RBS::Types::Variable
410
+ end
411
+ return if convert.empty?
412
+ ac = {}
413
+ definition.methods.each do |method_name, method|
414
+ return_type = method_return_type value, method_name
415
+ method.defs.each do |method_def|
416
+ interface_return_type = method_def.type.type.return_type
417
+ _match_free_variable convert, interface_return_type, return_type, ac
418
+ end
419
+ end
420
+ convert.each do |from, to|
421
+ values = ac[from]
422
+ (accumulator[to] ||= []).concat values if values
423
+ end
424
+ else
425
+ end
426
+ end
427
+ end
428
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ReplTypeCompletor
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'repl_type_completor/version'
4
+ require_relative 'repl_type_completor/type_analyzer'
5
+ require_relative 'repl_type_completor/result'
6
+
7
+ module ReplTypeCompletor
8
+ class << self
9
+ attr_reader :last_completion_error
10
+
11
+ def rbs_load_error
12
+ Types.rbs_load_error
13
+ end
14
+
15
+ def rbs_load_started?
16
+ Types.rbs_load_started?
17
+ end
18
+
19
+ def rbs_loaded?
20
+ !!Types.rbs_builder
21
+ end
22
+
23
+ def load_rbs
24
+ Types.load_rbs_builder unless rbs_loaded?
25
+ end
26
+
27
+ def preload_rbs
28
+ Types.preload_rbs_builder
29
+ end
30
+
31
+ def analyze(code, binding:, filename: nil)
32
+ verbose, $VERBOSE = $VERBOSE, nil
33
+ result = analyze_code(code, binding)
34
+ Result.new(result, binding, filename) if result
35
+ rescue Exception => e
36
+ handle_exception(e)
37
+ nil
38
+ ensure
39
+ $VERBOSE = verbose
40
+ end
41
+
42
+ def handle_exception(e)
43
+ @last_completion_error = e
44
+ end
45
+
46
+ def info
47
+ require 'rbs'
48
+ prism_info = "Prism: #{Prism::VERSION}"
49
+ rbs_info = "RBS: #{RBS::VERSION}"
50
+ if rbs_load_error
51
+ rbs_info << " #{rbs_load_error.inspect}"
52
+ elsif !rbs_load_started?
53
+ rbs_info << ' signatures not loaded'
54
+ elsif !rbs_loaded?
55
+ rbs_info << ' signatures loading'
56
+ end
57
+ "ReplTypeCompletor: #{VERSION}, #{prism_info}, #{rbs_info}"
58
+ end
59
+
60
+ private
61
+
62
+ def analyze_code(code, binding = Object::TOPLEVEL_BINDING)
63
+ ast = Prism.parse(code, scopes: [binding.local_variables]).value
64
+ *parents, target_node = find_target ast, code.bytesize
65
+ return unless target_node
66
+
67
+ calculate_scope = -> { TypeAnalyzer.calculate_target_type_scope(binding, parents, target_node).last }
68
+ calculate_type_scope = ->(node) { TypeAnalyzer.calculate_target_type_scope binding, [*parents, target_node], node }
69
+
70
+ case target_node
71
+ when Prism::StringNode, Prism::InterpolatedStringNode
72
+ call_node, args_node = parents.last(2)
73
+ return unless call_node.is_a?(Prism::CallNode) && call_node.receiver.nil?
74
+ return unless args_node.is_a?(Prism::ArgumentsNode) && args_node.arguments.size == 1
75
+
76
+ content = code.byteslice(target_node.opening_loc.end_offset..)
77
+ case call_node.name
78
+ when :require
79
+ [:require, content]
80
+ when :require_relative
81
+ [:require_relative, content]
82
+ end
83
+ when Prism::SymbolNode
84
+ name = target_node.value.to_s
85
+ if parents.last.is_a? Prism::BlockArgumentNode # method(&:target)
86
+ receiver_type, _scope = calculate_type_scope.call target_node
87
+ [:call, name, receiver_type, false]
88
+ else
89
+ [:symbol, name] unless name.empty?
90
+ end
91
+ when Prism::CallNode
92
+ name = target_node.message.to_s
93
+ return [:lvar_or_method, name, calculate_scope.call] if target_node.receiver.nil?
94
+
95
+ self_call = target_node.receiver.is_a? Prism::SelfNode
96
+ op = target_node.call_operator
97
+ receiver_type, _scope = calculate_type_scope.call target_node.receiver
98
+ receiver_type = receiver_type.nonnillable if op == '&.'
99
+ [op == '::' ? :call_or_const : :call, name, receiver_type, self_call]
100
+ when Prism::LocalVariableReadNode, Prism::LocalVariableTargetNode
101
+ [:lvar_or_method, target_node.name.to_s, calculate_scope.call]
102
+ when Prism::ConstantReadNode, Prism::ConstantTargetNode
103
+ name = target_node.name.to_s
104
+ if parents.last.is_a? Prism::ConstantPathNode
105
+ path_node = parents.last
106
+ if path_node.parent # A::B
107
+ receiver, scope = calculate_type_scope.call(path_node.parent)
108
+ [:const, name, receiver, scope]
109
+ else # ::A
110
+ scope = calculate_scope.call
111
+ [:const, name, Types::SingletonType.new(Object), scope]
112
+ end
113
+ else
114
+ [:const, name, nil, calculate_scope.call]
115
+ end
116
+ when Prism::GlobalVariableReadNode, Prism::GlobalVariableTargetNode
117
+ [:gvar, target_node.name.to_s, calculate_scope.call]
118
+ when Prism::InstanceVariableReadNode, Prism::InstanceVariableTargetNode
119
+ [:ivar, target_node.name.to_s, calculate_scope.call]
120
+ when Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode
121
+ [:cvar, target_node.name.to_s, calculate_scope.call]
122
+ end
123
+ end
124
+
125
+ def find_target(node, position)
126
+ case node
127
+ when Prism::StringNode
128
+ # Unclosed quoted string has empty content and empty closing
129
+ return [node] if node.opening && node.closing&.empty?
130
+ when Prism::InterpolatedStringNode
131
+ # Unclosed double quoted string is InterpolatedStringNode with empty parts
132
+ return [node] if node.parts.empty? && node.opening && node.closing&.empty?
133
+ end
134
+
135
+ node.compact_child_nodes.each do |n|
136
+ match = find_target(n, position)
137
+ next unless match
138
+
139
+ match.unshift node
140
+ return match
141
+ end
142
+
143
+ [node] if node.location.end_offset == position
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,19 @@
1
+ module ReplTypeCompletor
2
+ VERSION: String
3
+
4
+ class Result
5
+ def completion_candidates: () -> Array[String]
6
+ def doc_namespace: (String) -> String?
7
+ end
8
+
9
+ def self.analyze: (String, binding: Binding, ?filename: String?) -> Result?
10
+
11
+ def self.rbs_load_error: () -> Exception?
12
+ def self.last_completion_error: () -> Exception?
13
+
14
+ def self.rbs_load_started?: () -> bool
15
+ def self.rbs_loaded?: () -> bool
16
+ def self.load_rbs: () -> void
17
+ def self.preload_rbs: () -> void
18
+ def self.info: () -> String
19
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: repl_type_completor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - tompng
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-11-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: prism
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.18.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.18.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rbs
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.7.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.7.0
41
+ description: Type based completion for REPL.
42
+ email:
43
+ - tomoyapenguin@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - Gemfile
49
+ - LICENSE.txt
50
+ - README.md
51
+ - Rakefile
52
+ - lib/repl_type_completor.rb
53
+ - lib/repl_type_completor/methods.rb
54
+ - lib/repl_type_completor/require_paths.rb
55
+ - lib/repl_type_completor/result.rb
56
+ - lib/repl_type_completor/scope.rb
57
+ - lib/repl_type_completor/type_analyzer.rb
58
+ - lib/repl_type_completor/types.rb
59
+ - lib/repl_type_completor/version.rb
60
+ - sig/repl_type_completor.rbs
61
+ homepage: https://github.com/ruby/repl_type_completor
62
+ licenses:
63
+ - MIT
64
+ metadata:
65
+ homepage_uri: https://github.com/ruby/repl_type_completor
66
+ source_code_uri: https://github.com/ruby/repl_type_completor
67
+ documentation_uri: https://github.com/ruby/repl_type_completor
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 3.0.0
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubygems_version: 3.4.10
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Type based completion for REPL.
87
+ test_files: []