Module: Pry::DefaultCommands

Defined in:
lib/pry/default_commands/ls.rb,
lib/pry/default_commands/cd.rb,
lib/pry/default_commands/gems.rb,
lib/pry/default_commands/help.rb,
lib/pry/default_commands/hist.rb,
lib/pry/default_commands/misc.rb,
lib/pry/default_commands/editing.rb,
lib/pry/default_commands/context.rb,
lib/pry/default_commands/commands.rb,
lib/pry/default_commands/easter_eggs.rb,
lib/pry/default_commands/find_method.rb,
lib/pry/default_commands/introspection.rb,
lib/pry/default_commands/navigating_pry.rb,
lib/pry/default_commands/input_and_output.rb

Defined Under Namespace

Modules: ModuleIntrospectionHelpers

Constant Summary

Ls =
Pry::CommandSet.new do

  create_command "ls","Show the list of vars and methods in the current scope.",
  :shellwords => false, :interpolate => false do

    group "Context"

    def options(opt)
      opt.banner unindent <<-USAGE
        Usage: ls [-m|-M|-p|-pM] [-q|-v] [-c|-i] [Object]
               ls [-g] [-l]

        ls shows you which methods, constants and variables are accessible to Pry. By default it shows you the local variables defined in the current shell, and any public methods or instance variables defined on the current object.

        The colours used are configurable using Pry.config.ls.*_color, and the separator is Pry.config.ls.separator.

        Pry.config.ls.ceiling is used to hide methods defined higher up in the inheritance chain, this is by default set to [Object, Module, Class] so that methods defined on all Objects are omitted. The -v flag can be used to ignore this setting and show all methods, while the -q can be used to set the ceiling much lower and show only methods defined on the object or its direct class.
      USAGE

      opt.on :m, "methods", "Show public methods defined on the Object (default)"
      opt.on :M, "instance-methods", "Show methods defined in a Module or Class"

      opt.on :p, "ppp", "Show public, protected (in yellow) and private (in green) methods"
      opt.on :q, "quiet", "Show only methods defined on object.singleton_class and object.class"
      opt.on :v, "verbose", "Show methods and constants on all super-classes (ignores Pry.config.ls.ceiling)"

      opt.on :g, "globals", "Show global variables, including those builtin to Ruby (in cyan)"
      opt.on :l, "locals", "Show locals, including those provided by Pry (in red)"

      opt.on :c, "constants", "Show constants, highlighting classes (in blue), and exceptions (in purple).\n" +
      " " * 32 +              "Constants that are pending autoload? are also shown (in yellow)."

      opt.on :i, "ivars", "Show instance variables (in blue) and class variables (in bright blue)"

      opt.on :G, "grep", "Filter output by regular expression", :argument => true
      if jruby?
        opt.on :J, "all-java", "Show all the aliases for methods from java (default is to show only prettiest)"
      end
    end

    def process
      obj = args.empty? ? target_self : target.eval(args.join(" "))

      # exclude -q, -v and --grep because they don't specify what the user wants to see.
      has_opts = (opts.present?(:methods) || opts.present?(:instance-methods') || opts.present?(:ppp) ||
                  opts.present?(:globals) || opts.present?(:locals) || opts.present?(:constants) ||
                  opts.present?(:ivars))

      show_methods   = opts.present?(:methods) || opts.present?(:instance-methods') || opts.present?(:ppp) || !has_opts
      show_self_methods = (!has_opts && Module === obj)
      show_constants = opts.present?(:constants) || (!has_opts && Module === obj)
      show_ivars     = opts.present?(:ivars) || !has_opts
      show_locals    = opts.present?(:locals) || (!has_opts && args.empty?)

      grep_regex, grep = [Regexp.new(opts[:G] || "."), lambda{ |x| x.grep(grep_regex) }]

      raise Pry::CommandError, "-l does not make sense with a specified Object" if opts.present?(:locals) && !args.empty?
      raise Pry::CommandError, "-g does not make sense with a specified Object" if opts.present?(:globals) && !args.empty?
      raise Pry::CommandError, "-q does not make sense with -v" if opts.present?(:quiet) && opts.present?(:verbose)
      raise Pry::CommandError, "-M only makes sense with a Module or a Class" if opts.present?(:instance-methods') && !(Module === obj)
      raise Pry::CommandError, "-c only makes sense with a Module or a Class" if opts.present?(:constants) && !args.empty? && !(Module === obj)


      if opts.present?(:globals)
        output_section("global variables", grep[format_globals(target.eval("global_variables"))])
      end

      if show_constants
        mod = Module === obj ? obj : Object
        constants = mod.constants
        constants -= (mod.ancestors - [mod]).map(&:constants).flatten unless opts.present?(:verbose)
        output_section("constants", grep[format_constants(mod, constants)])
      end

      if show_methods
        # methods is a hash {Module/Class => [Pry::Methods]}
        methods = all_methods(obj).group_by(&:owner)

        # reverse the resolution order so that the most useful information appears right by the prompt
        resolution_order(obj).take_while(&below_ceiling(obj)).reverse.each do |klass|
          methods_here = format_methods((methods[klass] || []).select{ |m| m.name =~ grep_regex })
          output_section "#{Pry::WrappedModule.new(klass).method_prefix}methods", methods_here
        end
      end

      if show_self_methods
        methods = all_methods(obj, true).select{ |m| m.owner == obj && m.name =~ grep_regex }
        output_section "#{Pry::WrappedModule.new(obj).method_prefix}methods", format_methods(methods)
      end

      if show_ivars
        klass = (Module === obj ? obj : obj.class)
        ivars = Pry::Method.safe_send(obj, :instance_variables)
        kvars = Pry::Method.safe_send(klass, :class_variables)
        output_section("instance variables", format_variables(:instance_var, ivars))
        output_section("class variables", format_variables(:class_var, kvars))
      end

      if show_locals
        output_section("locals", format_locals(grep[target.eval("local_variables")]))
      end
    end

    private

    # http://ruby.runpaint.org/globals, and running "puts global_variables.inspect".
    BUILTIN_GLOBALS = %w($" $$ $* $, $-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w $. $/ $\\
                         $: $; $< $= $> $0 $ARGV $CONSOLE $DEBUG $DEFAULT_INPUT $DEFAULT_OUTPUT
                         $FIELD_SEPARATOR $FILENAME $FS $IGNORECASE $INPUT_LINE_NUMBER
                         $INPUT_RECORD_SEPARATOR $KCODE $LOADED_FEATURES $LOAD_PATH $NR $OFS
                         $ORS $OUTPUT_FIELD_SEPARATOR $OUTPUT_RECORD_SEPARATOR $PID $PROCESS_ID
                         $PROGRAM_NAME $RS $VERBOSE $deferr $defout $stderr $stdin $stdout)

    # $SAFE and $? are thread-local, the exception stuff only works in a rescue clause,
    # everything else is basically a local variable with a $ in its name.
    PSEUDO_GLOBALS = %w($! $' $& $` $@ $? $+ $_ $~ $1 $2 $3 $4 $5 $6 $7 $8 $9
                       $CHILD_STATUS $SAFE $ERROR_INFO $ERROR_POSITION $LAST_MATCH_INFO
                       $LAST_PAREN_MATCH $LAST_READ_LINE $MATCH $POSTMATCH $PREMATCH)

    # Get all the methods that we'll want to output
    def all_methods(obj, instance_methods=false)
      methods = if instance_methods || opts.present?(:instance-methods')
                  Pry::Method.all_from_class(obj)
                else
                  Pry::Method.all_from_obj(obj)
                end

      if jruby? && !opts.present?(:J)
        methods = trim_jruby_aliases(methods)
      end

      methods.select{ |method| opts.present?(:ppp) || method.visibility == :public }
    end

    # JRuby creates lots of aliases for methods imported from java in an attempt to
    # make life easier for ruby programmers.
    # (e.g. getFooBar becomes get_foo_bar and foo_bar, and maybe foo_bar? if it
    # returns a Boolean).
    # The full transformations are in the assignAliases method of:
    #   https://github.com/jruby/jruby/blob/master/src/org/jruby/javasupport/JavaClass.java
    #
    # This has the unfortunate side-effect of making the output of ls even more
    # incredibly verbose than it normally would be for these objects; and so we filter
    # out all but the nicest of these aliases here.
    #
    # TODO: This is a little bit vague, better heuristics could be used.
    #       JRuby also has a lot of scala-specific logic, which we don't copy.
    #
    def trim_jruby_aliases(methods)
      grouped = methods.group_by do |m|
        m.name.sub(/\A(is|get|set)(?=[A-Z_])/, '').gsub(/[_?=]/, '').downcase
      end

      grouped.map do |key, values|
        values = values.sort_by do |m|
          rubbishness(m.name)
        end

        found = []
        values.select do |x|
          (!found.any?{ |y| x == y }) && found << x
        end
      end.flatten(1)
    end

    # When removing jruby aliases, we want to keep the alias that is "least rubbish"
    # according to this metric.
    def rubbishness(name)
      name.each_char.map{ |x|
        case x
        when /[A-Z]/
          1
        when '?', '=', '!'
          -2
        else
          0
        end
      }.inject(&:+) + (name.size / 100.0)
    end

    def resolution_order(obj)
      opts.present?(:instance-methods') ? Pry::Method.instance_resolution_order(obj) : Pry::Method.resolution_order(obj)
    end

    # Get a lambda that can be used with .take_while to prevent over-eager
    # traversal of the Object's ancestry graph.
    def below_ceiling(obj)
      ceiling = if opts.present?(:quiet)
                   [opts.present?(:instance-methods') ? obj.ancestors[1] : obj.class.ancestors[1]] + Pry.config.ls.ceiling
                 elsif opts.present?(:verbose)
                   []
                 else
                   Pry.config.ls.ceiling.dup
                 end

      lambda { |klass| !ceiling.include?(klass) }
    end

    # Format and colourise a list of methods.
    def format_methods(methods)
      methods.sort_by(&:name).map do |method|
        if method.name == 'method_missing'
          color(:method_missing, 'method_missing')
        elsif method.visibility == :private
          color(:private_method, method.name)
        elsif method.visibility == :protected
          color(:protected_method, method.name)
        else
          color(:public_method, method.name)
        end
      end
    end

    def format_variables(type, vars)
      vars.sort_by(&:downcase).map{ |var| color(type, var) }
    end

    def format_constants(mod, constants)
      constants.sort_by(&:downcase).map do |name|
        if const = (!mod.autoload?(name) && (mod.const_get(name) || true) rescue nil)
          if (const < Exception rescue false)
            color(:exception_constant, name)
          elsif (Module === mod.const_get(name) rescue false)
            color(:class_constant, name)
          else
            color(:constant, name)
          end
        else
          color(:unloaded_constant, name)
        end
      end
    end

    def format_globals(globals)
      globals.sort_by(&:downcase).map do |name|
        if PSEUDO_GLOBALS.include?(name)
          color(:pseudo_global, name)
        elsif BUILTIN_GLOBALS.include?(name)
          color(:builtin_global, name)
        else
          color(:global_var, name)
        end
      end
    end

    def format_locals(locals)
      locals.sort_by(&:downcase).map do |name|
        if _pry_.sticky_locals.include?(name.to_sym)
          color(:pry_var, name)
        else
          color(:local_var, name)
        end
      end
    end

    # Add a new section to the output. Outputs nothing if the section would be empty.
    def output_section(heading, body)
      return if body.compact.empty?
      output.puts "#{text.bold(color(:heading, heading))}: #{body.compact.join(Pry.config.ls.separator)}"
    end

    # Color output based on config.ls.*_color
    def color(type, str)
      text.send(Pry.config.ls.send(:#{type}_color"), str)
    end
  end
end
Cd =
Pry::CommandSet.new do
  create_command "cd" do
    group "Context"
    description "Move into a new context (object or scope)."

    banner <<-BANNER
      Usage: cd [OPTIONS] [--help]

      Move into new context (object or scope). As in unix shells use
      `cd ..` to go back and `cd /` to return to Pry top-level).
      Complex syntax (e.g cd ../@x/y) also supported.

      e.g: `cd @x`
      e.g: `cd ..
      e.g: `cd /`

      https://github.com/pry/pry/wiki/State-navigation#wiki-Changing_scope
    BANNER

    def process
      path   = arg_string.split(/\//)
      stack  = _pry_.binding_stack.dup

      # special case when we only get a single "/", return to root
      stack  = [stack.first] if path.empty?

      path.each do |context|
        begin
          case context.chomp
          when ""
            stack = [stack.first]
          when "::"
            stack.push(TOPLEVEL_BINDING)
          when "."
            next
          when ".."
            unless stack.size == 1
              stack.pop
            end
          else
            stack.push(Pry.binding_for(stack.last.eval(context)))
          end

        rescue RescuableException => e
          output.puts "Bad object path: #{arg_string.chomp}. Failed trying to resolve: #{context}"
          output.puts e.inspect
          return
        end
      end

      _pry_.binding_stack = stack
    end
  end
end
Gems =
Pry::CommandSet.new do

  create_command "gem-install", "Install a gem and refresh the gem cache.", :argument_required => true do |gem|

    banner <<-BANNER
      Usage: gem-install GEM_NAME

      Installs the given gem and refreshes the gem cache so that you can immediately 'require GEM_FILE'
    BANNER

    def setup
      require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller
    end

    def process(gem)
      begin
        destination = File.writable?(Gem.dir) ? Gem.dir : Gem.user_dir
        installer = Gem::DependencyInstaller.new :install_dir => destination
        installer.install gem
      rescue Errno::EACCES
        raise CommandError, "Insufficient permissions to install `#{text.green gem}`."
      rescue Gem::GemNotFoundException
        raise CommandError, "Gem `#{text.green gem}` not found."
      else
        Gem.refresh
        output.puts "Gem `#{text.green gem}` installed."
      end
    end
  end

  create_command "gem-cd", "Change working directory to specified gem's directory.", :argument_required => true do |gem|
    banner <<-BANNER
      Usage: gem-cd GEM_NAME

      Change the current working directory to that in which the given gem is installed.
    BANNER

    def process(gem)
      specs = Gem::Specification.respond_to?(:each) ? Gem::Specification.find_all_by_name(gem) : Gem.source_index.find_name(gem)
      spec  = specs.sort { |a,b| Gem::Version.new(b.version) <=> Gem::Version.new(a.version) }.first
      if spec
        Dir.chdir(spec.full_gem_path)
        output.puts(Dir.pwd)
      else
        raise CommandError, "Gem `#{gem}` not found."
      end
    end
  end

  create_command "gem-list", "List and search installed gems." do |pattern|
    banner <<-BANNER
      Usage: gem-list [REGEX]

      List all installed gems, when a regex is provided, limit the output to those that
      match the regex.
    BANNER

    def process(pattern=nil)
      pattern = Regexp.compile(pattern || '')
      gems    = if Gem::Specification.respond_to?(:each)
                  Gem::Specification.select{|spec| spec.name =~ pattern }.group_by(&:name)
                else
                  Gem.source_index.gems.values.group_by(&:name).select { |gemname, specs| gemname =~ pattern }
                end

      gems.each do |gem, specs|
        specs.sort! do |a,b|
          Gem::Version.new(b.version) <=> Gem::Version.new(a.version)
        end

        versions = specs.each_with_index.map do |spec, index|
          index == 0 ? text.bright_green(spec.version.to_s) : text.green(spec.version.to_s)
        end

        output.puts "#{text.default gem} (#{versions.join ', '})"
      end
    end
  end
end
Help =
Pry::CommandSet.new do
  create_command "help" do |cmd|
    description "Show a list of commands. Type `help <foo>` for information about <foo>."

    banner <<-BANNER
      Usage: help [ COMMAND ]

      With no arguments, help lists all the available commands in the current
      command-set along with their description.

      When given a command name as an argument, shows the help for that command.
    BANNER

    # We only want to show commands that have descriptions, so that the
    # easter eggs don't show up.
    def visible_commands
      visible = {}
      commands.each do |key, command|
        visible[key] = command if command.description && !command.description.empty?
      end
      visible
    end

    # Get a hash of available commands grouped by the "group" name.
    def command_groups
      visible_commands.values.group_by(&:group)
    end

    def process
      if args.empty?
        display_index(command_groups)
      else
        display_search(args.first)
      end
    end

    # Display the index view, with headings and short descriptions per command.
    #
    # @param Hash[String => Array[Commands]]
    def display_index(groups)
      help_text = []

      groups.keys.sort_by(&method(:group_sort_key)).each do |key|
        commands = groups[key].sort_by{ |command| command.options[:listing].to_s }

        unless commands.empty?
          help_text << "#{text.bold(key)}\n" + commands.map do |command|
            "  #{command.options[:listing].to_s.ljust(18)} #{command.description}"
          end.join("\n")
        end
      end

      stagger_output(help_text.join("\n\n"))
    end

    # Display help for an individual command or group.
    #
    # @param String  The string to search for.
    def display_search(search)
      if command = command_set.find_command_for_help(search)
        display_command(command)
      else
        groups = search_hash(search, command_groups)

        if groups.size > 0
          display_index(groups)
          return
        end

        filtered = search_hash(search, visible_commands)
        raise CommandError, "No help found for '#{args.first}'" if filtered.empty?

        if filtered.size == 1
          display_command(filtered.values.first)
        else
          display_index({"'#{search}' commands" => filtered.values})
        end
      end
    end

    # Display help for an individual command.
    #
    # @param [Pry::Command]
    def display_command(command)
      stagger_output command.new.help
    end

    # Find a subset of a hash that matches the user's search term.
    #
    # If there's an exact match a Hash of one element will be returned,
    # otherwise a sub-Hash with every key that matches the search will
    # be returned.
    #
    # @param [String]  the search term
    # @param [Hash]  the hash to search
    def search_hash(search, hash)
      matching = {}

      hash.each_pair do |key, value|
        next unless key.is_a?(String)
        if normalize(key) == normalize(search)
          return {key => value}
        elsif normalize(key).start_with?(normalize(search))
          matching[key] = value
        end
      end

      matching
    end

    # Clean search terms to make it easier to search group names
    #
    # @param String
    # @return String
    def normalize(key)
      key.downcase.gsub(/pry\W+/, '')
    end

    def group_sort_key(group_name)
      [%w(Help Context Editing Introspection Input_and_output Navigating_pry Gems Basic Commands).index(group_name.gsub(' ', '_')) || 99, group_name]
    end
  end
end
Hist =
Pry::CommandSet.new do

  create_command "hist", "Show and replay Readline history. Aliases: history" do
    group "Editing"
    banner <<-USAGE
      Usage: hist
             hist --head N
             hist --tail N
             hist --show START..END
             hist --grep PATTERN
             hist --clear
             hist --replay START..END
             hist --save [START..END] FILE
    USAGE

    def options(opt)
      opt.on :H, :head, "Display the first N items.", :optional_argument => true, :as => Integer
      opt.on :T, :tail, "Display the last N items.", :optional_argument => true, :as => Integer
      opt.on :s, :show, "Show the given range of lines.", :optional_argument => true, :as => Range
      opt.on :G, :grep, "Show lines matching the given pattern.", :argument => true, :as => String
      opt.on :c, :clear, "Clear the current session's history."
      opt.on :r, :replay, "Replay a line or range of lines.", :argument => true, :as => Range
      opt.on     :save, "Save history to a file.", :argument => true, :as => Range

      opt.on :e, :exclude-pry', "Exclude Pry commands from the history."
      opt.on :n, :no-numbers', "Omit line numbers."
      opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
    end

    def process
      @history = Pry::Code(Pry.history.to_a)

      if opts.present?(:show)
        @history = @history.between(opts[:show])
      end

      if opts.present?(:grep)
        @history = @history.grep(opts[:grep])
      end

      @history = case
        when opts.present?(:head)
          @history.take_lines(1, opts[:head] || 10)
        when opts.present?(:tail)
          @history.take_lines(-(opts[:tail] || 10), opts[:tail] || 10)
        when opts.present?(:show)
          @history.between(opts[:show])
        else
          @history
        end

      if opts.present?(:exclude-pry')
        @history = @history.select { |l, ln| !command_set.valid_command?(l) }
      end

      if opts.present?(:save)
        process_save
      elsif opts.present?(:clear)
        process_clear
      elsif opts.present?(:replay)
        process_replay
      else
        process_display
      end
    end

    def process_display
      unless opts.present?(:no-numbers')
        @history = @history.with_line_numbers
      end

      render_output(@history, opts)
    end

    def process_save
      case opts[:save]
      when Range
        @history = @history.between(opts[:save])

        unless args.first
          raise CommandError, "Must provide a file name."
        end

        file_name = File.expand_path(args.first)
      when String
        file_name = File.expand_path(opts[:save])
      end

      output.puts "Saving history in #{file_name}..."

      File.open(file_name, 'w') { |f| f.write(@history.raw) }

      output.puts "History saved."
    end

    def process_clear
      Pry.history.clear
      output.puts "History cleared."
    end

    def process_replay
      @history = @history.between(opts[:r])

      _pry_.input_stack.push _pry_.input
      _pry_.input = StringIO.new(@history.raw)
      # eval_string << "#{@history.raw}\n"
      # run "show-input" unless _pry_.complete_expression?(eval_string)
    end
  end

  alias_command "history", "hist"




end
Misc =
Pry::CommandSet.new do

  command "toggle-color", "Toggle syntax highlighting." do
    Pry.color = !Pry.color
    output.puts "Syntax highlighting #{Pry.color ? "on" : "off"}"
  end

  command "simple-prompt", "Toggle the simple prompt." do
    case _pry_.prompt
    when Pry::SIMPLE_PROMPT
      _pry_.pop_prompt
    else
      _pry_.push_prompt Pry::SIMPLE_PROMPT
    end
  end

  command "pry-version", "Show Pry version." do
    output.puts "Pry version: #{Pry::VERSION} on Ruby #{RUBY_VERSION}."
  end

  command "reload-method", "Reload the source file that contains the specified method" do |meth_name|
    meth = get_method_or_raise(meth_name, target, {}, :omit_help)

    if meth.source_type == :c
      raise CommandError, "Can't reload a C method."
    elsif meth.dynamically_defined?
      raise CommandError, "Can't reload an eval method."
    else
      file_name = meth.source_file
      load file_name
      output.puts "Reloaded #{file_name}."
    end
  end
end
Editing =
Pry::CommandSet.new do
  import Hist

  create_command "!", "Clear the input buffer. Useful if the parsing process goes wrong and you get stuck in the read loop.", :use_prefix => false do
    def process
      output.puts "Input buffer cleared!"
      eval_string.replace("")
    end
  end

  create_command "show-input", "Show the contents of the input buffer for the current multi-line expression." do
    def process
      output.puts Code.new(eval_string).with_line_numbers
    end
  end

  create_command "edit" do
    description "Invoke the default editor on a file."

    banner <<-BANNER
      Usage: edit [--no-reload|--reload] [--line LINE] [--temp|--ex|FILE[:LINE]|--in N]

      Open a text editor. When no FILE is given, edits the pry input buffer.
      Ensure Pry.config.editor is set to your editor of choice.

      e.g: `edit sample.rb`
      e.g: `edit sample.rb --line 105`
      e.g: `edit --ex`

      https://github.com/pry/pry/wiki/Editor-integration#wiki-Edit_command
    BANNER

    def options(opt)
      opt.on :e, :ex, "Open the file that raised the most recent exception (_ex_.file)", :optional_argument => true, :as => Integer
      opt.on :i, :in, "Open a temporary file containing the Nth line of _in_. N may be a range.", :optional_argument => true, :as => Range, :default => -1..-1
      opt.on :t, :temp, "Open an empty temporary file"
      opt.on :l, :line, "Jump to this line in the opened file", :argument => true, :as => Integer
      opt.on :n, :no-reload", "Don't automatically reload the edited code"
      opt.on :c, :current", "Open the current __FILE__ and at __LINE__ (as returned by `whereami`)."
      opt.on :r, :reload, "Reload the edited code immediately (default for ruby files)"
    end

    def process
      if [opts.present?(:ex), opts.present?(:temp), opts.present?(:in), !args.empty?].count(true) > 1
        raise CommandError, "Only one of --ex, --temp, --in and FILE may be specified."
      end

      if !opts.present?(:ex) && !opts.present?(:current) && args.empty?
        # edit of local code, eval'd within pry.
        process_local_edit
      else
        # edit of remote code, eval'd at top-level
        process_remote_edit
      end
    end

    def process_i
      case opts[:i]
      when Range
        (_pry_.input_array[opts[:i]] || []).join
      when Fixnum
        _pry_.input_array[opts[:i]] || ""
      else
        return output.puts "Not a valid range: #{opts[:i]}"
      end
    end

    def process_local_edit
      content = case
        when opts.present?(:temp)
          ""
        when opts.present?(:in)
          process_i
        when eval_string.strip != ""
          eval_string
        else
          _pry_.input_array.reverse_each.find{ |x| x && x.strip != "" } || ""
      end

      line = content.lines.count

      temp_file do |f|
        f.puts(content)
        f.flush
        reload = !opts.present?(:no-reload') && !Pry.config.disable_auto_reload
        f.close(false)
        invoke_editor(f.path, line, reload)
        if reload
          silence_warnings do
            eval_string.replace(File.read(f.path))
          end
        end
      end
    end

    def process_remote_edit
      if opts.present?(:ex)
        if _pry_.last_exception.nil?
          raise CommandError, "No exception found."
        end

        ex = _pry_.last_exception
        bt_index = opts[:ex].to_i

        ex_file, ex_line = ex.bt_source_location_for(bt_index)
        if ex_file && RbxPath.is_core_path?(ex_file)
          file_name = RbxPath.convert_path_to_full(ex_file)
        else
          file_name = ex_file
        end

        line = ex_line

        if file_name.nil?
          raise CommandError, "Exception has no associated file."
        end

        if Pry.eval_path == file_name
          raise CommandError, "Cannot edit exceptions raised in REPL."
        end
      elsif opts.present?(:current)
        file_name = target.eval("__FILE__")
        line = target.eval("__LINE__")
      else

        # break up into file:line
        file_name = File.expand_path(args.first)
        line = file_name.sub!(/:(\d+)$/, "") ? $1.to_i : 1
      end

      if not_a_real_file?(file_name)
        raise CommandError, "#{file_name} is not a valid file name, cannot edit!"
      end

      line = opts[:l].to_i if opts.present?(:line)

      reload = opts.present?(:reload) || ((opts.present?(:ex) || file_name.end_with?(".rb")) && !opts.present?(:no-reload')) && !Pry.config.disable_auto_reload
      invoke_editor(file_name, line, reload)
      set_file_and_dir_locals(file_name)

      if reload
        silence_warnings do
          TOPLEVEL_BINDING.eval(File.read(file_name), file_name)
        end
      end
    end
  end

  create_command "edit-method" do
    description "Edit the source code for a method."

    banner <<-BANNER
      Usage: edit-method [OPTIONS] [METH]

      Edit the method METH in an editor.
      Ensure Pry.config.editor is set to your editor of choice.

      e.g: `edit-method hello_method`
      e.g: `edit-method Pry#rep`
      e.g: `edit-method`

      https://github.com/pry/pry/wiki/Editor-integration#wiki-Edit_method
    BANNER

    command_options :shellwords => false

    def options(opt)
      method_options(opt)
      opt.on :n, "no-reload", "Do not automatically reload the method's file after editing."
      opt.on "no-jump", "Do not fast forward editor to first line of method."
      opt.on :p, :patch, "Instead of editing the method's file, try to edit in a tempfile and apply as a monkey patch."
    end

    def process
      if !Pry.config.editor
        raise CommandError, "No editor set!\nEnsure that #{text.bold("Pry.config.editor")} is set to your editor of choice."
      end

      begin
        @method = method_object
      rescue NonMethodContextError => err
      end

      if opts.present?(:patch) || (@method && @method.dynamically_defined?)
        if err
          raise err # can't patch a non-method
        end

        process_patch
      else
        if err && !File.exist?(target.eval('__FILE__'))
          raise err # can't edit a non-file
        end

        process_file
      end
    end

    def process_patch
      lines = @method.source.lines.to_a

      lines[0] = definition_line_for_owner(lines[0])

      temp_file do |f|
        f.puts lines.join
        f.flush
        f.close(false)
        invoke_editor(f.path, 0, true)

        if @method.alias?
          with_method_transaction(original_name, @method.owner) do
            Pry.new(:input => StringIO.new(File.read(f.path))).rep(@method.owner)
            Pry.binding_for(@method.owner).eval("alias #{@method.name} #{original_name}")
          end
        else
          Pry.new(:input => StringIO.new(File.read(f.path))).rep(@method.owner)
        end
      end
    end

    def process_file
      file, line = extract_file_and_line

      reload = !opts.present?(:no-reload') && !Pry.config.disable_auto_reload
      invoke_editor(file, opts["no-jump"] ? 0 : line, reload)
      silence_warnings do
        load file if reload
      end
    end

    protected
      def extract_file_and_line
        if @method
          if @method.source_type == :c
            raise CommandError, "Can't edit a C method."
          else
            [@method.source_file, @method.source_line]
          end
        else
          [target.eval('__FILE__'), target.eval('__LINE__')]
        end
      end

      def with_method_transaction(meth_name, target=TOPLEVEL_BINDING)
        target = Pry.binding_for(target)
        temp_name = "__pry_#{meth_name}__"

        target.eval("alias #{temp_name} #{meth_name}")
        yield
        target.eval("alias #{meth_name} #{temp_name}")
      ensure
        target.eval("undef #{temp_name}") rescue nil
      end

      # The original name of the method, if it's not present raise an error telling
      # the user why we don't work.
      #
      def original_name
        @method.original_name or raise CommandError, "Pry can only patch methods created with the `def` keyword."
      end

      # Update the definition line so that it can be eval'd directly on the Method's
      # owner instead of from the original context.
      #
      # In particular this takes `def self.foo` and turns it into `def foo` so that we
      # don't end up creating the method on the singleton class of the singleton class
      # by accident.
      #
      # This is necessarily done by String manipulation because we can't find out what
      # syntax is needed for the argument list by ruby-level introspection.
      #
      # @param String   The original definition line. e.g. def self.foo(bar, baz=1)
      # @return String  The new definition line. e.g. def foo(bar, baz=1)
      #
      def definition_line_for_owner(line)
        if line =~ /^def (?:.*?\.)?#{Regexp.escape(original_name)}(?=[\(\s;]|$)/
          "def #{original_name}#{$'}"
        else
          raise CommandError, "Could not find original `def #{original_name}` line to patch."
        end
      end
  end

  create_command(/amend-line(?: (-?\d+)(?:\.\.(-?\d+))?)?/) do
    description "Amend a line of input in multi-line mode."
    command_options :interpolate => false, :listing => "-line"

    banner <<-'BANNER'
      Amend a line of input in multi-line mode. `amend-line N`, where the N in `amend-line N` represents line to replace.

      Can also specify a range of lines using `amend-line N..M` syntax. Passing '!' as replacement content deletes the line(s) instead.
      e.g amend-line 1 puts 'hello world! # replace line 1'
      e.g amend-line 1..4 !               # delete lines 1..4
      e.g amend-line 3 >puts 'goodbye'    # insert before line 3
      e.g amend-line puts 'hello again'   # no line number modifies immediately preceding line
    BANNER

    def process
      start_line_number, end_line_number, replacement_line = *args

      if eval_string.empty?
        raise CommandError, "No input to amend."
      end

      replacement_line = "" if !replacement_line
      input_array = eval_string.each_line.to_a

      end_line_number = start_line_number.to_i if !end_line_number
      line_range = start_line_number ? (one_index_number(start_line_number.to_i)..one_index_number(end_line_number.to_i))  : input_array.size - 1

      # delete selected lines if replacement line is '!'
      if arg_string == "!"
        input_array.slice!(line_range)
      elsif arg_string.start_with?(">")
        insert_slot = Array(line_range).first
        input_array.insert(insert_slot, arg_string[1..-1] + "\n")
      else
        input_array[line_range] = arg_string + "\n"
      end
      eval_string.replace input_array.join
      run "show-input"
    end
  end

  create_command "play" do
    include Helpers::DocumentationHelpers

    description "Play back a string variable or a method or a file as input."

    banner <<-BANNER
      Usage: play [OPTIONS] [--help]

      The play command enables you to replay code from files and methods as
      if they were entered directly in the Pry REPL. Default action (no
      options) is to play the provided string variable

      e.g: `play -i 20 --lines 1..3`
      e.g: `play -m Pry#repl --lines 1..-1`
      e.g: `play -f Rakefile --lines 5`

      https://github.com/pry/pry/wiki/User-Input#wiki-Play
    BANNER

    attr_accessor :content

    def setup
      self.content   = ""
    end

    def options(opt)
      opt.on :m, :method, "Play a method's source.", :argument => true do |meth_name|
        meth = get_method_or_raise(meth_name, target, {})
        self.content << meth.source
      end
      opt.on :d, :doc, "Play a method's documentation.", :argument => true do |meth_name|
        meth = get_method_or_raise(meth_name, target, {})
        text.no_color do
          self.content << process_comment_markup(meth.doc, :ruby)
        end
      end
      opt.on :c, :command, "Play a command's source.", :argument => true do |command_name|
        command = find_command(command_name)
        block = Pry::Method.new(command.block)
        self.content << block.source
      end
      opt.on :f, :file, "Play a file.", :argument => true do |file|
        self.content << File.read(File.expand_path(file))
      end
      opt.on :l, :lines, "Only play a subset of lines.", :optional_argument => true, :as => Range, :default => 1..-1
      opt.on :i, :in, "Play entries from Pry's input expression history. Takes an index or range. Note this can only replay pure Ruby code, not Pry commands.", :optional_argument => true,
      :as => Range, :default => -5..-1 do |range|
        input_expressions = _pry_.input_array[range] || []
        Array(input_expressions).each { |v| self.content << v }
      end
      opt.on :o, "open", 'When used with the -m switch, it plays the entire method except the last line, leaving the method definition "open". `amend-line` can then be used to modify the method.'
    end

    def process
      perform_play
      run "show-input" unless Pry::Code.complete_expression?(eval_string)
    end

    def process_non_opt
      args.each do |arg|
        begin
          self.content << target.eval(arg)
        rescue Pry::RescuableException
          raise CommandError, "Prblem when evaling #{arg}."
        end
      end
    end

    def perform_play
      process_non_opt

      if opts.present?(:lines)
        self.content = restrict_to_lines(self.content, opts[:l])
      end

      if opts.present?(:open)
        self.content = restrict_to_lines(self.content, 1..-2)
      end

      eval_string << self.content
    end
  end
end
Context =
Pry::CommandSet.new do
  import Ls
  import Cd
  import FindMethod

  create_command "whereami" do
    description "Show code surrounding the current context."
    banner <<-BANNER
      Usage: whereami [OPTIONS]
    BANNER

    def setup
      @method = Pry::Method.from_binding(target)
    end

    def process
      if show_method?
        file   = @method.source_file
        start  = @method.source_range.begin
        finish = @method.source_range.end
        marker = target.eval("__LINE__")
      else
        file   = target.eval("__FILE__")
        start  = target.eval("__LINE__")
        finish = (args.first && args.first.to_i) || 5
        marker = start
      end

      if invalid_file?(file)
        raise Pry::CommandError,
        "Cannot find local context. Did you use binding.pry?"
      end

      # TODO: refactor.
      if show_method?
        code = Pry::Code.from_file(file).between(start, finish)
      else
        code = Pry::Code.from_file(file).around(start, finish)
      end

      desc = (@method && @method.name_with_owner) || ""

      if !code.empty?
        set_file_and_dir_locals(file)

        output.puts "\n#{text.bold('From:')} #{file} @ line #{start} #{desc}:\n\n"
        output.puts code.with_line_numbers.with_marker(marker)
        output.puts
      end
    end

    private

    def show_method?
      args.empty? && @method && !@method.instance_of?(Pry::Method::Disowned) && @method.source_range.count < 20
    end

    def invalid_file?(file)
      file != Pry.eval_path &&
      (file =~ /(\(.*\))|<.*>/ || file == "" || file == "-e")
    end
  end

  create_command "pry-backtrace", "Show the backtrace for the Pry session." do
    banner <<-BANNER
      Usage:   pry-backtrace [OPTIONS] [--help]

      Show the backtrace for the position in the code where Pry was started. This can be used to
      infer the behavior of the program immediately before it entered Pry, just like the backtrace
      property of an exception.

      (NOTE: if you are looking for the backtrace of the most recent exception raised,
      just type: `_ex_.backtrace` instead, see https://github.com/pry/pry/wiki/Special-Locals)

      e.g: pry-backtrace
    BANNER

    def process
      output.puts "\n#{text.bold('Backtrace:')}\n--\n"
      stagger_output _pry_.backtrace.join("\n")
    end
  end

  command "reset", "Reset the REPL to a clean state." do
    output.puts "Pry reset."
    exec "pry"
  end

  create_command(/wtf([?!]*)/, "Show the backtrace of the most recent exception") do
    options :listing => 'wtf?'

    banner <<-BANNER
      Show's a few lines of the backtrace of the most recent exception (also available
      as _ex_.backtrace).

      If you want to see more lines, add more question marks or exclamation marks:

      e.g.
      pry(main)> wtf?
      pry(main)> wtf?!???!?!?

      To see the entire backtrace, pass the -v/--verbose flag:

      e.g.
      pry(main)> wtf -v
    BANNER

    def options(opt)
      opt.on(:v, :verbose, "Show the full backtrace.")
    end

    def process
      raise Pry::CommandError, "No most-recent exception" unless _pry_.last_exception

      output.puts "#{text.bold('Exception:')} #{_pry_.last_exception.class}: #{_pry_.last_exception}\n--"
      if opts.verbose?
        output.puts Code.new(_pry_.last_exception.backtrace, 0, :text).with_line_numbers.to_s
      else
        output.puts Code.new(_pry_.last_exception.backtrace.first([captures[0].size, 0.5].max * 10), 0, :text).with_line_numbers.to_s
      end
    end
  end

  # N.B. using a regular expresion here so that "raise-up 'foo'" does the right thing.
  create_command(/raise-up(!?\b.*)/, :listing => 'raise-up') do
    description "Raise an exception out of the current pry instance."
    banner <<-BANNER
      Raise up, like exit, allows you to quit pry. Instead of returning a value however, it raises an exception.
      If you don't provide the exception to be raised, it will use the most recent exception (in pry _ex_).

      e.g. `raise-up "get-me-out-of-here"` is equivalent to:
           `raise "get-me-out-of-here"
            raise-up`

      When called as raise-up! (with an exclamation mark), this command raises the exception through
      any nested prys you have created by "cd"ing into objects.
    BANNER

    def process
      return stagger_output help if captures[0] =~ /(-h|--help)\b/
      # Handle 'raise-up', 'raise-up "foo"', 'raise-up RuntimeError, 'farble' in a rubyesque manner
      target.eval("_pry_.raise_up#{captures[0]}")
    end
  end
end
Commands =
Pry::CommandSet.new do
  create_command "import-set", "Import a command set" do
    group "Commands"
    def process(command_set_name)
      raise CommandError, "Provide a command set name" if command_set.nil?

      set = target.eval(arg_string)
      _pry_.commands.import set
    end
  end

  create_command "install-command", "Install a disabled command." do |name|
    group 'Commands'

    banner <<-BANNER
      Usage: install-command COMMAND

      Installs the gems necessary to run the given COMMAND. You will generally not
      need to run this unless told to by an error message.
    BANNER

    def process(name)
      require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller
      command = find_command(name)

      if command_dependencies_met?(command.options)
        output.puts "Dependencies for #{command.name} are met. Nothing to do."
        return
      end

      output.puts "Attempting to install `#{name}` command..."
      gems_to_install = Array(command.options[:requires_gem])

      gems_to_install.each do |g|
        next if gem_installed?(g)
        output.puts "Installing `#{g}` gem..."

        begin
          Gem::DependencyInstaller.new.install(g)
        rescue Gem::GemNotFoundException
          raise CommandError, "Required Gem: `#{g}` not found. Aborting command installation."
        end
      end

      Gem.refresh
      gems_to_install.each do |g|
        begin
          require g
        rescue LoadError
          raise CommandError, "Required Gem: `#{g}` installed but not found?!. Aborting command installation."
        end
      end

      output.puts "Installation of `#{name}` successful! Type `help #{name}` for information"
    end
  end
end
EasterEggs =
Pry::CommandSet.new do

  command "nyan-cat", "", :requires_gem => ["nyancat"] do
    run ".nyancat"
  end

  command(/!s\/(.*?)\/(.*?)/, "") do |source, dest|
    eval_string.gsub!(/#{source}/) { dest }
    run "show-input"
  end

  command "get-naked", "" do
    text = %{
--
We dont have to take our clothes off to have a good time.
We could dance & party all night And drink some cherry wine.
-- Jermaine Stewart }
    output.puts text
    text
  end

  command "east-coker", "" do
    text = %{
--
Now the light falls
Across the open field, leaving the deep lane
Shuttered with branches, dark in the afternoon,
Where you lean against a bank while a van passes,
And the deep lane insists on the direction
Into the village, in the electric heat
Hypnotised. In a warm haze the sultry light
Is absorbed, not refracted, by grey stone.
The dahlias sleep in the empty silence.
Wait for the early owl.
-- T.S Eliot
        }
    output.puts text
    text
  end

  command "cohen-poem", "" do
    text = %{
--
When this American woman,
whose thighs are bound in casual red cloth,
comes thundering past my sitting place
like a forest-burning Mongol tribe,
the city is ravished
and brittle buildings of a hundred years
splash into the street;
and my eyes are burnt
for the embroidered Chinese girls,
already old,
and so small between the thin pines
on these enormous landscapes,
that if you turn your head
they are lost for hours.
              -- Leonard Cohen
            }
  output.puts text
  text
end

  command "test-ansi", "" do
    prev_color = Pry.color
    Pry.color = true

    picture = unindent <<-'EOS'.gsub(/[[:alpha:]!]/) { |s| text.red(s) }
       ____      _______________________
      /    \    |  A         W     G    |
     / O  O \   |   N    I    O   N !   |
    |        |  |    S    S    R I   !  |
     \ \__/ / __|     I         K     ! |
      \____/   \________________________|
    EOS

    if defined?(Win32::Console)
      move_up = proc { |n| "\e[#{n}F" }
    else
      move_up = proc { |n| "\e[#{n}A\e[0G" }
    end

    output.puts "\n" * 6
    output.puts picture.lines.map(&:chomp).reverse.join(move_up[1])
    output.puts "\n" * 6
    output.puts "** ENV['TERM'] is #{ENV['TERM']} **\n\n"

    Pry.color = prev_color
  end
end
FindMethod =
Pry::CommandSet.new do

  create_command "find-method" do
    extend Helpers::BaseHelpers

    group "Context"

    options :requires_gem => "ruby18_source_location" if mri_18?

    description "Recursively search for a method within a Class/Module or the current namespace. find-method [-n | -c] METHOD [NAMESPACE]"

    banner <<-BANNER
      Usage: find-method  [-n | -c] METHOD [NAMESPACE]

      Recursively search for a method within a Class/Module or the current namespace.
      Use the `-n` switch (the default) to search for methods whose name matches the given regex.
      Use the `-c` switch to search for methods that contain the given code.

      e.g find-method re Pry                # find all methods whose name match /re/ inside the Pry namespace. Matches Pry#repl, etc.
      e.g find-method -c 'output.puts' Pry  # find all methods that contain the code: output.puts inside the Pry namepsace.
    BANNER

    def setup
      require 'ruby18_source_location' if mri_18?
    end

    def options(opti)
      opti.on :n, :name, "Search for a method by name"
      opti.on :c, :content, "Search for a method based on content in Regex form"
    end

    def process
      return if args.size < 1
      pattern = ::Regexp.new args[0]
      if args[1]
        klass = target.eval(args[1])
        if !klass.is_a?(Module)
          klass = klass.class
        end
      else
        klass = (target_self.is_a?(Module)) ? target_self : target_self.class
      end

      matches = if opts.content?
                  content_search(pattern, klass)
                else
                  name_search(pattern, klass)
                end

      if matches.empty?
        output.puts text.bold("No Methods Matched")
      else
        print_matches(matches, pattern)
      end

    end

    private

    # pretty-print a list of matching methods.
    #
    # @param Array[Method]
    def print_matches(matches, pattern)
      grouped = matches.group_by(&:owner)
      order = grouped.keys.sort_by{ |x| x.name || x.to_s }

      order.each do |klass|
        output.puts text.bold(klass.name)
        grouped[klass].each do |method|
          header = method.name_with_owner

          extra = if opts.content?
                    header += ": "
                    colorize_code((method.source.split(/\n/).select {|x| x =~ pattern }).join("\n#{' ' * header.length}"))
                  else
                    ""
                  end

          output.puts header + extra
        end
      end
    end

    # Run the given block against every constant in the provided namespace.
    #
    # @param Module  The namespace in which to start the search.
    # @param Hash[Module,Boolean]  The namespaces we've already visited (private)
    # @yieldparam klazz  Each class/module in the namespace.
    #
    def recurse_namespace(klass, done={}, &block)
      return if !(Module === klass) || done[klass]

      done[klass] = true

      yield klass

      klass.constants.each do |name|
        next if klass.autoload?(name)
        begin
          const = klass.const_get(name)
        rescue RescuableException
          # constant loading is an inexact science at the best of times,
          # this often happens when a constant was .autoload? but someone
          # tried to load it. It's now not .autoload? but will still raise
          # a NameError when you access it.
        else
          recurse_namespace(const, done, &block)
        end
      end
    end

    # Gather all the methods in a namespace that pass the given block.
    #
    # @param Module  The namespace in which to search.
    # @yieldparam Method  The method to test
    # @yieldreturn Boolean
    # @return Array[Method]
    #
    def search_all_methods(namespace)
      done = Hash.new{ |h,k| h[k] = {} }
      matches = []

      recurse_namespace(namespace) do |klass|
        (Pry::Method.all_from_class(klass) + Pry::Method.all_from_obj(klass)).each do |method|
          next if done[method.owner][method.name]
          done[method.owner][method.name] = true

          matches << method if yield method
        end
      end

      matches
    end

    # Search for all methods with a name that matches the given regex
    # within a namespace.
    #
    # @param Regex  The regex to search for
    # @param Module  The namespace to search
    # @return Array[Method]
    #
    def name_search(regex, namespace)
      search_all_methods(namespace) do |meth|
        meth.name =~ regex
      end
    end

    # Search for all methods who's implementation matches the given regex
    # within a namespace.
    #
    # @param Regex  The regex to search for
    # @param Module  The namespace to search
    # @return Array[Method]
    #
    def content_search(regex, namespace)
      search_all_methods(namespace) do |meth|
        begin
          meth.source =~ regex
        rescue RescuableException
          false
        end
      end
    end
  end
end
Introspection =
Pry::CommandSet.new do

  create_command "show-doc", "Show the documentation for a method or class. Aliases: \?", :shellwords => false do
    include ModuleIntrospectionHelpers
    include Helpers::DocumentationHelpers
    extend Helpers::BaseHelpers

    banner <<-BANNER
      Usage: show-doc [OPTIONS] [METH]
      Aliases: ?

      Show the documentation for a method or class. Tries instance methods first and then methods by default.
      e.g show-doc hello_method    # docs for hello_method
      e.g show-doc Pry             # docs for Pry class
      e.g show-doc Pry -a          # docs for all definitions of Pry class (all monkey patches)
    BANNER

    options :requires_gem => "ruby18_source_location" if mri_18?

    def setup
      require 'ruby18_source_location' if mri_18?
    end

    def options(opt)
      method_options(opt)
      opt.on :l, "line-numbers", "Show line numbers."
      opt.on :b, "base-one", "Show line numbers but start numbering at 1 (useful for `amend-line` and `play` commands)."
      opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
      opt.on :a, :all, "Show docs for all definitions and monkeypatches of the module/class"
    end

    def process_module
      if opts.present?(:all)
        all_modules
      else
        normal_module
      end
    end

    def normal_module
      mod = module_object

      # source_file reveals the underlying .c file in case of core
      # classes on MRI. This is different to source_location, which
      # will return nil.
      if mod.yard_docs?
        file_name, line = mod.source_file, nil
      else
        file_name, line = mod.source_location
      end

      if mod.doc.empty?
        output.puts "No documentation found."
        ""
      else
        set_file_and_dir_locals(file_name) if !mod.yard_docs?
        doc = ""
        doc << mod.doc

        doc = Code.new(doc, module_start_line(mod), :text).
          with_line_numbers(use_line_numbers?).to_s

        doc.insert(0, "\n#{Pry::Helpers::Text.bold('From:')} #{file_name} @ line #{line ? line : "N/A"}:\n\n")
      end
    end

    def all_modules
      mod = module_object

      doc = ""
      doc << "Found #{mod.number_of_candidates} candidates for `#{mod.name}` definition:\n"
      mod.number_of_candidates.times do |v|
        begin
          doc << "\nCandidate #{v+1}/#{mod.number_of_candidates}: #{mod.source_file_for_candidate(v)} @ #{mod.source_line_for_candidate(v)}:\n\n"
          dc = mod.doc_for_candidate(v)
          doc << (dc.empty? ? "No documentation found.\n" : dc)
        rescue Pry::RescuableException
          next
        end
      end
      doc
    end

    def process_method
      meth = method_object
      raise Pry::CommandError, "No documentation found." if meth.doc.nil? || meth.doc.empty?

      doc = process_comment_markup(meth.doc, meth.source_type)
      output.puts make_header(meth, doc)
      output.puts "#{text.bold("Owner:")} #{meth.owner || "N/A"}"
      output.puts "#{text.bold("Visibility:")} #{meth.visibility}"
      output.puts "#{text.bold("Signature:")} #{meth.signature}"
      output.puts

      if use_line_numbers?
        doc = Code.new(doc, start_line, :text).
          with_line_numbers(true).to_s
      end

      doc
    end

    def module_start_line(mod, candidate=0)
      if opts.present?(:base-one')
        1
      else
        if mod.source_line_for_candidate(candidate)
          mod.source_line_for_candidate(candidate) - mod.doc_for_candidate(candidate).lines.count
        else
          1
        end
      end
    end

    def start_line
      if opts.present?(:base-one')
         1
      else
        (method_object.source_line - method_object.doc.lines.count) || 1
      end
    end
  end

  alias_command "?", "show-doc"

  create_command "stat", "View method information and set _file_ and _dir_ locals.", :shellwords => false do
    banner <<-BANNER
        Usage: stat [OPTIONS] [METH]
        Show method information for method METH and set _file_ and _dir_ locals.
        e.g: stat hello_method
    BANNER

    def options(opt)
      method_options(opt)
    end

    def process
      meth = method_object
      output.puts unindent <<-EOS
        Method Information:
        --
        Name: #{meth.name}
        Owner: #{meth.owner ? meth.owner : "Unknown"}
        Visibility: #{meth.visibility}
        Type: #{meth.is_a?(::Method) ? "Bound" : "Unbound"}
        Arity: #{meth.arity}
        Method Signature: #{meth.signature}
        Source Location: #{meth.source_location ? meth.source_location.join(":") : "Not found."}
      EOS
    end
  end

  create_command "show-source" do
    include ModuleIntrospectionHelpers
    extend Helpers::BaseHelpers

    description "Show the source for a method or class. Aliases: $, show-method"

    banner <<-BANNER
      Usage: show-source [OPTIONS] [METH|CLASS]
      Aliases: $, show-method

      Show the source for a method or class. Tries instance methods first and then methods by default.

      e.g: `show-source hello_method`
      e.g: `show-source -m hello_method`
      e.g: `show-source Pry#rep`         # source for Pry#rep method
      e.g: `show-source Pry`             # source for Pry class
      e.g: `show-source Pry -a`          # source for all Pry class definitions (all monkey patches)

      https://github.com/pry/pry/wiki/Source-browsing#wiki-Show_method
    BANNER

    options :shellwords => false
    options :requires_gem => "ruby18_source_location" if mri_18?

    def setup
      require 'ruby18_source_location' if mri_18?
    end

    def options(opt)
      method_options(opt)
      opt.on :l, "line-numbers", "Show line numbers."
      opt.on :b, "base-one", "Show line numbers but start numbering at 1 (useful for `amend-line` and `play` commands)."
      opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
      opt.on :a, :all, "Show source for all definitions and monkeypatches of the module/class"
    end

    def process_method
      raise CommandError, "Could not find method source" unless method_object.source

      code = ""
      code << make_header(method_object)
      code << "#{text.bold("Owner:")} #{method_object.owner || "N/A"}\n"
      code << "#{text.bold("Visibility:")} #{method_object.visibility}\n"
      code << "\n"

      code << Code.from_method(method_object, start_line).
               with_line_numbers(use_line_numbers?).to_s
    end

    def process_module
      if opts.present?(:all)
        all_modules
      else
        normal_module
      end
    end

    def normal_module
      mod = module_object

      file_name, line = mod.source_location
      set_file_and_dir_locals(file_name)
      code = Code.from_module(mod, module_start_line(mod)).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 << code
    end

    def all_modules
      mod = module_object

      result = ""
      result << "Found #{mod.number_of_candidates} candidates for `#{mod.name}` definition:\n"
      mod.number_of_candidates.times do |v|
        begin
          code = Code.new(mod.source_for_candidate(v), module_start_line(mod, v)).with_line_numbers(use_line_numbers?).to_s
          result << "\nCandidate #{v+1}/#{mod.number_of_candidates}: #{mod.source_file_for_candidate(v)} @ line #{mod.source_line_for_candidate(v)}:\n"
          result << "Number of lines: #{code.lines.count}\n\n"
          result << code
        rescue Pry::RescuableException
          next
        end
      end
      result
    end

    def use_line_numbers?
      opts.present?(:b) || opts.present?(:l)
    end

    def start_line
      if opts.present?(:base-one')
        1
      else
        method_object.source_line || 1
      end
    end
  end

  alias_command "show-method", "show-source"
  alias_command "$", "show-source"

  command "show-command", "Show the source for CMD." do |*args|
    target = target()

    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
      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

    next 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)

      next unless block.source
      set_file_and_dir_locals(block.source_file)

      output.puts make_header(block)
      output.puts

      code = 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
  end

  create_command "ri", "View ri documentation. e.g `ri Array#each`" do
    banner <<-BANNER
      Usage: ri [spec]
      e.g. ri Array#each

      Relies on the rdoc gem being installed. See also: show-doc.
    BANNER

    def process(spec)
      # Lazily load RI
      require 'rdoc/ri/driver'

      unless defined? RDoc::RI::PryDriver

        # Subclass RI so that it formats its output nicely, and uses `lesspipe`.
        subclass = Class.new(RDoc::RI::Driver) # the hard way.

        subclass.class_eval do
          def page
            Pry::Helpers::BaseHelpers.lesspipe {|less| yield less}
          end

          def formatter(io)
            if @formatter_klass then
              @formatter_klass.new
            else
              RDoc::Markup::ToAnsi.new
            end
          end
        end

        RDoc::RI.const_set :PryDriver, subclass   # hook it up!
      end

      # Spin-up an RI insance.
      ri = RDoc::RI::PryDriver.new :use_stdout => true, :interactive => false

      begin
        ri.display_names [spec]  # Get the documentation (finally!)
      rescue RDoc::RI::Driver::NotFoundError => e
        output.puts "error: '#{e.name}' not found"
      end
    end

  end

end
Pry::CommandSet.new do
  command "switch-to", "Start a new sub-session on a binding in the current stack (numbered by nesting)." do |selection|
    selection = selection.to_i

    if selection < 0 || selection > _pry_.binding_stack.size - 1
      raise CommandError, "Invalid binding index #{selection} - use `nesting` command to view valid indices."
    else
      Pry.start(_pry_.binding_stack[selection])
    end
  end

  command "nesting", "Show nesting information." do
    output.puts "Nesting status:"
    output.puts "--"
    _pry_.binding_stack.each_with_index do |obj, level|
      if level == 0
        output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))} (Pry top level)"
      else
        output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))}"
      end
    end
  end

  command "jump-to", "Jump to a binding further up the stack, popping all bindings below." do |break_level|
    break_level = break_level.to_i
    nesting_level = _pry_.binding_stack.size - 1

    case break_level
    when nesting_level
      output.puts "Already at nesting level #{nesting_level}"
    when (0...nesting_level)
      _pry_.binding_stack.slice!(break_level + 1, _pry_.binding_stack.size)

    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-all", "End the current Pry session (popping all bindings) and returning to caller. Accepts optional return value. Aliases: !!@" do
    # calculate user-given value
    exit_value = target.eval(arg_string)

    # clear the binding stack
    _pry_.binding_stack.clear

    # break out of the repl loop
    throw(:breakout, exit_value)
  end

  alias_command "!!@", "exit-all"

  create_command "exit" do
    description "Pop the previous binding (does NOT exit program). Aliases: quit"

    banner <<-BANNER
      Usage:   exit [OPTIONS] [--help]
      Aliases: quit

      It can be useful to exit a context with a user-provided value. For
      instance an exit value can be used to determine program flow.

      e.g: `exit "pry this"`
      e.g: `exit`

      https://github.com/pry/pry/wiki/State-navigation#wiki-Exit_with_value
    BANNER

    command_options(
                    :keep_retval => true
                    )

    def process
      if _pry_.binding_stack.one?
        _pry_.run_command "exit-all #{arg_string}"
      else
        # otherwise just pop a binding and return user supplied value
        process_pop_and_return
      end
    end

    def process_pop_and_return
      popped_object = _pry_.binding_stack.pop.eval('self')

      # return a user-specified value if given otherwise return the object
      return target.eval(arg_string) unless arg_string.empty?
      popped_object
    end
  end

  alias_command "quit", "exit"

  command "exit-program", "End the current program. Aliases: quit-program, !!!" do
    Pry.save_history if Pry.config.history.should_save
    Kernel.exit target.eval(arg_string).to_i
  end

  alias_command "quit-program", "exit-program"
  alias_command "!!!", "exit-program"

  command "!pry", "Start a Pry session on current self; this even works mid multi-line expression." do
    target.pry
  end

end
InputAndOutput =
Pry::CommandSet.new do
  command(/\.(.*)/, "All text following a '.' is forwarded to the shell.", :listing => ".<shell command>", :use_prefix => false, :takes_block => true) do |cmd|
    if cmd =~ /^cd\s+(.+)/i
      dest = $1
      begin
        Dir.chdir File.expand_path(dest)
      rescue Errno::ENOENT
        raise CommandError, "No such directory: #{dest}"
      end
    else
      pass_block(cmd)

      if command_block
        command_block.call `#{cmd}`
      else
        Pry.config.system.call(output, cmd, _pry_)
      end
    end
  end

  command "shell-mode", "Toggle shell mode. Bring in pwd prompt and file completion." do
    case _pry_.prompt
    when Pry::SHELL_PROMPT
      _pry_.pop_prompt
      _pry_.custom_completions = Pry::DEFAULT_CUSTOM_COMPLETIONS
    else
      _pry_.push_prompt Pry::SHELL_PROMPT
      _pry_.custom_completions = Pry::FILE_COMPLETIONS
      Readline.completion_proc = Pry::InputCompleter.build_completion_proc target,
      _pry_.instance_eval(&Pry::FILE_COMPLETIONS)
    end
  end
  alias_command "file-mode", "shell-mode"

  create_command "gist", "Gist a method or expression history to github.", :requires_gem => "gist" do

    include Pry::Helpers::DocumentationHelpers

    banner <<-USAGE
      Usage: gist [OPTIONS] [METH]
      Gist method (doc or source) or input expression to github.
      Ensure the `gist` gem is properly working before use. http://github.com/defunkt/gist for instructions.
      e.g: gist -m my_method       # gist the method my_method
      e.g: gist -d my_method       # gist the documentation for my_method
      e.g: gist -i 1..10           # gist the input expressions from 1 to 10
      e.g: gist -k show-method     # gist the command show-method
      e.g: gist -c Pry             # gist the Pry class
      e.g: gist -m hello_world --lines 2..-2    # gist from lines 2 to the second-last of the hello_world method
      e.g: gist -m my_method --clip             # Copy my_method source to clipboard, do not gist it.
    USAGE

    command_options :shellwords => false

    attr_accessor :content
    attr_accessor :code_type

    def setup
      require 'gist'
      self.content   = ""
      self.code_type = :ruby
    end

    def options(opt)
      opt.on :m, :method, "Gist a method's source.", :argument => true do |meth_name|
        meth = get_method_or_raise(meth_name, target, {})
        self.content << meth.source << "\n"
        self.code_type = meth.source_type
      end
      opt.on :d, :doc, "Gist a method's documentation.", :argument => true do |meth_name|
        meth = get_method_or_raise(meth_name, target, {})
        text.no_color do
          self.content << process_comment_markup(meth.doc, self.code_type) << "\n"
        end
        self.code_type = :plain
      end
      opt.on :k, :command, "Gist a command's source.", :argument => true do |command_name|
        command = find_command(command_name)
        block = Pry::Method.new(command.block)
        self.content << block.source << "\n"
      end
      opt.on :c, :class, "Gist a class or module's source.", :argument => true do |class_name|
        mod = Pry::WrappedModule.from_str(class_name, target)
        self.content << mod.source << "\n"
      end
      opt.on :var, "Gist a variable's content.", :argument => true do |variable_name|
        begin
          obj = target.eval(variable_name)
        rescue Pry::RescuableException
          raise CommandError, "Gist failed: Invalid variable name: #{variable_name}"
        end

        self.content << Pry.config.gist.inspecter.call(obj) << "\n"
      end
      opt.on :hist, "Gist a range of Readline history lines.",  :optional_argument => true, :as => Range, :default => -20..-1 do |range|
        h = Pry.history.to_a
        self.content << h[one_index_range(convert_to_range(range))].join("\n") << "\n"
      end

      opt.on :f, :file, "Gist a file.", :argument => true do |file|
        self.content << File.read(File.expand_path(file)) << "\n"
      end
      opt.on :o, :out, "Gist entries from Pry's output result history. Takes an index or range.", :optional_argument => true,
      :as => Range, :default => -5..-1 do |range|
        range = convert_to_range(range)

        range.each do |v|
          self.content << Pry.config.gist.inspecter.call(_pry_.output_array[v])
        end

        self.content << "\n"
      end
      opt.on :clip, "Copy the selected content to clipboard instead, do NOT gist it.", :default => false
      opt.on :p, :public, "Create a public gist (default: false)", :default => false
      opt.on :l, :lines, "Only gist a subset of lines from the gistable content.", :optional_argument => true, :as => Range, :default => 1..-1
      opt.on :i, :in, "Gist entries from Pry's input expression history. Takes an index or range.", :optional_argument => true,
      :as => Range, :default => -5..-1 do |range|
        range = convert_to_range(range)
        input_expressions = _pry_.input_array[range] || []
        Array(input_expressions).each_with_index do |code, index|
          corrected_index = index + range.first
          if code && code != ""
            self.content << code
            if code !~ /;\Z/
              self.content << "#{comment_expression_result_for_gist(Pry.config.gist.inspecter.call(_pry_.output_array[corrected_index]))}"
            end
          end
        end
      end
    end

    def process
      if self.content =~ /\A\s*\z/
        raise CommandError, "Found no code to gist."
      end

      if opts.present?(:clip)
        perform_clipboard
      else
        perform_gist
      end
    end

    # copy content to clipboard instead (only used with --clip flag)
    def perform_clipboard
      Gist.copy(self.content)
      output.puts "Copied content to clipboard!"
    end

    def perform_gist
      type_map = { :ruby => "rb", :c => "c", :plain => "plain" }

      # prevent Gist from exiting the session on error
      begin
        extname = opts.present?(:file) ? ".#{gist_file_extension(opts[:f])}" : ".#{type_map[self.code_type]}"

        if opts.present?(:lines)
          self.content = restrict_to_lines(content, opts[:l])
        end

        link = Gist.write([:extension => extname,
                           :input => self.content],
                          !opts[:p])
      rescue SystemExit
      end

      if link
        Gist.copy(link)
        output.puts "Gist created at #{link} and added to clipboard."
      end
    end

    def gist_file_extension(file_name)
      file_name.split(".").last
    end

    def convert_to_range(n)
      if !n.is_a?(Range)
        (n..n)
      else
        n
      end
    end

    def comment_expression_result_for_gist(result)
      content = ""
      result.lines.each_with_index do |line, index|
        if index == 0
          content << "# => #{line}"
        else
          content << "#    #{line}"
        end
      end
      content
    end
  end

  alias_command "clipit", "gist --clip"

  create_command "save-file", "Export to a file using content from the REPL."  do
    banner <<-USAGE
      Usage: save-file [OPTIONS] [FILE]
      Save REPL content to a file.
      e.g: save-file -m my_method -m my_method2 ./hello.rb
      e.g: save-file -i 1..10 ./hello.rb --append
      e.g: save-file -k show-method ./my_command.rb
      e.g: save-file -f sample_file --lines 2..10 ./output_file.rb
    USAGE

    attr_accessor :content
    attr_accessor :file_name

    def setup
      self.content = ""
    end

    def convert_to_range(n)
      if !n.is_a?(Range)
        (n..n)
      else
        n
      end
    end

    def options(opt)
      opt.on :m, :method, "Save a method's source.", :argument => true do |meth_name|
        meth = get_method_or_raise(meth_name, target, {})
        self.content << meth.source
      end
      opt.on :c, :class, "Save a class's source.", :argument => true do |class_name|
        mod = Pry::WrappedModule.from_str(class_name, target)
        self.content << mod.source
      end
      opt.on :k, :command, "Save a command's source.", :argument => true do |command_name|
        command = find_command(command_name)
        block = Pry::Method.new(command.block)
        self.content << block.source
      end
      opt.on :f, :file, "Save a file.", :argument => true do |file|
        self.content << File.read(File.expand_path(file))
      end
      opt.on :l, :lines, "Only save a subset of lines.", :optional_argument => true, :as => Range, :default => 1..-1
      opt.on :o, :out, "Save entries from Pry's output result history. Takes an index or range.", :optional_argument => true,
      :as => Range, :default => -5..-1 do |range|
        range = convert_to_range(range)

        range.each do |v|
          self.content << Pry.config.gist.inspecter.call(_pry_.output_array[v])
        end

        self.content << "\n"
      end
      opt.on :i, :in, "Save entries from Pry's input expression history. Takes an index or range.", :optional_argument => true,
      :as => Range, :default => -5..-1 do |range|
        input_expressions = _pry_.input_array[range] || []
        Array(input_expressions).each { |v| self.content << v }
      end
      opt.on :a, :append, "Append to the given file instead of overwriting it."
    end

    def process
      if args.empty?
        raise CommandError, "Must specify a file name."
      end

      self.file_name = File.expand_path(args.first)

      save_file
    end

    def save_file
      if self.content.empty?
        raise CommandError, "Found no code to save."
      end

      File.open(file_name, mode) do |f|
        if opts.present?(:lines)
          f.puts restrict_to_lines(content, opts[:l])
        else
          f.puts content
        end
      end
    end

    def mode
      if opts.present?(:append)
        "a"
      else
        "w"
      end
    end
  end

  create_command "cat", "Show code from a file, Pry's input buffer, or the last exception." do
    banner <<-USAGE
      Usage: cat FILE
             cat --ex [STACK_INDEX]
             cat --in [INPUT_INDEX_OR_RANGE]

      cat is capable of showing part or all of a source file, the context of the
      last exception, or an expression from Pry's input history.

      cat --ex defaults to showing the lines surrounding the location of the last
      exception. Invoking it more than once travels up the exception's backtrace,
      and providing a number shows the context of the given index of the backtrace.
    USAGE

    def options(opt)
      opt.on :ex,        "Show the context of the last exception.", :optional_argument => true, :as => Integer
      opt.on :i, :in,    "Show one or more entries from Pry's expression history.", :optional_argument => true, :as => Range, :default => -5..-1

      opt.on :s, :start, "Starting line (defaults to the first line).", :optional_argument => true, :as => Integer
      opt.on :e, :end,   "Ending line (defaults to the last line).", :optional_argument => true, :as => Integer
      opt.on :l, :line-numbers', "Show line numbers."
      opt.on :t, :type,  "The file type for syntax highlighting (e.g., 'ruby' or 'python').", :argument => true, :as => Symbol

      opt.on :f, :flood, "Do not use a pager to view text longer than one screen."
    end

    def process
      handler = case
                when opts.present?(:ex)
                  method :process_ex
                when opts.present?(:in)
                  method :process_in
                else
                  method :process_file
                end

      output = handler.call do |code|
        code.code_type = opts[:type] || :ruby

        code.between(opts[:start] || 1, opts[:end] || -1).
          with_line_numbers(opts.present?(:line-numbers') || opts.present?(:ex))
      end

      render_output(output, opts)
    end

    def process_ex
      window_size = Pry.config.default_window_size || 5
      ex = _pry_.last_exception

      raise CommandError, "No exception found." unless ex

      if opts[:ex].nil?
        bt_index = ex.bt_index
        ex.inc_bt_index
      else
        bt_index = opts[:ex]
      end

      ex_file, ex_line = ex.bt_source_location_for(bt_index)

      raise CommandError, "The given backtrace level is out of bounds." unless ex_file

      if RbxPath.is_core_path?(ex_file)
        ex_file = RbxPath.convert_path_to_full(ex_file)
      end

      set_file_and_dir_locals(ex_file)

      start_line = ex_line - window_size
      start_line = 1 if start_line < 1
      end_line = ex_line + window_size

      header = unindent <<-HEADER
        #{text.bold 'Exception:'} #{ex.class}: #{ex.message}
        --
        #{text.bold('From:')} #{ex_file} @ line #{ex_line} @ #{text.bold("level: #{bt_index}")} of backtrace (of #{ex.backtrace.size - 1}).

      HEADER

      code = yield(Pry::Code.from_file(ex_file).
                   between(start_line, end_line).
                   with_marker(ex_line))

      "#{header}#{code}"
    end

    def process_in
      normalized_range = absolute_index_range(opts[:i], _pry_.input_array.length)
      input_items = _pry_.input_array[normalized_range] || []

      zipped_items = normalized_range.zip(input_items).reject { |_, s| s.nil? || s == "" }

      unless zipped_items.length > 0
        raise CommandError, "No expressions found."
      end

      if zipped_items.length > 1
        contents = ""
        zipped_items.each do |i, s|
          contents << "#{text.bold(i.to_s)}:\n"
          contents << yield(Pry::Code(s).with_indentation(2)).to_s
        end
      else
        contents = yield(Pry::Code(zipped_items.first.last))
      end

      contents
    end

    def process_file
      file_name = args.shift

      unless file_name
        raise CommandError, "Must provide a filename, --in, or --ex."
      end

      file_name, line_num = file_name.split(':')
      file_name = File.expand_path(file_name)
      set_file_and_dir_locals(file_name)

      code = yield(Pry::Code.from_file(file_name))

      if line_num
        code = code.around(line_num.to_i,
                           Pry.config.default_window_size || 7)
      end

      code
    end
  end

end