irb 1.8.1 → 1.9.0

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: 0b6bfef2fbb8e5e7d7587a567055e2b7f019678d2355f6ae52f7a7d8161df0c0
4
- data.tar.gz: 653be685d9f249341c3e165cb4303baa05563c5556cc2bf2584243c39ec0cb92
3
+ metadata.gz: 810aa12a1fbfdbd0ff9f9c4d5c4ac71e62e8241f87cb1a306be1ff81aaef08cb
4
+ data.tar.gz: 781d169232ac355b9723e8dc03d6c0de4c16327b4a4a3339cd1e90bad7e1c0b1
5
5
  SHA512:
6
- metadata.gz: 215ce7d8130e1a43382706858c487d7835971e326e93eb1c2ccb559b7dec5f1ecd216b451ad535beaba5e908419ec3e1c13bcc8dea8a0e6d2d7c45fed4639fdb
7
- data.tar.gz: c950ff10929df5ac8365ec0b86fcafe1ac85685ef955b6d12ec7febaf2a9bc85be8b325a58b60583c2a7567889cc6fbe9c6c6eb9b16a10f35409b7da21c7cd32
6
+ metadata.gz: 51d97e80d977c6ce100f66cc0ad4becae17600d36653be88cc7c2f1e6cd0b7127cec01344bc69b04de1b043f8d45a1d07f6b4b8ad3fad4584585aca269cfa65f
7
+ data.tar.gz: a197cf8c07a1ea728a3c9d0d70fdd28a2a5b9a4e9ebf72a8fd8d6495627cf4c369e6831879a766c5dd8819438bb1f126b0d4d669becc4c63a7ad6c4ea51a3a79
data/Gemfile CHANGED
@@ -17,3 +17,10 @@ gem "rake"
17
17
  gem "test-unit"
18
18
  gem "test-unit-ruby-core"
19
19
  gem "debug", github: "ruby/debug"
20
+
21
+ gem "racc"
22
+
23
+ if RUBY_VERSION >= "3.0.0"
24
+ gem "rbs"
25
+ gem "prism", ">= 0.17.1"
26
+ end
data/README.md CHANGED
@@ -52,6 +52,10 @@ $ gem install irb
52
52
 
53
53
  ## Usage
54
54
 
