+(defvar el-search--pcase-macros '()
+ "List of additional \"el-search\" pcase macros.")
+
+(defun el-search--make-docstring ()
+ ;; code mainly from `pcase--make-docstring'
+ (let* ((main (documentation (symbol-function 'el-search-pattern) 'raw))
+ (ud (help-split-fundoc main 'pcase)))
+ (with-temp-buffer
+ (insert (or (cdr ud) main))
+ (mapc
+ (pcase-lambda (`(,symbol . ,fun))
+ (when-let ((doc (documentation fun)))
+ (insert "\n\n-- ")
+ (setq doc (help-fns--signature symbol doc nil fun nil))
+ (insert "\n" (or doc "Not documented."))))
+ (reverse el-search--pcase-macros))
+ (let ((combined-doc (buffer-string)))
+ (if ud (help-add-fundoc-usage combined-doc (car ud)) combined-doc)))))
+
+(put 'el-search-pattern 'function-documentation '(el-search--make-docstring))
+
+(defmacro el-search-defpattern (name args &rest body)
+ "Like `pcase-defmacro', but limited to el-search patterns.
+The semantics is exactly that of `pcase-defmacro', but the scope
+of the definitions is limited to \"el-search\"."
+ (declare (indent 2) (debug defun))
+ `(setf (alist-get ',name el-search--pcase-macros)
+ (lambda ,args ,@body)))
+
+(el-search-defpattern string (&rest regexps)
+ "Matches any string that is matched by all REGEXPS."
+ (let ((string (make-symbol "string"))
+ (regexp (make-symbol "regexp")))
+ `(and (pred stringp)
+ (pred (lambda (,string)
+ (cl-every
+ (lambda (,regexp) (string-match-p ,regexp ,string))
+ (list ,@regexps)))))))
+
+(el-search-defpattern symbol (&rest regexps)
+ "Matches any symbol whose name is matched by all REGEXPS."
+ `(and (pred symbolp)
+ (app symbol-name (string ,@regexps))))
+
+(defun el-search--match-symbol-file (regexp symbol)
+ (when-let ((symbol-file (and (symbolp symbol)
+ (symbol-file symbol))))
+ (string-match-p
+ (if (symbolp regexp) (concat "\\`" (symbol-name regexp) "\\'") regexp)
+ (file-name-sans-extension (file-name-nondirectory symbol-file)))))
+
+(el-search-defpattern source (regexp)
+ "Matches any symbol whose `symbol-file' is matched by REGEXP.
+
+This pattern matches when the object is a symbol for that
+`symbol-file' returns a (non-nil) FILE-NAME that fulfills
+ (string-match-p REGEXP (file-name-sans-extension
+ (file-name-nondirectory FILENAME)))
+
+REGEXP can also be a symbol, in which case
+
+ (concat \"^\" (symbol-name regexp) \"$\")
+
+is used as regular expression."
+ `(pred (el-search--match-symbol-file ,regexp)))
+
+(defun el-search--match-key-sequence (keys expr)
+ (when-let ((expr-keys (pcase expr
+ ((or (pred stringp) (pred vectorp)) expr)
+ (`(kbd ,(and (pred stringp) string)) (ignore-errors (kbd string))))))
+ (apply #'equal
+ (mapcar (lambda (keys) (ignore-errors (key-description keys)))
+ (list keys expr-keys)))))
+
+(el-search-defpattern keys (key-sequence)
+ "Matches any description of the KEY-SEQUENCE.
+KEY-SEQUENCE is a key description in a format that Emacs
+understands.
+
+This pattern matches any description of the same key sequence.
+
+Example: the pattern
+
+ (keys (kbd \"C-s\"))
+
+matches any of these expressions:
+
+ (kbd \"C-s\")
+ [(control ?s)]
+ \"\\C-s\"
+
+Any of these could be used as equivalent KEY-SEQUENCE in terms of
+this pattern type."
+ `(pred (el-search--match-key-sequence ,key-sequence)))
+
+(defmacro el-search--with-additional-pcase-macros (&rest body)
+ `(cl-letf ,(mapcar (pcase-lambda (`(,symbol . ,fun))
+ `((get ',symbol 'pcase-macroexpander) #',fun))
+ el-search--pcase-macros)
+ ,@body))
+
+(defun el-search--matcher (pattern &rest body)
+ (eval
+ `(el-search--with-additional-pcase-macros
+ (let ((warning-suppress-log-types '((bytecomp))))
+ (byte-compile
+ (lambda (expression)
+ (pcase expression
+ (,pattern ,@(or body (list t)))
+ (_ nil))))))))
+
+(defun el-search--match-p (matcher expression)
+ (funcall matcher expression))
+
+(defun el-search--wrap-pattern (pattern)
+ `(and ,el-search-this-expression-identifier ,pattern))
+
+(defun el-search--skip-expression (expression &optional read)
+ ;; Move forward at least one character. Don't move into a string or
+ ;; comment. Don't move further than the beginning of the next sexp.
+ ;; Try to move as far as possible. Point must be at the beginning
+ ;; of an expression.
+ ;; If there are positions where `read' would succeed, but that do
+ ;; not represent a valid sexp start, move past them (e.g. when
+ ;; before "#'" move past both characters).
+ ;;
+ ;; EXPRESSION must be the (read) expression at point, but when READ
+ ;; is non-nil, ignore the first argument and read the expression at
+ ;; point instead.
+ (when read (setq expression (save-excursion (read (current-buffer)))))
+ (cond
+ ((or (null expression)
+ (equal [] expression)
+ (not (or (listp expression) (vectorp expression))))
+ (goto-char (el-search--end-of-sexp)))
+ ((looking-at (rx (or ",@" "," "#'" "'")))
+ (goto-char (match-end 0)))
+ (t (forward-char))))