Skip to content

Commit

Permalink
Command is not a method
Browse files Browse the repository at this point in the history
  • Loading branch information
tompng committed Dec 27, 2023
1 parent c2f90c4 commit 37f415f
Show file tree
Hide file tree
Showing 32 changed files with 227 additions and 288 deletions.
52 changes: 33 additions & 19 deletions lib/irb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@ class 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
Expand All @@ -935,7 +935,7 @@ def debug_break
def debug_readline(binding)
workspace = IRB::WorkSpace.new(binding)
context.workspace = workspace
context.workspace.load_commands_to_main
context.workspace.load_helper_methods_to_main
@line_no += 1

# When users run:
Expand Down Expand Up @@ -1002,7 +1002,7 @@ def eval_input
return statement.code
end

@context.evaluate(statement.evaluable_code, line_no)
statement.execute(@context, line_no)

if @context.echo? && !statement.suppresses_echo?
if statement.is_assignment?
Expand Down Expand Up @@ -1058,9 +1058,7 @@ def readmultiline
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
Expand All @@ -1086,23 +1084,40 @@ def each_top_level_statement

def build_statement(code)
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_class, arg = parse_command(code))
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)
ASSIGN_OPERATORS = %w[= += -= *= /= %= **= &= |= &&= ||= ^= <<= >>=]
COMMAND_LIKE_ASSIGN_REGEXP = /\A[a-z_]\w* #{Regexp.union(ASSIGN_OPERATORS)}( |$)/

def parse_command(code)
command_or_alias, arg = code.strip.split(/\s/, 2)
return unless code.lines.size == 1 && command_or_alias
return if COMMAND_LIKE_ASSIGN_REGEXP.match?(code)

# Check visibility
local_variable = @context.local_variables.include?(command_or_alias.to_sym)
public_method = !!@context.main.public_method(command_or_alias) rescue false
private_method = !public_method && !!@context.main.method(command_or_alias) rescue false
return unless ExtendCommandBundle.execute_as_command?(
command_or_alias,
public_method: public_method,
private_method: private_method,
local_variable: local_variable
)

command_name = @context.command_aliases[command_or_alias.to_sym] || command_or_alias
command_class = ExtendCommandBundle.load_command(command_name)
[command_class, arg] if command_class
end

def command?(code)
!!parse_command(code)
end

def configure_io
Expand All @@ -1120,8 +1135,7 @@ def configure_io
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
Expand Down
8 changes: 2 additions & 6 deletions lib/irb/cmd/backtrace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,8 @@ module IRB

module ExtendCommand
class Backtrace < DebugCommand
def self.transform_args(args)
args&.dump
end

def execute(*args)
super(pre_cmds: ["backtrace", *args].join(" "))
def execute(arg)
execute_debug_command(pre_cmds: "backtrace #{arg}")
end
end
end
Expand Down
8 changes: 2 additions & 6 deletions lib/irb/cmd/break.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,8 @@ module IRB

module ExtendCommand
class Break < DebugCommand
def self.transform_args(args)
args&.dump
end

def execute(args = nil)
super(pre_cmds: "break #{args}")
def execute(arg)
execute_debug_command(pre_cmds: "break #{arg}")
end
end
end
Expand Down
8 changes: 2 additions & 6 deletions lib/irb/cmd/catch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,8 @@ module IRB

module ExtendCommand
class Catch < DebugCommand
def self.transform_args(args)
args&.dump
end

def execute(*args)
super(pre_cmds: ["catch", *args].join(" "))
def execute(arg)
execute_debug_command(pre_cmds: "catch #{arg}")
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/irb/cmd/chws.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class CurrentWorkingWorkspace < Nop
category "Workspace"
description "Show the current workspace."

def execute(*obj)
def execute_as_ruby(*obj)
irb_context.main
end
end
Expand All @@ -25,7 +25,7 @@ class ChangeWorkspace < Nop
category "Workspace"
description "Change the current workspace to an object."

def execute(*obj)
def execute_as_ruby(*obj)
irb_context.change_workspace(*obj)
irb_context.main
end
Expand Down
18 changes: 18 additions & 0 deletions lib/irb/cmd/context.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

require_relative "nop"

module IRB
module ExtendCommand
class Context < Nop
category "IRB"
description "Displays current configuration."

def execute(arg)
# This command just displays the configuration.
# Modifying the configuration is achieved by sending a message to IRB.conf.
Pager.page_content(IRB.CurrentContext.inspect)
end
end
end
end
4 changes: 2 additions & 2 deletions lib/irb/cmd/continue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ module IRB

