katakata_irb 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,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: []