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.
- checksums.yaml +7 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +23 -0
- data/LICENSE.txt +21 -0
- data/README.md +51 -0
- data/Rakefile +12 -0
- data/bin/console +10 -0
- data/bin/setup +8 -0
- data/exe/kirb +10 -0
- data/katakata_irb.gemspec +36 -0
- data/lib/katakata_irb/completor.rb +187 -0
- data/lib/katakata_irb/reline_patch.rb +40 -0
- data/lib/katakata_irb/reline_patches/escapeseq.patch +45 -0
- data/lib/katakata_irb/reline_patches/fullwidth.patch +200 -0
- data/lib/katakata_irb/reline_patches/indent.patch +25 -0
- data/lib/katakata_irb/reline_patches/raw.patch +95 -0
- data/lib/katakata_irb/reline_patches/scrollbar.patch +43 -0
- data/lib/katakata_irb/reline_patches/wholelines.patch +102 -0
- data/lib/katakata_irb/ruby_lex_patch.rb +197 -0
- data/lib/katakata_irb/trex.rb +207 -0
- data/lib/katakata_irb/type_simulator.rb +1108 -0
- data/lib/katakata_irb/types.rb +341 -0
- data/lib/katakata_irb/version.rb +5 -0
- data/lib/katakata_irb.rb +15 -0
- data/sig/katakata_irb.rbs +4 -0
- metadata +84 -0
@@ -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
|
data/lib/katakata_irb.rb
ADDED
@@ -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
|
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: []
|