diff --git a/lib/irb.rb b/lib/irb.rb index 5598885866..0c481ff1dc 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -933,7 +933,7 @@ module IRB def debug_readline(binding) workspace = IRB::WorkSpace.new(binding) - context.workspace = workspace + context.replace_workspace(workspace) context.workspace.load_commands_to_main @line_no += 1 @@ -1269,12 +1269,11 @@ module IRB # Used by the irb command +irb_load+, see IRB@IRB+Sessions for more # information. def suspend_workspace(workspace) - @context.workspace, back_workspace = workspace, @context.workspace - begin - yield back_workspace - ensure - @context.workspace = back_workspace - end + current_workspace = @context.workspace + @context.replace_workspace(workspace) + yield + ensure + @context.replace_workspace current_workspace end # Evaluates the given block using the given +input_method+ as the @@ -1534,7 +1533,7 @@ class Binding if debugger_irb # If we're already in a debugger session, set the workspace and irb_path for the original IRB instance - debugger_irb.context.workspace = workspace + debugger_irb.context.replace_workspace(workspace) debugger_irb.context.irb_path = irb_path # If we've started a debugger session and hit another binding.irb, we don't want to start an IRB session # instead, we want to resume the irb:rdbg session. diff --git a/lib/irb/command/pushws.rb b/lib/irb/command/pushws.rb index fadd10d6aa..888fe466f1 100644 --- a/lib/irb/command/pushws.rb +++ b/lib/irb/command/pushws.rb @@ -15,7 +15,23 @@ module IRB description "Show workspaces." def execute(*obj) - irb_context.workspaces.collect{|ws| ws.main} + inspection_resuls = irb_context.instance_variable_get(:@workspace_stack).map do |ws| + truncated_inspect(ws.main) + end + + puts "[" + inspection_resuls.join(", ") + "]" + end + + private + + def truncated_inspect(obj) + obj_inspection = obj.inspect + + if obj_inspection.size > 20 + obj_inspection = obj_inspection[0, 19] + "...>" + end + + obj_inspection end end diff --git a/lib/irb/context.rb b/lib/irb/context.rb index 9647327037..60dfb9668d 100644 --- a/lib/irb/context.rb +++ b/lib/irb/context.rb @@ -22,10 +22,11 @@ module IRB # +other+:: uses this as InputMethod def initialize(irb, workspace = nil, input_method = nil) @irb = irb + @workspace_stack = [] if workspace - @workspace = workspace + @workspace_stack << workspace else - @workspace = WorkSpace.new + @workspace_stack << WorkSpace.new end @thread = Thread.current @@ -229,15 +230,24 @@ module IRB IRB.conf[:HISTORY_FILE] = hist end + # Workspace in the current context. + def workspace + @workspace_stack.last + end + + # Replace the current workspace with the given +workspace+. + def replace_workspace(workspace) + @workspace_stack.pop + @workspace_stack.push(workspace) + end + # The top-level workspace, see WorkSpace#main def main - @workspace.main + workspace.main end # The toplevel workspace, see #home_workspace attr_reader :workspace_home - # WorkSpace in the current context. - attr_accessor :workspace # The current thread in this context. attr_reader :thread # The current input method. @@ -489,7 +499,7 @@ module IRB # to #last_value. def set_last_value(value) @last_value = value - @workspace.local_variable_set :_, value + workspace.local_variable_set :_, value end # Sets the +mode+ of the prompt in this context. @@ -585,7 +595,7 @@ module IRB if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty? last_proc = proc do - result = @workspace.evaluate(line, @eval_path, line_no) + result = workspace.evaluate(line, @eval_path, line_no) end IRB.conf[:MEASURE_CALLBACKS].inject(last_proc) do |chain, item| _name, callback, arg = item @@ -596,7 +606,7 @@ module IRB end end.call else - result = @workspace.evaluate(line, @eval_path, line_no) + result = workspace.evaluate(line, @eval_path, line_no) end set_last_value(result) diff --git a/lib/irb/ext/change-ws.rb b/lib/irb/ext/change-ws.rb index ec29f7a2bc..87fe03e23d 100644 --- a/lib/irb/ext/change-ws.rb +++ b/lib/irb/ext/change-ws.rb @@ -12,7 +12,7 @@ module IRB # :nodoc: if defined? @home_workspace @home_workspace else - @home_workspace = @workspace + @home_workspace = workspace end end @@ -25,11 +25,11 @@ module IRB # :nodoc: # See IRB::WorkSpace.new for more information. def change_workspace(*_main) if _main.empty? - @workspace = home_workspace + replace_workspace(home_workspace) return main end - @workspace = WorkSpace.new(_main[0]) + replace_workspace(WorkSpace.new(_main[0])) if !(class< 1 + # swap the top two workspaces + previous_workspace, current_workspace = @workspace_stack.pop(2) + @workspace_stack.push current_workspace, previous_workspace + end + else + @workspace_stack.push WorkSpace.new(workspace.binding, _main[0]) + if !(class< 1 end end end diff --git a/test/irb/test_command.rb b/test/irb/test_command.rb index e27fac67b7..d6c8c534f9 100644 --- a/test/irb/test_command.rb +++ b/test/irb/test_command.rb @@ -482,7 +482,8 @@ module TestIRB class CwwsTest < WorkspaceCommandTestCase def test_cwws_returns_the_current_workspace_object out, err = execute_lines( - "cwws.class", + "cwws", + "self.class" ) assert_empty err @@ -493,51 +494,56 @@ module TestIRB class PushwsTest < WorkspaceCommandTestCase def test_pushws_switches_to_new_workspace_and_pushes_the_current_one_to_the_stack out, err = execute_lines( - "pushws #{self.class}::Foo.new\n", - "cwws.class", + "pushws #{self.class}::Foo.new", + "self.class", + "popws", + "self.class" ) assert_empty err - assert_include(out, "#{self.class}::Foo") + + assert_match(/=> #{self.class}::Foo\n/, out) + assert_match(/=> #{self.class}\n$/, out) end def test_pushws_extends_the_new_workspace_with_command_bundle out, err = execute_lines( - "pushws Object.new\n", + "pushws Object.new", "self.singleton_class.ancestors" ) assert_empty err assert_include(out, "IRB::ExtendCommandBundle") end - def test_pushws_prints_help_message_when_no_arg_is_given + def test_pushws_prints_workspace_stack_when_no_arg_is_given out, err = execute_lines( - "pushws\n", + "pushws", ) assert_empty err - assert_match(/No other workspace/, out) + assert_include(out, "[#]") + end + + def test_pushws_without_argument_swaps_the_top_two_workspaces + out, err = execute_lines( + "pushws #{self.class}::Foo.new", + "self.class", + "pushws", + "self.class" + ) + assert_empty err + assert_match(/=> #{self.class}::Foo\n/, out) + assert_match(/=> #{self.class}\n$/, out) end end class WorkspacesTest < WorkspaceCommandTestCase - def test_workspaces_returns_the_array_of_non_main_workspaces + def test_workspaces_returns_the_stack_of_workspaces out, err = execute_lines( "pushws #{self.class}::Foo.new\n", - "workspaces.map { |w| w.class.name }", + "workspaces", ) assert_empty err - # self.class::Foo would be the current workspace - # self.class would be the old workspace that's pushed to the stack - assert_include(out, "=> [\"#{self.class}\"]") - end - - def test_workspaces_returns_empty_array_when_no_workspaces_were_added - out, err = execute_lines( - "workspaces.map(&:to_s)", - ) - - assert_empty err - assert_include(out, "=> []") + assert_match(/\[#, #]\n/, out) end end @@ -557,7 +563,7 @@ module TestIRB "popws\n", ) assert_empty err - assert_match(/workspace stack empty/, out) + assert_match(/\[#\]\n/, out) end end