55
+ > **Note**
56
+ >
57
+ > We're working hard to match Pry's variety of powerful features in IRB, and you can track our progress or find contribution ideas in [this document](https://github.com/ruby/irb/blob/master/COMPARED_WITH_PRY.md).
58
+
55
59
  ### The `irb` Executable
56
60
 
57
61
  You can start a fresh IRB session by typing `irb` in your terminal.
@@ -150,7 +154,7 @@ Context
150
154
 
151
155
  Starting from version 1.8.0, IRB boasts a powerful integration with `debug.gem`, providing a debugging experience akin to `pry-byebug`.
152
156
 
153
- After hitting a `binding.irb` breakpoint, you can activate the debugger with the `debug` command.
157
+ After hitting a `binding.irb` breakpoint, you can activate the debugger with the `debug` command. Alternatively, if the `debug` method happens to already be defined in the current scope, you can call `irb_debug`.
154
158
 
155
159
  ```shell
156
160
  From: test.rb @ line 3 :
@@ -231,6 +235,79 @@ However, there are also some limitations to be aware of:
231
235
  2. As IRB [doesn't currently support remote-connection](https://github.com/ruby/irb/issues/672), it can't be used with `debug.gem`'s remote debugging feature.
232
236
  3. Access to the previous return value via the underscore `_` is not supported.
233
237
 
238
+ ## Type Based Completion
239
+
240
+ IRB's default completion `IRB::RegexpCompletor` uses Regexp. IRB has another experimental completion `IRB::TypeCompletion` that uses type analysis.
241
+
242
+ ### How to Enable IRB::TypeCompletion
243
+
244
+ To enable IRB::TypeCompletion, run IRB with `--type-completor` option
245
+ ```
246
+ $ irb --type-completor
247
+ ```
248
+ Or write the code below to IRB's rc-file.
249
+ ```ruby
250
+ IRB.conf[:COMPLETOR] = :type # default is :regexp
251
+ ```
252
+ You also need `gem prism` and `gem rbs` to use this feature.
253
+
254
+ To check if it's enabled, type `irb_info` into IRB and see the `Completion` section.
255
+ ```
256
+ irb(main):001> irb_info
257
+ ...
258
+ # Enabled
259
+ Completion: Autocomplete, TypeCompletion::Completor(Prism: 0.17.1, RBS: 3.3.0)
260
+ # Not enabled
261
+ Completion: Autocomplete, RegexpCompletor
262
+ ...
263
+ ```
264
+ If you have `sig/` directory or `rbs_collection.lock.yaml` in current directory, IRB will load it.
265
+
266
+ ### Advantage over Default IRB::RegexpCompletor
267
+
268
+ IRB::TypeCompletion can autocomplete chained methods, block parameters and more if type information is available.
269
+ These are some examples IRB::RegexpCompletor cannot complete.
270
+
271
+ ```ruby
272
+ irb(main):001> 'Ruby'.upcase.chars.s # Array methods (sample, select, shift, size)
273
+ ```
274
+
275
+ ```ruby
276
+ irb(main):001> 10.times.map(&:to_s).each do |s|
277
+ irb(main):002> s.up # String methods (upcase, upcase!, upto)
278
+ ```
279
+
280
+ ```ruby
281
+ irb(main):001> class User < ApplicationRecord
282
+ irb(main):002> def foo
283
+ irb(main):003> sa # save, save!
284
+ ```
285
+
286
+ As a trade-off, completion calculation takes more time than IRB::RegexpCompletor.
287
+
288
+ ### Difference between Steep's Completion
289
+
290
+ Compared with Steep, IRB::TypeCompletion has some difference and limitations.
291
+ ```ruby
292
+ [0, 'a'].sample.
293
+ # Steep completes intersection of Integer methods and String methods
294
+ # IRB::TypeCompletion completes both Integer and String methods
295
+ ```
296
+
297
+ Some features like type narrowing is not implemented.
298
+ ```ruby
299
+ def f(arg = [0, 'a'].sample)
300
+ if arg.is_a?(String)
301
+ arg. # Completes both Integer and String methods
302
+ ```
303
+
304
+ Unlike other static type checker, IRB::TypeCompletion uses runtime information to provide better completion.
305
+ ```ruby
306
+ irb(main):001> a = [1]
307
+ => [1]
308
+ irb(main):002> a.first. # Completes Integer methods
309
+ ```
310
+
234
311
  ## Configuration
235
312
 
236
313
  ### Environment Variables
@@ -269,6 +346,30 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
269
346
 
270
347
  Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/irb.
271
348
 
349
+ ### Set up the environment
350
+
351
+ 1. Fork the project to your GithHub account
352
+ 2. Clone the fork with `git clone git@github.com:[your_username]/irb.git`
353
+ 3. Run `bundle install`
354
+ 4. Run `bundle exec rake` to make sure tests pass locally
355
+
356
+ ### Run integration tests
357
+
358
+ If your changes affect component rendering, such as the autocompletion's dialog/dropdown, you may need to run IRB's integration tests, known as `yamatanarroroti`.
359
+
360
+ Before running these tests, ensure that you have `libvterm` installed. If you're using Homebrew, you can install it by running:
361
+
362
+ ```bash
363
+ brew install libvterm
364
+ ```
365
+
366
+ After installing `libvterm`, you can run the integration tests using the following commands:
367
+
368
+ ```bash
369
+ WITH_VTERM=1 bundle install
370
+ WITH_VTERM=1 bundle exec rake test test_yamatanooroti
371
+ ```
372
+
272
373
  ## Releasing
273
374
 
274
375
  ```
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ Rake::TestTask.new(:test) do |t|
5
5
  t.libs << "test" << "test/lib"
6
6
  t.libs << "lib"
7
7
  t.ruby_opts << "-rhelper"
8
- t.test_files = FileList["test/irb/test_*.rb"]
8
+ t.test_files = FileList["test/irb/test_*.rb", "test/irb/type_completion/test_*.rb"]
9
9
  end
10
10
 
11
11
  # To make sure they have been correctly setup for Ruby CI.
@@ -13,7 +13,7 @@ desc "Run each irb test file in isolation."
13
13
  task :test_in_isolation do
14
14
  failed = false
15
15
 
16
- FileList["test/irb/test_*.rb"].each do |test_file|
16
+ FileList["test/irb/test_*.rb", "test/irb/type_completion/test_*.rb"].each do |test_file|
17
17
  ENV["TEST"] = test_file
18
18
  begin
19
19
  Rake::Task["test"].execute
@@ -14,6 +14,7 @@ module IRB
14
14
  str = "Ruby version: #{RUBY_VERSION}\n"
15
15
  str += "IRB version: #{IRB.version}\n"
16
16
  str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
17
+ str += "Completion: #{IRB.CurrentContext.io.respond_to?(:completion_info) ? IRB.CurrentContext.io.completion_info : 'off'}\n"
17
18
  str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
18
19
  str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
19
20
  str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "nop"
4
4
  require_relative "../source_finder"
5
+ require_relative "../pager"
5
6
  require_relative "../color"
6
7
 
7
8
  module IRB
@@ -40,12 +41,16 @@ module IRB
40
41
  private
41
42
 
42
43
  def show_source(source)
43
- puts
44
- puts "#{bold("From")}: #{source.file}:#{source.first_line}"
45
- puts
46
- code = IRB::Color.colorize_code(File.read(source.file))
47
- puts code.lines[(source.first_line - 1)...source.last_line].join
48
- puts
44
+ file_content = IRB::Color.colorize_code(File.read(source.file))
45
+ code = file_content.lines[(source.first_line - 1)...source.last_line].join
46
+ content = <<~CONTENT
47
+
48
+ #{bold("From")}: #{source.file}:#{source.first_line}
49
+
50
+ #{code}
51
+ CONTENT
52
+
53
+ Pager.page_content(content)
49
54
  end
50
55
 
51
56
  def bold(str)
@@ -8,30 +8,7 @@
8
8
  require_relative 'ruby-lex'
9
9
 
10
10
  module IRB
11
- module InputCompletor # :nodoc:
12
- using Module.new {
13
- refine ::Binding do
14
- def eval_methods
15
- ::Kernel.instance_method(:methods).bind(eval("self")).call
16
- end
17
-
18
- def eval_private_methods
19
- ::Kernel.instance_method(:private_methods).bind(eval("self")).call
20
- end
21
-
22
- def eval_instance_variables
23
- ::Kernel.instance_method(:instance_variables).bind(eval("self")).call
24
- end
25
-
26
- def eval_global_variables
27
- ::Kernel.instance_method(:global_variables).bind(eval("self")).call
28
- end
29
-
30
- def eval_class_constants
31
- ::Module.instance_method(:constants).bind(eval("self.class")).call
32
- end
33
- end
34
- }
11
+ class BaseCompletor # :nodoc:
35
12
 
36
13
  # Set of reserved words used by Ruby, you should not use these for
37
14
  # constants or variables
@@ -56,7 +33,13 @@ module IRB
56
33
  yield
57
34
  ]
