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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ce4d7b2e5ebfc546ddb501c43c13c62b166a55dc34a215a460d560b946ad4f02
4
- data.tar.gz: f23396dc9804a798d109342d063943da759139fd849fd828f85d51f71089d5e7
3
+ metadata.gz: 810109ee76180f61c9b3672a8def67d0a32d53ca23b2436c65575457b586bf68
4
+ data.tar.gz: e17ff5c4e226d785031f2b433b35a8763a49e6a254420fcacf96507975dfa70b
5
5
  SHA512:
6
- metadata.gz: 5e44f1dbab26c56949e89053a0309ad9f3195a8f545ea95b546a541c547b55b86a69afb3944bdbfeb5e498a62105cb2f54630e559b32188b2532cfa4625ee765
7
- data.tar.gz: 9beaaf9996eba51111cabc5a1ba4082f94657ecdeb06b3cf1c0859c346822d772134673d190639b874caacf3f220c19d8ac8ad150c2c98f85129e658a58a2b95
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 method == :require
22
- IRB::InputCompletor.retrieve_files_to_require_from_load_path
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
- IRB::InputCompletor.retrieve_files_to_require_relative_from_current_dir
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
- IRB::InputCompletor.singleton_class.prepend Module.new{
64
- def retrieve_completion_data(input, _bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
65
- return super unless doc_namespace
66
- name = input[/[a-zA-Z_0-9]+[!?=]?\z/]
67
- method_doc = -> type do
68
- type = type.types.find { _1.all_methods.include? name.to_sym }
69
- if type in KatakataIrb::Types::SingletonType
70
- "#{KatakataIrb::Types.class_name_of(type.module_or_class)}.#{name}"
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
- call_or_const_doc = -> type do
76
- if name =~ /\A[A-Z]/
77
- type = type.types.grep(KatakataIrb::Types::SingletonType).find { _1.module_or_class.const_defined?(name) }
78
- type.module_or_class == Object ? name : "#{KatakataIrb::Types.class_name_of(type.module_or_class)}::#{name}" if type
79
- else
80
- method_doc.call(type)
81
- end
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
- case KatakataIrb::Completor.prev_analyze_result
85
- in [:call_or_const, type, _name, _self_call]
86
- call_or_const_doc.call type
87
- in [:const, type, _name]
88
- # when prev_analyze_result is const, current analyze result might be call
89
- call_or_const_doc.call type
90
- in [:gvar, _name]
91
- name
92
- in [:call, type, _name, _self_call]
93
- method_doc.call type
94
- in [:lvar_or_method, _name, scope]
95
- method_doc.call scope.self_type unless scope.local_variables.include?(name)
96
- else
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
- receiver_type = (
109
- case KatakataIrb::Completor.prev_analyze_result
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 = RubyLex.ripper_lex_without_warning code
149
- tokens = KatakataIrb::NestingParser.interpolate_ripper_ignored_tokens code, tokens
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%.[<>]\z/
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, [:@period,] | [:@op, '&.',] | :'::' => dot, [:@ident | :@const,]]
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
- [dot == :'::' ? :call_or_const : :call, calculate_receiver.call(receiver), name, self_call]
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
- IGNOREABLE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
46
+ IGNORE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
30
47
 
31
- def self.parse(tokens)
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 == :on_sp
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 IGNOREABLE_TOKENS.include?(t.event)
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 !IGNOREABLE_TOKENS.include?(t.event)
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.parse_line(tokens)
236
- line_tokens = []
237
- prev_opens = []
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
@@ -3,8 +3,12 @@ require 'rbs/cli'
3
3
 
4
4
  module KatakataIrb; end
5
5
  module KatakataIrb::Types
6
- def self.rbs_builder
7
- @rbs_builder ||= load_rbs_builder
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
- puts "\r\nKatakataIRB failed to initialize RBS::DefinitionBuilder\r\n#{e}\r\n"
16
- Object.new
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(values).each do |t, v|
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KatakataIrb
4
- VERSION = "0.1.9"
4
+ VERSION = "0.1.11"
5
5
  end
data/lib/katakata_irb.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'katakata_irb/version'
2
2
  require 'katakata_irb/completor'
3
+ require 'katakata_irb/types'
3
4
 
4
5
  module KatakataIrb
5
6
  class << self
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.9
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-05-19 00:00:00.000000000 Z
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.6
98
+ rubygems_version: 3.4.10
99
99
  signing_key:
100
100
  specification_version: 4
101
101
  summary: IRB with Typed Completion