katakata_irb 0.1.3 → 0.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fb7e44af7218463a13dd4e072748d5880924a3b5678f8c30200f3e5bfde391cd
4
- data.tar.gz: c0a6c33286b4a8197ad594f16baa87892448ea487e2faa153cac4d8f48d4208f
3
+ metadata.gz: 882c0834b6d52916911b26b82083e1100491dd24cafdb1c948c4c1795fe03d7e
4
+ data.tar.gz: 2250eaec5d0b1cabc236f238249e132206b62e70ada1c16429567b680ace2093
5
5
  SHA512:
6
- metadata.gz: c79ea66192ad0c3f671a8cbaa9cf2d497b6b662482615a6f497e9a86ad6f797cc4d7b8d228bd44afd4c0968736a1ea83630311d2056b3bd4ae4befc1d2c94609
7
- data.tar.gz: d3cde5bdd7a966ecf6a06e82bdd9f276664e513999902e37e3abd1a0870452e4eb8265c43949ce8c7f110e6786980a79e4b07abaf0dd7880c37dee39a27865c3
6
+ metadata.gz: 60f3c4de13636f2948e740b3a57b6091c1f193ae7fa259d665c69a404833ef413b90aca3da7360f97be2d7a669e9a0d5fc101ff172890c4f0714c9eb63db8dd4
7
+ data.tar.gz: eb496c54819de4f1e035e261a6afffcc13d2809b316428602567614dbc5671df5ead97fb4a0696afbb34a7ade9ce8a2b3e9579ebad4ddd006d9a32b120b3e05d
data/Gemfile.lock CHANGED
@@ -1,15 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- katakata_irb (0.1.3)
4
+ katakata_irb (0.1.5)
5
+ irb (>= 1.4.0)
5
6
  rbs
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
11
+ io-console (0.6.0)
12
+ irb (1.6.2)
13
+ reline (>= 0.3.0)
10
14
  minitest (5.16.3)
11
15
  rake (13.0.6)
12
16
  rbs (2.7.0)
17
+ reline (0.3.2)
18
+ io-console (~> 0.5)
13
19
 
14
20
  PLATFORMS
15
21
  x86_64-darwin-20
@@ -20,4 +26,4 @@ DEPENDENCIES
20
26
  rake (~> 13.0)
21
27
 
22
28
  BUNDLED WITH
23
- 2.4.0.dev
29
+ 2.4.5
data/README.md CHANGED
@@ -9,9 +9,15 @@ gem install katakata_irb
9
9
  ```
10
10
  ## Usage
11
11
 
12
+ Just require katakata_irb or write it to your `.irbrc` file.
13
+ ```ruby
14
+ require 'katakata_irb'
15
+ ```
16
+
12
17
  ```
13
- % kirb
14
- irb(main):001:0> [1,'a'].sample.a█
18
+ irb(main):001:0> require 'katakata_irb'
19
+ => true
20
+ irb(main):002:0> [1,'a'].sample.a█
15
21
  |[1,'a'].sample.abs |
16
22
  |[1,'a'].sample.abs2 |
17
23
  |[1,'a'].sample.allbits? |
@@ -22,30 +28,14 @@ irb(main):001:0> [1,'a'].sample.a█
22
28
  ```
23
29
 
24
30
  ```
25
- % kirb
26
- irb(main):001:0> a = 10
31
+ irb(main):001:0> require 'katakata_irb'
32
+ => true
33
+ irb(main):002:0> a = 10
27
34
  => 10
28
- irb(main):002:1* if true
29
- irb(main):003:2* b = a.times.map do
30
- irb(main):004:2* _1.to_s
31
- irb(main):005:1* end
32
- irb(main):006:1* b[0].a█
35
+ irb(main):003:1* if true
36
+ irb(main):004:2* b = a.times.map do
37
+ irb(main):005:2* _1.to_s
38
+ irb(main):006:1* end
39
+ irb(main):007:1* b[0].a█
33
40
  |b[0].ascii_only?|
