irb 1.13.2 → 1.15.2

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.
@@ -14,7 +14,7 @@ module IRB
14
14
 
15
15
  def execute(arg)
16
16
 
17
- if (match = arg&.match(/(-g|-G)\s+(?<grep>.+)\s*\n\z/))
17
+ if (match = arg&.match(/(-g|-G)\s+(?<grep>.+)\s*\z/))
18
18
  grep = Regexp.new(match[:grep])
19
19
  end
20
20
 
@@ -19,7 +19,7 @@ module IRB
19
19
  # Use throw and catch to handle arg that includes `;`
20
20
  # For example: "1, kw: (2; 3); 4" will be parsed to [[1], { kw: 3 }]
21
21
  catch(:EXTRACT_RUBY_ARGS) do
22
- @irb_context.workspace.binding.eval "IRB::Command.extract_ruby_args #{arg}"
22
+ @irb_context.workspace.binding.eval "::IRB::Command.extract_ruby_args #{arg}"
23
23
  end || [[], {}]
24
24
  end
25
25
  end
@@ -11,7 +11,7 @@ module IRB
11
11
 
12
12
  module Command
13
13
  class Ls < Base
14
- include RubyArgsExtractor
14
+ class EvaluationError < StandardError; end
15
15
 
16
16
  category "Context"
17
17
  description "Show methods, constants, and variables."
@@ -22,24 +22,35 @@ module IRB
22
22
  -g [query] Filter the output with a query.
23
23
  HELP_MESSAGE
24
24
 
25
+ def evaluate(code)
26
+ @irb_context.workspace.binding.eval(code)
27
+ rescue Exception => e
28
+ puts "#{e.class}: #{e.message}"
29
+ raise EvaluationError
30
+ end
31
+
25
32
  def execute(arg)
26
33
  if match = arg.match(/\A(?<target>.+\s|)(-g|-G)\s+(?<grep>.+)$/)
27
- if match[:target].empty?
28
- use_main = true
29
- else
30
- obj = @irb_context.workspace.binding.eval(match[:target])
31
- end
34
+ target = match[:target]
32
35
  grep = Regexp.new(match[:grep])
36
+ elsif match = arg.match(/\A((?<target>.+),|)\s*grep:(?<grep>.+)/)
37
+ # Legacy style `ls obj, grep: /regexp/`
38
+ # Evaluation order should be eval(target) then eval(grep)
39
+ target = match[:target] || ''
40
+ grep_regexp_code = match[:grep]
33
41
  else
34
- args, kwargs = ruby_args(arg)
35
- use_main = args.empty?
36
- obj = args.first
37
- grep = kwargs[:grep]
42
+ target = arg.strip
38
43
  end
39
44
 
40
- if use_main
45
+ if target.empty?
41
46
  obj = irb_context.workspace.main
42
47
  locals = irb_context.workspace.binding.local_variables
48
+ else
49
+ obj = evaluate(target)
50
+ end
51
+
52
+ if grep_regexp_code
53
+ grep = evaluate(grep_regexp_code)
43
54
  end
44
55
 
45
56
  o = Output.new(grep: grep)
@@ -52,6 +63,7 @@ module IRB
52
63
  o.dump("class variables", klass.class_variables)
53
64
  o.dump("locals", locals) if locals
54
65
  o.print_result
66
+ rescue EvaluationError
55
67
  end
56
68
 
57
69
  def dump_methods(o, klass, obj)
@@ -33,6 +33,8 @@ module IRB
33
33
  yield
34
34
  ]
35
35
 
36
+ HELP_COMMAND_PREPOSING = /\Ahelp\s+/
37
+
36
38
  def completion_candidates(preposing, target, postposing, bind:)
37
39
  raise NotImplementedError
38
40
  end
@@ -86,8 +88,8 @@ module IRB
86
88
  )
87
89
  end
88
90
 
89
- def command_completions(preposing, target)
90
- if preposing.empty? && !target.empty?
91
+ def command_candidates(target)
92
+ if !target.empty?
91
93
  IRB::Command.command_names.select { _1.start_with?(target) }
92
94
  else
93
95
  []
@@ -111,8 +113,18 @@ module IRB
111
113
  end
112
114
 
113
115
  def completion_candidates(preposing, target, _postposing, bind:)
