Skip to content

Commit

Permalink
Allow non-identifier aliases like Pry's @ and $ (#426)
Browse files Browse the repository at this point in the history
* Allow non-identifier aliases

* Move the configuration to IRB.conf

* Avoid abusing method lookup for symbol aliases

* Add more alias tests

* A small optimization

* Assume non-nil Context

* Load IRB.conf earlier
  • Loading branch information
k0kubun committed Nov 3, 2022
1 parent 08ac803 commit e23db51
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 0 deletions.
3 changes: 3 additions & 0 deletions lib/irb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,9 @@ class Irb
def initialize(workspace = nil, input_method = nil)
@context = Context.new(self, workspace, input_method)
@context.main.extend ExtendCommandBundle
@context.command_aliases.each do |alias_name, cmd_name|
@context.main.install_alias_method(alias_name, cmd_name)
end
@signal_status = :IN_IRB
@scanner = RubyLex.new
end
Expand Down
18 changes: 18 additions & 0 deletions lib/irb/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ def initialize(irb, workspace = nil, input_method = nil)
if @newline_before_multiline_output.nil?
@newline_before_multiline_output = true
end

@command_aliases = IRB.conf[:COMMAND_ALIASES]
end

# The top-level workspace, see WorkSpace#main
Expand Down Expand Up @@ -326,6 +328,9 @@ def main
# See IRB@Command+line+options for more command line options.
attr_accessor :back_trace_limit

# User-defined IRB command aliases
attr_accessor :command_aliases

# Alias for #use_multiline
alias use_multiline? use_multiline
# Alias for #use_singleline
Expand Down Expand Up @@ -477,6 +482,13 @@ def evaluate(line, line_no, exception: nil) # :nodoc:
line = "begin ::Kernel.raise _; rescue _.class\n#{line}\n""end"
@workspace.local_variable_set(:_, exception)
end

# Transform a non-identifier alias (ex: @, $)
command = line.split(/\s/, 2).first
if original = symbol_alias(command)
line = line.gsub(/\A#{Regexp.escape(command)}/, original.to_s)
end

set_last_value(@workspace.evaluate(self, line, irb_path, line_no))
end

Expand Down Expand Up @@ -522,5 +534,11 @@ def inspect # :nodoc:
def local_variables # :nodoc:
workspace.binding.local_variables
end

# Return a command name if it's aliased from the argument and it's not an identifier.
def symbol_alias(command)
return nil if command.match?(/\A\w+\z/)
command_aliases[command.to_sym]
end
end
end
2 changes: 2 additions & 0 deletions lib/irb/init.rb
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ def IRB.init_config(ap_path)
@CONF[:LC_MESSAGES] = Locale.new

@CONF[:AT_EXIT] = []

@CONF[:COMMAND_ALIASES] = {}
end

def IRB.set_measure_callback(type = nil, arg = nil, &block)
Expand Down
6 changes: 6 additions & 0 deletions lib/irb/ruby-lex.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ def set_input(io, p = nil, context:, &block)
false
end
else
# Accept any single-line input starting with a non-identifier alias (ex: @, $)
command = code.split(/\s/, 2).first
if context.symbol_alias(command)
next true
end

code.gsub!(/\s*\z/, '').concat("\n")
ltype, indent, continue, code_block_open = check_state(code, context: context)
if ltype or indent > 0 or continue or code_block_open
Expand Down
58 changes: 58 additions & 0 deletions test/irb/test_cmd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,24 @@ def test_show_source
assert_match(%r[/irb\.rb], out)
end

def test_show_source_alias
input = TestInputMethod.new([
"$ 'IRB.conf'\n",
])
IRB.init_config(nil)
IRB.conf[:COMMAND_ALIASES] = { :'$' => :show_source }
workspace = IRB::WorkSpace.new(Object.new)
IRB.conf[:VERBOSE] = false
irb = IRB::Irb.new(workspace, input)
IRB.conf[:MAIN_CONTEXT] = irb.context
irb.context.return_format = "=> %s\n"
out, err = capture_output do
irb.eval_input
end
assert_empty err
assert_match(%r[/irb\.rb], out)
end

def test_show_source_end_finder
pend if RUBY_ENGINE == 'truffleruby'
eval(code = <<-EOS, binding, __FILE__, __LINE__ + 1)
Expand Down Expand Up @@ -610,5 +628,45 @@ def test_whereami
assert_empty err
assert_match(/^From: .+ @ line \d+ :\n/, out)
end

def test_whereami_alias
input = TestInputMethod.new([
"@\n",
])
IRB.init_config(nil)
IRB.conf[:COMMAND_ALIASES] = { :'@' => :whereami }
workspace = IRB::WorkSpace.new(Object.new)
irb = IRB::Irb.new(workspace, input)
IRB.conf[:MAIN_CONTEXT] = irb.context
out, err = capture_output do
irb.eval_input
end
assert_empty err
assert_match(/^From: .+ @ line \d+ :\n/, out)
end

def test_vars_with_aliases
input = TestInputMethod.new([
"@foo\n",
"$bar\n",
])
IRB.init_config(nil)
IRB.conf[:COMMAND_ALIASES] = {
:'@' => :whereami,
:'$' => :show_source,
}
main = Object.new
main.instance_variable_set(:@foo, "foo")
$bar = "bar"
workspace = IRB::WorkSpace.new(main)
irb = IRB::Irb.new(workspace, input)
IRB.conf[:MAIN_CONTEXT] = irb.context
out, err = capture_output do
irb.eval_input
end
assert_empty err
assert_match(/"foo"/, out)
assert_match(/"bar"/, out)
end
end
end

0 comments on commit e23db51

Please sign in to comment.