class Pry::Command::Edit

def apply_runtime_patch

def apply_runtime_patch
  if patch_exception?
    ExceptionPatcher.new(
      pry_instance, state, file_and_line_for_current_exception
    ).perform_patch
  elsif code_object.is_a?(Pry::Method)
    code_object.redefine(
      Pry::Editor.new(pry_instance).edit_tempfile_with_content(
        code_object.source
      )
    )
  else
    raise NotImplementedError, "Cannot yet patch #{code_object} objects!"
  end
end

def bad_option_combination?

def bad_option_combination?
  [
    opts.present?(:ex), opts.present?(:temp),
    opts.present?(:in), opts.present?(:method),
    !filename_argument.empty?
  ].count(true) > 1
end

def code_object

def code_object
  @code_object ||=
    !probably_a_file?(filename_argument) &&
    Pry::CodeObject.lookup(filename_argument, pry_instance)
end

def ensure_file_name_is_valid(file_name)

def ensure_file_name_is_valid(file_name)
  unless file_name
    raise CommandError, "Cannot find a valid file for #{filename_argument}"
  end
  return unless not_a_real_file?(file_name)
  raise CommandError, "#{file_name} is not a valid file name, cannot edit!"
end

def file_and_line

def file_and_line
  file_name, line =
    if opts.present?(:current)
      FileAndLineLocator.from_binding(target)
    elsif opts.present?(:ex)
      file_and_line_for_current_exception
    elsif code_object
      FileAndLineLocator.from_code_object(code_object, filename_argument)
    else
      # when file and line are passed as a single arg, e.g my_file.rb:30
      FileAndLineLocator.from_filename_argument(filename_argument)
    end
  [file_name, opts.present?(:line) ? opts[:l].to_i : line]
end

def file_and_line_for_current_exception

def file_and_line_for_current_exception
  FileAndLineLocator.from_exception(pry_instance.last_exception, opts[:ex].to_i)
end

def file_based_exception?

def file_based_exception?
  opts.present?(:ex) && !opts.present?(:patch)
end

def file_edit

def file_edit
  file_name, line = file_and_line
  ensure_file_name_is_valid(file_name)
  Pry::Editor.new(pry_instance).invoke_editor(file_name, line, reload?(file_name))
  set_file_and_dir_locals(file_name)
  return unless reload?(file_name)
  silence_warnings { load(file_name) }
end

def filename_argument

def filename_argument
  args.join(' ')
end

def initial_temp_file_content

def initial_temp_file_content
  if opts.present?(:temp)
    ""
  elsif opts.present?(:in)
    input_expression
  elsif eval_string.strip != ""
    eval_string
  else
    pry_instance.input_ring.to_a.reverse_each.find { |x| x && x.strip != "" } || ""
  end
end

def input_expression

def input_expression
  case opts[:i]
  when Range
    (pry_instance.input_ring[opts[:i]] || []).join
  when Integer
    pry_instance.input_ring[opts[:i]] || ""
  else
    raise Pry::CommandError, "Not a valid range: #{opts[:i]}"
  end
end

def never_reload?

def never_reload?
  opts.present?(:'no-reload') || pry_instance.config.disable_auto_reload
end

def options(opt)

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 input " \
                  "expression. 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 file"
  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)"
  opt.on :p, :patch, "Instead of editing the object's file, try to edit " \
                     "in a tempfile and apply as a monkey patch"
  opt.on :m, :method, "Explicitly edit the _current_ method (when " \
                      "inside a method context)."
end

def patch_exception?

def patch_exception?
  opts.present?(:ex) && opts.present?(:patch)
end

def previously_patched?(code_object)

def previously_patched?(code_object)
  code_object.is_a?(Pry::Method) &&
    Pry::Method::Patcher.code_for(code_object.source_location.first)
end

def probably_a_file?(str)

def probably_a_file?(str)
  [".rb", ".c", ".py", ".yml", ".gemspec"].include?(File.extname(str)) ||
    str =~ %r{/|\\}
end

def process

def process
  if bad_option_combination?
    raise CommandError, "Only one of --ex, --temp, --in, --method and " \
                        "FILE may be specified."
  end
  if repl_edit?
    # code defined in pry, eval'd within pry.
    repl_edit
  elsif runtime_patch?
    # patch code without persisting changes, implies future changes are patches
    apply_runtime_patch
  else
    # code stored in actual files, eval'd at top-level
    file_edit
  end
end

def pry_method?(code_object)

def pry_method?(code_object)
  code_object.is_a?(Pry::Method) &&
    code_object.pry_method?
end

def reload?(file_name = "")

def reload?(file_name = "")
  (reloadable? || file_name.end_with?(".rb")) && !never_reload?
end

def reloadable?

def reloadable?
  opts.present?(:reload) || opts.present?(:ex)
end

def repl_edit

def repl_edit
  content = Pry::Editor.new(pry_instance).edit_tempfile_with_content(
    initial_temp_file_content,
    initial_temp_file_content.lines.count
  )
  pry_instance.eval_string = content
  Pry.history.push(content)
end

def repl_edit?

def repl_edit?
  !opts.present?(:ex) && !opts.present?(:current) && !opts.present?(:method) &&
    filename_argument.empty?
end

def runtime_patch?

def runtime_patch?
  !file_based_exception? &&
    (opts.present?(:patch) ||
     previously_patched?(code_object) ||
     pry_method?(code_object))
end