114
- commands = command_completions(preposing, target)
116
+ # When completing the argument of `help` command, only commands should be candidates
117
+ return command_candidates(target) if preposing.match?(HELP_COMMAND_PREPOSING)
118
+
119
+ commands = if preposing.empty?
120
+ command_candidates(target)
121
+ # It doesn't make sense to propose commands with other preposing
122
+ else
123
+ []
124
+ end
125
+
115
126
  result = ReplTypeCompletor.analyze(preposing + target, binding: bind, filename: @context.irb_path)
127
+
116
128
  return commands unless result
117
129
 
118
130
  commands | result.completion_candidates.map { target + _1 }
@@ -125,26 +137,33 @@ module IRB
125
137
  end
126
138
 
127
139
  class RegexpCompletor < BaseCompletor # :nodoc:
140
+ KERNEL_METHODS = ::Kernel.instance_method(:methods)
141
+ KERNEL_PRIVATE_METHODS = ::Kernel.instance_method(:private_methods)
142
+ KERNEL_INSTANCE_VARIABLES = ::Kernel.instance_method(:instance_variables)
143
+ OBJECT_CLASS_INSTANCE_METHOD = ::Object.instance_method(:class)
144
+ MODULE_CONSTANTS_INSTANCE_METHOD = ::Module.instance_method(:constants)
145
+
128
146
  using Module.new {
129
147
  refine ::Binding do
130
148
  def eval_methods
131
- ::Kernel.instance_method(:methods).bind(eval("self")).call
149
+ KERNEL_METHODS.bind_call(receiver)
132
150
  end
133
151
 
134
152
  def eval_private_methods
135
- ::Kernel.instance_method(:private_methods).bind(eval("self")).call
153
+ KERNEL_PRIVATE_METHODS.bind_call(receiver)
136
154
  end
137
155
 
138
156
  def eval_instance_variables
139
- ::Kernel.instance_method(:instance_variables).bind(eval("self")).call
157
+ KERNEL_INSTANCE_VARIABLES.bind_call(receiver)
140
158
  end
141
159
 
142
160
  def eval_global_variables
143
- ::Kernel.instance_method(:global_variables).bind(eval("self")).call
161
+ ::Kernel.global_variables
144
162
  end
145
163
 
146
164
  def eval_class_constants
147
- ::Module.instance_method(:constants).bind(eval("self.class")).call
165
+ klass = OBJECT_CLASS_INSTANCE_METHOD.bind_call(receiver)
166
+ MODULE_CONSTANTS_INSTANCE_METHOD.bind_call(klass)
148
167
  end
149
168
  end
150
169
  }
@@ -187,12 +206,20 @@ module IRB
187
206
  end
188
207
 
189
208
  def completion_candidates(preposing, target, postposing, bind:)
190
- if preposing && postposing
191
- result = complete_require_path(target, preposing, postposing)
192
- return result if result
209
+ if result = complete_require_path(target, preposing, postposing)
210
+ return result
193
211
  end
194
- commands = command_completions(preposing || '', target)
195
- commands | retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) }
212
+
213
+ commands = command_candidates(target)
214
+
215
+ # When completing the argument of `help` command, only commands should be candidates
216
+ return commands if preposing.match?(HELP_COMMAND_PREPOSING)
217
+
218
+ # It doesn't make sense to propose commands with other preposing
219
+ commands = [] unless preposing.empty?
220
+
221
+ completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) }
222
+ commands | completion_data
196
223
  end
197
224
 
198
225
  def doc_namespace(_preposing, matched, _postposing, bind:)
@@ -470,7 +497,7 @@ module IRB
470
497
  end
471
498
  end
472
499
  CompletionProc = ->(target, preposing = nil, postposing = nil) {
473
- regexp_completor.completion_candidates(preposing, target, postposing, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding)
500
+ regexp_completor.completion_candidates(preposing || '', target, postposing || '', bind: IRB.conf[:MAIN_CONTEXT].workspace.binding)
474
501
  }
475
502
  end
476
503
  deprecate_constant :InputCompletor
data/lib/irb/context.rb CHANGED
@@ -13,6 +13,10 @@ module IRB
13
13
  # A class that wraps the current state of the irb session, including the
14
14
  # configuration of IRB.conf.
15
15
  class Context