module ExtendCommand
class Continue < DebugCommand
def execute(*args)
super(do_cmds: ["continue", *args].join(" "))
def execute_as_ruby(*args)
execute_debug_command(do_cmds: ["continue", *args].join(" "))
end
end
end
Expand Down
6 changes: 5 additions & 1 deletion lib/irb/cmd/debug.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ class Debug < Nop
binding.method(:irb).source_location.first,
].map { |file| /\A#{Regexp.escape(file)}:\d+:in `irb'\z/ }

def execute(pre_cmds: nil, do_cmds: nil)
def execute(_arg)
execute_debug_command
end

def execute_debug_command(pre_cmds: nil, do_cmds: nil)
if irb_context.with_debugger
# If IRB is already running with a debug session, throw the command and IRB.debug_readline will pass it to the debugger.
if cmd = pre_cmds || do_cmds
Expand Down
4 changes: 2 additions & 2 deletions lib/irb/cmd/delete.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ module IRB

module ExtendCommand
class Delete < DebugCommand
def execute(*args)
super(pre_cmds: ["delete", *args].join(" "))
def execute_as_ruby(*arg)
execute_debug_command(pre_cmds: ["delete", *args].join(" "))
end
end
end
Expand Down
15 changes: 2 additions & 13 deletions lib/irb/cmd/edit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,8 @@ class Edit < Nop
category "Misc"
description 'Open a file with the editor command defined with `ENV["VISUAL"]` or `ENV["EDITOR"]`.'

class << self
def transform_args(args)
# Return a string literal as is for backward compatibility
if args.nil? || args.empty? || string_literal?(args)
args
else # Otherwise, consider the input as a String for convenience
args.strip.dump
end
end
end

def execute(*args)
path = args.first
def execute(arg)
path = unwrap_string_literal(arg)

if path.nil? && (irb_path = @irb_context.irb_path)
path = irb_path
Expand Down
16 changes: 16 additions & 0 deletions lib/irb/cmd/exit.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

require_relative "nop"

module IRB
module ExtendCommand
class Exit < Nop
category "IRB"
description "Quits the current irb context."

def execute(arg)
IRB.CurrentContext.exit(arg.to_i)
end
end
end
end
4 changes: 2 additions & 2 deletions lib/irb/cmd/finish.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ module IRB

module ExtendCommand
class Finish < DebugCommand
def execute(*args)
super(do_cmds: ["finish", *args].join(" "))
def execute_as_ruby(*args)
execute_debug_command(do_cmds: ["finish", *args].join(" "))
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/irb/cmd/help.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Help < ShowDoc
For command help, please use `show_cmds` for now.
MSG

def execute(*names)
def execute(arg)
warn DEPRECATION_MESSAGE
super
end
Expand Down
10 changes: 4 additions & 6 deletions lib/irb/cmd/history.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ class History < Nop
category "IRB"
description "Shows the input history. `-g [query]` or `-G [query]` allows you to filter the output."

def self.transform_args(args)
match = args&.match(/(-g|-G)\s+(?<grep>.+)\s*\n\z/)
return unless match
def execute(arg)

"grep: #{Regexp.new(match[:grep]).inspect}"
end
if (match = arg&.match(/(-g|-G)\s+(?<grep>.+)\s*\n\z/))
grep = Regexp.new(match[:grep])
end

def execute(grep: nil)
formatted_inputs = irb_context.io.class::HISTORY.each_with_index.reverse_each.filter_map do |input, index|
next if grep && !input.match?(grep)

Expand Down
8 changes: 2 additions & 6 deletions lib/irb/cmd/info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,8 @@ module IRB

module ExtendCommand
class Info < DebugCommand
def self.transform_args(args)
args&.dump
end

def execute(*args)
super(pre_cmds: ["info", *args].join(" "))
def execute(arg)
execute_debug_command(pre_cmds: "info #{arg}")
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/irb/cmd/irb_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class IrbInfo < Nop
category "IRB"
description "Show information about IRB."

def execute
def execute(_arg)
str = "Ruby version: #{RUBY_VERSION}\n"
str += "IRB version: #{IRB.version}\n"
str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
Expand Down
6 changes: 3 additions & 3 deletions lib/irb/cmd/load.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Load < LoaderCommand
category "IRB"
description "Load a Ruby file."

def execute(file_name = nil, priv = nil)
def execute_as_ruby(file_name = nil, priv = nil)
raise_cmd_argument_error unless file_name
irb_load(file_name, priv)
end
Expand All @@ -32,7 +32,7 @@ def execute(file_name = nil, priv = nil)
class Require < LoaderCommand
category "IRB"
description "Require a Ruby file."
def execute(file_name = nil)
def execute_as_ruby(file_name = nil)
raise_cmd_argument_error unless file_name

rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?")
Expand Down Expand Up @@ -65,7 +65,7 @@ class Source < LoaderCommand
category "IRB"
description "Loads a given file in the current session."

def execute(file_name = nil)
def execute_as_ruby(file_name = nil)
raise_cmd_argument_error unless file_name

source_file(file_name)
Expand Down
18 changes: 9 additions & 9 deletions lib/irb/cmd/ls.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ class Ls < Nop
category "Context"
description "Show methods, constants, and variables. `-g [query]` or `-G [query]` allows you to filter out the output."

def self.transform_args(args)
if match = args&.match(/\A(?<args>.+\s|)(-g|-G)\s+(?<grep>[^\s]+)\s*\n\z/)
args = match[:args]
"#{args}#{',' unless args.chomp.empty?} grep: /#{match[:grep]}/"
def execute(arg)
if match = arg&.match(/\A(?<args>.+\s|)(-g|-G)\s+(?<grep>.+)$/)
arg = @irb_context.workspace.binding.eval(match[:args])
grep = Regexp.new(match[:grep])
(arg,), _kwargs = ruby_args(arg)
else
args
(arg,), kwargs = ruby_args(arg)
grep = kwargs[:grep]
end
end

def execute(*arg, grep: nil)
o = Output.new(grep: grep)

obj = arg.empty? ? irb_context.workspace.main : arg.first
locals = arg.empty? ? irb_context.workspace.binding.local_variables : []
obj = arg.nil? ? irb_context.workspace.main : arg
locals = arg.nil? ? irb_context.workspace.binding.local_variables : []
klass = (obj.class == Class || obj.class == Module ? obj : obj.class)

o.dump("constants", obj.constants) if obj.respond_to?(:constants)
Expand Down
Loading

0 comments on commit 37f415f

Please sign in to comment.