irb 1.8.1 → 1.9.0

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