katakata_irb 0.1.8 → 0.1.10

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: e03e0f334a22895540f29f3bb27551bff9bfcbede5aac767559006edbc77118f
4
- data.tar.gz: 4db442409ca2ac1d853b4e7675ff3ac10aed0d6ced431d1abdbe2eea7a1fa298
3
+ metadata.gz: 297457c829dac09ddee68c1cc5365acfcc2583e0282fb1070ebae8fa138523ba
4
+ data.tar.gz: a45663afa386d4093814a3ea2a49c98cb03e90004e2a42c0dc723f4686500a51
5
5
  SHA512:
6
- metadata.gz: fb7d8eae8b68d497c6e0c5c0d9bc8d8455d5af5638fb680b3048166188a1035a2e36229a7555203de4f8d18bc3089b92f441f496d6e2a074ad61587805b6de82
7
- data.tar.gz: 75ef2f2f26505c98e3550342493474f47f11623fd1342f0529249e6e902e95f5ebf03f5053974c6813beee3697c0d13cb5a0adec073c65ece35dce9a3e3f01ed
6
+ metadata.gz: e66cd04bba34272f585adb1a6dde686a49d064604a48b1c0d14093474857b30537f6b05dd965eaf7ae822b174e373122479c05a643cc951fe4ecea3009a625c6
7
+ data.tar.gz: 4cb475b48b8e73db73340a1a11c97b087f99d2d6159e235d93a8845e4a718e9b18deb4e693d86059ba98bdbecbf41f24c13cb70d969fdd6d18f9cb171c76478b
data/katakata_irb.gemspec CHANGED
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
30
30
 
31
31
  # Uncomment to register a new dependency of your gem
32
32
  spec.add_dependency 'irb', '>= 1.4.0'
33
+ spec.add_dependency 'reline', '>= 0.3.0'
33
34
  spec.add_dependency 'rbs'
34
35
 
35
36
  # For more information and examples about making a new gem, check out our
@@ -10,6 +10,7 @@ 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
14
15
  code = "#{preposing}#{target}"
15
16
  irb_context = IRB.conf[:MAIN_CONTEXT]
@@ -54,14 +55,14 @@ module KatakataIrb::Completor
54
55
  IRB::InputCompletor::CompletionProc.define_singleton_method :call do |*args|
55
56
  completion_proc.call(*args)
56
57
  rescue => e
57
- $error = e
58
+ KatakataIrb.last_completion_error = e
58
59
  KatakataIrb.log_puts
59
- KatakataIrb.log_puts "#{e.inspect} stored to $error"
60
+ KatakataIrb.log_puts "#{e.inspect} stored to KatakataIrb.last_completion_error"
60
61
  KatakataIrb.log_puts
61
62
  end
62
63
 