58
35
 
59
- BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
36
+ def completion_candidates(preposing, target, postposing, bind:)
37
+ raise NotImplementedError
38
+ end
39
+
40
+ def doc_namespace(preposing, matched, postposing, bind:)
41
+ raise NotImplementedError
42
+ end
60
43
 
61
44
  GEM_PATHS =
62
45
  if defined?(Gem::Specification)
@@ -73,7 +56,7 @@ module IRB
73
56
  []
74
57
  end.freeze
75
58
 
76
- def self.retrieve_gem_and_system_load_path
59
+ def retrieve_gem_and_system_load_path
77
60
  candidates = (GEM_PATHS | $LOAD_PATH)
78
61
  candidates.map do |p|
79
62
  if p.respond_to?(:to_path)
@@ -84,8 +67,8 @@ module IRB
84
67
  end.compact.sort
85
68
  end
86
69
 
87
- def self.retrieve_files_to_require_from_load_path
88
- @@files_from_load_path ||=
70
+ def retrieve_files_to_require_from_load_path
71
+ @files_from_load_path ||=
89
72
  (
90
73
  shortest = []
91
74
  rest = retrieve_gem_and_system_load_path.each_with_object([]) { |path, result|
@@ -103,13 +86,43 @@ module IRB
103
86
  )
104
87
  end
105
88
 
106
- def self.retrieve_files_to_require_relative_from_current_dir
107
- @@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
89
+ def retrieve_files_to_require_relative_from_current_dir
90
+ @files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
108
91
  path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
109
92
  }