16
+ KERNEL_PUBLIC_METHOD = ::Kernel.instance_method(:public_method)
17
+ KERNEL_METHOD = ::Kernel.instance_method(:method)
18
+
19
+ ASSIGN_OPERATORS_REGEXP = Regexp.union(%w[= += -= *= /= %= **= &= |= &&= ||= ^= <<= >>=])
16
20
  # Creates a new IRB context.
17
21
  #
18
22
  # The optional +input_method+ argument:
@@ -148,26 +152,9 @@ module IRB
148
152
  @newline_before_multiline_output = true
149
153
  end
150
154
 
151
- @user_aliases = IRB.conf[:COMMAND_ALIASES].dup
152
- @command_aliases = @user_aliases.merge(KEYWORD_ALIASES)
153
- end
154
-
155
- private def term_interactive?
156
- return true if ENV['TEST_IRB_FORCE_INTERACTIVE']
157
- STDIN.tty? && ENV['TERM'] != 'dumb'
155
+ @command_aliases = IRB.conf[:COMMAND_ALIASES].dup
158
156
  end
159
157
 
160
- # because all input will eventually be evaluated as Ruby code,
161
- # command names that conflict with Ruby keywords need special workaround
162
- # we can remove them once we implemented a better command system for IRB
163
- KEYWORD_ALIASES = {
164
- :break => :irb_break,
165
- :catch => :irb_catch,
166
- :next => :irb_next,
167
- }.freeze
168
-
169
- private_constant :KEYWORD_ALIASES
170
-
171
158
  def use_tracer=(val)
172
159
  require_relative "ext/tracer" if val
173
160
  IRB.conf[:USE_TRACER] = val
@@ -185,39 +172,6 @@ module IRB
185
172
  __send__(__method__, val)
186
173
  end
187
174
 
188
- private def build_completor
189
- completor_type = IRB.conf[:COMPLETOR]
190
- case completor_type
191
- when :regexp
192
- return RegexpCompletor.new
193
- when :type
194
- completor = build_type_completor
195
- return completor if completor
196
- else
197
- warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
198
- end
199
- # Fallback to RegexpCompletor
200
- RegexpCompletor.new
201
- end
202
-
203
- private def build_type_completor
204
- if RUBY_ENGINE == 'truffleruby'
205
- # Avoid SyntaxError. truffleruby does not support endless method definition yet.
206
- warn 'TypeCompletor is not supported on TruffleRuby yet'
207
- return
208
- end
209
-
210
- begin
211
- require 'repl_type_completor'
212
- rescue LoadError => e
213
- warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}"
214
- return
215
- end
216
-
217
- ReplTypeCompletor.preload_rbs
218
- TypeCompletor.new(self)
219
- end
220
-
221
175
  def save_history=(val)
222
176
  IRB.conf[:SAVE_HISTORY] = val
223
177
  end
@@ -310,6 +264,8 @@ module IRB
310
264
  attr_reader :use_autocomplete
311
265
  # A copy of the default <code>IRB.conf[:INSPECT_MODE]</code>
312
266
  attr_reader :inspect_mode
267
+ # Inspector for the current context
268
+ attr_reader :inspect_method
313
269
 
314
270
  # A copy of the default <code>IRB.conf[:PROMPT_MODE]</code>
315
271
  attr_reader :prompt_mode
@@ -602,7 +558,8 @@ module IRB
602
558
  set_last_value(result)
603
559
  when Statement::Command
604
560
  statement.command_class.execute(self, statement.arg)
605
- set_last_value(nil)
561
+ when Statement::IncorrectAlias
562
+ warn statement.message
606
563
  end
607
564
 
608
565
  nil
@@ -636,8 +593,77 @@ module IRB
636
593
  result
637
594
  end
638
595
 
