summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/irb.rb16
-rw-r--r--lib/irb/context.rb55
-rw-r--r--lib/irb/statement.rb21
-rw-r--r--test/irb/command/test_command_aliasing.rb50
4 files changed, 118 insertions, 24 deletions
diff --git a/lib/irb.rb b/lib/irb.rb
index 1699857737..29be6386c5 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -269,29 +269,25 @@ module IRB
loop do
code = readmultiline
break unless code
- yield build_statement(code), @line_no
+ yield parse_input(code), @line_no
@line_no += code.count("\n")
rescue RubyLex::TerminateLineInput
end
end
- def build_statement(code)
+ def parse_input(code)
if code.match?(/\A\n*\z/)
return Statement::EmptyInput.new
end
code = code.dup.force_encoding(@context.io.encoding)
- if (command, arg = @context.parse_command(code))
- command_class = Command.load_command(command)
- Statement::Command.new(code, command_class, arg)
- else
- is_assignment_expression = @scanner.assignment_expression?(code, local_variables: @context.local_variables)
- Statement::Expression.new(code, is_assignment_expression)
- end
+ is_assignment_expression = @scanner.assignment_expression?(code, local_variables: @context.local_variables)
+
+ @context.parse_input(code, is_assignment_expression)
end
def command?(code)
- !!@context.parse_command(code)
+ parse_input(code).is_a?(Statement::Command)
end
def configure_io
diff --git a/lib/irb/context.rb b/lib/irb/context.rb
index c656281922..8d6545224d 100644
--- a/lib/irb/context.rb
+++ b/lib/irb/context.rb
@@ -600,6 +600,8 @@ module IRB
set_last_value(result)
when Statement::Command
statement.command_class.execute(self, statement.arg)
+ when Statement::IncorrectAlias
+ warn statement.message
end
nil
@@ -633,35 +635,60 @@ module IRB
result
end
- def parse_command(code)
+ def parse_input(code, is_assignment_expression)
command_name, arg = code.strip.split(/\s+/, 2)
- return unless code.lines.size == 1 && command_name
-
arg ||= ''
- command = command_name.to_sym
- # Command aliases are always command. example: $, @
- if (alias_name = command_aliases[command])
- return [alias_name, arg]
+
+ # command can only be 1 line
+ if code.lines.size != 1 ||
+ # command name is required
+ command_name.nil? ||
+ # local variable have precedence over command
+ local_variables.include?(command_name.to_sym) ||
+ # assignment expression is not a command
+ (is_assignment_expression ||
+ (arg.start_with?(ASSIGN_OPERATORS_REGEXP) && !arg.start_with?(/==|=~/)))
+ return Statement::Expression.new(code, is_assignment_expression)
end
- # Assignment-like expression is not a command
- return if arg.start_with?(ASSIGN_OPERATORS_REGEXP) && !arg.start_with?(/==|=~/)
+ command = command_name.to_sym
- # Local variable have precedence over command
- return if local_variables.include?(command)
+ # Check command aliases
+ if aliased_name = command_aliases[command]
+ if command_class = Command.load_command(aliased_name)
+ command = aliased_name
+ elsif HelperMethod.helper_methods[aliased_name]
+ message = <<~MESSAGE
+ Using command alias `#{command}` for helper method `#{aliased_name}` is not supported.
+ Please check the value of `IRB.conf[:COMMAND_ALIASES]`.
+ MESSAGE
+ return Statement::IncorrectAlias.new(message)
+ else
+ message = <<~MESSAGE
+ You're trying to use command alias `#{command}` for command `#{aliased_name}`, but `#{aliased_name}` does not exist.
+ Please check the value of `IRB.conf[:COMMAND_ALIASES]`.
+ MESSAGE
+ return Statement::IncorrectAlias.new(message)
+ end
+ else
+ command_class = Command.load_command(command)
+ end
# Check visibility
public_method = !!KERNEL_PUBLIC_METHOD.bind_call(main, command) rescue false
private_method = !public_method && !!KERNEL_METHOD.bind_call(main, command) rescue false
- if Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
- [command, arg]
+ if command_class && Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
+ Statement::Command.new(code, command_class, arg)
+ else
+ Statement::Expression.new(code, is_assignment_expression)
end
end
def colorize_input(input, complete:)
if IRB.conf[:USE_COLORIZE] && IRB::Color.colorable?
lvars = local_variables || []
- if parse_command(input)
+ parsed_input = parse_input(input, false)
+ if parsed_input.is_a?(Statement::Command)
name, sep, arg = input.split(/(\s+)/, 2)
arg = IRB::Color.colorize_code(arg, complete: complete, local_variables: lvars)
"#{IRB::Color.colorize(name, [:BOLD])}\e[m#{sep}#{arg}"
diff --git a/lib/irb/statement.rb b/lib/irb/statement.rb
index 9591a40357..6a959995d1 100644
--- a/lib/irb/statement.rb
+++ b/lib/irb/statement.rb
@@ -54,6 +54,27 @@ module IRB
end
end
+ class IncorrectAlias < Statement
+ attr_reader :message
+
+ def initialize(message)
+ @code = ""
+ @message = message
+ end
+
+ def should_be_handled_by_debugger?
+ false
+ end
+
+ def is_assignment?
+ false
+ end
+
+ def suppresses_echo?
+ true
+ end
+ end
+
class Command < Statement
attr_reader :command_class, :arg
diff --git a/test/irb/command/test_command_aliasing.rb b/test/irb/command/test_command_aliasing.rb
new file mode 100644
index 0000000000..4ecc88c0aa
--- /dev/null
+++ b/test/irb/command/test_command_aliasing.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require "tempfile"
+require_relative "../helper"
+
+module TestIRB
+ class CommandAliasingTest < IntegrationTestCase
+ def setup
+ super
+ write_rc <<~RUBY
+ IRB.conf[:COMMAND_ALIASES] = {
+ :c => :conf, # alias to helper method
+ :f => :foo
+ }
+ RUBY
+
+ write_ruby <<~'RUBY'
+ binding.irb
+ RUBY
+ end
+
+ def test_aliasing_to_helper_method_triggers_warning
+ out = run_ruby_file do
+ type "c"
+ type "exit"
+ end
+ assert_include(out, "Using command alias `c` for helper method `conf` is not supported.")
+ assert_not_include(out, "Maybe IRB bug!")
+ end
+
+ def test_alias_to_non_existent_command_triggers_warning
+ message = "You're trying to use command alias `f` for command `foo`, but `foo` does not exist."
+ out = run_ruby_file do
+ type "f"
+ type "exit"
+ end
+ assert_include(out, message)
+ assert_not_include(out, "Maybe IRB bug!")
+
+ # Local variables take precedence over command aliases
+ out = run_ruby_file do
+ type "f = 123"
+ type "f"
+ type "exit"
+ end
+ assert_not_include(out, message)
+ assert_not_include(out, "Maybe IRB bug!")
+ end
+ end
+end