110
93
  end
94
+ end
95
+
96
+ class RegexpCompletor < BaseCompletor # :nodoc:
97
+ using Module.new {
98
+ refine ::Binding do
99
+ def eval_methods
100
+ ::Kernel.instance_method(:methods).bind(eval("self")).call
101
+ end
102
+
103
+ def eval_private_methods
104
+ ::Kernel.instance_method(:private_methods).bind(eval("self")).call
105
+ end
106
+
107
+ def eval_instance_variables
108
+ ::Kernel.instance_method(:instance_variables).bind(eval("self")).call
109
+ end
110
+
111
+ def eval_global_variables
112
+ ::Kernel.instance_method(:global_variables).bind(eval("self")).call
113
+ end
114
+
115
+ def eval_class_constants
116
+ ::Module.instance_method(:constants).bind(eval("self.class")).call
117
+ end
118
+ end
119
+ }
120
+
121
+ def inspect
122
+ 'RegexpCompletor'
123
+ end
111
124
 
112
- CompletionRequireProc = lambda { |target, preposing = nil, postposing = nil|
125
+ def complete_require_path(target, preposing, postposing)
113
126
  if target =~ /\A(['"])([^'"]+)\Z/
114
127
  quote = $1
115
128
  actual_target = $2
@@ -124,39 +137,37 @@ module IRB
124
137
  break
125
138
  end
126
139
  end
127
- result = []
128
- if tok && tok.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
129
- case tok.tok
130
- when 'require'
131
- result = retrieve_files_to_require_from_load_path.select { |path|
132
- path.start_with?(actual_target)
133
- }.map { |path|
134
- quote + path
135
- }
136
- when 'require_relative'
137
- result = retrieve_files_to_require_relative_from_current_dir.select { |path|
138
- path.start_with?(actual_target)
139
- }.map { |path|
140
- quote + path
141
- }
142
- end
140
+ return unless tok&.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
141
+
142
+ case tok.tok
143
+ when 'require'
144
+ retrieve_files_to_require_from_load_path.select { |path|
145
+ path.start_with?(actual_target)
146
+ }.map { |path|
147
+ quote + path
148
+ }
149
+ when 'require_relative'
150
+ retrieve_files_to_require_relative_from_current_dir.select { |path|
151
+ path.start_with?(actual_target)
152
+ }.map { |path|
153
+ quote + path
154
+ }
143
155
  end
144
- result
145
- }
156
+ end
146
157
 
147
- CompletionProc = lambda { |target, preposing = nil, postposing = nil|
158
+ def completion_candidates(preposing, target, postposing, bind:)
148
159
  if preposing && postposing
149
- result = CompletionRequireProc.(target, preposing, postposing)
150
- unless result
151
- result = retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
152
- end
153
- result
154
- else
155
- retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
160
+ result = complete_require_path(target, preposing, postposing)
161
+ return result if result
156
162
  end
157
- }
163
+ retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) }
164
+ end
158
165
 
159
- def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
166
+ def doc_namespace(_preposing, matched, _postposing, bind:)
167
+ retrieve_completion_data(matched, bind: bind, doc_namespace: true)
168
+ end
169
+
170
+ def retrieve_completion_data(input, bind:, doc_namespace:)
160
171
  case input
161
172
  # this regexp only matches the closing character because of irb's Reline.completer_quote_characters setting
162
173
  # details are described in: https://github.com/ruby/irb/pull/523
@@ -394,44 +405,10 @@ module IRB
394
405
  end
395
406
  end
396
407
 
397
- PerfectMatchedProc = ->(matched, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) {
398
- begin
399
- require 'rdoc'
400
- rescue LoadError
401
- return
402
- end
403
-
404
- RDocRIDriver ||= RDoc::RI::Driver.new
405
-
406
- if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
407
- IRB.__send__(:easter_egg)
408
- return
409
- end
410
-
411
- namespace = retrieve_completion_data(matched, bind: bind, doc_namespace: true)
412
- return unless namespace
413
-
414
- if namespace.is_a?(Array)
415
- out = RDoc::Markup::Document.new
416
- namespace.each do |m|
417
- begin
418
- RDocRIDriver.add_method(out, m)
419
- rescue RDoc::RI::Driver::NotFoundError
420
- end
421
- end
422
- RDocRIDriver.display(out)
423
- else
424
- begin
425
- RDocRIDriver.display_names([namespace])
426
- rescue RDoc::RI::Driver::NotFoundError
427
- end
428
- end
429
- }
430
-
431
408
  # Set of available operators in Ruby
