Unveiling the big leap in Ruby 3.3's IRB
In this blog post, we will delve into the major enhancements introduced in Ruby 3.3’s IRB, as well as what’s currently planned for the next year.
In a nutshell, Ruby 3.3’s IRB offers improved debugging capabilities, an enhanced autocompletion experience, and an overall improved user experience.
It’s worth noting that even if you don’t upgrade to Ruby 3.3 immediately, you can still install the same version of IRB
(v1.11.0
) in any Ruby 2.7+ projects by adding gem "irb"
to its Gemfile.
Table Of Contents
- Advancing Debugging with IRB
- Enhancing Autocompletion
- Quality of Life Improvements
- What’s on the Horizon for Ruby 3.4?
- Acknowledgements
Advancing Debugging with IRB
In Ruby 3.2’s IRB (v1.6
), we introduced a set of shortcut commands such as debug
and next
that allowed for a swift
transition to the debug
gem’s session from a binding.irb
breakpoint.
(To avoid confusion, I’ll refer to the debug
gem’s session as rdbg
from here on.)
This was a significant improvement. However, with the switch, users would instantly lose certain IRB features, such as multi-line input:
irb(main):001:0> debug
(ruby) if true
eval error: (rdbg)/test.rb:1: syntax error, unexpected end-of-input, expecting `then' or ';' or '\n'
if true
^
nil
(rdbg)
This year, we’ve taken the user experience to a new level by introducing the irb:rdbg
session, which allows users to execute all rdbg
commands without exiting the IRB session.
irb(main):001> debug
irb:rdbg(main):002* if true
irb:rdbg(main):003* puts "Multi-line input and more!"
irb:rdbg(main):004> end
Multi-line input and more!
nil
irb:rdbg(main):005> th
# `th` (show all threads) is a example of a command only present in a `rdbg` session
--> #0 (sleep)@test.rb:2:in `<main>'
irb:rdbg(main):006>
This deep integration offers several advantages:
- Access to both IRB and rdbg’s commands
- Multi-line input
- Autocompletion
- Symbol aliases to commands, like
@
(whereami
) and$
(show_source
) - Pager support
But most importantly, it offers users a debugging experience equivalent to pry-byebug
.
Accessing irb:rdbg
from binding.break
/ debugger
With the debug
gem v1.9.0
, which will also be part of Ruby 3.3, you can configure it to run irb:rdbg
either by:
- Setting its
irb_console
config through theRUBY_DEBUG_IRB_CONSOLE=1
environment variable - Running the
irb
command in ardbg
session
Doing so will give you the same irb:rdbg
session in your debug
console:
$ RUBY_DEBUG_IRB_CONSOLE=1 bundle exec rdbg test.rb
[1, 1] in test.rb
=> 1| a = 1
=>#0 <main> at test.rb:1
irb:rdbg(main):002>
Enhancing Autocompletion
Addressing Completion Dropdown Issues
In Ruby 3.1, IRB introduced the autocompletion feature. While it has proven to be a useful feature for many users, it also came with some issues that were hard to overlook:
- The dropdown could push up the prompt when the candidate list is long. This not only distracts users but also pushes up the output history, forcing users to frequently scroll up and down through the session.
- The dropdown’s colors are not configurable, making text hard to read with some terminal themes.
- Under certain conditions, the dropdown could erase previously rendered content.
Here’s a short demonstration of these issues:
However, thanks to several fixes on the Reline gem (a pure-Ruby replacement of readline
),
these issues have been addressed.
We are aware that there are other existing issues, such as the lack of debouncing or tab-completion options. And we will keep improving the feature in future releases.
Customizing Dropdown UI with Reline::Face
With Reline
v0.4.0+
, you can use the Reline::Face
class to customize the dropdown
UI’s style in your .irbrc
.
For instance, this snippet will change the dropdown from its default color scheme to a black-and-white theme:
Reline::Face.config(:completion_dialog) do |conf|
conf.define :default, foreground: :white, background: :black
conf.define :enhanced, foreground: :black, background: :white
conf.define :scrollbar, foreground: :white, background: :black
end
The result can be seen below:
Experimenting with More Accurate Completion Using RBS
Currently, IRB’s autocompletion is powered by a combination of regular expressions and runtime information from Binding
.
This provides a decent result for the first level of completion, but can’t support chained method calls.
For example, when the user types 'Ruby'.up
, IRB can list upcase
and upcase!
, because it knows 'Ruby'
is a String
.
But when the user types 'Ruby'.upcase.
, IRB can’t make another suggestion.
This is because it cannot evaluate 'Ruby'.upcase
to get its value as it risks causing side-effects
(for example, calling User.all.
in a Rails app).
However, there is another way we can get an object’s information without evaluation in Ruby: gradual typing.
Therefore, this year @tompng initiated the repl_type_completor
project to explore if it’s a feasible replacement for the current regexp-based completor.
To give it a try, you need to add this to your Gemfile:
gem "repl_type_completor", group: [:development, :test]
And to activate it:
- Pass the
--type-completor
flag when starting IRB - Or add this to your
irbrc
file:
IRB.conf[:COMPLETOR] = :type # default is :regexp
For more details, please refer to IRB readme’s type completion section.
Quality of Life Improvements
Pager Support
This year, IRB has introduced pager support on the output of:
- The
show_source
,ls
, andshow_cmds
commands - The new
history
command - Evaluation result
When these outputs’ height exceeds your terminal’s, IRB will now paginate them:
This offers several benefits:
- You can freely scroll up and down with your arrow-up/down keys
- You won’t overscroll to some previous IRB output
- By typing
/[search_term]
, like/foo
, inside a pager, you can easily search any text that containsfoo
- This could be extremely useful when inspecting record(s) in a Rails console
We understand that for some users or under certain conditions, a pager could be more of a hindrance than a help.
Therefore, it’s also possible to disable it with the --no-pager
flag, or by adding this to your irbrc
file:
IRB.conf[:USE_PAGER] = false
Omitting Return Value Inspection with ;
Even though paging evaluation result should make long output easier to inspect, sometimes we simply don’t want to see
the return value of certain expressions. For example, the result of users = User.all
when it returns hundreds of records.
In such cases, the new IRB allows users to omit the return value by adding a ;
at the end, like users = User.all;
.
Example
irb(main):001> long_string = "foo" * 10000;
irb(main):002> long_string.size
=> 30000
History Command
IRB now has a history
command to display all stored input history (which by default is limited to 1000
).
Since we usually have a long input history, this command also comes with a -g [term]
flag for filtering.
irb(main):006> history -g self
1000: history -g self
803: self
769: self
731: watch self @foo
698: break self.inspect
584: self
582: self
Streamlined Prompt
As you may have noticed, IRB’s prompt is now a bit shorter:
Before
irb(main):001:0>
After
irb(main):001>
The removed part is the indent level number, which became redundant after IRB implemented a robust auto-indent feature for multi-line input.
Enhanced show_source
Command
The show_source
command has always been an essential tool for many IRB users, especially for debugging.
This year it received two enhancements that will make it even more useful:
- You can now use
-s
to get the method’s super definition if it has one - It can now display private methods too
The -s
Flag
Given this script:
class Foo
def foo
"foo"
end
end
class Bar < Foo
def foo
super + "bar"
end
end
You can now get both Bar#foo
and its super method (Foo#foo
)’s definition:
irb(main):001> show_source Bar#foo
From: test.rb:8
def foo
super + "bar"
end
=> nil
irb(main):002> show_source Bar#foo -s
From: test.rb:2
def foo
"foo"
end
=> nil
You can also stack the flag, like -ss
, to walk up the super method chain. And if IRB can’t find the super definition anymore,
it’ll notify you with:
irb(main):003> show_source Bar#foo -ss
Error: Couldn't locate a super definition for Bar#foo
=> nil
What’s on the Horizon for Ruby 3.4?
The help
Command Will Print Help Message
When we use a terminal-based application, the first thing we normally do is to type help
and learn how to use it.
However, due to historical reasons, IRB’s help
command opens up an ri
input for Ruby document lookup instead.
This unconventional design can make it hard, especially for new Ruby developers, to learn IRB’s usage.
So we decided to change it in several steps:
- In Ruby 3.2, we added
show_cmds
to print the help message andshow_doc
command as an alias to thehelp
command. - In Ruby 3.3, IRB will warn users that
help
is going to be repurposed to act asshow_cmds
. - In early 2024, we plan to release IRB
v2.0
, with the repurposing ofhelp
command being one of the breaking changes.
Enhancing Extensibility: Command and Helper Method
Several major Ruby web frameworks, such as Rails and Hanami, utilize IRB as a platform for their consoles. Additionally, other libraries extend IRB with their custom features via commands.
However, until now, there have been no standard APIs on the IRB side to accommodate new commands or helper methods. This situation leads to several challenges:
- IRB is unable to display these extended features in its help message.
- Refactoring the relevant parts becomes challenging because they are essentially public when private APIs are used directly.
- Projects need to devise their own ways to utilize IRB, often resulting in similar but slightly different solutions.
To address these issues, we plan to provide official APIs and relevant documentation to help libraries and applications extend IRB. Our goal is to transform IRB from not just a great tool, but also into a great platform for other tools.
Acknowledgements
The remarkable progress we see on IRB was made possible by the IRB/Reline maintainers team:
And also, a big thank you to all the community contributors.