katakata_irb 0.1.9 → 0.1.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/katakata_irb/completor.rb +108 -65
- data/lib/katakata_irb/nesting_parser.rb +32 -35
- data/lib/katakata_irb/type_simulator.rb +2 -2
- data/lib/katakata_irb/types.rb +14 -6
- data/lib/katakata_irb/version.rb +1 -1
- data/lib/katakata_irb.rb +1 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 810109ee76180f61c9b3672a8def67d0a32d53ca23b2436c65575457b586bf68
|
4
|
+
data.tar.gz: e17ff5c4e226d785031f2b433b35a8763a49e6a254420fcacf96507975dfa70b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47f0ea76bdf5a5599d219524346acca935b67d1ea73ed60d55887534bce442fffc8c483590b53cd84754c754b829551d837c38687282c497f2924c3ec616f0ff
|
7
|
+
data.tar.gz: f3aec83bbee8f2e889fdcf31eecc85627972511bf1673894d7f45f8a6834e6b52212ec4beb1814e0e8ddd705cf39eb9370ba78ff395d288caadcaa07008ebed8
|
@@ -10,7 +10,9 @@ module KatakataIrb::Completor
|
|
10
10
|
singleton_class.attr_accessor :prev_analyze_result
|
11
11
|
|
12
12
|
def self.setup
|
13
|
+
KatakataIrb::Types.preload_in_thread
|
13
14
|
completion_proc = ->(target, preposing = nil, postposing = nil) do
|
15
|
+
verbose, $VERBOSE = $VERBOSE, nil
|
14
16
|
code = "#{preposing}#{target}"
|
15
17
|
irb_context = IRB.conf[:MAIN_CONTEXT]
|
16
18
|
binding = irb_context.workspace.binding
|
@@ -18,10 +20,17 @@ module KatakataIrb::Completor
|
|
18
20
|
KatakataIrb::Completor.prev_analyze_result = result
|
19
21
|
candidates = case result
|
20
22
|
in [:require | :require_relative => method, name]
|
21
|
-
if
|
22
|
-
IRB::InputCompletor
|
23
|
+
if IRB.const_defined? :InputCompletor # IRB::VERSION <= 1.8.1
|
24
|
+
path_completor = IRB::InputCompletor
|
25
|
+
elsif IRB.const_defined? :RegexpCompletor # IRB::VERSION >= 1.8.2
|
26
|
+
path_completor = IRB::RegexpCompletor.new
|
27
|
+
end
|
28
|
+
if !path_completor
|
29
|
+
[]
|
30
|
+
elsif method == :require
|
31
|
+
path_completor.retrieve_files_to_require_from_load_path
|
23
32
|
else
|
24
|
-
|
33
|
+
path_completor.retrieve_files_to_require_relative_from_current_dir
|
25
34
|
end
|
26
35
|
in [:call_or_const, type, name, self_call]
|
27
36
|
((self_call ? type.all_methods: type.methods).map(&:to_s) - HIDDEN_METHODS) | type.constants
|
@@ -50,82 +59,111 @@ module KatakataIrb::Completor
|
|
50
59
|
candidates.map(&:to_s).select { !_1.match?(all_symbols_pattern) && _1.start_with?(name) }.uniq.sort.map do
|
51
60
|
target + _1[name.size..]
|
52
61
|
end
|
53
|
-
end
|
54
|
-
IRB::InputCompletor::CompletionProc.define_singleton_method :call do |*args|
|
55
|
-
completion_proc.call(*args)
|
56
62
|
rescue => e
|
57
63
|
KatakataIrb.last_completion_error = e
|
58
64
|
KatakataIrb.log_puts
|
59
65
|
KatakataIrb.log_puts "#{e.inspect} stored to KatakataIrb.last_completion_error"
|
60
66
|
KatakataIrb.log_puts
|
67
|
+
ensure
|
68
|
+
$VERBOSE = verbose
|
61
69
|
end
|
62
70
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
type
|
69
|
-
|
70
|
-
|
71
|
-
elsif type in KatakataIrb::Types::InstanceType
|
72
|
-
"#{KatakataIrb::Types.class_name_of(type.klass)}##{name}"
|
73
|
-
end
|
71
|
+
doc_namespace_proc = ->(input) do
|
72
|
+
name = input[/[a-zA-Z_0-9]+[!?=]?\z/]
|
73
|
+
method_doc = -> type do
|
74
|
+
type = type.types.find { _1.all_methods.include? name.to_sym }
|
75
|
+
if type in KatakataIrb::Types::SingletonType
|
76
|
+
"#{KatakataIrb::Types.class_name_of(type.module_or_class)}.#{name}"
|
77
|
+
elsif type in KatakataIrb::Types::InstanceType
|
78
|
+
"#{KatakataIrb::Types.class_name_of(type.klass)}##{name}"
|
74
79
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
80
|
+
end
|
81
|
+
call_or_const_doc = -> type do
|
82
|
+
if name =~ /\A[A-Z]/
|
83
|
+
type = type.types.grep(KatakataIrb::Types::SingletonType).find { _1.module_or_class.const_defined?(name) }
|
84
|
+
type.module_or_class == Object ? name : "#{KatakataIrb::Types.class_name_of(type.module_or_class)}::#{name}" if type
|
85
|
+
else
|
86
|
+
method_doc.call(type)
|
82
87
|
end
|
88
|
+
end
|
83
89
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
90
|
+
case KatakataIrb::Completor.prev_analyze_result
|
91
|
+
in [:call_or_const, type, _name, _self_call]
|
92
|
+
call_or_const_doc.call type
|
93
|
+
in [:const, type, _name]
|
94
|
+
# when prev_analyze_result is const, current analyze result might be call
|
95
|
+
call_or_const_doc.call type
|
96
|
+
in [:gvar, _name]
|
97
|
+
name
|
98
|
+
in [:call, type, _name, _self_call]
|
99
|
+
method_doc.call type
|
100
|
+
in [:lvar_or_method, _name, scope]
|
101
|
+
method_doc.call scope.self_type unless scope.local_variables.include?(name)
|
102
|
+
else
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
if IRB.const_defined? :InputCompletor # IRB::VERSION <= 1.8.1
|
107
|
+
IRB::InputCompletor::CompletionProc.define_singleton_method :call do |*args|
|
108
|
+
completion_proc.call(*args)
|
109
|
+
end
|
110
|
+
IRB::InputCompletor.singleton_class.prepend(
|
111
|
+
Module.new do
|
112
|
+
define_method :retrieve_completion_data do |input, doc_namespace: false, **kwargs|
|
113
|
+
return super(input, doc_namespace: false, **kwargs) unless doc_namespace
|
114
|
+
doc_namespace_proc.call input
|
115
|
+
end
|
116
|
+
end
|
117
|
+
)
|
118
|
+
elsif IRB.const_defined? :RegexpCompletor # IRB::VERSION >= 1.8.2
|
119
|
+
IRB::RegexpCompletor.class_eval do
|
120
|
+
define_method :completion_candidates do |preposing, target, postposing, bind:|
|
121
|
+
completion_proc.call(target, preposing, postposing)
|
122
|
+
end
|
123
|
+
define_method :doc_namespace do |_preposing, matched, _postposing, bind:|
|
124
|
+
doc_namespace_proc.call matched
|
97
125
|
end
|
98
126
|
end
|
99
|
-
|
127
|
+
else
|
128
|
+
puts 'Cannot activate katakata_irb'
|
129
|
+
end
|
130
|
+
|
100
131
|
setup_type_dialog
|
101
132
|
end
|
102
133
|
|
134
|
+
def self.type_dialog_content
|
135
|
+
receiver_type = (
|
136
|
+
case KatakataIrb::Completor.prev_analyze_result
|
137
|
+
in [:call_or_const, type, name, _self_call] if name.empty?
|
138
|
+
type
|
139
|
+
in [:call, type, name, _self_call] if name.empty?
|
140
|
+
type
|
141
|
+
else
|
142
|
+
return
|
143
|
+
end
|
144
|
+
)
|
145
|
+
if KatakataIrb::Types.rbs_builder.nil? && !KatakataIrb::Types.rbs_load_error
|
146
|
+
return [' Loading ', ' RBS... ']
|
147
|
+
end
|
148
|
+
types = receiver_type.types
|
149
|
+
contents = types.filter_map do |type|
|
150
|
+
case type
|
151
|
+
when KatakataIrb::Types::InstanceType
|
152
|
+
type.inspect_without_params
|
153
|
+
else
|
154
|
+
type.inspect
|
155
|
+
end
|
156
|
+
end.uniq
|
157
|
+
contents if contents.any?
|
158
|
+
end
|
159
|
+
|
103
160
|
def self.setup_type_dialog
|
104
161
|
type_dialog_proc = -> {
|
105
162
|
return if just_cursor_moving && completion_journey_data
|
106
163
|
cursor_pos_to_render, _result, pointer, autocomplete_dialog = context.last(4)
|
107
164
|
return unless cursor_pos_to_render && autocomplete_dialog&.width && pointer.nil?
|
108
|
-
|
109
|
-
|
110
|
-
in [:call_or_const, type, name, _self_call] if name.empty?
|
111
|
-
type
|
112
|
-
in [:call, type, name, _self_call] if name.empty?
|
113
|
-
type
|
114
|
-
else
|
115
|
-
return
|
116
|
-
end
|
117
|
-
)
|
118
|
-
return unless receiver_type
|
119
|
-
types = type.types
|
120
|
-
contents = types.filter_map do |type|
|
121
|
-
case type
|
122
|
-
when KatakataIrb::Types::InstanceType
|
123
|
-
type.inspect_without_params
|
124
|
-
else
|
125
|
-
type.inspect
|
126
|
-
end
|
127
|
-
end.uniq
|
128
|
-
return if contents.empty?
|
165
|
+
contents = KatakataIrb::Completor.type_dialog_content
|
166
|
+
return unless contents&.any?
|
129
167
|
|
130
168
|
width = contents.map { Reline::Unicode.calculate_width _1 }.max
|
131
169
|
x = cursor_pos_to_render.x + autocomplete_dialog.width
|
@@ -145,12 +183,11 @@ module KatakataIrb::Completor
|
|
145
183
|
"#{name}="
|
146
184
|
end.join + "nil;\n"
|
147
185
|
code = lvars_code + code
|
148
|
-
tokens =
|
149
|
-
|
150
|
-
last_opens = KatakataIrb::NestingParser.parse(tokens)
|
186
|
+
tokens = KatakataIrb::NestingParser.tokenize code
|
187
|
+
last_opens = KatakataIrb::NestingParser.open_tokens(tokens)
|
151
188
|
closings = last_opens.map do |t|
|
152
189
|
case t.tok
|
153
|
-
when /\A
|
190
|
+
when /\A%.?[<>]\z/
|
154
191
|
$/ + '>'
|
155
192
|
when '{', '#{', /\A%.?[{}]\z/
|
156
193
|
$/ + '}'
|
@@ -271,9 +308,15 @@ module KatakataIrb::Completor
|
|
271
308
|
[:ivar, name, calculate_scope.call.self_type] if icvar_available
|
272
309
|
in [:var_ref, [:@cvar,]]
|
273
310
|
[:cvar, name, calculate_scope.call.self_type] if icvar_available
|
274
|
-
in [:call, receiver, [:@
|
311
|
+
in [:call, receiver, [:@op, '::',] | :'::', [:@ident | :@const,]]
|
312
|
+
self_call = (receiver in [:var_ref, [:@kw, 'self',]])
|
313
|
+
[:call_or_const, calculate_receiver.call(receiver), name, self_call]
|
314
|
+
in [:call, receiver, [:@period,], [:@ident | :@const,]]
|
315
|
+
self_call = (receiver in [:var_ref, [:@kw, 'self',]])
|
316
|
+
[:call, calculate_receiver.call(receiver), name, self_call]
|
317
|
+
in [:call, receiver, [:@op, '&.',], [:@ident | :@const,]]
|
275
318
|
self_call = (receiver in [:var_ref, [:@kw, 'self',]])
|
276
|
-
[
|
319
|
+
[:call, calculate_receiver.call(receiver).nonnillable, name, self_call]
|
277
320
|
in [:const_path_ref, receiver, [:@const,]]
|
278
321
|
[:const, calculate_receiver.call(receiver), name]
|
279
322
|
in [:top_const_ref, [:@const,]]
|
@@ -1,4 +1,21 @@
|
|
1
1
|
module KatakataIrb::NestingParser
|
2
|
+
ERROR_TOKENS = %i[
|
3
|
+
on_parse_error
|
4
|
+
compile_error
|
5
|
+
on_assign_error
|
6
|
+
on_alias_error
|
7
|
+
on_class_name_error
|
8
|
+
on_param_error
|
9
|
+
]
|
10
|
+
|
11
|
+
def self.tokenize(code)
|
12
|
+
verbose, $VERBOSE = $VERBOSE, nil
|
13
|
+
tokens = Ripper::Lexer.new(code).scan.reject { ERROR_TOKENS.include? _1.event }
|
14
|
+
KatakataIrb::NestingParser.interpolate_ripper_ignored_tokens code, tokens
|
15
|
+
ensure
|
16
|
+
$VERBOSE = verbose
|
17
|
+
end
|
18
|
+
|
2
19
|
def self.interpolate_ripper_ignored_tokens(code, tokens)
|
3
20
|
line_positions = code.lines.reduce([0]) { _1 << _1.last + _2.bytesize }
|
4
21
|
prev_byte_pos = 0
|
@@ -26,9 +43,10 @@ module KatakataIrb::NestingParser
|
|
26
43
|
interpolated
|
27
44
|
end
|
28
45
|
|
29
|
-
|
46
|
+
IGNORE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
|
30
47
|
|
31
|
-
|
48
|
+
# Scan each token and call the given block with array of token and other information for parsing
|
49
|
+
def self.scan_opens(tokens)
|
32
50
|
opens = []
|
33
51
|
pending_heredocs = []
|
34
52
|
first_token_on_line = true
|
@@ -37,14 +55,14 @@ module KatakataIrb::NestingParser
|
|
37
55
|
last_tok, state, args = opens.last
|
38
56
|
case state
|
39
57
|
when :in_unquoted_symbol
|
40
|
-
unless t.event
|
58
|
+
unless IGNORE_TOKENS.include?(t.event)
|
41
59
|
opens.pop
|
42
60
|
skip = true
|
43
61
|
end
|
44
62
|
when :in_lambda_head
|
45
63
|
opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do')
|
46
64
|
when :in_method_head
|
47
|
-
unless
|
65
|
+
unless IGNORE_TOKENS.include?(t.event)
|
48
66
|
next_args = []
|
49
67
|
body = nil
|
50
68
|
if args.include?(:receiver)
|
@@ -133,7 +151,7 @@ module KatakataIrb::NestingParser
|
|
133
151
|
if t.event == :on_op && t.tok == '|'
|
134
152
|
opens[-1] = [last_tok, nil]
|
135
153
|
opens << [t, :in_block_args]
|
136
|
-
elsif !
|
154
|
+
elsif !IGNORE_TOKENS.include?(t.event)
|
137
155
|
opens[-1] = [last_tok, nil]
|
138
156
|
end
|
139
157
|
when :in_block_args
|
@@ -201,6 +219,12 @@ module KatakataIrb::NestingParser
|
|
201
219
|
opens << [t, nil]
|
202
220
|
when :on_tstring_end, :on_regexp_end, :on_label_end
|
203
221
|
opens.pop
|
222
|
+
when :on_symbeg
|
223
|
+
if t.tok == ':'
|
224
|
+
opens << [t, :in_unquoted_symbol]
|
225
|
+
else
|
226
|
+
opens << [t, nil]
|
227
|
+
end
|
204
228
|
when :on_op
|
205
229
|
case t.tok
|
206
230
|
when '?'
|
@@ -210,12 +234,6 @@ module KatakataIrb::NestingParser
|
|
210
234
|
# closing of `cond ? value : value``
|
211
235
|
opens.pop
|
212
236
|
end
|
213
|
-
when :on_symbeg
|
214
|
-
if t.tok == ':'
|
215
|
-
opens << [t, :in_unquoted_symbol]
|
216
|
-
else
|
217
|
-
opens << [t, nil]
|
218
|
-
end
|
219
237
|
end
|
220
238
|
end
|
221
239
|
if t.event == :on_nl || t.event == :on_semicolon
|
@@ -232,29 +250,8 @@ module KatakataIrb::NestingParser
|
|
232
250
|
opens.map(&:first) + pending_heredocs.reverse
|
233
251
|
end
|
234
252
|
|
235
|
-
def self.
|
236
|
-
|
237
|
-
|
238
|
-
min_depth = 0
|
239
|
-
output = []
|
240
|
-
last_opens = parse(tokens) do |t, opens|
|
241
|
-
depth = t == opens.last&.first ? opens.size - 1 : opens.size
|
242
|
-
min_depth = depth if depth < min_depth
|
243
|
-
if t.tok.include?("\n")
|
244
|
-
t.tok.each_line do |line|
|
245
|
-
line_tokens << [t, line]
|
246
|
-
next if line[-1] != "\n"
|
247
|
-
next_opens = opens.map(&:first)
|
248
|
-
output << [line_tokens, prev_opens, next_opens, min_depth]
|
249
|
-
prev_opens = next_opens
|
250
|
-
min_depth = prev_opens.size
|
251
|
-
line_tokens = []
|
252
|
-
end
|
253
|
-
else
|
254
|
-
line_tokens << [t, t.tok]
|
255
|
-
end
|
256
|
-
end
|
257
|
-
output << [line_tokens, prev_opens, last_opens, min_depth] if line_tokens.any?
|
258
|
-
output
|
253
|
+
def self.open_tokens(tokens)
|
254
|
+
# scan_opens without block will return a list of open tokens at last token position
|
255
|
+
scan_opens(tokens)
|
259
256
|
end
|
260
257
|
end
|
@@ -769,9 +769,9 @@ class KatakataIrb::TypeSimulator
|
|
769
769
|
case sexp
|
770
770
|
in [:fcall | :vcall, [:@ident | :@const | :@kw | :@op, method,]] # hoge
|
771
771
|
[nil, method, [], [], nil, false]
|
772
|
-
in [:call, receiver, [:@period,] | [:@op, '&.',] | :'::' => dot, :call]
|
772
|
+
in [:call, receiver, [:@period,] | [:@op, '&.',] | [:@op, '::',] | :'::' => dot, :call]
|
773
773
|
[receiver, :call, [], [], nil, optional[dot]]
|
774
|
-
in [:call, receiver, [:@period,] | [:@op, '&.',] | :'::' => dot, method]
|
774
|
+
in [:call, receiver, [:@period,] | [:@op, '&.',] | [:@op, '::',] | :'::' => dot, method]
|
775
775
|
method => [:@ident | :@const | :@kw | :@op, method,] unless method == :call
|
776
776
|
[receiver, method, [], [], nil, optional[dot]]
|
777
777
|
in [:command, [:@ident | :@const | :@kw | :@op, method,], args] # hoge 1, 2
|
data/lib/katakata_irb/types.rb
CHANGED
@@ -3,8 +3,12 @@ require 'rbs/cli'
|
|
3
3
|
|
4
4
|
module KatakataIrb; end
|
5
5
|
module KatakataIrb::Types
|
6
|
-
|
7
|
-
|
6
|
+
singleton_class.attr_reader :rbs_builder, :rbs_load_error
|
7
|
+
|
8
|
+
def self.preload_in_thread
|
9
|
+
@loader_thread ||= Thread.new do
|
10
|
+
@rbs_builder = load_rbs_builder
|
11
|
+
end
|
8
12
|
end
|
9
13
|
|
10
14
|
def self.load_rbs_builder
|
@@ -12,8 +16,10 @@ module KatakataIrb::Types
|
|
12
16
|
loader.add path: Pathname('sig')
|
13
17
|
RBS::DefinitionBuilder.new env: RBS::Environment.from_loader(loader).resolve_type_names
|
14
18
|
rescue => e
|
15
|
-
|
16
|
-
|
19
|
+
@rbs_load_error = e
|
20
|
+
puts "\r\nKatakataIRB failed to initialize RBS::DefinitionBuilder: #{e.class}\r\n"
|
21
|
+
puts "See `KatakataIrb::Types.rbs_load_error` for more details.\r\n"
|
22
|
+
nil
|
17
23
|
end
|
18
24
|
|
19
25
|
Splat = Struct.new :item
|
@@ -59,6 +65,8 @@ module KatakataIrb::Types
|
|
59
65
|
end
|
60
66
|
|
61
67
|
def self.rbs_methods(type, method_name, args_types, kwargs_type, has_block)
|
68
|
+
return [] unless rbs_builder
|
69
|
+
|
62
70
|
receivers = type.types.map do |t|
|
63
71
|
case t
|
64
72
|
in ProcType
|
@@ -397,8 +405,8 @@ module KatakataIrb::Types
|
|
397
405
|
|
398
406
|
def self.match_free_variables(vars, types, values)
|
399
407
|
accumulator = {}
|
400
|
-
types.zip
|
401
|
-
_match_free_variable(vars, t, v, accumulator)
|
408
|
+
types.zip values do |t, v|
|
409
|
+
_match_free_variable(vars, t, v, accumulator) if v
|
402
410
|
end
|
403
411
|
accumulator.transform_values { UnionType[*_1] }
|
404
412
|
end
|
data/lib/katakata_irb/version.rb
CHANGED
data/lib/katakata_irb.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: katakata_irb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- tompng
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: irb
|
@@ -95,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
requirements: []
|
98
|
-
rubygems_version: 3.4.
|
98
|
+
rubygems_version: 3.4.10
|
99
99
|
signing_key:
|
100
100
|
specification_version: 4
|
101
101
|
summary: IRB with Typed Completion
|