432
409
  Operators = %w[% & * ** + - / < << <= <=> == === =~ > >= >> [] []= ^ ! != !~]
433
410
 
434
- def self.select_message(receiver, message, candidates, sep = ".")
411
+ def select_message(receiver, message, candidates, sep = ".")
435
412
  candidates.grep(/^#{Regexp.quote(message)}/).collect do |e|
436
413
  case e
437
414
  when /^[a-zA-Z_]/
@@ -443,4 +420,20 @@ module IRB
443
420
  end
444
421
  end
445
422
  end
423
+
424
+ module InputCompletor
425
+ class << self
426
+ private def regexp_completor
427
+ @regexp_completor ||= RegexpCompletor.new
428
+ end
429
+
430
+ def retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
431
+ regexp_completor.retrieve_completion_data(input, bind: bind, doc_namespace: doc_namespace)
432
+ end
433
+ end
434
+ CompletionProc = ->(target, preposing = nil, postposing = nil) {
435
+ regexp_completor.completion_candidates(preposing, target, postposing, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding)
436
+ }
437
+ end
438
+ deprecate_constant :InputCompletor
446
439
  end
data/lib/irb/context.rb CHANGED
@@ -86,14 +86,14 @@ module IRB
86
86
  when nil
87
87
  if STDIN.tty? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline?
88
88
  # Both of multiline mode and singleline mode aren't specified.
89
- @io = RelineInputMethod.new
89
+ @io = RelineInputMethod.new(build_completor)
90
90
  else
91
91
  @io = nil
92
92
  end
93
93
  when false
94
94
  @io = nil
95
95
  when true
96
- @io = RelineInputMethod.new
96
+ @io = RelineInputMethod.new(build_completor)
97
97
  end
98
98
  unless @io
99
99
  case use_singleline?
@@ -149,6 +149,43 @@ module IRB
149
149
  @command_aliases = IRB.conf[:COMMAND_ALIASES]
150
150
  end
151
151
 
152
+ private def build_completor
153
+ completor_type = IRB.conf[:COMPLETOR]
154
+ case completor_type
155
+ when :regexp
156
+ return RegexpCompletor.new
157
+ when :type
158
+ completor = build_type_completor
159
+ return completor if completor
160
+ else
161
+ warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}"
162
+ end
163
+ # Fallback to RegexpCompletor
164
+ RegexpCompletor.new
165
+ end
166
+
167
+ TYPE_COMPLETION_REQUIRED_PRISM_VERSION = '0.17.1'
168
+
169
+ private def build_type_completor
170
+ unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') && RUBY_ENGINE != 'truffleruby'
171
+ warn 'TypeCompletion requires RUBY_VERSION >= 3.0.0'
172
+ return
173
+ end
174
+ begin
175
+ require 'prism'
176
+ rescue LoadError => e
177
+ warn "TypeCompletion requires Prism: #{e.message}"
178
+ return
179
+ end
180
+ unless Gem::Version.new(Prism::VERSION) >= Gem::Version.new(TYPE_COMPLETION_REQUIRED_PRISM_VERSION)
181
+ warn "TypeCompletion requires Prism::VERSION >= #{TYPE_COMPLETION_REQUIRED_PRISM_VERSION}"
182
+ return
183
+ end
184
+ require 'irb/type_completion/completor'
185
+ TypeCompletion::Types.preload_in_thread
186
+ TypeCompletion::Completor.new
187
+ end
188
+
152
189
  def save_history=(val)
153
190
  IRB.conf[:SAVE_HISTORY] = val
154
191
  end
data/lib/irb/debug/ui.rb CHANGED
@@ -4,8 +4,7 @@ require 'debug/console'
4
4
  module IRB
5
5
  module Debug
6
6
  class UI < DEBUGGER__::UI_Base
