1
0
Fork 0
mirror of https://github.com/pry/pry.git synced 2022-11-09 12:35:05 -05:00

Refactor and test the "edit" command.

This commit is contained in:
Conrad Irwin 2011-09-10 12:16:25 -07:00
parent 76d1c4ff70
commit 700adfbfb4
2 changed files with 222 additions and 56 deletions

View file

@ -110,74 +110,82 @@ class Pry
command "edit", "Invoke the default editor on a file. Type `edit --help` for more info" do |*args| command "edit", "Invoke the default editor on a file. Type `edit --help` for more info" do |*args|
opts = Slop.parse!(args) do |opt| opts = Slop.parse!(args) do |opt|
opt.banner "Usage: edit [OPTIONS] [FILE]\n" \ opt.banner "Usage: edit [--no-reload|--reload] [--line LINE] [--temp|--ex|FILE[:LINE]]\n" \
"Edit the method FILE in an editor.\nWhen no file given, opens editor with contents of input buffer or previous input and evals after closing." \ "Open a text editor. When no FILE is given, edits the pry input buffer.\n" \
"\nEnsure #{text.bold("Pry.config.editor")} is set to your editor of choice.\n" \ "Ensure #{text.bold("Pry.config.editor")} is set to your editor of choice.\n" \
"e.g: edit sample.rb" "e.g: edit sample.rb"
opt.on :r, "reload", "Eval file content after editing (evals at top level)" opt.on :e, :ex, "Open the file that raised the most recent exception (_ex_.file)"
opt.on :n, "no-reload", "Do not automatically reload the file after editing (only applies to --ex and -t)." opt.on :t, :temp, "Open an empty temporary file"
opt.on :ex, "Open an editor at the line and file that generated the most recent Exception, reloads file after editing." opt.on :l, :line, "Jump to this line in the opened file", true, :as => Integer
opt.on :t, "temp", "Open a temporary file in an editor with contents of input buffer and eval it in current context after closing (does not default to previous input)" opt.on :n, :"no-reload", "Don't automatically reload the edited code"
opt.on :p, "play", "Use the pry `play` command to eval the file content after editing." opt.on :r, :reload, "Reload the edited code immediately (default for ruby files)"
opt.on :l, "line", "Specify line number to jump to in file", true, :as => Integer
opt.on :h, :help, "This message." do opt.on :h, :help, "This message." do
output.puts opt output.puts opt
end end
end end
next if opts.h? next if opts.h?
should_reload_at_top_level = opts[:r] if [opts.ex? || nil, opts.t? || nil, !args.empty? || nil].compact.size > 1
should_reload_locally = false next output.puts "Only one of --ex, --temp, and FILE may be specified"
if opts.ex?
next output.puts "No Exception found." if _pry_.last_exception.nil?
if is_core_rbx_path?(_pry_.last_exception.file)
file_name = rbx_convert_path_to_full(_pry_.last_exception.file)
else
file_name = _pry_.last_exception.file
end
line = _pry_.last_exception.line
next output.puts "Exception has no associated file." if file_name.nil?
next output.puts "Cannot edit exceptions raised in REPL." if Pry.eval_path == file_name
should_reload_at_top_level = opts[:n] ? false : true
elsif opts.t? || args.empty?
file_name = temp_file do |f|
if !eval_string.empty?
f.puts eval_string
elsif !opts.t? && !_pry_.input_array.empty?
f.puts _pry_.input_array[-1]
end
end
line = eval_string.lines.count + 1
should_reload_locally = opts[:n] ? false : true
else
# break up into file:line
/(:(\d+))?$/ =~ File.expand_path(args.first)
# $` is pre-match
file_name, line = [$`, $2]
line = line ? line.to_i : opts[:l].to_i
end end
invoke_editor(file_name, line) # edit of local code, eval'd within pry.
set_file_and_dir_locals(file_name) if !opts.ex? && args.empty?
if opts[:p] content = if !eval_string.empty?
silence_warnings do eval_string
_pry_.input = StringIO.new(File.readlines(file_name).join) elsif !opts.t?
_pry_.input_array.reverse_each.find{ |x| x && x.strip != "" } # No present? in 1.8
end || ""
file_name = temp_file do |f|
f.puts(content)
end end
elsif should_reload_locally line = content.lines.count
silence_warnings do
eval_string.replace(File.read(file_name)) invoke_editor(file_name, line)
if !opts.n?
silence_warnings do
eval_string.replace(File.read(file_name))
end
end end
elsif should_reload_at_top_level
silence_warnings do # don't leak temporary files
TOPLEVEL_BINDING.eval(File.read(file_name), file_name) File.unlink(file_name)
# edit of remote code, eval'd at top-level
else
if opts.ex?
next output.puts "No Exception found." if _pry_.last_exception.nil?
if is_core_rbx_path?(_pry_.last_exception.file)
file_name = rbx_convert_path_to_full(_pry_.last_exception.file)
else
file_name = _pry_.last_exception.file
end
line = _pry_.last_exception.line
next output.puts "Exception has no associated file." if file_name.nil?
next output.puts "Cannot edit exceptions raised in REPL." if Pry.eval_path == file_name
else
# break up into file:line
file_name = File.expand_path(args.first)
line = file_name.sub!(/:(\d+)$/, "") ? $1.to_i : 1
end
line = opts[:l].to_i if opts.l?
invoke_editor(file_name, line)
set_file_and_dir_locals(file_name)
if opts.r? || ((opts.ex? || file_name.end_with?(".rb")) && !opts.n?)
silence_warnings do
TOPLEVEL_BINDING.eval(File.read(file_name), file_name)
end
end end
end end
end end

View file

@ -1,6 +1,164 @@
require 'helper' require 'helper'
describe "Pry::DefaultCommands::Introspection" do describe "Pry::DefaultCommands::Introspection" do
describe "edit" do
before do
@old_editor = Pry.config.editor
@file = nil; @line = nil; @contents = nil
Pry.config.editor = lambda do |file, line|
@file = file; @line = line; @contents = File.read(@file)
":" # The : command does nothing.
end
end
after do
Pry.config.editor = @old_editor
end
describe "with FILE" do
it "should invoke Pry.config.editor with absolutified filenames" do
mock_pry("edit foo.rb")
@file.should == File.expand_path("foo.rb")
mock_pry("edit /tmp/bar.rb")
@file.should == "/tmp/bar.rb"
end
it "should guess the line number from a colon" do
mock_pry("edit /tmp/foo.rb:10")
@line.should == 10
end
it "should use the line number from -l" do
mock_pry("edit -l 10 /tmp/foo.rb")
@line.should == 10
end
it "should not delete the file!" do
mock_pry("edit Rakefile")
File.exist?(@file).should == true
end
describe do
before do
@rand = rand
Pry.config.editor = lambda { |file, line|
File.open(file, 'w') { |f| f << "$rand = #{@rand.inspect}" }
":"
}
end
it "should reload the file if it is a ruby file" do
path = Tempfile.new(["tmp", ".rb"]).path
mock_pry("edit #{path}", "$rand").should =~ /#{@rand}/
File.unlink(path)
end
it "should not reload the file if it is not a ruby file" do
path = Tempfile.new(["tmp", ".py"]).path
mock_pry("edit #{path}", "$rand").should.not =~ /#{@rand}/
File.unlink(path)
end
it "should not reload a ruby file if -n is given" do
path = Tempfile.new(["tmp", ".rb"]).path
mock_pry("edit -n #{path}", "$rand").should.not =~ /#{@rand}/
File.unlink(path)
end
it "should reload a non-ruby file if -r is given" do
path = Tempfile.new(["tmp", ".pryrc"]).path
mock_pry("edit -r #{path}", "$rand").should =~ /#{@rand}/
File.unlink(path)
end
end
end
describe "with --ex" do
before do
@path = Tempfile.new(["tmp", ".rb"]).path
File.open(@path, 'w'){ |f| f << "1\n2\nraise RuntimeError" }
end
after do
File.unlink(@path)
File.unlink("#{@path}c") if File.exists?("#{@path}c") #rbx
end
it "should open the correct file" do
mock_pry("require #{@path.inspect}", "edit --ex")
@file.should == @path
@line.should == 3
end
it "should reload the file" do
Pry.config.editor = lambda {|file, line|
File.open(file, 'w'){|f| f << "FOO = 'BAR'" }
":"
}
mock_pry("require #{@path.inspect}", "edit --ex", "FOO").should =~ /BAR/
end
it "should not reload the file if -n is passed" do
Pry.config.editor = lambda {|file, line|
File.open(file, 'w'){|f| f << "FOO2 = 'BAR'" }
":"
}
mock_pry("require #{@path.inspect}", "edit -n --ex", "FOO2").should.not =~ /BAR/
end
end
describe "without FILE" do
it "should edit the current expression if it's incomplete" do
mock_pry("def a", "edit")
@contents.should == "def a\n"
end
it "should edit the previous expression if the current is empty" do
mock_pry("def a; 2; end", "edit")
@contents.should == "def a; 2; end\n"
end
it "should use a blank file if -t is specified" do
mock_pry("def a; 5; end", "edit -t")
@contents.should == "\n"
end
it "should position the cursor at the end of the expression" do
mock_pry("def a; 2;"," end", "edit")
@line.should == 2
end
it "should delete the temporary file" do
mock_pry("edit")
File.exist?(@file).should == false
end
it "should evaluate the expression" do
Pry.config.editor = lambda {|file, line|
File.open(file, 'w'){|f| f << "'FOO'\n" }
":"
}
mock_pry("edit").should =~ /FOO/
end
it "should not evaluate the expression with -n" do
Pry.config.editor = lambda {|file, line|
File.open(file, 'w'){|f| f << "'FOO'\n" }
":"
}
mock_pry("edit -n").should.not =~ /FOO/
end
end
end
describe "show-method" do describe "show-method" do
it 'should output a method\'s source' do it 'should output a method\'s source' do
str_output = StringIO.new str_output = StringIO.new