63
64
  IRB::InputCompletor.singleton_class.prepend Module.new{
64
- def retrieve_completion_data(input, _bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
65
+ def retrieve_completion_data(input, doc_namespace: false, **)
65
66
  return super unless doc_namespace
66
67
  name = input[/[a-zA-Z_0-9]+[!?=]?\z/]
67
68
  method_doc = -> type do
@@ -100,37 +101,45 @@ module KatakataIrb::Completor
100
101
  setup_type_dialog
101
102
  end
102
103
 
104
+ def self.type_dialog_content
105
+ receiver_type = (
106
+ case KatakataIrb::Completor.prev_analyze_result
107
+ in [:call_or_const, type, name, _self_call] if name.empty?
108
+ type
109
+ in [:call, type, name, _self_call] if name.empty?
110
+ type
111
+ else
112
+ return
113
+ end
114
+ )
115
+ if KatakataIrb::Types.rbs_builder.nil? && !KatakataIrb::Types.rbs_load_error
116
+ return [' Loading ', ' RBS... ']
117
+ end
118
+ types = receiver_type.types
119
+ contents = types.filter_map do |type|
120
+ case type
121
+ when KatakataIrb::Types::InstanceType
122
+ type.inspect_without_params
123
+ else
124
+ type.inspect
125
+ end
126
+ end.uniq
127
+ contents if contents.any?
128
+ end
129
+
103
130
  def self.setup_type_dialog
104
131
  type_dialog_proc = -> {
105
132
  return if just_cursor_moving && completion_journey_data
106
133
  cursor_pos_to_render, _result, pointer, autocomplete_dialog = context.last(4)
107
134
  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?
135
+ contents = KatakataIrb::Completor.type_dialog_content
136
+ return unless contents&.any?
129
137
 
130
138
  width = contents.map { Reline::Unicode.calculate_width _1 }.max
131
139
  x = cursor_pos_to_render.x + autocomplete_dialog.width
132
140
  y = cursor_pos_to_render.y
133
- Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: 44, fg_color: 37)
141
+ info = { pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: 44, fg_color: 37 }
142
+ Reline::DialogRenderInfo.new(**info.slice(*Reline::DialogRenderInfo.members))
134
143
  }
135
144
  Reline.add_dialog_proc(:show_type, type_dialog_proc, Reline::DEFAULT_DIALOG_CONTEXT)
136
145
  end
@@ -144,12 +153,11 @@ module KatakataIrb::Completor
144
153
  "#{name}="
145
154
  end.join + "nil;\n"
146
155
  code = lvars_code + code
147
- tokens = RubyLex.ripper_lex_without_warning code
148
- tokens = KatakataIrb::NestingParser.interpolate_ripper_ignored_tokens code, tokens
149
- last_opens = KatakataIrb::NestingParser.parse(tokens)
156
+ tokens = KatakataIrb::NestingParser.tokenize code
157
+ last_opens = KatakataIrb::NestingParser.open_tokens(tokens)
150
158
  closings = last_opens.map do |t|
151
159
  case t.tok
152
- when /\A%.[<>]\z/
160
+ when /\A%.?[<>]\z/
153
161
  $/ + '>'
154
162
  when '{', '#{', /\A%.?[{}]\z/
155
163
  $/ + '}'
@@ -233,7 +241,9 @@ module KatakataIrb::Completor
233
241
  in_class_module = parents&.any? { _1 in [:class | :module,] }
234
242
  icvar_available = !in_class_module
235
243
  return unless target in [_type, String, [Integer, Integer]]
236
- if target in [:@ivar,]
244
+ if target in [:@gvar,]
245
+ return [:gvar, name]
246
+ elsif target in [:@ivar,]
237
247
  return [:ivar, name] if icvar_available
238
248
  elsif target in [:@cvar,]
239
249
  return [:cvar, name] if icvar_available
@@ -268,19 +278,26 @@ module KatakataIrb::Completor
268
278
  [:ivar, name, calculate_scope.call.self_type] if icvar_available
269
279
  in [:var_ref, [:@cvar,]]
270
280
  [:cvar, name, calculate_scope.call.self_type] if icvar_available
271
- in [:call, receiver, [:@period,] | [:@op, '&.',] | :'::' => dot, [:@ident | :@const,]]
281
+ in [:call, receiver, [:@op, '::',] | :'::', [:@ident | :@const,]]
282
+ self_call = (receiver in [:var_ref, [:@kw, 'self',]])
283
+ [:call_or_const, calculate_receiver.call(receiver), name, self_call]
284
+ in [:call, receiver, [:@period,], [:@ident | :@const,]]
285
+ self_call = (receiver in [:var_ref, [:@kw, 'self',]])
286
+ [:call, calculate_receiver.call(receiver), name, self_call]
287
+ in [:call, receiver, [:@op, '&.',], [:@ident | :@const,]]
272
288
  self_call = (receiver in [:var_ref, [:@kw, 'self',]])
273
- [dot == :'::' ? :call_or_const : :call, calculate_receiver.call(receiver), name, self_call]
289
+ [:call, calculate_receiver.call(receiver).nonnillable, name, self_call]
274
290
  in [:const_path_ref, receiver, [:@const,]]
275
291
  [:const, calculate_receiver.call(receiver), name]
276
292
  in [:top_const_ref, [:@const,]]
277
293
  [:const, KatakataIrb::Types::SingletonType.new(Object), name]
278
- in [:def,] | [:string_content,] | [:var_field,] | [:defs,] | [:rest_param,] | [:kwrest_param,] | [:blockarg,] | [[:@ident,],]
294
+ in [:def,] | [:string_content,] | [:field | :var_field | :const_path_field,] | [:defs,] | [:rest_param,] | [:kwrest_param,] | [:blockarg,] | [[:@ident,],]
279
295
  in [Array,] # `xstring`, /regexp/
280
296
  else
281
297
  KatakataIrb.log_puts
282
- KatakataIrb.log_puts [:NEW_EXPRESSION, expression].inspect
298
+ KatakataIrb.log_puts [:UNIMPLEMENTED_EXPRESSION, expression].inspect
283
299
  KatakataIrb.log_puts
300
+ nil
284
301
  end
285
302
  end
286
303
 
@@ -1,4 +1,18 @@
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
+ tokens = Ripper::Lexer.new(code).scan.reject { ERROR_TOKENS.include? _1.event }
13
+ KatakataIrb::NestingParser.interpolate_ripper_ignored_tokens code, tokens
14
+ end
15
+
2
16
  def self.interpolate_ripper_ignored_tokens(code, tokens)
3
17
  line_positions = code.lines.reduce([0]) { _1 << _1.last + _2.bytesize }
4
18
  prev_byte_pos = 0
@@ -26,9 +40,10 @@ module KatakataIrb::NestingParser
26
40
  interpolated
27
41
  end
28
42
 
29
- IGNOREABLE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
43
+ IGNORE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
30
44
 
31
- def self.parse(tokens)
45
+ # Scan each token and call the given block with array of token and other information for parsing
46
+ def self.scan_opens(tokens)
32
47
  opens = []
33
48
  pending_heredocs = []
34
49
  first_token_on_line = true
@@ -37,14 +52,14 @@ module KatakataIrb::NestingParser
37
52
  last_tok, state, args = opens.last
38
53
  case state
39
54
  when :in_unquoted_symbol
40
- unless t.event == :on_sp
55
+ unless IGNORE_TOKENS.include?(t.event)
41
56
  opens.pop
42
57
  skip = true
43
58
  end
44
59
  when :in_lambda_head
45
60
  opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do')
46
61
  when :in_method_head
47
- unless IGNOREABLE_TOKENS.include?(t.event)
62
+ unless IGNORE_TOKENS.include?(t.event)
48
63
  next_args = []
49
64
  body = nil
50
65
  if args.include?(:receiver)
@@ -133,7 +148,7 @@ module KatakataIrb::NestingParser
133
148
  if t.event == :on_op && t.tok == '|'
134
149
  opens[-1] = [last_tok, nil]
135
150
  opens << [t, :in_block_args]
136
- elsif !IGNOREABLE_TOKENS.include?(t.event)
151
+ elsif !IGNORE_TOKENS.include?(t.event)
137
152
  opens[-1] = [last_tok, nil]
138
153
  end
139
154
  when :in_block_args
@@ -201,6 +216,12 @@ module KatakataIrb::NestingParser
201
216
  opens << [t, nil]
202
217
  when :on_tstring_end, :on_regexp_end, :on_label_end
203
218
  opens.pop
219
+ when :on_symbeg
220
+ if t.tok == ':'
221
+ opens << [t, :in_unquoted_symbol]
222
+ else
223
+ opens << [t, nil]
224
+ end
204
225
  when :on_op
205
226
  case t.tok
206
227
  when '?'
@@ -210,12 +231,6 @@ module KatakataIrb::NestingParser
210
231
  # closing of `cond ? value : value``
211
232
  opens.pop
212
233
  end
213
- when :on_symbeg
214
- if t.tok == ':'
215
- opens << [t, :in_unquoted_symbol]
216
- else
217
- opens << [t, nil]
218
- end
219
234
  end
220
235
  end
221
236
  if t.event == :on_nl || t.event == :on_semicolon
@@ -232,29 +247,8 @@ module KatakataIrb::NestingParser
232
247
  opens.map(&:first) + pending_heredocs.reverse
233
248
  end
234
249
 
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
250
+ def self.open_tokens(tokens)
251
+ # scan_opens without block will return a list of open tokens at last token position
252
+ scan_opens(tokens)
259
253
  end
260
254
  end
@@ -80,6 +80,7 @@ class KatakataIrb::TypeSimulator
80
80
  end
81
81
  if @dig_targets.dig? sexp
82
82
  params in [:paren, params]
83
+ params ||= [:params, nil, nil, nil, nil, nil, nil, nil] # params might be nil in ruby 3.0
83
84
  params_table = extract_param_names(params).to_h { [_1, KatakataIrb::Types::NIL] }
84
85
  method_scope = KatakataIrb::Scope.new(
85
86
  scope,
@@ -556,7 +557,7 @@ class KatakataIrb::TypeSimulator
556
557
  scope.update pattern_scope
557
558
  KatakataIrb::Types::UnionType[*results]
558
559
  in [:case, target_exp, match_exp]
559
- target = simulate_evaluate target_exp, scope
560
+ target = target_exp ? simulate_evaluate(target_exp, scope) : KatakataIrb::Types::NIL
560
561
  simulate_evaluate match_exp, scope, case_target: target
561
562
  in [:void_stmt]
562
563
  KatakataIrb::Types::NIL
@@ -768,9 +769,9 @@ class KatakataIrb::TypeSimulator
768
769
  case sexp
769
770
  in [:fcall | :vcall, [:@ident | :@const | :@kw | :@op, method,]] # hoge
770
771
  [nil, method, [], [], nil, false]
771
- in [:call, receiver, [:@period,] | [:@op, '&.',] | :'::' => dot, :call]
772
+ in [:call, receiver, [:@period,] | [:@op, '&.',] | [:@op, '::',] | :'::' => dot, :call]
772
773
  [receiver, :call, [], [], nil, optional[dot]]
773
- in [:call, receiver, [:@period,] | [:@op, '&.',] | :'::' => dot, method]
774
+ in [:call, receiver, [:@period,] | [:@op, '&.',] | [:@op, '::',] | :'::' => dot, method]
774
775
  method => [:@ident | :@const | :@kw | :@op, method,] unless method == :call
775
776
  [receiver, method, [], [], nil, optional[dot]]
776
777
  in [:command, [:@ident | :@const | :@kw | :@op, method,], args] # hoge 1, 2
@@ -894,6 +895,7 @@ class KatakataIrb::TypeSimulator
894
895
  in [:field | :aref_field,]
895
896
  # a.b, c[i] = value
896
897
  in [:excessed_comma]
898
+ in [:args_forward]
897
899
  end
898
900
  end
899
901
  [*pre_required, *post_required].each(&extract_mlhs)
@@ -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
@@ -186,12 +194,19 @@ module KatakataIrb::Types
186
194
  @params = params
187
195
  end
188
196
  def transform() = yield(self)
189
- def methods() = @klass.instance_methods
190
- def all_methods() = @klass.instance_methods | @klass.private_instance_methods
197
+ def methods() = rbs_methods.select { _2.public? }.keys | @klass.instance_methods
198
+ def all_methods() = rbs_methods.keys | @klass.instance_methods | @klass.private_instance_methods
191
199
  def constants() = []
192
200
  def types() = [self]
193
201
  def nillable?() = (@klass == NilClass)
194
202
  def nonnillable() = self
203
+ def rbs_methods
204
+ name = KatakataIrb::Types.class_name_of(@klass)
205
+ return {} unless name
206
+
207
+ type_name = RBS::TypeName(name).absolute!
208
+ KatakataIrb::Types.rbs_builder.build_instance(type_name).methods rescue {}
209
+ end
195
210
  def inspect
196
211
  if params.empty?
197
212
  inspect_without_params
@@ -390,8 +405,8 @@ module KatakataIrb::Types
390
405
 
391
406
  def self.match_free_variables(vars, types, values)
392
407
  accumulator = {}
393
- types.zip(values).each do |t, v|
394
- _match_free_variable(vars, t, v, accumulator)
408
+ types.zip values do |t, v|
409
+ _match_free_variable(vars, t, v, accumulator) if v
395
410
  end
396
411
  accumulator.transform_values { UnionType[*_1] }
397
412
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KatakataIrb
4
- VERSION = "0.1.8"
4
+ VERSION = "0.1.10"
5
5
  end
data/lib/katakata_irb.rb CHANGED
@@ -1,13 +1,17 @@
1
1
  require 'katakata_irb/version'
2
2
  require 'katakata_irb/completor'
3
+ require 'katakata_irb/types'
3
4
 
4
5
  module KatakataIrb
5
- def self.log_output=(output)
6
- @log_output = output
7
- end
8
-
9
- def self.log_puts(...)
10
- STDOUT.cooked { @log_output&.puts(...) }
6
+ class << self
7
+ attr_accessor :log_output, :last_completion_error
8
+ def log_puts(...)
9
+ if STDOUT.tty?
10
+ STDOUT.cooked { log_output&.puts(...) }
11
+ else
12
+ log_output&.puts(...)
13
+ end
14
+ end
11
15
  end
12
16
  end
13
17
 
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.8
4
+ version: 0.1.10
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-09 00:00:00.000000000 Z
11
+ date: 2023-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: irb
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.4.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: reline
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.3.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.3.0
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rbs
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -46,7 +60,6 @@ extensions: []
46
60
  extra_rdoc_files: []
47
61
  files:
48
62
  - Gemfile
49
- - Gemfile.lock
50
63
  - LICENSE.txt
51
64
  - README.md
52
65
  - Rakefile
@@ -82,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
82
95
  - !ruby/object:Gem::Version
83
96
  version: '0'
84
97
  requirements: []
85
- rubygems_version: 3.4.6
98
+ rubygems_version: 3.4.10
86
99
  signing_key:
87
100
  specification_version: 4
88
101
  summary: IRB with Typed Completion
data/Gemfile.lock DELETED
@@ -1,29 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- katakata_irb (0.1.8)
5
- irb (>= 1.4.0)
6
- rbs
7
-
8
- GEM
9
- remote: https://rubygems.org/
10
- specs:
11
- io-console (0.6.0)
12
- irb (1.6.4)
13
- reline (>= 0.3.0)
14
- minitest (5.18.0)
15
- rake (13.0.6)
16
- rbs (3.1.0)
17
- reline (0.3.3)
18
- io-console (~> 0.5)
19
-
20
- PLATFORMS
21
- x86_64-darwin-20
22
-
23
- DEPENDENCIES
24
- katakata_irb!
25
- minitest (~> 5.0)
26
- rake (~> 13.0)
27
-
28
- BUNDLED WITH
29
- 2.4.5