639
- def inspect_last_value # :nodoc:
640
- @inspect_method.inspect_value(@last_value)
596
+ def parse_input(code, is_assignment_expression)
597
+ command_name, arg = code.strip.split(/\s+/, 2)
598
+ arg ||= ''
599
+
600
+ # command can only be 1 line
601
+ if code.lines.size != 1 ||
602
+ # command name is required
603
+ command_name.nil? ||
604
+ # local variable have precedence over command
605
+ local_variables.include?(command_name.to_sym) ||
606
+ # assignment expression is not a command
607
+ (is_assignment_expression ||
608
+ (arg.start_with?(ASSIGN_OPERATORS_REGEXP) && !arg.start_with?(/==|=~/)))
609
+ return Statement::Expression.new(code, is_assignment_expression)
610
+ end
611
+
612
+ command = command_name.to_sym
613
+
614
+ # Check command aliases
615
+ if aliased_name = command_aliases[command]
616
+ if command_class = Command.load_command(aliased_name)
617
+ command = aliased_name
618
+ elsif HelperMethod.helper_methods[aliased_name]
619
+ message = <<~MESSAGE
620
+ Using command alias `#{command}` for helper method `#{aliased_name}` is not supported.
621
+ Please check the value of `IRB.conf[:COMMAND_ALIASES]`.
622
+ MESSAGE
623
+ return Statement::IncorrectAlias.new(message)
624
+ else
625
+ message = <<~MESSAGE
626
+ You're trying to use command alias `#{command}` for command `#{aliased_name}`, but `#{aliased_name}` does not exist.
627
+ Please check the value of `IRB.conf[:COMMAND_ALIASES]`.
628
+ MESSAGE
629
+ return Statement::IncorrectAlias.new(message)
630
+ end
631
+ else
632
+ command_class = Command.load_command(command)
633
+ end
634
+
635
+ # Check visibility
636
+ public_method = !!KERNEL_PUBLIC_METHOD.bind_call(main, command) rescue false
637
+ private_method = !public_method && !!KERNEL_METHOD.bind_call(main, command) rescue false
638
+ if command_class && Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
639
+ Statement::Command.new(code, command_class, arg)
640
+ else
641
+ Statement::Expression.new(code, is_assignment_expression)
642
+ end
643
+ end
644
+
645
+ def colorize_input(input, complete:)
646
+ if IRB.conf[:USE_COLORIZE] && IRB::Color.colorable?
647
+ lvars = local_variables || []
648
+ parsed_input = parse_input(input, false)
649
+ if parsed_input.is_a?(Statement::Command)
650
+ name, sep, arg = input.split(/(\s+)/, 2)
651
+ arg = IRB::Color.colorize_code(arg, complete: complete, local_variables: lvars)
652
+ "#{IRB::Color.colorize(name, [:BOLD])}\e[m#{sep}#{arg}"
653
+ else
654
+ IRB::Color.colorize_code(input, complete: complete, local_variables: lvars)
655
+ end
656
+ else
657
+ Reline::Unicode.escape_for_print(input)
658
+ end
659
+ end
660
+
661
+ def inspect_last_value(output = +'') # :nodoc:
662
+ @inspect_method.inspect_value(@last_value, output)
663
+ end
664
+
665
+ def inspector_support_stream_output?
666
+ @inspect_method.support_stream_output?
641
667
  end
642
668
 
643
669
  NOPRINTING_IVARS = ["@last_value"] # :nodoc:
@@ -670,5 +696,56 @@ module IRB
670
696
  def local_variables # :nodoc:
671
697
  workspace.binding.local_variables
672
698
  end
699
+
700
+ def safe_method_call_on_main(method_name)
701
+ main_object = main
702
+ Object === main_object ? main_object.__send__(method_name) : Object.instance_method(method_name).bind_call(main_object)
703
+ end
704
+
705
+ private
706
+
707
+ def term_interactive?
708
+ return true if ENV['TEST_IRB_FORCE_INTERACTIVE']
709
+ STDIN.tty? && ENV['TERM'] != 'dumb'
710
+ end
711
+
712
+ def build_completor
713
+ completor_type = IRB.conf[:COMPLETOR]
714
+
715
+ # Gem repl_type_completor is added to bundled gems in Ruby 3.4.
716
+ # Use :type as default completor only in Ruby 3.4 or later.
717
+ verbose = !!completor_type
718
+ completor_type ||= RUBY_VERSION >= '3.4' ? :type : :regexp
719
+
720
+ case completor_type
721
+ when :regexp
722
+ return RegexpCompletor.new
723
+ when :type
724
+ completor = build_type_completor(verbose: verbose)
725
+ return completor if completor
726
+ else
727
+ warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
728
+ end
729
+ # Fallback to RegexpCompletor
730
+ RegexpCompletor.new
731
+ end
732
+
733
+ def build_type_completor(verbose:)
734
+ if RUBY_ENGINE == 'truffleruby'
735
+ # Avoid SyntaxError. truffleruby does not support endless method definition yet.
736
+ warn 'TypeCompletor is not supported on TruffleRuby yet' if verbose
737
+ return
738
+ end
739
+
740
+ begin
741
+ require 'repl_type_completor'
742
+ rescue LoadError => e
743
+ warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}" if verbose
744
+ return
745
+ end
746
+
747
+ ReplTypeCompletor.preload_rbs
748
+ TypeCompletor.new(self)
749
+ end
673
750
  end
