katakata_irb 0.1.3 → 0.1.5

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: 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")