7
- def initialize(thread, irb)
8
- @thread = thread
7
+ def initialize(irb)
9
8
  @irb = irb
10
9
  end
11
10
 
@@ -56,7 +55,7 @@ module IRB
56
55
 
57
56
  def readline _
58
57
  setup_interrupt do
59
- tc = DEBUGGER__::SESSION.get_thread_client(@thread)
58
+ tc = DEBUGGER__::SESSION.instance_variable_get(:@tc)
60
59
  cmd = @irb.debug_readline(tc.current_frame.binding || TOPLEVEL_BINDING)
61
60
 
62
61
  case cmd
data/lib/irb/debug.rb CHANGED
@@ -32,17 +32,14 @@ module IRB
32
32
  end
33
33
  DEBUGGER__::CONFIG.set_config
34
34
  configure_irb_for_debugger(irb)
35
- thread = Thread.current
36
35
 
37
- DEBUGGER__.initialize_session{ IRB::Debug::UI.new(thread, irb) }
36
+ DEBUGGER__.initialize_session{ IRB::Debug::UI.new(irb) }
38
37
  end
39
38
 
40
39
  # When debug session was previously started but not by IRB
41
40
  if defined?(DEBUGGER__::SESSION) && !irb.context.with_debugger
42
41
  configure_irb_for_debugger(irb)
43
- thread = Thread.current
44
-
45
- DEBUGGER__::SESSION.reset_ui(IRB::Debug::UI.new(thread, irb))
42
+ DEBUGGER__::SESSION.reset_ui(IRB::Debug::UI.new(irb))
46
43
  end
47
44
 
48
45
  # Apply patches to debug gem so it skips IRB frames
@@ -98,18 +98,26 @@ module IRB
98
98
  end
99
99
  end
100
100
 
101
+ private def easter_egg_logo(type)
102
+ @easter_egg_logos ||= File.read(File.join(__dir__, 'ruby_logo.aa'), encoding: 'UTF-8:UTF-8')
103
+ .split(/TYPE: ([A-Z]+)\n/)[1..]
104
+ .each_slice(2)
105
+ .to_h
106
+ @easter_egg_logos[type.to_s.upcase]
107
+ end
108
+
101
109
  private def easter_egg(type = nil)
102
110
  type ||= [:logo, :dancing].sample
103
111
  case type
104
112
  when :logo
105
- File.open(File.join(__dir__, 'ruby_logo.aa')) do |f|
106
- require "rdoc"
107
- RDoc::RI::Driver.new.page do |io|
108
- IO.copy_stream(f, io)
109
- end
113
+ require "rdoc"
114
+ RDoc::RI::Driver.new.page do |io|
115
+ io.write easter_egg_logo(:large)
110
116
  end
111
117
  when :dancing
112
- begin
118
+ STDOUT.cooked do
119
+ interrupted = false
120
+ prev_trap = trap("SIGINT") { interrupted = true }
113
121
  canvas = Canvas.new(Reline.get_screen_size)
114
122
  Reline::IOGate.set_winch_handler do
115
123
  canvas = Canvas.new(Reline.get_screen_size)
@@ -125,10 +133,12 @@ module IRB
125
133
  buff[0, 20] = "\e[0mPress Ctrl+C to stop\e[31m\e[1m"
126
134
  print "\e[H" + buff
127
135
  sleep 0.05
136
+ break if interrupted
128
137
  end
129
138
  rescue Interrupt
130
139
  ensure
131
140
  print "\e[0m\e[?1049l"
141
+ trap("SIGINT", prev_trap)
132
142
  end
133
143
  end
134
144
  end
data/lib/irb/history.rb CHANGED
@@ -10,6 +10,7 @@ module IRB
10
10
 
11
11
  def load_history
12
12
  history = self.class::HISTORY
13
+
13
14
  if history_file = IRB.conf[:HISTORY_FILE]
14
15
  history_file = File.expand_path(history_file)
15
16
  end
@@ -32,7 +33,8 @@ module IRB
32
33
  end
33
34
 
34
35
  def save_history
35
- history = self.class::HISTORY
36
+ history = self.class::HISTORY.to_a
37
+
36
38
  if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) != 0
37
39
  if history_file = IRB.conf[:HISTORY_FILE]
38
40
  history_file = File.expand_path(history_file)