repl_type_completor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []