direc = File.dirname(__FILE__) require "#{direc}/command_base" require "optparse" class Pry # Default commands used by Pry. class Commands < CommandBase # We make this a lambda to avoid documenting it meth_name_from_binding = lambda do |b| meth_name = b.eval('__method__') if [nil, :__binding__, :__binding_impl__].include?(meth_name) nil else meth_name end end command "!", "Clear the input buffer. Useful if the parsing process goes wrong and you get stuck in the read loop." do output.puts "Input buffer cleared!" opts[:eval_string].clear end command "!pry", "Start a Pry session on current self; this even works mid-expression." do Pry.start(target) end command "exit-program", "End the current program. Aliases: quit-program" do exit end alias_command "quit-program", "exit-program", "" command "nesting", "Show nesting information." do nesting = opts[:nesting] output.puts "Nesting status:" output.puts "--" nesting.each do |level, obj| if level == 0 output.puts "#{level}. #{Pry.view_clip(obj)} (Pry top level)" else output.puts "#{level}. #{Pry.view_clip(obj)}" end end end command "status", "Show status information." do nesting = opts[:nesting] output.puts "Status:" output.puts "--" output.puts "Receiver: #{Pry.view_clip(target.eval('self'))}" output.puts "Nesting level: #{nesting.level}" output.puts "Pry version: #{Pry::VERSION}" output.puts "Ruby version: #{RUBY_VERSION}" mn = meth_name_from_binding.call(target) output.puts "Current method: #{mn ? mn : "N/A"}" output.puts "Pry instance: #{Pry.active_instance}" output.puts "Last result: #{Pry.view(Pry.last_result)}" end command "version", "Show Pry version." do output.puts "Pry version: #{Pry::VERSION}" end command "exit-all", "End all nested Pry sessions." do throw(:breakout, 0) end command "ls", "Show the list of vars in the current scope. Type `ls --help` for more info." do |*args| options = {} # Set target local to the default -- note that we can set a different target for # ls if we like: e.g ls my_var target = target() OptionParser.new do |opts| opts.banner = %{Usage: ls [OPTIONS] [VAR]\n\ List information about VAR (the current context by default). Shows local and instance variables by default. -- } opts.on("-g", "--globals", "Display global variables.") do options[:g] = true end opts.on("-c", "--constants", "Display constants.") do options[:c] = true end opts.on("-l", "--locals", "Display locals.") do options[:l] = true end opts.on("-i", "--ivars", "Display instance variables.") do options[:i] = true end opts.on("-k", "--class-vars", "Display class variables.") do options[:k] = true end opts.on("-m", "--methods", "Display methods (public methods by default).") do options[:m] = true end opts.on("-M", "--instance-methods", "Display instance methods (only relevant to classes and modules).") do options[:M] = true end opts.on("-P", "--public", "Display public methods (with -m).") do options[:P] = true end opts.on("-r", "--protected", "Display protected methods (with -m).") do options[:r] = true end opts.on("-p", "--private", "Display private methods (with -m).") do options[:p] = true end opts.on("-s", "--super", "Include superclass entries (relevant to constant and methods options).") do options[:s] = true end opts.on("-a", "--all", "Display all types of entries.") do options[:a] = true end opts.on("-v", "--verbose", "Verbose ouput.") do options[:v] = true end opts.on_tail("-h", "--help", "Show this message.") do output.puts opts options[:h] = true end end.order(args) do |new_target| target = Pry.binding_for(target.eval("#{new_target}")) if !options[:h] end # exit if we've displayed help next if options[:h] # default is locals/ivars/class vars. # Only occurs when no options or when only option is verbose options.merge!({ :l => true, :i => true, :k => true }) if options.empty? || (options.size == 1 && options[:v]) # Display public methods by default if -m or -M switch is used. options[:P] = true if (options[:m] || options[:M]) && !(options[:p] || options[:r]) info = {} target_self = target.eval('self') # ensure we have a real boolean and not a `nil` (important when # interpolating in the string) options[:s] = !!options[:s] # Numbers (e.g 0, 1, 2) are for ordering the hash values in Ruby 1.8 i = -1 # Start collecting the entries selected by the user info["local variables"] = [Array(target.eval("local_variables")).sort, i += 1] if options[:l] || options[:a] info["instance variables"] = [Array(target.eval("instance_variables")).sort, i += 1] if options[:i] || options[:a] info["class variables"] = [if target_self.is_a?(Module) Array(target.eval("class_variables")).sort else Array(target.eval("self.class.class_variables")).sort end, i += 1] if options[:k] || options[:a] info["global variables"] = [Array(target.eval("global_variables")).sort, i += 1] if options[:g] || options[:a] info["public methods"] = [Array(target.eval("public_methods(#{options[:s]})")).uniq.sort, i += 1] if (options[:m] && options[:P]) || options[:a] info["protected methods"] = [Array(target.eval("protected_methods(#{options[:s]})")).sort, i += 1] if (options[:m] && options[:r]) || options[:a] info["private methods"] = [Array(target.eval("private_methods(#{options[:s]})")).sort, i += 1] if (options[:m] && options[:p]) || options[:a] info["public instance methods"] = [Array(target.eval("public_instance_methods(#{options[:s]})")).uniq.sort, i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:P]) || options[:a]) info["protected instance methods"] = [Array(target.eval("protected_instance_methods(#{options[:s]})")).uniq.sort, i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:r]) || options[:a]) info["private instance methods"] = [Array(target.eval("private_instance_methods(#{options[:s]})")).uniq.sort, i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:p]) || options[:a]) # dealing with 1.8/1.9 compatibility issues :/ csuper = options[:s] if Module.method(:constants).arity == 0 csuper = nil end info["constants"] = [Array(target_self.is_a?(Module) ? target.eval("constants(#{csuper})") : target.eval("self.class.constants(#{csuper})")).uniq.sort, i += 1] if options[:c] || options[:a] # verbose output? if options[:v] # verbose info.each.sort_by { |k, v| v.last }.each do |k, v| if !v.first.empty? output.puts "#{k}:\n--" output.puts Pry.view(v.first) output.puts end end # plain else output.puts Pry.view(info.values.sort_by { |v| v.last }.map { |v| v.first }.inject(&:+)) end end command "cat-file", "Show output of file FILE" do |file_name| if !file_name output.puts "Must provide a file name." next end output.puts File.read(file_name) end command "eval-file", "Eval a Ruby script. Type `eval-file --help` for more info." do |*args| options = {} file_name = nil OptionParser.new do |opts| opts.banner = %{Usage: eval-file [OPTIONS] FILE Eval a Ruby script at top-level or in the current context. Defaults to top-level. e.g: eval-file -c "hello.rb" -- } opts.on("-c", "--context", "Eval the script in the current context.") do options[:c] = true end opts.on_tail("-h", "--help", "This message.") do output.puts opts options[:h] = true end end.order(args) do |v| file_name = v end next if options[:h] if !file_name output.puts "You need to specify a file name. Type `eval-file --help` for help" next end old_constants = Object.constants if options[:c] target.eval(File.read(file_name)) output.puts "--\nEval'd '#{file_name}' in the current context." else TOPLEVEL_BINDING.eval(File.read(file_name)) output.puts "--\nEval'd '#{file_name}' at top-level." end new_constants = Object.constants - old_constants output.puts "Brought in the following top-level constants: #{new_constants.inspect}" if !new_constants.empty? end command "cat", "Show output of VAR.inspect. Aliases: inspect" do |obj| if !obj output.puts "Must provide an object to inspect." next end output.puts Pry.view(target.eval("#{obj}")) end alias_command "inspect", "cat", "" command "cd", "Start a Pry session on VAR (use `cd ..` to go back)" do |obj| if !obj output.puts "Must provide an object." next end throw(:breakout, opts[:nesting].level) if obj == ".." target.eval("#{obj}.pry") end command "show-doc", "Show the comments above METH. Type `show-doc --help` for more info." do |*args| options = {} meth_name = nil OptionParser.new do |opts| opts.banner = %{Usage: show-doc [OPTIONS] [METH] Show the comments above method METH. Shows _method_ comments (rather than instance methods) by default. e.g show-doc hello_method -- } opts.on("-M", "--instance-methods", "Operate on instance methods instead.") do options[:M] = true end opts.on_tail("-h", "--help", "This message.") do output.puts opts options[:h] = true end end.order(args) do |v| meth_name = v end next if options[:h] if !meth_name output.puts "You need to specify a method. Type `show-doc --help` for help" next end begin if options[:M] meth = target.eval("instance_method(:#{meth_name})") else meth = target.eval("method(:#{meth_name})") end rescue output.puts "Invalid method name: #{meth_name}. Type `show-doc --help` for help" next end doc = meth.comment file, line = meth.source_location output.puts "--\nFrom #{file} @ line ~#{line}:\n--" output.puts doc end command "show-method", "Show the source for METH. Type `show-method --help` for more info." do |*args| options = {} meth_name = nil OptionParser.new do |opts| opts.banner = %{Usage: show-method [OPTIONS] [METH] Show the source for method METH. Shows _method_ source (rather than instance methods) by default. e.g: show-method hello_method -- } opts.on("-M", "--instance-methods", "Operate on instance methods instead.") do options[:M] = true end opts.on_tail("-h", "--help", "This message.") do output.puts opts options[:h] = true end end.order(args) do |v| meth_name = v end next if options[:h] # If no method name is given then use current method, if it exists meth_name = meth_name_from_binding.call(target) if !meth_name if !meth_name output.puts "You need to specify a method. Type `show-method --help` for help" next end begin if options[:M] meth = target.eval("instance_method(:#{meth_name})") else meth = target.eval("method(:#{meth_name})") end rescue output.puts "Invalid method name: #{meth_name}. Type `show-method --help` for help" next end code = meth.source file, line = meth.source_location output.puts "--\nFrom #{file} @ line #{line}:\n--" output.puts code end command "show-command", "Show sourcecode for a Pry command, e.g: show-command ls" do |command_name| cmds = Pry.active_instance.commands.commands if !command_name output.puts "You must provide a command name." next end if cmds[command_name] meth = cmds[command_name][:action] code = meth.source file, line = meth.source_location output.puts "--\nFrom #{file} @ line #{line}:\n--" output.puts code else output.puts "No such command: #{command_name}." end end command "jump-to", "Jump to a Pry session further up the stack, exiting all sessions below." do |break_level| break_level = break_level.to_i nesting = opts[:nesting] case break_level when nesting.level output.puts "Already at nesting level #{nesting.level}" when (0...nesting.level) throw(:breakout, break_level + 1) else max_nest_level = nesting.level - 1 output.puts "Invalid nest level. Must be between 0 and #{max_nest_level}. Got #{break_level}." end end command "exit", "End the current Pry session. Aliases: quit, back" do throw(:breakout, opts[:nesting].level) end alias_command "quit", "exit", "" alias_command "back", "exit", "" end end