diff --git a/lib/pry.rb b/lib/pry.rb index fb9cd522..3498dcee 100644 --- a/lib/pry.rb +++ b/lib/pry.rb @@ -120,6 +120,10 @@ class Pry end end + # Store the current working directory. This allows show-source etc. to work if + # your process has changed directory since boot. [Issue #675] + INITIAL_PWD = Dir.pwd + # As a REPL, we often want to catch any unexpected exceptions that may have # been raised; however we don't want to go overboard and prevent the user # from exiting Pry when they want to. diff --git a/lib/pry/cli.rb b/lib/pry/cli.rb index 1e09313a..bd096ecd 100644 --- a/lib/pry/cli.rb +++ b/lib/pry/cli.rb @@ -57,12 +57,18 @@ class Pry end def parse_options(args=ARGV.dup) - raise NoOptionsError, "No command line options defined! Use Pry::CLI.add_options to add command line options." if !options + unless options + raise NoOptionsError, "No command line options defined! Use Pry::CLI.add_options to add command line options." + end self.input_args = args opts = Slop.parse!(args, :help => true, :multiple_switches => false, &options) - option_processors.each { |processor| processor.call(opts) } if option_processors # option processors are optional + + # Option processors are optional. + if option_processors + option_processors.each { |processor| processor.call(opts) } + end self end @@ -139,8 +145,12 @@ Copyright (c) 2011 John Mair (banisterfiend) Pry.config.requires << file end - on :I, "Add a path to the $LOAD_PATH", :argument => true do |path| - $LOAD_PATH << path + on :I, "Add a path to the $LOAD_PATH", :argument => true, :as => Array, :delimiter => ":" do |load_path| + load_path.map! do |path| + /\A\.\// =~ path ? path : File.expand_path(path) + end + + $LOAD_PATH.unshift(*load_path) end on :v, :version, "Display the Pry version" do diff --git a/lib/pry/code.rb b/lib/pry/code.rb index aeaa8803..eed68852 100644 --- a/lib/pry/code.rb +++ b/lib/pry/code.rb @@ -35,20 +35,23 @@ class Pry # @param [String] fn The name of a file, or "(pry)". # @param [Symbol] code_type The type of code the file contains. # @return [Code] - def from_file(fn, code_type = nil) + def from_file(fn, code_type=type_from_filename(fn)) + if fn == Pry.eval_path - f = Pry.line_buffer.drop(1) + new(Pry.line_buffer.drop(1), 1, code_type) else - if File.readable?(fn) - f = File.open(fn, 'r') - code_type ||= type_from_filename(fn) - else + abs_path = [File.expand_path(fn, Dir.pwd), + File.expand_path(fn, Pry::INITIAL_PWD) + ].detect{ |abs_path| File.readable?(abs_path) } + + unless abs_path raise MethodSource::SourceNotFoundError, "Cannot open #{fn.inspect} for reading." end + + File.open(abs_path, 'r') do |f| + new(f, 1, code_type) + end end - new(f, 1, code_type || :ruby) - ensure - f.close if f.respond_to?(:close) end # Instantiate a `Code` object containing code extracted from a @@ -85,8 +88,9 @@ class Pry # unknown. # # @param [String] filename + # @param [Symbol] default (:ruby) the file type to assume if none could be detected # @return [Symbol, nil] - def type_from_filename(filename) + def type_from_filename(filename, default=:ruby) map = { %w(.c .h) => :c, %w(.cpp .hpp .cc .h cxx) => :cpp, @@ -108,7 +112,7 @@ class Pry k.any? { |ext| ext == File.extname(filename) } end - type + type || default end end diff --git a/lib/pry/command.rb b/lib/pry/command.rb index 05e5159c..30890d00 100644 --- a/lib/pry/command.rb +++ b/lib/pry/command.rb @@ -1,9 +1,12 @@ +require 'pry/helpers/documentation_helpers' + class Pry # The super-class of all commands, new commands should be created by calling # {Pry::CommandSet#command} which creates a BlockCommand or {Pry::CommandSet#create_command} # which creates a ClassCommand. Please don't use this class directly. class Command + extend Helpers::DocumentationHelpers # represents a void return value for a command VOID_VALUE = Object.new @@ -49,6 +52,14 @@ class Pry def block @block || instance_method(:process) && instance_method(:process) end + + def source + strip_leading_whitespace(block.source) + end + + def source_location + block.source_location + end end # Make those properties accessible to instances @@ -58,6 +69,8 @@ class Pry def block; self.class.block; end def command_options; self.class.options; end def command_name; command_options[:listing]; end + def source; self.class.source; end + def source_location; self.class.source_location; end class << self def name diff --git a/lib/pry/commands/cd.rb b/lib/pry/commands/cd.rb index cb6ec1c7..1d4a8055 100644 --- a/lib/pry/commands/cd.rb +++ b/lib/pry/commands/cd.rb @@ -20,64 +20,9 @@ class Pry BANNER def process - # Extract command arguments. Delete blank arguments like " ", but - # don't delete empty strings like "". - path = arg_string.split(/\//).delete_if { |a| a =~ /\A\s+\z/ } - stack = _pry_.binding_stack.dup - old_stack = state.old_stack || [] - - # Special case when we only get a single "/", return to root. - if path.empty? - state.old_stack = stack.dup unless old_stack.empty? - stack = [stack.first] - end - - path.each_with_index do |context, i| - begin - case context.chomp - when "" - state.old_stack = stack.dup - stack = [stack.first] - when "::" - state.old_stack = stack.dup - stack.push(TOPLEVEL_BINDING) - when "." - next - when ".." - unless stack.size == 1 - # Don't rewrite old_stack if we're in complex expression - # (e.g.: `cd 1/2/3/../4). - state.old_stack = stack.dup if path.first == ".." - stack.pop - end - when "-" - unless old_stack.empty? - # Interchange current stack and old stack with each other. - stack, state.old_stack = state.old_stack, stack - end - else - state.old_stack = stack.dup if i == 0 - stack.push(Pry.binding_for(stack.last.eval(context))) - end - - rescue RescuableException => e - # Restore old stack to its initial values. - state.old_stack = old_stack - - msg = [ - "Bad object path: #{arg_string}.", - "Failed trying to resolve: #{context}.", - e.inspect - ].join(' ') - - CommandError.new(msg).tap do |err| - err.set_backtrace e.backtrace - raise err - end - end - end - - _pry_.binding_stack = stack + stack, old_stack = context_from_object_path(arg_string, _pry_, state.old_stack||[]) + state.old_stack = old_stack + _pry_.binding_stack = stack unless stack.nil? end end end diff --git a/lib/pry/commands/show_command.rb b/lib/pry/commands/show_command.rb index b65e371a..c701bc4a 100644 --- a/lib/pry/commands/show_command.rb +++ b/lib/pry/commands/show_command.rb @@ -8,40 +8,15 @@ class Pry opts = Slop.parse!(args) do |opt| opt.banner unindent <<-USAGE - Usage: show-command [OPTIONS] [CMD] - Show the source for command CMD. - e.g: show-command show-method + NOTE: show-command is DEPRACTED. Use show-source [command_name] instead. USAGE - opt.on :l, "line-numbers", "Show line numbers." - opt.on :f, :flood, "Do not use a pager to view text longer than one screen." opt.on :h, :help, "This message." do output.puts opt.help end end - return if opts.present?(:help) - - command_name = args.shift - if !command_name - raise CommandError, "You must provide a command name." - end - - if find_command(command_name) - block = Pry::Method.new(find_command(command_name).block) - - return unless block.source - set_file_and_dir_locals(block.source_file) - - output.puts make_header(block) - output.puts - - code = Pry::Code.from_method(block).with_line_numbers(opts.present?(:'line-numbers')).to_s - - render_output(code, opts) - else - raise CommandError, "No such command: #{command_name}." - end + render_output opts.banner end end end diff --git a/lib/pry/commands/show_doc.rb b/lib/pry/commands/show_doc.rb index 73b4c05d..730e91af 100644 --- a/lib/pry/commands/show_doc.rb +++ b/lib/pry/commands/show_doc.rb @@ -111,6 +111,24 @@ class Pry doc end + def process_command + name = args.join(" ").gsub(/\"/,"") + command = find_command(name) + + doc = command.new.help + doc = strip_leading_whitespace(doc) + + file_name, line = command.source_location + set_file_and_dir_locals(file_name) + + + result = "" + result << "\n#{Pry::Helpers::Text.bold('From:')} #{file_name} @ line #{line}:\n" + result << "#{Pry::Helpers::Text.bold('Number of lines:')} #{doc.lines.count}\n\n" + result << doc + result << "\n" + end + def module_start_line(mod, candidate=0) if opts.present?(:'base-one') 1 diff --git a/lib/pry/commands/show_source.rb b/lib/pry/commands/show_source.rb index 11dd60b3..3e53ed64 100644 --- a/lib/pry/commands/show_source.rb +++ b/lib/pry/commands/show_source.rb @@ -111,6 +111,23 @@ class Pry result end + def process_command + name = args.join(" ").gsub(/\"/,"") + command = find_command(name) + + file_name, line = command.source_location + set_file_and_dir_locals(file_name) + + code = Pry::Code.new(command.source, line).with_line_numbers(use_line_numbers?).to_s + + result = "" + result << "\n#{Pry::Helpers::Text.bold('From:')} #{file_name} @ line #{line}:\n" + result << "#{Pry::Helpers::Text.bold('Number of lines:')} #{code.lines.count}\n\n" + result << "\n" + + result << code + end + def use_line_numbers? opts.present?(:b) || opts.present?(:l) end diff --git a/lib/pry/completion.rb b/lib/pry/completion.rb index 9d7c913e..ff33bc41 100644 --- a/lib/pry/completion.rb +++ b/lib/pry/completion.rb @@ -41,36 +41,50 @@ class Pry # Return a new completion proc for use by Readline. # @param [Binding] target The current binding context. # @param [Array] commands The array of Pry commands. - def self.build_completion_proc(target, commands=[""]) + def self.build_completion_proc(target, pry=nil, commands=[""]) proc do |input| + + # if there are multiple contexts e.g. cd 1/2/3 + # get new target for 1/2 and find candidates for 3 + path, input = build_path(input) + + unless path.call.empty? + target, _ = Pry::Helpers::BaseHelpers.context_from_object_path(path.call, pry) + target = target.last + end + begin bind = target case input + + + # Complete stdlib symbols + when /^(\/[^\/]*\/)\.([^.]*)$/ # Regexp receiver = $1 message = Regexp.quote($2) - candidates = Regexp.instance_methods.collect{|m| m.to_s} - select_message(receiver, message, candidates) + candidates = Regexp.instance_methods.collect(&:to_s) + select_message(path, receiver, message, candidates) when /^([^\]]*\])\.([^.]*)$/ # Array receiver = $1 message = Regexp.quote($2) - candidates = Array.instance_methods.collect{|m| m.to_s} - select_message(receiver, message, candidates) + candidates = Array.instance_methods.collect(&:to_s) + select_message(path, receiver, message, candidates) when /^([^\}]*\})\.([^.]*)$/ # Proc or Hash receiver = $1 message = Regexp.quote($2) - candidates = Proc.instance_methods.collect{|m| m.to_s} - candidates |= Hash.instance_methods.collect{|m| m.to_s} - select_message(receiver, message, candidates) + candidates = Proc.instance_methods.collect(&:to_s) + candidates |= Hash.instance_methods.collect(&:to_s) + select_message(path, receiver, message, candidates) when /^(:[^:.]*)$/ # Symbol @@ -86,16 +100,32 @@ class Pry when /^::([A-Z][^:\.\(]*)$/ # Absolute Constant or class methods receiver = $1 - candidates = Object.constants.collect{|m| m.to_s} + candidates = Object.constants.collect(&:to_s) candidates.grep(/^#{receiver}/).collect{|e| "::" + e} - when /^([A-Z].*)::([^:.]*)$/ + + # Complete target symbols + + when /^([A-Z][A-Za-z0-9]*)$/ + # Constant + message = $1 + + begin + context = target.eval("self") + context = context.class unless context.respond_to? :constants + candidates = context.constants.collect(&:to_s) + rescue + candidates = [] + end + candidates = candidates.grep(/^#{message}/).collect(&path) + + when /^([A-Z].*)::([^:.]*)$/ # Constant or class methods receiver = $1 message = Regexp.quote($2) begin - candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind) - candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind) + candidates = eval("#{receiver}.constants.collect(&:to_s)", bind) + candidates |= eval("#{receiver}.methods.collect(&:to_s)", bind) rescue RescuableException candidates = [] end @@ -106,8 +136,8 @@ class Pry receiver = $1 message = Regexp.quote($2) - candidates = Symbol.instance_methods.collect{|m| m.to_s} - select_message(receiver, message, candidates) + candidates = Symbol.instance_methods.collect(&:to_s) + select_message(path, receiver, message, candidates) when /^(-?(0[dbo])?[0-9_]+(\.[0-9_]+)?([eE]-?[0-9]+)?)\.([^.]*)$/ # Numeric @@ -115,42 +145,43 @@ class Pry message = Regexp.quote($5) begin - candidates = eval(receiver, bind).methods.collect{|m| m.to_s} + candidates = eval(receiver, bind).methods.collect(&:to_s) rescue RescuableException candidates = [] end - select_message(receiver, message, candidates) + select_message(path, receiver, message, candidates) - when /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/ + when /^(-?0x[0-9a-fA-F_]+)\.([^.]*)$/# # Numeric(0xFFFF) receiver = $1 message = Regexp.quote($2) begin - candidates = eval(receiver, bind).methods.collect{|m| m.to_s} + candidates = eval(receiver, bind).methods.collect(&:to_s) rescue RescuableException candidates = [] end - select_message(receiver, message, candidates) + select_message(path, receiver, message, candidates) when /^(\$[^.]*)$/ + # Global variables regmessage = Regexp.new(Regexp.quote($1)) - candidates = global_variables.collect{|m| m.to_s}.grep(regmessage) + candidates = global_variables.collect(&:to_s).grep(regmessage) - when /^([^."].*)\.([^.]*)$/ - # variable + when /^([^."].*)\.([^.]*)$/ + # Variable receiver = $1 message = Regexp.quote($2) - gv = eval("global_variables", bind).collect{|m| m.to_s} - lv = eval("local_variables", bind).collect{|m| m.to_s} - cv = eval("self.class.constants", bind).collect{|m| m.to_s} + gv = eval("global_variables", bind).collect(&:to_s) + lv = eval("local_variables", bind).collect(&:to_s) + cv = eval("self.class.constants", bind).collect(&:to_s) if (gv | lv | cv).include?(receiver) or /^[A-Z]/ =~ receiver && /\./ !~ receiver # foo.func and foo is local var. OR # Foo::Bar.func begin - candidates = eval("#{receiver}.methods", bind).collect{|m| m.to_s} + candidates = eval("#{receiver}.methods", bind).collect(&:to_s) rescue RescuableException candidates = [] end @@ -169,35 +200,35 @@ class Pry # jruby doesn't always provide #instance_methods() on each # object. if m.respond_to?(:instance_methods) - candidates.concat m.instance_methods(false).collect{|x| x.to_s} + candidates.concat m.instance_methods(false).collect(&:to_s) end } candidates.sort! candidates.uniq! end - select_message(receiver, message, candidates) + select_message(path, receiver, message, candidates) when /^\.([^.]*)$/ - # unknown(maybe String) - + # Unknown(maybe String) receiver = "" message = Regexp.quote($1) - candidates = String.instance_methods(true).collect{|m| m.to_s} - select_message(receiver, message, candidates) + candidates = String.instance_methods(true).collect(&:to_s) + select_message(path, receiver, message, candidates) else + candidates = eval( "methods | private_methods | local_variables | " \ "self.class.constants | instance_variables", bind - ).collect{|m| m.to_s} + ).collect(&:to_s) if eval("respond_to?(:class_variables)", bind) - candidates += eval("class_variables", bind).collect { |m| m.to_s } + candidates += eval("class_variables", bind).collect(&:to_s) end - - (candidates|ReservedWords|commands).grep(/^#{Regexp.quote(input)}/) + candidates = (candidates|ReservedWords|commands).grep(/^#{Regexp.quote(input)}/) + candidates.collect(&path) end rescue RescuableException [] @@ -205,17 +236,38 @@ class Pry end end - def self.select_message(receiver, message, candidates) + def self.select_message(path, receiver, message, candidates) candidates.grep(/^#{message}/).collect do |e| - case e - when /^[a-zA-Z_]/ - receiver + "." + e - when /^[0-9]/ - when *Operators - #receiver + " " + e - end + case e + when /^[a-zA-Z_]/ + path.call(receiver + "." + e) + when /^[0-9]/ + when *Operators + #receiver + " " + e + end end end + + # build_path seperates the input into two parts: path and input. + # input is the partial string that should be completed + # path is a proc that takes an input and builds a full path. + def self.build_path(input) + + # check to see if the input is a regex + return proc {|input| input.to_s }, input if input[/\/\./] + + trailing_slash = input.end_with?('/') + contexts = input.chomp('/').split(/\//) + input = contexts[-1] + + path = proc do |input| + p = contexts[0..-2].push(input).join('/') + p += '/' if trailing_slash && !input.nil? + p + end + + return path, input + end end end diff --git a/lib/pry/helpers/base_helpers.rb b/lib/pry/helpers/base_helpers.rb index a6d63b37..7c6d83c3 100644 --- a/lib/pry/helpers/base_helpers.rb +++ b/lib/pry/helpers/base_helpers.rb @@ -70,7 +70,7 @@ class Pry end def use_ansi_codes? - windows_ansi? || ENV['TERM'] && ENV['TERM'] != "dumb" + windows_ansi? || ENV['TERM'] && ENV['TERM'] != "dumb" end def colorize_code(code) @@ -123,16 +123,16 @@ class Pry # simple_pager. Also do not page if Pry.pager is falsey def stagger_output(text, out = nil) out ||= case - when respond_to?(:output) - # Mixin. - output - when Pry.respond_to?(:output) - # Parent. - Pry.output - else - # Sys. - $stdout - end + when respond_to?(:output) + # Mixin. + output + when Pry.respond_to?(:output) + # Parent. + Pry.output + else + # Sys. + $stdout + end if text.lines.count < Pry::Pager.page_size || !Pry.pager out.puts text @@ -143,6 +143,73 @@ class Pry Pry::Pager.page(text, :simple) rescue Errno::EPIPE end + + # @param [String] arg_string The object path expressed as a string. + # @param [Pry] _pry_ The relevant Pry instance. + # @param [Array] old_stack The state of the old binding stack + # @return [Array, Array>] An array + # containing two elements: The new `binding_stack` and the old `binding_stack`. + def context_from_object_path(arg_string, _pry_=nil, old_stack=[]) + + # Extract command arguments. Delete blank arguments like " ", but + # don't delete empty strings like "". + path = arg_string.split(/\//).delete_if { |a| a =~ /\A\s+\z/ } + stack = _pry_.binding_stack.dup + state_old_stack = old_stack + + # Special case when we only get a single "/", return to root. + if path.empty? + state_old_stack = stack.dup unless old_stack.empty? + stack = [stack.first] + end + + path.each_with_index do |context, i| + begin + case context.chomp + when "" + state_old_stack = stack.dup + stack = [stack.first] + when "::" + state_old_stack = stack.dup + stack.push(TOPLEVEL_BINDING) + when "." + next + when ".." + unless stack.size == 1 + # Don't rewrite old_stack if we're in complex expression + # (e.g.: `cd 1/2/3/../4). + state_old_stack = stack.dup if path.first == ".." + stack.pop + end + when "-" + unless old_stack.empty? + # Interchange current stack and old stack with each other. + stack, state_old_stack = state_old_stack, stack + end + else + state_old_stack = stack.dup if i == 0 + stack.push(Pry.binding_for(stack.last.eval(context))) + end + + rescue RescuableException => e + # Restore old stack to its initial values. + state_old_stack = old_stack + + msg = [ + "Bad object path: #{arg_string}.", + "Failed trying to resolve: #{context}.", + e.inspect + ].join(' ') + + CommandError.new(msg).tap do |err| + err.set_backtrace e.backtrace + raise err + end + end + end + return stack, state_old_stack + end + end end end diff --git a/lib/pry/helpers/module_introspection_helpers.rb b/lib/pry/helpers/module_introspection_helpers.rb index fbaf0395..96c374ba 100644 --- a/lib/pry/helpers/module_introspection_helpers.rb +++ b/lib/pry/helpers/module_introspection_helpers.rb @@ -31,13 +31,23 @@ class Pry :method elsif Pry::WrappedModule.from_str(input, target) :module + elsif target.eval("defined? #{input} ") =~ /variable|constant/ + :variable_or_constant + elsif find_command(input) + :command + else + :unknown + end + rescue SyntaxError + if find_command(input) + :command else :unknown end end def process(name) - input = args.join(" ") + input = args.join(" ").gsub(/\"/,"") type = input_type(input, target) code_or_doc = case type @@ -49,8 +59,12 @@ class Pry process_method when :module process_module + when :variable_or_constant + process_variable_or_constant + when :command + process_command else - command_error("method or module for '#{input}' could not be found or derived", false) + command_error("method/module/command for '#{input}' could not be found or derived", false) end render_output(code_or_doc, opts) @@ -79,6 +93,14 @@ class Pry Pry::Method.from_binding(target) end + def process_variable_or_constant + name = args.first + object = target.eval(name) + + @module_object = Pry::WrappedModule(object.class) + process_module + end + def module_start_line(mod, candidate_rank=0) if opts.present?(:'base-one') 1 diff --git a/lib/pry/pry_instance.rb b/lib/pry/pry_instance.rb index 60b7e41e..94dff947 100644 --- a/lib/pry/pry_instance.rb +++ b/lib/pry/pry_instance.rb @@ -365,7 +365,7 @@ class Pry @indent.reset if eval_string.empty? current_prompt = select_prompt(eval_string, target) - completion_proc = Pry::InputCompleter.build_completion_proc(target, + completion_proc = Pry::InputCompleter.build_completion_proc(target, self, instance_eval(&custom_completions)) diff --git a/pry.gemspec b/pry.gemspec index c5c2f102..87f1ccef 100644 --- a/pry.gemspec +++ b/pry.gemspec @@ -6,11 +6,11 @@ Gem::Specification.new do |s| s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["John Mair (banisterfiend)", "Conrad Irwin"] - s.date = "2012-08-10" + s.date = "2012-08-13" s.description = "An IRB alternative and runtime developer console" s.email = ["jrmair@gmail.com", "conrad.irwin@gmail.com"] s.executables = ["pry"] - s.files = [".document", ".gemtest", ".gitignore", ".travis.yml", ".yardopts", "CHANGELOG", "CONTRIBUTORS", "Gemfile", "LICENSE", "README.markdown", "Rakefile", "TODO", "bin/pry", "examples/example_basic.rb", "examples/example_command_override.rb", "examples/example_commands.rb", "examples/example_hooks.rb", "examples/example_image_edit.rb", "examples/example_input.rb", "examples/example_input2.rb", "examples/example_output.rb", "examples/example_print.rb", "examples/example_prompt.rb", "examples/helper.rb", "lib/pry.rb", "lib/pry/cli.rb", "lib/pry/code.rb", "lib/pry/command.rb", "lib/pry/command_set.rb", "lib/pry/commands.rb", "lib/pry/completion.rb", "lib/pry/config.rb", "lib/pry/core_extensions.rb", "lib/pry/custom_completions.rb", "lib/pry/default_commands/cd.rb", "lib/pry/default_commands/commands.rb", "lib/pry/default_commands/context.rb", "lib/pry/default_commands/easter_eggs.rb", "lib/pry/default_commands/editing.rb", "lib/pry/default_commands/find_method.rb", "lib/pry/default_commands/gems.rb", "lib/pry/default_commands/gist.rb", "lib/pry/default_commands/help.rb", "lib/pry/default_commands/hist.rb", "lib/pry/default_commands/input_and_output.rb", "lib/pry/default_commands/introspection.rb", "lib/pry/default_commands/ls.rb", "lib/pry/default_commands/misc.rb", "lib/pry/default_commands/navigating_pry.rb", "lib/pry/default_commands/whereami.rb", "lib/pry/extended_commands/experimental.rb", "lib/pry/helpers.rb", "lib/pry/helpers/base_helpers.rb", "lib/pry/helpers/command_helpers.rb", "lib/pry/helpers/documentation_helpers.rb", "lib/pry/helpers/options_helpers.rb", "lib/pry/helpers/text.rb", "lib/pry/history.rb", "lib/pry/history_array.rb", "lib/pry/hooks.rb", "lib/pry/indent.rb", "lib/pry/method.rb", "lib/pry/module_candidate.rb", "lib/pry/pager.rb", "lib/pry/plugins.rb", "lib/pry/pry_class.rb", "lib/pry/pry_instance.rb", "lib/pry/rbx_method.rb", "lib/pry/rbx_path.rb", "lib/pry/repl_file_loader.rb", "lib/pry/version.rb", "lib/pry/wrapped_module.rb", "man/pry.1", "man/pry.1.html", "man/pry.1.ronn", "pry.gemspec", "test/candidate_helper1.rb", "test/candidate_helper2.rb", "test/example_nesting.rb", "test/helper.rb", "test/test_cli.rb", "test/test_code.rb", "test/test_command.rb", "test/test_command_helpers.rb", "test/test_command_integration.rb", "test/test_command_set.rb", "test/test_completion.rb", "test/test_control_d_handler.rb", "test/test_default_commands/example.erb", "test/test_default_commands/test_cd.rb", "test/test_default_commands/test_context.rb", "test/test_default_commands/test_documentation.rb", "test/test_default_commands/test_find_method.rb", "test/test_default_commands/test_gems.rb", "test/test_default_commands/test_help.rb", "test/test_default_commands/test_input.rb", "test/test_default_commands/test_introspection.rb", "test/test_default_commands/test_ls.rb", "test/test_default_commands/test_shell.rb", "test/test_default_commands/test_show_source.rb", "test/test_exception_whitelist.rb", "test/test_history_array.rb", "test/test_hooks.rb", "test/test_indent.rb", "test/test_input_stack.rb", "test/test_method.rb", "test/test_prompt.rb", "test/test_pry.rb", "test/test_pry_defaults.rb", "test/test_pry_history.rb", "test/test_pry_output.rb", "test/test_sticky_locals.rb", "test/test_syntax_checking.rb", "test/test_wrapped_module.rb", "test/testrc", "test/testrcbad", "wiki/Customizing-pry.md", "wiki/Home.md"] + s.files = [".document", ".gemtest", ".gitignore", ".travis.yml", ".yardopts", "CHANGELOG", "CONTRIBUTORS", "Gemfile", "LICENSE", "README.markdown", "Rakefile", "TODO", "bin/pry", "examples/example_basic.rb", "examples/example_command_override.rb", "examples/example_commands.rb", "examples/example_hooks.rb", "examples/example_image_edit.rb", "examples/example_input.rb", "examples/example_input2.rb", "examples/example_output.rb", "examples/example_print.rb", "examples/example_prompt.rb", "examples/helper.rb", "lib/pry.rb", "lib/pry/cli.rb", "lib/pry/code.rb", "lib/pry/command.rb", "lib/pry/command_set.rb", "lib/pry/commands.rb", "lib/pry/commands/amend_line.rb", "lib/pry/commands/bang.rb", "lib/pry/commands/bang_pry.rb", "lib/pry/commands/cat.rb", "lib/pry/commands/cd.rb", "lib/pry/commands/easter_eggs.rb", "lib/pry/commands/edit.rb", "lib/pry/commands/edit_method.rb", "lib/pry/commands/exit.rb", "lib/pry/commands/exit_all.rb", "lib/pry/commands/exit_program.rb", "lib/pry/commands/find_method.rb", "lib/pry/commands/gem_cd.rb", "lib/pry/commands/gem_install.rb", "lib/pry/commands/gem_list.rb", "lib/pry/commands/gist.rb", "lib/pry/commands/help.rb", "lib/pry/commands/hist.rb", "lib/pry/commands/import_set.rb", "lib/pry/commands/install_command.rb", "lib/pry/commands/jump_to.rb", "lib/pry/commands/ls.rb", "lib/pry/commands/nesting.rb", "lib/pry/commands/play.rb", "lib/pry/commands/pry_backtrace.rb", "lib/pry/commands/pry_version.rb", "lib/pry/commands/raise_up.rb", "lib/pry/commands/reload_method.rb", "lib/pry/commands/reset.rb", "lib/pry/commands/ri.rb", "lib/pry/commands/save_file.rb", "lib/pry/commands/shell_command.rb", "lib/pry/commands/shell_mode.rb", "lib/pry/commands/show_command.rb", "lib/pry/commands/show_doc.rb", "lib/pry/commands/show_input.rb", "lib/pry/commands/show_source.rb", "lib/pry/commands/simple_prompt.rb", "lib/pry/commands/stat.rb", "lib/pry/commands/switch_to.rb", "lib/pry/commands/toggle_color.rb", "lib/pry/commands/whereami.rb", "lib/pry/commands/wtf.rb", "lib/pry/completion.rb", "lib/pry/config.rb", "lib/pry/core_extensions.rb", "lib/pry/custom_completions.rb", "lib/pry/helpers.rb", "lib/pry/helpers/base_helpers.rb", "lib/pry/helpers/command_helpers.rb", "lib/pry/helpers/documentation_helpers.rb", "lib/pry/helpers/module_introspection_helpers.rb", "lib/pry/helpers/options_helpers.rb", "lib/pry/helpers/text.rb", "lib/pry/history.rb", "lib/pry/history_array.rb", "lib/pry/hooks.rb", "lib/pry/indent.rb", "lib/pry/method.rb", "lib/pry/module_candidate.rb", "lib/pry/pager.rb", "lib/pry/plugins.rb", "lib/pry/pry_class.rb", "lib/pry/pry_instance.rb", "lib/pry/rbx_method.rb", "lib/pry/rbx_path.rb", "lib/pry/repl_file_loader.rb", "lib/pry/version.rb", "lib/pry/wrapped_module.rb", "man/pry.1", "man/pry.1.html", "man/pry.1.ronn", "pry.gemspec", "test/candidate_helper1.rb", "test/candidate_helper2.rb", "test/example_nesting.rb", "test/helper.rb", "test/test_cli.rb", "test/test_code.rb", "test/test_command.rb", "test/test_command_helpers.rb", "test/test_command_integration.rb", "test/test_command_set.rb", "test/test_completion.rb", "test/test_control_d_handler.rb", "test/test_default_commands/example.erb", "test/test_default_commands/test_cd.rb", "test/test_default_commands/test_context.rb", "test/test_default_commands/test_documentation.rb", "test/test_default_commands/test_find_method.rb", "test/test_default_commands/test_gems.rb", "test/test_default_commands/test_help.rb", "test/test_default_commands/test_input.rb", "test/test_default_commands/test_introspection.rb", "test/test_default_commands/test_ls.rb", "test/test_default_commands/test_shell.rb", "test/test_default_commands/test_show_source.rb", "test/test_exception_whitelist.rb", "test/test_history_array.rb", "test/test_hooks.rb", "test/test_indent.rb", "test/test_input_stack.rb", "test/test_method.rb", "test/test_prompt.rb", "test/test_pry.rb", "test/test_pry_defaults.rb", "test/test_pry_history.rb", "test/test_pry_output.rb", "test/test_sticky_locals.rb", "test/test_syntax_checking.rb", "test/test_wrapped_module.rb", "test/testrc", "test/testrcbad", "wiki/Customizing-pry.md", "wiki/Home.md"] s.homepage = "http://pry.github.com" s.require_paths = ["lib"] s.rubygems_version = "1.8.24" diff --git a/test/test_code.rb b/test/test_code.rb index 51d3f145..8e89b2b9 100644 --- a/test/test_code.rb +++ b/test/test_code.rb @@ -34,6 +34,18 @@ describe Pry::Code do Pry::Code.from_file('/knalkjsdnalsd/alkjdlkq') end.should.raise(MethodSource::SourceNotFoundError) end + + should 'check for files relative to origin pwd' do + Dir.chdir('test') do |f| + Pry::Code.from_file('test/' + File.basename(__FILE__)).code_type.should == :ruby + end + end + + should 'find files that are relative to the current working directory' do + Dir.chdir('test') do |f| + Pry::Code.from_file(File.basename(__FILE__)).code_type.should == :ruby + end + end end describe '.from_method' do diff --git a/test/test_command.rb b/test/test_command.rb index 9ecc053b..66dee88b 100644 --- a/test/test_command.rb +++ b/test/test_command.rb @@ -619,8 +619,10 @@ describe "Pry::Command" do mock_pry("my---test").should =~ /my-testmy-test/ end - it "should show the source of the process method" do - mock_pry("show-command my-test").should =~ /output.puts command_name/ + if !mri18_and_no_real_source_location? + it "should show the source of the process method" do + mock_pry("show-source my-test").should =~ /output.puts command_name/ + end end end diff --git a/test/test_completion.rb b/test/test_completion.rb index 8b0b3d4c..14c995d1 100644 --- a/test/test_completion.rb +++ b/test/test_completion.rb @@ -1,5 +1,16 @@ require 'helper' +def new_completer(bind, pry=nil) + Pry::InputCompleter.build_completion_proc(Pry.binding_for(bind), pry) +end + +def completer_test(bind, pry=nil, assert_flag=true) + completer = new_completer(bind, pry) + test = proc {|symbol| completer.call(symbol[0..-2]).include?(symbol).should == assert_flag} + return proc {|*symbols| symbols.each(&test) } +end + + describe Pry::InputCompleter do before do @@ -33,30 +44,176 @@ describe Pry::InputCompleter do it 'should complete instance variables' do object = Object.new + # set variables in appropriate scope object.instance_variable_set(:'@name', 'Pry') object.class.send(:class_variable_set, :'@@number', 10) - object.instance_variables.map { |v| v.to_sym } \ - .include?(:'@name').should == true + # check to see if variables are in scope + object.instance_variables. + map { |v| v.to_sym }. + include?(:'@name').should == true - object.class.class_variables.map { |v| v.to_sym } \ - .include?(:'@@number').should == true - - completer = Pry::InputCompleter.build_completion_proc( - Pry.binding_for(object) - ) + object.class.class_variables. + map { |v| v.to_sym }. + include?(:'@@number').should == true # Complete instance variables. - completer.call('@na').include?('@name').should == true - completer.call('@name.down').include?('@name.downcase').should == true + b = Pry.binding_for(object) + completer_test(b).call('@name', '@name.downcase') # Complete class variables. - completer = Pry::InputCompleter.build_completion_proc( - Pry.binding_for(object.class) - ) + b = Pry.binding_for(object.class) + completer_test(b).call('@@number', '@@number.class') - completer.call('@@nu').include?('@@number').should == true - completer.call('@@number.cl').include?('@@number.class').should == true end + + + it 'should complete for stdlib symbols' do + + o = Object.new + # Regexp + completer_test(o).call('/foo/.extend') + + # Array + completer_test(o).call('[1].push') + + # Hash + completer_test(o).call('{"a" => "b"}.keys') + + # Proc + completer_test(o).call('{2}.call') + + # Symbol + completer_test(o).call(':symbol.to_s') + + # Absolute Constant + completer_test(o).call('::IndexError') + end + + it 'should complete for target symbols' do + o = Object.new + + # Constant + module Mod + Con = 'Constant' + module Mod2 + end + end + + completer_test(Mod).call('Con') + + # Constants or Class Methods + completer_test(o).call('Mod::Con') + + # Symbol + foo = :symbol + completer_test(o).call(':symbol') + + # Variables + class << o + attr_accessor :foo + end + o.foo = 'bar' + completer_test(binding).call('o.foo') + + # trailing slash + new_completer(Mod).call('Mod2/').include?('Mod2/').should == true + end + + it 'should complete for arbitrary scopes' do + module Bar + @barvar = :bar + end + + module Baz + @bar = Bar + @bazvar = :baz + Con = :constant + end + + pry = Pry.new() + stack = pry.binding_stack + stack.push(Pry.binding_for(Baz)) + stack.push(Pry.binding_for(Bar)) + + b = Pry.binding_for(Bar) + completer_test(b, pry).call("../@bazvar") + completer_test(b, pry).call('/Con') + end + + it 'should complete for stdlib symbols' do + + o = Object.new + # Regexp + completer_test(o).call('/foo/.extend') + + # Array + completer_test(o).call('[1].push') + + # Hash + completer_test(o).call('{"a" => "b"}.keys') + + # Proc + completer_test(o).call('{2}.call') + + # Symbol + completer_test(o).call(':symbol.to_s') + + # Absolute Constant + completer_test(o).call('::IndexError') + end + + it 'should complete for target symbols' do + o = Object.new + + # Constant + module Mod + Con = 'Constant' + module Mod2 + end + end + + completer_test(Mod).call('Con') + + # Constants or Class Methods + completer_test(o).call('Mod::Con') + + # Symbol + foo = :symbol + completer_test(o).call(':symbol') + + # Variables + class << o + attr_accessor :foo + end + o.foo = 'bar' + completer_test(binding).call('o.foo') + + # trailing slash + new_completer(Mod).call('Mod2/').include?('Mod2/').should == true + + end + + it 'should complete for arbitrary scopes' do + module Bar + @barvar = :bar + end + + module Baz + @bar = Bar + @bazvar = :baz + Con = :constant + end + + pry = Pry.new() + stack = pry.binding_stack + stack.push(Pry.binding_for(Baz)) + stack.push(Pry.binding_for(Bar)) + + b = Pry.binding_for(Bar) + completer_test(b, pry).call("../@bazvar") + completer_test(b, pry).call('/Con') + end + end diff --git a/test/test_default_commands/test_cd.rb b/test/test_default_commands/test_cd.rb index 3e62dfa5..deffe656 100644 --- a/test/test_default_commands/test_cd.rb +++ b/test/test_default_commands/test_cd.rb @@ -20,9 +20,9 @@ describe 'Pry::DefaultCommands::Cd' do eval '_pry_.command_state["cd"]' end - def old_stack - eval '_pry_.command_state["cd"].old_stack.dup' - end + # def old_stack + # eval '_pry_.command_state["cd"].old_stack.dup' + # end end end @@ -42,20 +42,20 @@ describe 'Pry::DefaultCommands::Cd' do end end - describe 'when an error was raised' do - it 'should not toggle and should keep correct stacks' do - proc { - @t.eval 'cd @' - }.should.raise(Pry::CommandError) + # describe 'when an error was raised' do + # it 'should not toggle and should keep correct stacks' do + # proc { + # @t.eval 'cd @' + # }.should.raise(Pry::CommandError) - @t.old_stack.should == [] - @t.assert_binding_stack [@o] + # @t.old_stack.should == [] + # @t.assert_binding_stack [@o] - @t.eval 'cd -' - @t.old_stack.should == [] - @t.assert_binding_stack [@o] - end - end + # @t.eval 'cd -' + # @t.old_stack.should == [] + # @t.assert_binding_stack [@o] + # end + # end describe 'when using simple cd syntax' do it 'should toggle' do diff --git a/test/test_default_commands/test_documentation.rb b/test/test_default_commands/test_documentation.rb index 3c994fe5..2d3aea8a 100644 --- a/test/test_default_commands/test_documentation.rb +++ b/test/test_default_commands/test_documentation.rb @@ -307,5 +307,35 @@ if !mri18_and_no_real_source_location? end end end + + describe "on commands" do + + # mostly copied & modified from test_help.rb + before do + @oldset = Pry.config.commands + @set = Pry.config.commands = Pry::CommandSet.new do + import Pry::Commands + end + end + + after do + Pry.config.commands = @oldset + end + + it 'should display help for a specific command' do + mock_pry('show-doc ls').should =~ /Usage: ls/ + end + + it 'should display help for a regex command with a "listing"' do + @set.command /bar(.*)/, "Test listing", :listing => "foo" do; end + mock_pry('show-doc foo').should =~ /Test listing/ + end + + it 'should display help for a command with a spaces in its name' do + @set.command "command with spaces", "description of a command with spaces" do; end + mock_pry('show-doc "command with spaces"').should =~ /description of a command with spaces/ + end + + end end end diff --git a/test/test_default_commands/test_introspection.rb b/test/test_default_commands/test_introspection.rb index 3b3d413d..24631f55 100644 --- a/test/test_default_commands/test_introspection.rb +++ b/test/test_default_commands/test_introspection.rb @@ -470,58 +470,4 @@ describe "Pry::DefaultCommands::Introspection" do end end - - # show-command only works in implementations that support Proc#source_location - if Proc.method_defined?(:source_location) - describe "show-command" do - before do - @str_output = StringIO.new - end - - it 'should show source for an ordinary command' do - set = Pry::CommandSet.new do - import_from Pry::Commands, "show-command" - command "foo" do - :body_of_foo - end - end - - redirect_pry_io(InputTester.new("show-command foo"), @str_output) do - Pry.new(:commands => set).rep - end - - @str_output.string.should =~ /:body_of_foo/ - end - - it 'should show source for a command with spaces in its name' do - set = Pry::CommandSet.new do - import_from Pry::Commands, "show-command" - command "foo bar" do - :body_of_foo_bar - end - end - - redirect_pry_io(InputTester.new("show-command \"foo bar\""), @str_output) do - Pry.new(:commands => set).rep - end - - @str_output.string.should =~ /:body_of_foo_bar/ - end - - it 'should show source for a command by listing name' do - set = Pry::CommandSet.new do - import_from Pry::Commands, "show-command" - command /foo(.*)/, "", :listing => "bar" do - :body_of_foo_regex - end - end - - redirect_pry_io(InputTester.new("show-command bar"), @str_output) do - Pry.new(:commands => set).rep - end - - @str_output.string.should =~ /:body_of_foo_regex/ - end - end - end end diff --git a/test/test_default_commands/test_show_source.rb b/test/test_default_commands/test_show_source.rb index 68248344..37daeab3 100644 --- a/test/test_default_commands/test_show_source.rb +++ b/test/test_default_commands/test_show_source.rb @@ -255,6 +255,34 @@ if !mri18_and_no_real_source_location? end + describe "on variable or constant" do + before do + class TestHost + def hello + "hi there" + end + end + end + + after do + Object.remove_const(:TestHost) + end + + it "should output source of its class if variable doesn't respond to source_location" do + test_host = TestHost.new + string = mock_pry(binding,"show-source test_host","exit-all") + string.should =~ /class TestHost\n.*def hello/ + end + + it "should output source of its class if constant doesn't respond to source_location" do + TEST_HOST = TestHost.new + string = mock_pry(binding,"show-source TEST_HOST","exit-all") + string.should =~ /class TestHost\n.*def hello/ + + Object.remove_const(:TEST_HOST) + end + end + describe "on modules" do before do class ShowSourceTestSuperClass @@ -525,6 +553,51 @@ if !mri18_and_no_real_source_location? end end end + + describe "on commands" do + before do + @oldset = Pry.config.commands + @set = Pry.config.commands = Pry::CommandSet.new do + import Pry::Commands + end + end + + after do + Pry.config.commands = @oldset + end + + it 'should show source for an ordinary command' do + @set.command "foo", :body_of_foo do; end + + + string = mock_pry("show-source foo") + string.should =~ /:body_of_foo/ + end + + it "should output source of commands using special characters" do + @set.command "!", "Clear the input buffer" do; end + + + string = mock_pry("show-source !") + string.should =~ /Clear the input buffer/ + end + + it 'should show source for a command with spaces in its name' do + @set.command "foo bar", :body_of_foo_bar do; end + + + string = mock_pry("show-source \"foo bar\"") + string.should =~ /:body_of_foo_bar/ + end + + it 'should show source for a command by listing name' do + @set.command /foo(.*)/, :body_of_foo_bar_regex, :listing => "bar" do; end + + string = mock_pry("show-source bar") + string.should =~ /:body_of_foo_bar_regex/ + end + end + end end