           ,definition
           "This function was patched by `el-patch'.")))))
 
-(defun el-patch-classify-variable (definition)
-  "Classify the items defined by a variable DEFINITION.
-DEFINITION is a list starting with `defvar' or similar."
-  (list (cons 'variable (nth 1 definition))))
-
-(defun el-patch-classify-function (definition)
-  "Classify the items defined by a function DEFINITION.
-DEFINITION is a list starting with `defun' or similar."
-  (list (cons 'function (nth 1 definition))))
-
-(defun el-patch-classify-define-minor-mode (definition)
-  "Classify the items defined by a minor mode DEFINITION.
-DEFINITION is a list starting with `define-minor-mode' or
-similar."
-  (let* ((function-name (nth 1 definition))
-         (variable-name (nth 1 definition))
-         (kw-args (nthcdr 3 definition)))
-    (dotimes (_ 3)
-      (unless (keywordp (car kw-args))
-        (setq kw-args (cdr kw-args))))
-    (while (keywordp (car kw-args))
-      (when (eq (car kw-args) :variable)
-        (setq variable-name (car kw-args)))
-      (setq kw-args (nthcdr 2 kw-args)))
-    (list (cons 'function function-name)
-          (cons 'variable variable-name))))
-
+;;;;; Removing patches
+
+;;;###autoload
+(defun el-patch-unpatch (name type)
+  "Remove the patch given by the PATCH-DEFINITION.
+This restores the original functionality of the object being
+patched. NAME and TYPE are as returned by `el-patch-get'."
+  (interactive (el-patch--select-patch))
+  (if-let ((patch-definition (el-patch-get name type)))
+      (eval `(el-patch--stealthy-eval
+              ,(el-patch--resolve-definition
+                patch-definition nil)
+              "This function was patched and then unpatched by `el-patch'."))
+    (error "There is no patch for %S %S" type name)))
+
+;;;; Defining patch types
+
+;; Use `progn' to cause the entire macro definition to be autoloaded
+;; rather than just a stub.
 ;;;###autoload
 (progn
   (cl-defmacro el-patch-deftype

  :classify el-patch-classify-variable
  :declare ((doc-string 3)
            (indent defun)))

;;;###autoload
(defmacro el-patch-feature (feature &rest args)

                     (el-patch--resolve form new))
                   (cdr patch-definition))))

;;;; Patch directives

;;;###autoload
(defmacro el-patch-add (&rest args)
  "Patch directive for inserting forms.
In the original definition, the ARGS and their containing form
are removed. In the new definition, the ARGS are spliced into the
containing s-expression."
  (declare (indent 0))
  (ignore args)
  `(error "Can't use `el-patch-add' outside of an `el-patch'"))

;;;###autoload
(defmacro el-patch-remove (&rest args)
  "Patch directive for removing forms.
In the original definition, the ARGS are spliced into the
containing s-expression. In the new definition, the ARGS and
their containing form are removed."
  (declare (indent 0))
  (ignore args)
  `(error "Can't use `el-patch-remove' outside of an `el-patch'"))

;;;###autoload
(defmacro el-patch-swap (old new)
  "Patch directive for swapping forms.
In the original definition, OLD is spliced into the containing
s-expression. In the new definition, NEW is spliced instead."
  (declare (indent 0))
  (ignore old new)
  `(error "Can't use `el-patch-swap' outside of an `el-patch'"))

;;;###autoload
(defmacro el-patch-wrap (&optional triml trimr args)
  "Patch directive for wrapping forms.
TRIML and TRIMR are optional arguments. If only one is provided,
it is assumed to be TRIML. ARGS is required, and it must be a
list.

In the original definition, the ARGS are spliced into the
containing s-expression. If TRIML is provided, the first TRIML of
the ARGS are removed first. If TRIMR is provided, the last TRIMR
are also removed. In the new definition, the ARGS and their
containing list are spliced into the containing s-expression."
  (declare (indent defun))
  (ignore triml trimr args)
  `(error "Can't use `el-patch-wrap' outside of an `el-patch'"))

;;;###autoload
(defmacro el-patch-splice (&optional triml trimr args)
  "Patch directive for splicing forms.
TRIML and TRIMR are optional arguments. If only one is provided,
it is assumed to be TRIML. ARGS is required, and it must be a
list.

In the original definition, the ARGS and their containing list
are spliced into the containing s-expression. In the new
definition, the ARGS are spliced into the containing
s-expression. If TRIML is provided, the first TRIML of the ARGS
are removed first. If TRIMR is provided, the last TRIMR are also
removed."
  (declare (indent defun))
  (ignore triml trimr args)
  `(error "Can't use `el-patch-splice' outside of an `el-patch'"))

;;;###autoload
(defmacro el-patch-let (varlist &rest args)
  "Patch directive for creating local el-patch bindings.
Creates local bindings according to VARLIST, then splices ARGS
into both the original and new definitions. You may bind symbols
that are also patch directives, but the bindings will not have
effect if the symbols are used at the beginning of a list (they
will act as patch directives)."
  (declare (indent 1))
  (ignore varlist args)
  `(error "Can't use `el-patch-let' outside of an `el-patch'"))

;;;###autoload
(defmacro el-patch-literal (&rest args)
  "Patch directive for treating patch directives literally.
ARGS are spliced into the containing s-expression, but are not
processed further by el-patch."
  (declare (indent 0))
  (ignore args)
  `(error "Can't use `el-patch-literal' outside of an `el-patch'"))

;;;###autoload
(defmacro el-patch-concat (&rest args)
  "Patch directive for modifying string literals.
ARGS should resolve to strings; those strings are passed to
`concat' and spliced into the containing s-expression in both the
original and new definitions."
  (declare (indent 0))
  (ignore args)
  `(error "Can't use `el-patch-concat' outside of an `el-patch'"))

;;;; Applying patches

