[ruby/irb] Command implementation not by method

(https://github.com/ruby/irb/pull/824)

* Command is not a method

* Fix command test

* Implement non-method command name completion

* Add test for ExtendCommandBundle.def_extend_command

* Add helper method install test

* Remove spaces in command input parse

* Remove command arg unquote in help command

* Simplify Statement and handle execution in IRB::Irb

* Tweak require, const name

* Always install CommandBundle module to main object

* Remove considering local variable in command or expression check

* Remove unused method, tweak

* Remove outdated comment for help command arg

Co-authored-by: Stan Lo <stan001212@gmail.com>

---------

8fb776e379

Co-authored-by: Stan Lo <stan001212@gmail.com>
This commit is contained in:
tomoya ishida 2024-04-11 01:52:47 +09:00 committed by git
parent 9f6deaa688
commit 6a505d1b59
36 changed files with 414 additions and 328 deletions

View file

@ -929,7 +929,7 @@ module IRB
# Creates a new irb session
def initialize(workspace = nil, input_method = nil)
@context = Context.new(self, workspace, input_method)
@context.workspace.load_commands_to_main
@context.workspace.load_helper_methods_to_main
@signal_status = :IN_IRB
@scanner = RubyLex.new
@line_no = 1
@ -950,7 +950,7 @@ module IRB
def debug_readline(binding)
workspace = IRB::WorkSpace.new(binding)
context.replace_workspace(workspace)
context.workspace.load_commands_to_main
context.workspace.load_helper_methods_to_main
@line_no += 1
# When users run:
@ -1028,7 +1028,15 @@ module IRB
return statement.code
end
@context.evaluate(statement.evaluable_code, line_no)
case statement
when Statement::EmptyInput
# Do nothing
when Statement::Expression
@context.evaluate(statement.code, line_no)
when Statement::Command
ret = statement.command_class.execute(@context, statement.arg)
@context.set_last_value(ret)
end
if @context.echo? && !statement.suppresses_echo?
if statement.is_assignment?
@ -1084,10 +1092,7 @@ module IRB
end
code << line
# Accept any single-line input for symbol aliases or commands that transform
# args
return code if single_line_command?(code)
return code if command?(code)
tokens, opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
return code if terminated
@ -1114,23 +1119,36 @@ module IRB
end
code.force_encoding(@context.io.encoding)
command_or_alias, arg = code.split(/\s/, 2)
# Transform a non-identifier alias (@, $) or keywords (next, break)
command_name = @context.command_aliases[command_or_alias.to_sym]
command = command_name || command_or_alias
command_class = ExtendCommandBundle.load_command(command)
if command_class
Statement::Command.new(code, command, arg, command_class)
if (command, arg = parse_command(code))
command_class = ExtendCommandBundle.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
end
def single_line_command?(code)
command = code.split(/\s/, 2).first
@context.symbol_alias?(command) || @context.transform_args?(command)
def parse_command(code)
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 = @context.command_aliases[command])
return [alias_name, arg]
end
# Check visibility
public_method = !!Kernel.instance_method(:public_method).bind_call(@context.main, command) rescue false
private_method = !public_method && !!Kernel.instance_method(:method).bind_call(@context.main, command) rescue false
if ExtendCommandBundle.execute_as_command?(command, public_method: public_method, private_method: private_method)
[command, arg]
end
end
def command?(code)
!!parse_command(code)
end
def configure_io
@ -1148,9 +1166,7 @@ module IRB
false
end
else
# Accept any single-line input for symbol aliases or commands that transform
# args
next true if single_line_command?(code)
next true if command?(code)
_tokens, _opens, terminated = @scanner.check_code_state(code, local_variables: @context.local_variables)
terminated