katakata_irb 0.1.11 → 0.2.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 +4 -4
- data/Gemfile +1 -0
- data/katakata_irb.gemspec +1 -0
- data/lib/katakata_irb/completor.rb +143 -219
- data/lib/katakata_irb/scope.rb +179 -74
- data/lib/katakata_irb/type_simulator.rb +774 -779
- data/lib/katakata_irb/types.rb +17 -3
- data/lib/katakata_irb/version.rb +1 -1
- data/lib/katakata_irb.rb +2 -0
- metadata +16 -3
- data/lib/katakata_irb/nesting_parser.rb +0 -257
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46f9fc2dd4b85529dff8a278f183954e48d7c7386944672d588e09315f6151ef
|
4
|
+
data.tar.gz: ae0dbe97feb4f1150f16bd061ea346ac2ff45a19eb1d38be4b6ffa25941418ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0306fd41b676ba4b65d3e739704f258950f7406deb4022455c11b4a1b6d42e98d5dceeb5a51e464e1fba36b42705ae6a87b237a2cd24d206f132ca9baa26125
|
7
|
+
data.tar.gz: 6eddf48f22628cd2ce4bb44e56be931b137f68b4d3194e374600c8e420eb2c84765021c5678bb30cabc0e8ce5f4229d0993c6cfa8502e5aaead8274cf4fd156c
|
data/Gemfile
CHANGED
data/katakata_irb.gemspec
CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
|
|
31
31
|
# Uncomment to register a new dependency of your gem
|
32
32
|
spec.add_dependency 'irb', '>= 1.4.0'
|
33
33
|
spec.add_dependency 'reline', '>= 0.3.0'
|
34
|
+
spec.add_dependency 'prism', '>= 0.13.0'
|
34
35
|
spec.add_dependency 'rbs'
|
35
36
|
|
36
37
|
# For more information and examples about making a new gem, check out our
|
@@ -1,65 +1,74 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative 'type_simulator'
|
3
4
|
require 'rbs'
|
4
5
|
require 'rbs/cli'
|
5
6
|
require 'irb'
|
7
|
+
require 'prism'
|
6
8
|
|
7
9
|
module KatakataIrb::Completor
|
8
|
-
using KatakataIrb::TypeSimulator::LexerElemMatcher
|
9
10
|
HIDDEN_METHODS = %w[Namespace TypeName] # defined by rbs, should be hidden
|
10
11
|
singleton_class.attr_accessor :prev_analyze_result
|
11
12
|
|
13
|
+
def self.candidates_from_result(result)
|
14
|
+
candidates = case result
|
15
|
+
in [:require | :require_relative => method, name]
|
16
|
+
if IRB.const_defined? :RegexpCompletor # IRB::VERSION >= 1.8.2
|
17
|
+
path_completor = IRB::RegexpCompletor.new
|
18
|
+
elsif IRB.const_defined? :InputCompletor # IRB::VERSION <= 1.8.1
|
19
|
+
path_completor = IRB::InputCompletor
|
20
|
+
end
|
21
|
+
if !path_completor
|
22
|
+
[]
|
23
|
+
elsif method == :require
|
24
|
+
path_completor.retrieve_files_to_require_from_load_path
|
25
|
+
else
|
26
|
+
path_completor.retrieve_files_to_require_relative_from_current_dir
|
27
|
+
end
|
28
|
+
in [:call_or_const, type, name, self_call]
|
29
|
+
((self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS) | type.constants
|
30
|
+
in [:const, type, name, scope]
|
31
|
+
if type
|
32
|
+
scope_constants = type.types.flat_map do |t|
|
33
|
+
scope.table_module_constants(t.module_or_class) if t.is_a?(KatakataIrb::Types::SingletonType)
|
34
|
+
end
|
35
|
+
(scope_constants.compact | type.constants.map(&:to_s)).sort
|
36
|
+
else
|
37
|
+
scope.constants.sort
|
38
|
+
end
|
39
|
+
in [:ivar, name, scope]
|
40
|
+
ivars = scope.instance_variables.sort
|
41
|
+
name == '@' ? ivars + scope.class_variables.sort : ivars
|
42
|
+
in [:cvar, name, scope]
|
43
|
+
scope.class_variables
|
44
|
+
in [:gvar, name, scope]
|
45
|
+
scope.global_variables
|
46
|
+
in [:symbol, name]
|
47
|
+
Symbol.all_symbols.map { _1.inspect[1..] }
|
48
|
+
in [:call, type, name, self_call]
|
49
|
+
(self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS
|
50
|
+
in [:lvar_or_method, name, scope]
|
51
|
+
scope.self_type.all_methods.map(&:to_s) | scope.local_variables
|
52
|
+
else
|
53
|
+
[]
|
54
|
+
end
|
55
|
+
[name || '', candidates]
|
56
|
+
end
|
57
|
+
|
12
58
|
def self.setup
|
13
59
|
KatakataIrb::Types.preload_in_thread
|
14
|
-
completion_proc = ->(target,
|
60
|
+
completion_proc = ->(preposing, target, _postposing, bind:) do
|
15
61
|
verbose, $VERBOSE = $VERBOSE, nil
|
16
62
|
code = "#{preposing}#{target}"
|
17
|
-
|
18
|
-
binding = irb_context.workspace.binding
|
19
|
-
result = analyze code, binding
|
63
|
+
result = analyze code, bind
|
20
64
|
KatakataIrb::Completor.prev_analyze_result = result
|
21
|
-
candidates =
|
22
|
-
|
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
|
32
|
-
else
|
33
|
-
path_completor.retrieve_files_to_require_relative_from_current_dir
|
34
|
-
end
|
35
|
-
in [:call_or_const, type, name, self_call]
|
36
|
-
((self_call ? type.all_methods: type.methods).map(&:to_s) - HIDDEN_METHODS) | type.constants
|
37
|
-
in [:const, type, name]
|
38
|
-
type.constants
|
39
|
-
in [:ivar, name, *_scope]
|
40
|
-
# TODO: scope
|
41
|
-
ivars = binding.eval('self').instance_variables rescue []
|
42
|
-
cvars = (binding.eval('self').class_variables rescue nil) if name == '@'
|
43
|
-
ivars | (cvars || [])
|
44
|
-
in [:cvar, name, *_scope]
|
45
|
-
# TODO: scope
|
46
|
-
binding.eval('self').class_variables rescue []
|
47
|
-
in [:gvar, name]
|
48
|
-
global_variables
|
49
|
-
in [:symbol, name]
|
50
|
-
Symbol.all_symbols.map { _1.inspect[1..] }
|
51
|
-
in [:call, type, name, self_call]
|
52
|
-
(self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS
|
53
|
-
in [:lvar_or_method, name, scope]
|
54
|
-
scope.self_type.all_methods.map(&:to_s) | scope.local_variables
|
55
|
-
else
|
56
|
-
[]
|
57
|
-
end
|
65
|
+
name, candidates = candidates_from_result(result).dup
|
66
|
+
|
58
67
|
all_symbols_pattern = /\A[ -\/:-@\[-`\{-~]*\z/
|
59
68
|
candidates.map(&:to_s).select { !_1.match?(all_symbols_pattern) && _1.start_with?(name) }.uniq.sort.map do
|
60
69
|
target + _1[name.size..]
|
61
70
|
end
|
62
|
-
rescue => e
|
71
|
+
rescue SyntaxError, StandardError => e
|
63
72
|
KatakataIrb.last_completion_error = e
|
64
73
|
KatakataIrb.log_puts
|
65
74
|
KatakataIrb.log_puts "#{e.inspect} stored to KatakataIrb.last_completion_error"
|
@@ -72,9 +81,9 @@ module KatakataIrb::Completor
|
|
72
81
|
name = input[/[a-zA-Z_0-9]+[!?=]?\z/]
|
73
82
|
method_doc = -> type do
|
74
83
|
type = type.types.find { _1.all_methods.include? name.to_sym }
|
75
|
-
if type
|
84
|
+
if type.is_a? KatakataIrb::Types::SingletonType
|
76
85
|
"#{KatakataIrb::Types.class_name_of(type.module_or_class)}.#{name}"
|
77
|
-
elsif type
|
86
|
+
elsif type.is_a? KatakataIrb::Types::InstanceType
|
78
87
|
"#{KatakataIrb::Types.class_name_of(type.klass)}##{name}"
|
79
88
|
end
|
80
89
|
end
|
@@ -90,10 +99,10 @@ module KatakataIrb::Completor
|
|
90
99
|
case KatakataIrb::Completor.prev_analyze_result
|
91
100
|
in [:call_or_const, type, _name, _self_call]
|
92
101
|
call_or_const_doc.call type
|
93
|
-
in [:const, type, _name]
|
102
|
+
in [:const, type, _name, scope]
|
94
103
|
# when prev_analyze_result is const, current analyze result might be call
|
95
|
-
call_or_const_doc.call type
|
96
|
-
in [:gvar, _name]
|
104
|
+
call_or_const_doc.call type if type
|
105
|
+
in [:gvar, _name, _scope]
|
97
106
|
name
|
98
107
|
in [:call, type, _name, _self_call]
|
99
108
|
method_doc.call type
|
@@ -103,9 +112,19 @@ module KatakataIrb::Completor
|
|
103
112
|
end
|
104
113
|
end
|
105
114
|
|
106
|
-
if IRB.const_defined? :
|
107
|
-
IRB::
|
108
|
-
|
115
|
+
if IRB.const_defined? :RegexpCompletor # IRB::VERSION >= 1.8.2
|
116
|
+
IRB::RegexpCompletor.class_eval do
|
117
|
+
define_method :completion_candidates do |preposing, target, postposing, bind:|
|
118
|
+
completion_proc.call(preposing, target, postposing, bind: bind)
|
119
|
+
end
|
120
|
+
define_method :doc_namespace do |_preposing, matched, _postposing, bind:|
|
121
|
+
doc_namespace_proc.call matched
|
122
|
+
end
|
123
|
+
end
|
124
|
+
elsif IRB.const_defined? :InputCompletor # IRB::VERSION <= 1.8.1
|
125
|
+
IRB::InputCompletor::CompletionProc.define_singleton_method :call do |target, preposing = '', postposing = ''|
|
126
|
+
bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
|
127
|
+
completion_proc.call(preposing, target, postposing, bind: bind)
|
109
128
|
end
|
110
129
|
IRB::InputCompletor.singleton_class.prepend(
|
111
130
|
Module.new do
|
@@ -115,15 +134,6 @@ module KatakataIrb::Completor
|
|
115
134
|
end
|
116
135
|
end
|
117
136
|
)
|
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
|
125
|
-
end
|
126
|
-
end
|
127
137
|
else
|
128
138
|
puts 'Cannot activate katakata_irb'
|
129
139
|
end
|
@@ -174,179 +184,93 @@ module KatakataIrb::Completor
|
|
174
184
|
Reline.add_dialog_proc(:show_type, type_dialog_proc, Reline::DEFAULT_DIALOG_CONTEXT)
|
175
185
|
end
|
176
186
|
|
177
|
-
def self.
|
178
|
-
|
179
|
-
|
187
|
+
def self.analyze(code, binding = Object::TOPLEVEL_BINDING)
|
188
|
+
# Workaround for https://github.com/ruby/prism/issues/1592
|
189
|
+
return if code.match?(/%[qQ]\z/)
|
180
190
|
|
181
|
-
def self.analyze(code, binding = empty_binding)
|
182
191
|
lvars_code = binding.local_variables.map do |name|
|
183
192
|
"#{name}="
|
184
193
|
end.join + "nil;\n"
|
185
194
|
code = lvars_code + code
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
when /\A%.?[<>]\z/
|
191
|
-
$/ + '>'
|
192
|
-
when '{', '#{', /\A%.?[{}]\z/
|
193
|
-
$/ + '}'
|
194
|
-
when '(', /\A%.?[()]\z/
|
195
|
-
# do not insert \n before closing paren. workaround to avoid syntax error of "a in ^(b\n)"
|
196
|
-
')'
|
197
|
-
when '[', /\A%.?[\[\]]\z/
|
198
|
-
$/ + ']'
|
199
|
-
when /\A%.?(.)\z/
|
200
|
-
$1
|
201
|
-
when '"', "'", '/', '`'
|
202
|
-
t.tok
|
203
|
-
when /\A<<[~-]?(?:"(?<s>.+)"|'(?<s>.+)'|(?<s>.+))/
|
204
|
-
$/ + ($1 || $2 || $3) + $/
|
205
|
-
when ':"', ":'", ':'
|
206
|
-
t.tok[1]
|
207
|
-
when '?'
|
208
|
-
# ternary operator
|
209
|
-
' : value'
|
210
|
-
when '|'
|
211
|
-
# block args
|
212
|
-
'|'
|
213
|
-
else
|
214
|
-
$/ + 'end'
|
215
|
-
end
|
216
|
-
end
|
217
|
-
# remove error tokens
|
218
|
-
tokens.pop while tokens&.last&.tok&.empty?
|
195
|
+
ast = Prism.parse(code).value
|
196
|
+
name = code[/(@@|@|\$)?\w*\z/]
|
197
|
+
*parents, target_node = find_target ast, code.bytesize - name.bytesize
|
198
|
+
return unless target_node
|
219
199
|
|
220
|
-
|
221
|
-
|
222
|
-
suffix = 'method'
|
223
|
-
name = ''
|
224
|
-
in { dot: true }
|
225
|
-
suffix = 'method'
|
226
|
-
name = ''
|
227
|
-
in { event: :on_symbeg }
|
228
|
-
suffix = 'symbol'
|
229
|
-
name = ''
|
230
|
-
in { event: :on_ident | :on_kw, tok: }
|
231
|
-
return unless code.delete_suffix! tok
|
232
|
-
suffix = 'method'
|
233
|
-
name = tok
|
234
|
-
in { event: :on_const, tok: }
|
235
|
-
return unless code.delete_suffix! tok
|
236
|
-
suffix = 'Const'
|
237
|
-
name = tok
|
238
|
-
in { event: :on_tstring_content, tok: }
|
239
|
-
return unless code.delete_suffix! tok
|
240
|
-
suffix = 'string'
|
241
|
-
name = tok.rstrip
|
242
|
-
in { event: :on_gvar, tok: }
|
243
|
-
return unless code.delete_suffix! tok
|
244
|
-
suffix = '$gvar'
|
245
|
-
name = tok
|
246
|
-
in { event: :on_ivar, tok: }
|
247
|
-
return unless code.delete_suffix! tok
|
248
|
-
suffix = '@ivar'
|
249
|
-
name = tok
|
250
|
-
in { event: :on_cvar, tok: }
|
251
|
-
return unless code.delete_suffix! tok
|
252
|
-
suffix = '@@cvar'
|
253
|
-
name = tok
|
254
|
-
else
|
255
|
-
return
|
256
|
-
end
|
257
|
-
sexp = Ripper.sexp code + suffix + closings.reverse.join
|
258
|
-
lines = code.lines
|
259
|
-
line_no = lines.size
|
260
|
-
col = lines.last.bytesize
|
261
|
-
if lines.last.end_with? "\n"
|
262
|
-
line_no += 1
|
263
|
-
col = 0
|
264
|
-
end
|
200
|
+
calculate_scope = -> { KatakataIrb::TypeSimulator.calculate_target_type_scope(binding, parents, target_node).last }
|
201
|
+
calculate_type_scope = ->(node) { KatakataIrb::TypeSimulator.calculate_target_type_scope binding, [*parents, target_node], node }
|
265
202
|
|
266
|
-
if
|
267
|
-
|
203
|
+
if target_node.is_a?(Prism::StringNode) || target_node.is_a?(Prism::InterpolatedStringNode)
|
204
|
+
args_node = parents[-1]
|
205
|
+
call_node = parents[-2]
|
206
|
+
return unless args_node.is_a?(Prism::ArgumentsNode) && args_node.arguments.size == 1
|
207
|
+
return unless call_node.is_a?(Prism::CallNode) && call_node.receiver.nil? && (call_node.message == 'require' || call_node.message == 'require_relative')
|
208
|
+
return [call_node.message.to_sym, name.rstrip]
|
268
209
|
end
|
269
210
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
return [:
|
280
|
-
end
|
281
|
-
return unless expression
|
282
|
-
calculate_scope = -> { KatakataIrb::TypeSimulator.calculate_binding_scope binding, parents, expression }
|
283
|
-
calculate_receiver = -> receiver { KatakataIrb::TypeSimulator.calculate_receiver binding, parents, receiver }
|
211
|
+
case target_node
|
212
|
+
when Prism::SymbolNode
|
213
|
+
if parents.last.is_a? Prism::BlockArgumentNode # method(&:target)
|
214
|
+
receiver_type, _scope = calculate_type_scope.call target_node
|
215
|
+
[:call, receiver_type, name, false]
|
216
|
+
else
|
217
|
+
[:symbol, name] unless name.empty?
|
218
|
+
end
|
219
|
+
when Prism::CallNode
|
220
|
+
return [:lvar_or_method, name, calculate_scope.call] if target_node.receiver.nil?
|
284
221
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
receiver_ref = [:var_ref, [:@ident, '_1', [0, 0]]]
|
292
|
-
block_statements = [receiver_ref]
|
293
|
-
parents[-1] = parents[-2][-1] = [:brace_block, nil, block_statements]
|
294
|
-
parents << block_statements
|
295
|
-
return [:call, calculate_receiver.call(receiver_ref), name, false]
|
296
|
-
end
|
297
|
-
case expression
|
298
|
-
in [:vcall | :var_ref, [:@ident,]]
|
222
|
+
self_call = target_node.receiver.is_a? Prism::SelfNode
|
223
|
+
op = target_node.call_operator
|
224
|
+
receiver_type, _scope = calculate_type_scope.call target_node.receiver
|
225
|
+
receiver_type = receiver_type.nonnillable if op == '&.'
|
226
|
+
[op == '::' ? :call_or_const : :call, receiver_type, name, self_call]
|
227
|
+
when Prism::LocalVariableReadNode, Prism::LocalVariableTargetNode
|
299
228
|
[:lvar_or_method, name, calculate_scope.call]
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
[:
|
317
|
-
|
318
|
-
|
319
|
-
[:call, calculate_receiver.call(receiver).nonnillable, name, self_call]
|
320
|
-
in [:const_path_ref, receiver, [:@const,]]
|
321
|
-
[:const, calculate_receiver.call(receiver), name]
|
322
|
-
in [:top_const_ref, [:@const,]]
|
323
|
-
[:const, KatakataIrb::Types::SingletonType.new(Object), name]
|
324
|
-
in [:def,] | [:string_content,] | [:field | :var_field | :const_path_field,] | [:defs,] | [:rest_param,] | [:kwrest_param,] | [:blockarg,] | [[:@ident,],]
|
325
|
-
in [Array,] # `xstring`, /regexp/
|
326
|
-
else
|
327
|
-
KatakataIrb.log_puts
|
328
|
-
KatakataIrb.log_puts [:UNIMPLEMENTED_EXPRESSION, expression].inspect
|
329
|
-
KatakataIrb.log_puts
|
330
|
-
nil
|
229
|
+
when Prism::ConstantReadNode, Prism::ConstantTargetNode
|
230
|
+
if parents.last.is_a? Prism::ConstantPathNode
|
231
|
+
path_node = parents.last
|
232
|
+
if path_node.parent # A::B
|
233
|
+
receiver, scope = calculate_type_scope.call(path_node.parent)
|
234
|
+
[:const, receiver, name, scope]
|
235
|
+
else # ::A
|
236
|
+
scope = calculate_scope.call
|
237
|
+
[:const, KatakataIrb::Types::SingletonType.new(Object), name, scope]
|
238
|
+
end
|
239
|
+
else
|
240
|
+
[:const, nil, name, calculate_scope.call]
|
241
|
+
end
|
242
|
+
when Prism::GlobalVariableReadNode, Prism::GlobalVariableTargetNode
|
243
|
+
[:gvar, name, calculate_scope.call]
|
244
|
+
when Prism::InstanceVariableReadNode, Prism::InstanceVariableTargetNode
|
245
|
+
[:ivar, name, calculate_scope.call]
|
246
|
+
when Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode
|
247
|
+
[:cvar, name, calculate_scope.call]
|
331
248
|
end
|
332
249
|
end
|
333
250
|
|
334
|
-
def self.find_target(
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
result = find_target(child, line, col, stack)
|
346
|
-
return result if result
|
347
|
-
stack.pop
|
251
|
+
def self.find_target(node, position)
|
252
|
+
location = (
|
253
|
+
case node
|
254
|
+
when Prism::CallNode
|
255
|
+
node.message_loc
|
256
|
+
when Prism::SymbolNode
|
257
|
+
node.value_loc
|
258
|
+
when Prism::StringNode
|
259
|
+
node.content_loc
|
260
|
+
when Prism::InterpolatedStringNode
|
261
|
+
node.closing_loc if node.parts.empty?
|
348
262
|
end
|
263
|
+
)
|
264
|
+
return [node] if location&.start_offset == position
|
265
|
+
|
266
|
+
node.child_nodes.each do |n|
|
267
|
+
next unless n.is_a? Prism::Node
|
268
|
+
match = find_target(n, position)
|
269
|
+
next unless match
|
270
|
+
match.unshift node
|
271
|
+
return match
|
349
272
|
end
|
350
|
-
|
273
|
+
|
274
|
+
[node] if node.location.start_offset == position
|
351
275
|
end
|
352
276
|
end
|