674
751
  end
data/lib/irb/debug/ui.rb CHANGED
@@ -45,9 +45,7 @@ module IRB
45
45
  $stdout.puts line.chomp
46
46
  }
47
47
  when String
48
- str.each_line{|line|
49
- $stdout.puts line.chomp
50
- }
48
+ Pager.page_content(str, retain_content: true)
51
49
  when nil
52
50
  $stdout.puts
53
51
  end
@@ -56,7 +54,7 @@ module IRB
56
54
  def readline _
57
55
  setup_interrupt do
58
56
  tc = DEBUGGER__::SESSION.instance_variable_get(:@tc)
59
- cmd = @irb.debug_readline(tc.current_frame.binding || TOPLEVEL_BINDING)
57
+ cmd = @irb.debug_readline(tc.current_frame.eval_binding || TOPLEVEL_BINDING)
60
58
 
61
59
  case cmd
62
60
  when nil # when user types C-d
data/lib/irb/debug.rb CHANGED
@@ -57,22 +57,18 @@ module IRB
57
57
  DEBUGGER__::ThreadClient.prepend(SkipPathHelperForIRB)
58
58
  end
59
59
 
60
- if !@output_modifier_defined && !DEBUGGER__::CONFIG[:no_hint]
61
- irb_output_modifier_proc = Reline.output_modifier_proc
62
-
63
- Reline.output_modifier_proc = proc do |output, complete:|
64
- unless output.strip.empty?
65
- cmd = output.split(/\s/, 2).first
60
+ if !DEBUGGER__::CONFIG[:no_hint] && irb.context.io.is_a?(RelineInputMethod)
61
+ Reline.output_modifier_proc = proc do |input, complete:|
62
+ unless input.strip.empty?
63
+ cmd = input.split(/\s/, 2).first
66
64
 
67
65
  if !complete && DEBUGGER__.commands.key?(cmd)
68
- output = output.sub(/\n$/, " # debug command\n")
66
+ input = input.sub(/\n$/, " # debug command\n")
69
67
  end
70
68
  end
71
69
 
72
- irb_output_modifier_proc.call(output, complete: complete)
70
+ irb.context.colorize_input(input, complete: complete)
73
71
  end
74
-
75
- @output_modifier_defined = true
76
72
  end
77
73
 
78
74
  true
@@ -85,6 +81,7 @@ module IRB
85
81
  IRB.instance_variable_set(:@debugger_irb, irb)
86
82
  irb.context.with_debugger = true
87
83
  irb.context.irb_name += ":rdbg"
84
+ irb.context.io.load_history if irb.context.io.class < HistorySavingAbility
88
85
  end
89
86
 
90
87
  module SkipPathHelperForIRB
@@ -5,9 +5,11 @@ require_relative "command/internal_helpers"
5
5
  require_relative "command/backtrace"
6
6
  require_relative "command/break"
7
7
  require_relative "command/catch"
8
+ require_relative "command/cd"
8
9
  require_relative "command/chws"
9
10
  require_relative "command/context"
10
11
  require_relative "command/continue"
12
+ require_relative "command/copy"
11
13
  require_relative "command/debug"
12
14
  require_relative "command/delete"
13
15
  require_relative "command/disable_irb"
@@ -180,9 +182,15 @@ module IRB
180
182
  [:edit, NO_OVERRIDE]
181
183
  )
182
184
 
