katakata_irb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,341 @@
1
+ require 'rbs'
2
+ require 'rbs/cli'
3
+
4
+ module KatakataIrb; end
5
+ module KatakataIrb::Types
6
+ def self.rbs_builder
7
+ @rbs_builder ||= RBS::DefinitionBuilder.new(
8
+ env: RBS::Environment.from_loader(RBS::CLI::LibraryOptions.new.loader).resolve_type_names
9
+ )
10
+ end
11
+
12
+ Splat = Struct.new :item
13
+
14
+ def self.rbs_search_method(klass, method_name, singleton)
15
+ klass.ancestors.each do |ancestor|
16
+ next unless ancestor.name
17
+ type_name = RBS::TypeName(ancestor.name).absolute!
18
+ definition = (singleton ? rbs_builder.build_singleton(type_name) : rbs_builder.build_instance(type_name)) rescue nil
19
+ method = definition&.methods&.[](method_name)
20
+ return method if definition
21
+ end
22
+ nil
23
+ end
24
+
25
+ def self.rbs_methods(type, method_name, args_types, kwargs_type, has_block)
26
+ types = (type in UnionType) ? type.types : [type]
27
+ receivers = types.map do |t|
28
+ case t
29
+ in ProcType
30
+ [t, Proc, false]
31
+ in SingletonType
32
+ [t, t.module_or_class, true]
33
+ in InstanceType
34
+ [t, t.klass, false]
35
+ end
36
+ end
37
+ has_splat = args_types.any? { _1 in Splat }
38
+ methods_with_score = receivers.flat_map do |receiver_type, klass, singleton|
39
+ method = rbs_search_method klass, method_name, singleton
40
+ next [] unless method
41
+ method.method_types.map do |method_type|
42
+ score = 0
43
+ score += 2 if !!method_type.block == has_block
44
+ reqs = method_type.type.required_positionals
45
+ opts = method_type.type.optional_positionals
46
+ rest = method_type.type.rest_positionals
47
+ trailings = method_type.type.trailing_positionals
48
+ keyreqs = method_type.type.required_keywords
49
+ keyopts = method_type.type.optional_keywords
50
+ keyrest = method_type.type.rest_keywords
51
+ args = (kwargs_type && keyreqs.empty? && keyopts.empty? && keyrest.nil?) ? args_types + [kwargs_type] : args_types
52
+ if has_splat
53
+ score += 1 if args.count { !(_1 in Splat) } <= reqs.size + opts.size + trailings.size
54
+ elsif reqs.size + trailings.size <= args.size && (rest || args.size <= reqs.size + opts.size + trailings.size)
55
+ score += 2
56
+ centers = args[reqs.size...-trailings.size]
57
+ given = args.first(reqs.size) + centers.take(opts.size) + args.last(trailings.size)
58
+ expected = (reqs + opts.take(centers.size) + trailings).map(&:type)
59
+ if rest
60
+ given << UnionType[*centers.drop(opts.size)]
61
+ expected << rest.type
62
+ end
63
+ score += given.zip(expected).count do |t, e|
64
+ intersect? t, from_rbs_type(e, receiver_type)
65
+ end
66
+ end
67
+ [[method_type, given || [], expected || []], score]
68
+ end
69
+ end
70
+ max_score = methods_with_score.map(&:last).max
71
+ methods_with_score.select { _2 == max_score }.map(&:first)
72
+ end
73
+
74
+ def self.intersect?(a, b)
75
+ atypes = ((a in UnionType) ? a.types : [a]).group_by(&:class)
76
+ btypes = ((b in UnionType) ? b.types : [b]).group_by(&:class)
77
+ intersect = ->(type, &block) do
78
+ aa, bb = [atypes, btypes].map {|types| (types[type] || []).map(&block) }
79
+ (aa & bb).any?
80
+ end
81
+ return true if atypes[ProcType] && btypes[ProcType]
82
+ return true if intersect.call(SingletonType, &:module_or_class)
83
+ intersect.call(InstanceType, &:klass)
84
+ end
85
+
86
+ def self.type_from_object(object, max_level: 4)
87
+ max_level -= 1
88
+ case object
89
+ when Array
90
+ if max_level > 0
91
+ values = object.map { type_from_object(_1, max_level:) }
92
+ InstanceType.new Array, { Elem: UnionType[*values] }
93
+ else
94
+ InstanceType.new Array, { Elem: UnionType[*object.map(&:class).uniq.map { InstanceType.new _1 }] }
95
+ end
96
+ when Hash
97
+ if max_level > 0
98
+ keys = object.keys.map { type_from_object(_1, max_level:) }
99
+ values = object.values.map { type_from_object(_1, max_level:) }
100
+ InstanceType.new Hash, { K: UnionType[*keys], V: UnionType[*values] }
101
+ else
102
+ keys = object.keys.map(&:class).uniq.map { InstanceType.new _1 }
103
+ values = object.values.map(&:class).uniq.map { InstanceType.new _1 }
104
+ InstanceType.new Hash, { K: UnionType[*keys], V: UnionType[*values] }
105
+ end
106
+ when Module
107
+ SingletonType.new object
108
+ else
109
+ InstanceType.new object.class
110
+ end
111
+ end
112
+
113
+ class SingletonType
114
+ attr_reader :module_or_class
115
+ def initialize(module_or_class)
116
+ @module_or_class = module_or_class
117
+ end
118
+ def transform() = yield(self)
119
+ def methods() = @module_or_class.methods
120
+ def all_methods() = methods
121
+ def constants() = @module_or_class.constants
122
+ def types() = [self]
123
+ end
124
+
125
+ class InstanceType
126
+ attr_reader :klass, :params
127
+ def initialize(klass, params = {})
128
+ @klass = klass
129
+ @params = params
130
+ end
131
+ def transform() = yield(self)
132
+ def methods() = @klass.instance_methods
133
+ def all_methods() = @klass.instance_methods | @klass.private_instance_methods
134
+ def constants() = []
135
+ def types() = [self]
136
+ end
137
+
138
+ class ProcType
139
+ attr_reader :params, :kwparams, :return_type
140
+ def initialize(params = [], kwparams = {}, return_type = NIL)
141
+ @params = params
142
+ @kwparams = kwparams
143
+ @return_type = return_type
144
+ end
145
+ def transform() = yield(self)
146
+ def methods() = Proc.instance_methods
147
+ def all_methods() = Proc.instance_methods | Proc.private_instance_methods
148
+ def constants() = []
149
+ def types() = [self]
150
+ end
151
+
152
+ NIL = InstanceType.new NilClass
153
+ OBJECT = InstanceType.new Object
154
+ TRUE = InstanceType.new FalseClass
155
+ FALSE = InstanceType.new FalseClass
156
+ SYMBOL = InstanceType.new Symbol
157
+ STRING = InstanceType.new String
158
+ INTEGER = InstanceType.new Integer
159
+ RANGE = InstanceType.new Range
160
+ REGEXP = InstanceType.new Regexp
161
+ FLOAT = InstanceType.new Float
162
+ RATIONAL = InstanceType.new Rational
163
+ COMPLEX = InstanceType.new Complex
164
+ ARRAY = InstanceType.new Array
165
+ HASH = InstanceType.new Hash
166
+ CLASS = InstanceType.new Class
167
+ MODULE = InstanceType.new Module
168
+ PROC = ProcType.new
169
+
170
+ class UnionType
171
+ attr_reader :types
172
+
173
+ def initialize(*types)
174
+ @types = []
175
+ singletons = []
176
+ instances = {}
177
+ procs = []
178
+ collect = -> type do
179
+ case type
180
+ in UnionType
181
+ type.types.each(&collect)
182
+ in InstanceType
183
+ params = (instances[type.klass] ||= {})
184
+ type.params.each do |k, v|
185
+ (params[k] ||= []) << v
186
+ end
187
+ in SingletonType
188
+ singletons << type
189
+ in ProcType
190
+ procs << type
191
+ end
192
+ end
193
+ types.each(&collect)
194
+ @types = procs.uniq + singletons.uniq + instances.map do |klass, params|
195
+ InstanceType.new(klass, params.transform_values { |v| UnionType[*v] })
196
+ end
197
+ end
198
+
199
+ def transform(&block)
200
+ UnionType[*types.map(&block)]
201
+ end
202
+
203
+ def self.[](*types)
204
+ type = new(*types)
205
+ if type.types.empty?
206
+ OBJECT
207
+ elsif type.types.size == 1
208
+ type.types.first
209
+ else
210
+ type
211
+ end
212
+ end
213
+
214
+ def methods() = @types.flat_map(&:methods).uniq
215
+ def all_methods() = @types.flat_map(&:all_methods).uniq
216
+ def constants() = @types.flat_map(&:constants).uniq
217
+ end
218
+
219
+ def self.from_rbs_type(return_type, self_type, extra_vars = {})
220
+ case return_type
221
+ when RBS::Types::Bases::Self
222
+ self_type
223
+ when RBS::Types::Bases::Void, RBS::Types::Bases::Bottom, RBS::Types::Bases::Nil
224
+ NIL
225
+ when RBS::Types::Bases::Any
226
+ OBJECT
227
+ when RBS::Types::Bases::Class
228
+ self_type.transform do |type|
229
+ case type
230
+ in SingletonType
231
+ InstanceType.new(self_type.module_or_class.is_a?(Class) ? Class : Module)
232
+ in InstanceType
233
+ SingletonType.new type.klass
234
+ end
235
+ end
236
+ UnionType[*types]
237
+ when RBS::Types::Bases::Bool
238
+ UnionType[TRUE, FALSE]
239
+ when RBS::Types::Bases::Instance
240
+ self_type.transform do |type|
241
+ case type
242
+ in SingletonClass
243
+ InstanceType.new type.klass
244
+ in InstanceType
245
+ case type.klass
246
+ in Class
247
+ InstanceType.new Class
248
+ in Module
249
+ InstanceType.new Module
250
+ else
251
+ OBJECT
252
+ end
253
+ end
254
+ end
255
+ when RBS::Types::Union
256
+ UnionType[*return_type.types.map { from_rbs_type _1, self_type, extra_vars }]
257
+ when RBS::Types::Proc
258
+ InstanceType.new Proc
259
+ when RBS::Types::Tuple
260
+ elem = UnionType[*return_type.types.map { from_rbs_type _1, self_type, extra_vars }]
261
+ InstanceType.new Array, Elem: elem
262
+ when RBS::Types::Record
263
+ InstanceType.new Hash, K: SYMBOL, V: OBJECT
264
+ when RBS::Types::Literal
265
+ InstanceType.new return_type.literal.class
266
+ when RBS::Types::Variable
267
+ if extra_vars.key? return_type.name
268
+ extra_vars[return_type.name]
269
+ elsif self_type in InstanceType
270
+ self_type.params[return_type.name] || OBJECT
271
+ elsif self_type in UnionType
272
+ types = self_type.types.filter_map do |t|
273
+ t.params[return_type.name] if t in InstanceType
274
+ end
275
+ UnionType[*types]
276
+ else
277
+ OBJECT
278
+ end
279
+ when RBS::Types::Optional
280
+ UnionType[from_rbs_type(return_type.type, self_type, extra_vars), NIL]
281
+ when RBS::Types::Alias
282
+ case return_type.name.name
283
+ when :int
284
+ INTEGER
285
+ when :boolish
286
+ UnionType[TRUE, FALSE]
287
+ when :string
288
+ STRING
289
+ else
290
+ # TODO: ???
291
+ OBJECT
292
+ end
293
+ when RBS::Types::Interface
294
+ # unimplemented
295
+ OBJECT
296
+ when RBS::Types::ClassInstance
297
+ classes = self_type.types.filter_map do |type|
298
+ type.module_or_class if (type in SingletonType) && type.module_or_class.is_a?(Class)
299
+ end
300
+ if classes.empty?
301
+ klass = Object.const_get(return_type.name.name)
302
+ classes << klass if klass in Class
303
+ end
304
+ if return_type.args
305
+ args = return_type.args.map { from_rbs_type _1, self_type, extra_vars }
306
+ names = rbs_builder.build_singleton(return_type.name).type_params
307
+ params = names.map.with_index { [_1, args[_2] || OBJECT] }.to_h
308
+ end
309
+ UnionType[*classes.map { InstanceType.new _1, params || {} }]
310
+ end
311
+ end
312
+
313
+ def self.match_free_variables(vars, types, values)
314
+ accumulator = {}
315
+ types.zip(values).each do |t, v|
316
+ _match_free_variable(vars, t, v, accumulator)
317
+ end
318
+ accumulator.transform_values { UnionType[*_1] }
319
+ end
320
+
321
+ def self._match_free_variable(vars, rbs_type, value, accumulator)
322
+ case [rbs_type, value]
323
+ in [RBS::Types::Variable,]
324
+ (accumulator[rbs_type.name] ||= []) << value if vars.include? rbs_type.name
325
+ in [RBS::Types::ClassInstance, InstanceType]
326
+ names = rbs_builder.build_singleton(rbs_type.name).type_params
327
+ names.zip(rbs_type.args).each do |name, arg|
328
+ v = value.params[name]
329
+ _match_free_variable vars, arg, v, accumulator if v
330
+ end
331
+ in [RBS::Types::Tuple, InstanceType] if value.klass == Array
332
+ v = value.params[:Elem]
333
+ rbs_type.types.each do |t|
334
+ _match_free_variable vars, t, v, accumulator
335
+ end
336
+ in [RBS::Types::Record, InstanceType] if value.klass == Hash
337
+ # TODO
338
+ else
339
+ end
340
+ end
341
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KatakataIrb
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,15 @@
1
+ module KatakataIrb
2
+ def self.repl
3
+ require 'katakata_irb/completor'
4
+ KatakataIrb::Completor.setup
5
+ IRB.start(__FILE__)
6
+ end
7
+
8
+ def self.log_output=(output)
9
+ @log_output = output
10
+ end
11
+
12
+ def self.log_puts(...)
13
+ STDOUT.cooked { @log_output&.puts(...) }
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ module KatakataIrb
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: katakata_irb
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: 2022-12-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rbs
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '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'
27
+ description: IRB with Typed Completion
28
+ email:
29
+ - tomoyapenguin@gmail.com
30
+ executables:
31
+ - kirb
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - Gemfile
36
+ - Gemfile.lock
37
+ - LICENSE.txt
38
+ - README.md
39
+ - Rakefile
40
+ - bin/console
41
+ - bin/setup
42
+ - exe/kirb
43
+ - katakata_irb.gemspec
44
+ - lib/katakata_irb.rb
45
+ - lib/katakata_irb/completor.rb
46
+ - lib/katakata_irb/reline_patch.rb
47
+ - lib/katakata_irb/reline_patches/escapeseq.patch
48
+ - lib/katakata_irb/reline_patches/fullwidth.patch
49
+ - lib/katakata_irb/reline_patches/indent.patch
50
+ - lib/katakata_irb/reline_patches/raw.patch
51
+ - lib/katakata_irb/reline_patches/scrollbar.patch
52
+ - lib/katakata_irb/reline_patches/wholelines.patch
53
+ - lib/katakata_irb/ruby_lex_patch.rb
54
+ - lib/katakata_irb/trex.rb
55
+ - lib/katakata_irb/type_simulator.rb
56
+ - lib/katakata_irb/types.rb
57
+ - lib/katakata_irb/version.rb
58
+ - sig/katakata_irb.rbs
59
+ homepage: http://github.com/tompng/katakata_irb
60
+ licenses:
61
+ - MIT
62
+ metadata:
63
+ homepage_uri: http://github.com/tompng/katakata_irb
64
+ source_code_uri: http://github.com/tompng/katakata_irb
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 3.1.0
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubygems_version: 3.3.3
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: IRB with Typed Completion
84
+ test_files: []