34
41
  ```
35
-
36
- ```ruby
37
- require 'katakata_irb/completor'
38
- KatakataIrb::Completor.setup
39
- 10.times do |i|
40
- binding.irb
41
- end
42
- ```
43
-
44
- ## Options
45
-
46
- ### `kirb --debug-output`
47
- Show debug output if it meets unimplemented syntax or something
48
-
49
- ### `kirb --without-patch`
50
- `kirb` will apply some patches to reline and irb/ruby-lex.rb by default. This option will disable it.
51
- See `lib/katakata_irb/ruby_lex_patch.rb` and `lib/katakata_irb/reline_patches/*.patch`
data/bin/console CHANGED
@@ -1,10 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- require_relative '../lib/katakata_irb'
3
- require_relative '../lib/katakata_irb/reline_patch'
4
- KatakataIrb.log_output = STDERR
5
- KatakataIrb::RelinePatch.require_patched_reline
6
2
  require 'bundler/setup'
7
3
  require 'katakata_irb'
8
- require 'katakata_irb/ruby_lex_patch'
9
- KatakataIrb::RubyLexPatch.patch_to_ruby_lex
10
- KatakataIrb.repl
4
+ KatakataIrb.log_output = STDERR
5
+ IRB.start(__FILE__)
data/katakata_irb.gemspec CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.description = "IRB with Typed Completion"
13
13
  spec.homepage = "http://github.com/tompng/katakata_irb"
14
14
  spec.license = "MIT"
15
- spec.required_ruby_version = ">= 3.1.0"
15
+ spec.required_ruby_version = ">= 3.0.0" # recommend >= 3.1.0
16
16
 
17
17
  spec.metadata["homepage_uri"] = spec.homepage
18
18
  spec.metadata["source_code_uri"] = "http://github.com/tompng/katakata_irb"
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.require_paths = ["lib"]
30
30
 
31
31
  # Uncomment to register a new dependency of your gem
32
+ spec.add_dependency 'irb', '>= 1.4.0'
32
33
  spec.add_dependency 'rbs'
33
34
 
34
35
  # For more information and examples about making a new gem, check out our
@@ -1,4 +1,4 @@
1
- require_relative 'trex'
1
+ require_relative 'nesting_parser'
2
2
  require_relative 'type_simulator'
3
3
  require 'rbs'
4
4
  require 'rbs/cli'
@@ -6,15 +6,17 @@ require 'irb'
6
6
 
7
7
  module KatakataIrb::Completor
8
8
  using KatakataIrb::TypeSimulator::LexerElemMatcher
9
-
10
9
  HIDDEN_METHODS = %w[Namespace TypeName] # defined by rbs, should be hidden
10
+ singleton_class.attr_accessor :prev_analyze_result
11
11
 
12
12
  def self.setup
13
13
  completion_proc = ->(target, preposing = nil, postposing = nil) do
14
14
  code = "#{preposing}#{target}"
15
15
  irb_context = IRB.conf[:MAIN_CONTEXT]
16
16
  binding = irb_context.workspace.binding
17
- candidates = case analyze code, binding
17
+ result = analyze code, binding
18
+ KatakataIrb::Completor.prev_analyze_result = result
19
+ candidates = case result
18
20
  in [:require | :require_relative => method, name]
19
21
  if method == :require
20
22
  IRB::InputCompletor.retrieve_files_to_require_from_load_path
@@ -25,18 +27,18 @@ module KatakataIrb::Completor
25
27
  ((self_call ? type.all_methods: type.methods).map(&:to_s) - HIDDEN_METHODS) | type.constants
26
28
  in [:const, type, name]
27
29
  type.constants
28
- in [:ivar, name, _scope]
30
+ in [:ivar, name, *_scope]
29
31
  # TODO: scope
30
32
  ivars = binding.eval('self').instance_variables rescue []
31
33
  cvars = (binding.eval('self').class_variables rescue nil) if name == '@'
32
34
  ivars | (cvars || [])
33
- in [:cvar, name, _scope]
35
+ in [:cvar, name, *_scope]
34
36
  # TODO: scope
35
37
  binding.eval('self').class_variables rescue []
36
38
  in [:gvar, name]
37
39
  global_variables
38
40
  in [:symbol, name]
39
- Symbol.all_symbols
41
+ Symbol.all_symbols.map { _1.inspect[1..] }
40
42
  in [:call, type, name, self_call]
41
43
  (self_call ? type.all_methods : type.methods).map(&:to_s) - HIDDEN_METHODS
42
44
  in [:lvar_or_method, name, scope]
@@ -57,16 +59,95 @@ module KatakataIrb::Completor
57
59
  KatakataIrb.log_puts "#{e.inspect} stored to $error"
58
60
  KatakataIrb.log_puts
59
61
  end
62
+
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
74
+ 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
82
+ end
83
+
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
97
+ end
98
+ end
99
+ }
100
+ setup_type_dialog
101
+ end
102
+
103
+ def self.setup_type_dialog
104
+ type_dialog_proc = -> {
105
+ return if just_cursor_moving && completion_journey_data
106
+ cursor_pos_to_render, _result, pointer, autocomplete_dialog = context.last(4)
107
+ 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
+ KatakataIrb::Types.class_name_of type.klass
124
+ when KatakataIrb::Types::SingletonType
125
+ module_name = KatakataIrb::Types.class_name_of type.module_or_class
126
+ "#{module_name}.itself" if module_name
127
+ end
128
+ end.uniq
129
+ return if contents.empty?
130
+
131
+ width = contents.map { Reline::Unicode.calculate_width _1 }.max
132
+ x = cursor_pos_to_render.x + autocomplete_dialog.width
133
+ y = cursor_pos_to_render.y
134
+ Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: 44, fg_color: 37)
135
+ }
136
+ Reline.add_dialog_proc(:show_type, type_dialog_proc, Reline::DEFAULT_DIALOG_CONTEXT)
137
+ end
138
+
139
+ def self.empty_binding()
140
+ Kernel.binding
60
141
  end
61
142
 
62
- def self.analyze(code, binding = Kernel.binding)
143
+ def self.analyze(code, binding = empty_binding)
63
144
  lvars_code = binding.local_variables.map do |name|
64
145
  "#{name}="
65
146
  end.join + "nil;\n"
66
147
  code = lvars_code + code
67
148
  tokens = RubyLex.ripper_lex_without_warning code
68
- tokens = KatakataIrb::TRex.interpolate_ripper_ignored_tokens code, tokens
69
- last_opens = KatakataIrb::TRex.parse(tokens)
149
+ tokens = KatakataIrb::NestingParser.interpolate_ripper_ignored_tokens code, tokens
150
+ last_opens = KatakataIrb::NestingParser.parse(tokens)
70
151
  closings = last_opens.map do |t|
71
152
  case t.tok
72
153
  when /\A%.[<>]\z/
@@ -84,12 +165,21 @@ module KatakataIrb::Completor
84
165
  t.tok
85
166
  when /\A<<[~-]?(?:"(?<s>.+)"|'(?<s>.+)'|(?<s>.+))/
86
167
  $/ + ($1 || $2 || $3) + $/
168
+ when ':"', ":'", ':'
169
+ t.tok[1]
170
+ when '?'
171
+ # ternary operator
172
+ ' : value'
173
+ when '|'
174
+ # block args
175
+ '|'
87
176
  else
88
177
  $/ + 'end'
89
178
  end
90
179
  end
180
+ # remove error tokens
181
+ tokens.pop while tokens&.last&.tok&.empty?
91
182
 
92
- return if code =~ /[!?]\z/
93
183
  case tokens.last
94
184
  in { event: :on_ignored_by_ripper, tok: '.' }
95
185
  suffix = 'method'
@@ -97,6 +187,9 @@ module KatakataIrb::Completor
97
187
  in { dot: true }
98
188
  suffix = 'method'
99
189
  name = ''
190
+ in { event: :on_symbeg }
191
+ suffix = 'symbol'
192
+ name = ''
100
193
  in { event: :on_ident | :on_kw, tok: }
101
194
  return unless code.delete_suffix! tok
102
195
  suffix = 'method'
@@ -109,10 +202,21 @@ module KatakataIrb::Completor
109
202
  return unless code.delete_suffix! tok
110
203
  suffix = 'string'
111
204
  name = tok.rstrip
205
+ in { event: :on_gvar, tok: }
206
+ return unless code.delete_suffix! tok
207
+ suffix = '$gvar'
208
+ name = tok
209
+ in { event: :on_ivar, tok: }
210
+ return unless code.delete_suffix! tok
211
+ suffix = '@ivar'
212
+ name = tok
213
+ in { event: :on_cvar, tok: }
214
+ return unless code.delete_suffix! tok
215
+ suffix = '@@cvar'
216
+ name = tok
112
217
  else
113
218
  return
114
219
  end
115
-
116
220
  sexp = Ripper.sexp code + suffix + closings.reverse.join
117
221
  lines = code.lines
118
222
  line_no = lines.size
@@ -136,16 +240,26 @@ module KatakataIrb::Completor
136
240
  return [:cvar, name] if icvar_available
137
241
  end
138
242
  return unless expression
243
+ calculate_scope = -> { KatakataIrb::TypeSimulator.calculate_binding_scope binding, parents, expression }
244
+ calculate_receiver = -> receiver { KatakataIrb::TypeSimulator.calculate_receiver binding, parents, receiver }
245
+
139
246
  if (target in [:@tstring_content,]) && (parents[-4] in [:command, [:@ident, 'require' | 'require_relative' => require_method,],])
247
+ # `require 'target'`
140
248
  return [require_method.to_sym, name.rstrip]
141
249
  end
142
- calculate_scope = -> { KatakataIrb::TypeSimulator.calculate_binding_scope binding, parents, expression }
143
- calculate_receiver = -> receiver { KatakataIrb::TypeSimulator.calculate_receiver binding, parents, receiver }
250
+ if (target in [:@ident,]) && (expression in [:symbol,]) && (parents[-2] in [:args_add_block, Array => args, [:symbol_literal, ^expression]])
251
+ # `method(&:target)`
252
+ receiver_ref = [:var_ref, [:@ident, '_1', [0, 0]]]
253
+ block_statements = [receiver_ref]
254
+ parents[-1] = parents[-2][-1] = [:brace_block, nil, block_statements]
255
+ parents << block_statements
256
+ return [:call, calculate_receiver.call(receiver_ref), name, false]
257
+ end
144
258
  case expression
145
259
  in [:vcall | :var_ref, [:@ident,]]
146
260
  [:lvar_or_method, name, calculate_scope.call]
147
261
  in [:symbol, [:@ident | :@const | :@op | :@kw,]]
148
- [:symbol, name]
262
+ [:symbol, name] unless name.empty?
149
263
  in [:var_ref | :const_ref, [:@const,]]
150
264
  # TODO
151
265
  [:const, KatakataIrb::Types::SingletonType.new(Object), name]
@@ -1,5 +1,4 @@
1
- module KatakataIrb; end
2
- module KatakataIrb::TRex
1
+ module KatakataIrb::NestingParser
3
2
  def self.interpolate_ripper_ignored_tokens(code, tokens)
4
3
  line_positions = code.lines.reduce([0]) { _1 << _1.last + _2.bytesize }
5
4
  prev_byte_pos = 0
@@ -27,19 +26,25 @@ module KatakataIrb::TRex
27
26
  interpolated
28
27
  end
29
28
 
29
+ IGNOREABLE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end]
30
+
30
31
  def self.parse(tokens)
31
32
  opens = []
32
33
  pending_heredocs = []
33
34
  first_token_on_line = true
34
- tokens.each_with_index do |t, index|
35
+ tokens.each do |t|
35
36
  skip = false
36
37
  last_tok, state, args = opens.last
37
38
  case state
38
39
  when :in_unquoted_symbol
39
- opens.pop
40
- skip = true if %i[on_ident on_const on_op on_cvar on_ivar on_gvar on_kw on_int on_backtick].include?(t.event)
40
+ unless t.event == :on_sp
41
+ opens.pop
42
+ skip = true
43
+ end
44
+ when :in_lambda_head
45
+ opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do')
41
46
  when :in_method_head
42
- unless %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end].include?(t.event)
47
+ unless IGNOREABLE_TOKENS.include?(t.event)
43
48
  next_args = []
44
49
  body = nil
45
50
  if args.include?(:receiver)
@@ -124,13 +129,27 @@ module KatakataIrb::TRex
124
129
  skip = true if t.event == :on_kw && t.tok == 'do'
125
130
  opens[-1] = [last_tok, nil]
126
131
  end
132
+ when :in_block_head
133
+ if t.event == :on_op && t.tok == '|'
134
+ opens[-1] = [last_tok, nil]
135
+ opens << [t, :in_block_args]
136
+ elsif !IGNOREABLE_TOKENS.include?(t.event)
137
+ opens[-1] = [last_tok, nil]
138
+ end
139
+ when :in_block_args
140
+ if t.event == :on_op && t.tok == '|' && t.state.allbits?(Ripper::EXPR_BEG)
141
+ opens.pop
142
+ skip = true
143
+ end
127
144
  end
128
145
 
129
146
  unless skip
130
147
  case t.event
131
148
  when :on_kw
132
149
  case t.tok
133
- when 'begin', 'class', 'module', 'do', 'case'
150
+ when 'do'
151
+ opens << [t, :in_block_head]
152
+ when 'begin', 'class', 'module', 'case'
134
153
  opens << [t, nil]
135
154
  when 'end'
136
155
  opens.pop
@@ -160,7 +179,15 @@ module KatakataIrb::TRex
160
179
  opens << [t, nil]
161
180
  end
162
181
  end
163
- when :on_lparen, :on_lbracket, :on_lbrace, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg
182
+ when :on_lbrace
183
+ if t.state.allbits?(Ripper::EXPR_LABEL)
184
+ opens << [t, nil]
185
+ else
186
+ opens << [t, :in_block_head]
187
+ end
188
+ when :on_tlambda
189
+ opens << [t, :in_lambda_head]
190
+ when :on_lparen, :on_lbracket, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg
164
191
  opens << [t, nil]
165
192
  when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end
166
193
  opens.pop
@@ -174,6 +201,15 @@ module KatakataIrb::TRex
174
201
  opens << [t, nil]
175
202
  when :on_tstring_end, :on_regexp_end, :on_label_end
176
203
  opens.pop
204
+ when :on_op
205
+ case t.tok
206
+ when '?'
207
+ # opening of `cond ? value : value``
208
+ opens << [t, nil]
209
+ when ':'
210
+ # closing of `cond ? value : value``
211
+ opens.pop
212
+ end
177
213
  when :on_symbeg
178
214
  if t.tok == ':'
179
215
  opens << [t, :in_unquoted_symbol]
@@ -188,10 +224,10 @@ module KatakataIrb::TRex
188
224
  first_token_on_line = false
189
225
  end
190
226
  if pending_heredocs.any? && t.tok.include?("\n")
191
- pending_heredocs.reverse_each { opens << [_1, nil] }
227
+ pending_heredocs.reverse_each { |t| opens << [t, nil] }
192
228
  pending_heredocs = []
193
229
  end
194
- yield t, index, opens if block_given?
230
+ yield t, opens if block_given?
195
231
  end
196
232
  opens.map(&:first) + pending_heredocs.reverse
197
233
  end
@@ -201,7 +237,7 @@ module KatakataIrb::TRex
201
237
  prev_opens = []
202
238
  min_depth = 0
203
239
  output = []
204
- last_opens = parse(tokens) do |t, _index, opens|
240
+ last_opens = parse(tokens) do |t, opens|
205
241
  depth = t == opens.last&.first ? opens.size - 1 : opens.size
206
242
  min_depth = depth if depth < min_depth
207
243
  if t.tok.include?("\n")