183
- _register_with_aliases(:irb_break, Command::Break)
184
- _register_with_aliases(:irb_catch, Command::Catch)
185
- _register_with_aliases(:irb_next, Command::Next)
185
+ _register_with_aliases(:irb_break, Command::Break,
186
+ [:break, OVERRIDE_ALL]
187
+ )
188
+ _register_with_aliases(:irb_catch, Command::Catch,
189
+ [:catch, OVERRIDE_PRIVATE_ONLY]
190
+ )
191
+ _register_with_aliases(:irb_next, Command::Next,
192
+ [:next, OVERRIDE_ALL]
193
+ )
186
194
  _register_with_aliases(:irb_delete, Command::Delete,
187
195
  [:delete, NO_OVERRIDE]
188
196
  )
@@ -211,7 +219,8 @@ module IRB
211
219
  )
212
220
 
213
221
  _register_with_aliases(:irb_show_doc, Command::ShowDoc,
214
- [:show_doc, NO_OVERRIDE]
222
+ [:show_doc, NO_OVERRIDE],
223
+ [:ri, NO_OVERRIDE]
215
224
  )
216
225
 
217
226
  _register_with_aliases(:irb_info, Command::IrbInfo)
@@ -240,6 +249,9 @@ module IRB
240
249
  _register_with_aliases(:irb_disable_irb, Command::DisableIrb,
241
250
  [:disable_irb, NO_OVERRIDE]
242
251
  )
252
+
253
+ register(:cd, Command::CD)
254
+ register(:copy, Command::Copy)
243
255
  end
244
256
 
245
257
  ExtendCommand = Command
@@ -256,10 +268,12 @@ module IRB
256
268
  # Deprecated. Doesn't have any effect.
257
269
  @EXTEND_COMMANDS = []
258
270
 
259
- # Drepcated. Use Command.regiser instead.
260
- def self.def_extend_command(cmd_name, cmd_class, _, *aliases)
261
- Command._register_with_aliases(cmd_name, cmd_class, *aliases)
262
- Command.class_variable_set(:@@command_override_policies, nil)
271
+ class << self
272
+ # Drepcated. Use Command.regiser instead.
273
+ def def_extend_command(cmd_name, cmd_class, _, *aliases)
274
+ Command._register_with_aliases(cmd_name, cmd_class, *aliases)
275
+ Command.class_variable_set(:@@command_override_policies, nil)
276
+ end
263
277
  end
264
278
  end
265
279
  end
@@ -100,19 +100,21 @@ module IRB
100
100
 
101
101
  private def easter_egg_logo(type)
102
102
  @easter_egg_logos ||= File.read(File.join(__dir__, 'ruby_logo.aa'), encoding: 'UTF-8:UTF-8')
103
- .split(/TYPE: ([A-Z]+)\n/)[1..]
103
+ .split(/TYPE: ([A-Z_]+)\n/)[1..]
104
104
  .each_slice(2)
105
105
  .to_h
106
106
  @easter_egg_logos[type.to_s.upcase]
107
107
  end
108
108
 
109
109
  private def easter_egg(type = nil)
110
+ print "\e[?1049h"
110
111
  type ||= [:logo, :dancing].sample
111
112
  case type
112
113
  when :logo
113
- require "rdoc"
114
- RDoc::RI::Driver.new.page do |io|
115
- io.write easter_egg_logo(:large)
114
+ Pager.page do |io|
115
+ logo_type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode_large : :ascii_large
116
+ io.write easter_egg_logo(logo_type)
117
+ STDIN.raw { STDIN.getc } if io == STDOUT
116
118
  end
117
119
  when :dancing
118
120
  STDOUT.cooked do
@@ -123,7 +125,7 @@ module IRB
123
125
  canvas = Canvas.new(Reline.get_screen_size)
124
126
  end
125
127
  ruby_model = RubyModel.new
126
- print "\e[?1049h"
128
+ print "\e[?25l" # hide cursor
127
129
  0.step do |i| # TODO (0..).each needs Ruby 2.6 or later
128
130
  buff = canvas.draw do
129
131
  ruby_model.render_frame(i) do |p1, p2|
@@ -137,10 +139,12 @@ module IRB
137
139
  end
138
140
  rescue Interrupt
139
141
  ensure
140
- print "\e[0m\e[?1049l"
142
+ print "\e[?25h" # show cursor
141
143
  trap("SIGINT", prev_trap)
142
144
  end
143
145
  end
146
+ ensure
147
+ print "\e[0m\e[?1049l"
144
148
  end
145
149
  end
146
150
  end