Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce cd command #971

Merged
merged 1 commit into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions lib/irb/command/cd.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# frozen_string_literal: true

module IRB
module Command
class CD < Base
category "Workspace"
description "Move into the given object or leave the current context."

help_message(<<~HELP)
Usage: cd ([target]|..)

IRB uses a stack of workspaces to keep track of context(s), with `pushws` and `popws` commands to manipulate the stack.
The `cd` command is an attempt to simplify the operation and will be subject to change.

When given:
- an object, cd will use that object as the new context by pushing it onto the workspace stack.
- "..", cd will leave the current context by popping the top workspace off the stack.
- no arguments, cd will move to the top workspace on the stack by popping off all workspaces.

Examples:

cd Foo
cd Foo.new
cd @ivar
cd ..
cd
HELP

def execute(arg)
case arg

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the cd - case? As in, go to the previous but also push the current back on the stack.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike the rest of the PR that's built on top of existing features, this requires adding new internal API and will not work nicely with other workspace commands like pushws...etc. I implemented it anyway but I'll let other maintainers decide if it's worth the extra complexity.

when ".."
irb_context.pop_workspace
when ""
# TODO: decide what workspace commands should be kept, and underlying APIs should look like,
# and perhaps add a new API to clear the workspace stack.
prev_workspace = irb_context.pop_workspace
while prev_workspace
prev_workspace = irb_context.pop_workspace
end
else
begin
obj = eval(arg, irb_context.workspace.binding)
irb_context.push_workspace(obj)
rescue StandardError => e
warn "Error: #{e}"
end
end
end
end
end
end
3 changes: 3 additions & 0 deletions lib/irb/default_commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require_relative "command/backtrace"
require_relative "command/break"
require_relative "command/catch"
require_relative "command/cd"
require_relative "command/chws"
require_relative "command/context"
require_relative "command/continue"
Expand Down Expand Up @@ -240,6 +241,8 @@ def load_command(command)
_register_with_aliases(:irb_disable_irb, Command::DisableIrb,
[:disable_irb, NO_OVERRIDE]
)

register(:cd, Command::CD)
end

ExtendCommand = Command
Expand Down
65 changes: 65 additions & 0 deletions test/irb/command/test_cd.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require "tempfile"
require_relative "../helper"

module TestIRB
class CDTest < IntegrationTestCase
def setup
super

write_ruby <<~'RUBY'
class Foo
class Bar
def bar
"this is bar"
end
end

def foo
"this is foo"
end
end

binding.irb
RUBY
end

def test_cd
out = run_ruby_file do
type "cd Foo"
type "ls"
type "cd Bar"
type "ls"
type "cd .."
type "exit"
end

assert_match(/irb\(Foo\):002>/, out)
assert_match(/Foo#methods: foo/, out)
assert_match(/irb\(Foo::Bar\):004>/, out)
assert_match(/Bar#methods: bar/, out)
assert_match(/irb\(Foo\):006>/, out)
end

def test_cd_moves_top_level_with_no_args
out = run_ruby_file do
type "cd Foo"
type "cd Bar"
type "cd"
type "exit"
end

assert_match(/irb\(Foo::Bar\):003>/, out)
assert_match(/irb\(main\):004>/, out)
end

def test_cd_with_error
out = run_ruby_file do
type "cd Baz"
type "exit"
end

assert_match(/Error: uninitialized constant Baz/, out)
assert_match(/irb\(main\):002>/, out) # the context should not change
end
end
end
Loading