emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master 7a854aa 2/2: Rewrite of el-search for version 1.0


From: Michael Heerdegen
Subject: [elpa] master 7a854aa 2/2: Rewrite of el-search for version 1.0
Date: Sat, 15 Oct 2016 05:05:31 +0000 (UTC)

branch: master
commit 7a854aa63c5cff363c0f8dd369325732cc6b849f
Author: Michael Heerdegen <address@hidden>
Commit: Michael Heerdegen <address@hidden>

    Rewrite of el-search for version 1.0
    
    Big rewrite and cleanup.  Add multi-search support.  Update and improve
    docs.
---
 packages/el-search/el-search-x.el |  215 ++++-
 packages/el-search/el-search.el   | 1740 +++++++++++++++++++++++--------------
 2 files changed, 1310 insertions(+), 645 deletions(-)

diff --git a/packages/el-search/el-search-x.el 
b/packages/el-search/el-search-x.el
index 92d2dc3..51cab11 100644
--- a/packages/el-search/el-search-x.el
+++ b/packages/el-search/el-search-x.el
@@ -33,15 +33,131 @@
 
 ;;; Code:
 
+(eval-when-compile (require 'subr-x))
 (require 'el-search)
 
 
+;;;; `append and `l'
+
+(defun el-search--split (matcher1 matcher2 list)
+  "Helper for the \"append\" pattern type.
+
+When a splitting of LIST into two lists L1, L2 exist so that Li
+is matched by MATCHERi, return (L1 L2) for such Li, else return
+nil."
+  (let ((try-match (lambda (list1 list2)
+                     (when (and (el-search--match-p matcher1 list1)
+                                (el-search--match-p matcher2 list2))
+                       (list list1 list2))))
+        (list1 list) (list2 '()) (match nil))
+    ;; don't use recursion, this could hit `max-lisp-eval-depth'
+    (while (and (not (setq match (funcall try-match list1 list2)))
+                (consp list1))
+      (let ((last-list1 (last list1)))
+        (if-let ((cdr-last-list1 (cdr last-list1)))
+            ;; list1 is a dotted list.  Then list2 must be empty.
+            (progn (setcdr last-list1 nil)
+                   (setq list2 cdr-last-list1))
+          (setq list1 (butlast list1 1)
+                list2 (cons (car last-list1) list2)))))
+    match))
+
+(el-search-defpattern append (&rest patterns)
+  "Matches any list factorable into lists matched by PATTERNS in order.
+
+PATTERNS is a list of patterns P1..Pn.  Match any list L for that
+lists L1..Ln exist that are matched by P1..Pn in order and L is
+equal to the concatenation of L1..Ln.  Ln is allowed to be no
+list.
+
+When different ways of matching are possible, it is unspecified
+which one is chosen.
+
+Example: the pattern
+
+   (append '(1 2 3) x (app car-safe 7))
+
+matches the list (1 2 3 4 5 6 7 8 9) and binds `x' to (4 5 6)."
+  (if (null patterns)
+      '(pred null)
+    (pcase-let ((`(,pattern . ,more-patterns) patterns))
+      (cond
+       ((null more-patterns)  pattern)
+       ((null (cdr more-patterns))
+        `(and (pred listp)
+              (app ,(apply-partially #'el-search--split
+                                     (el-search--matcher pattern)
+                                     (el-search--matcher (car more-patterns)))
+                   `(,,pattern ,,(car more-patterns)))))
+       (t `(append ,pattern (append ,@more-patterns)))))))
+
+(defun el-search--transform-nontrivial-lpat (expr)
+  (pcase expr
+    ((and (pred symbolp) (let symbol-name (symbol-name expr)))
+     `(or (symbol ,symbol-name)
+          `',(symbol  ,symbol-name)
+          `#',(symbol ,symbol-name)))
+    ((pred stringp) `(string ,expr))
+    (_ expr)))
+
+(el-search-defpattern l (&rest lpats)
+  "Alternative pattern type for matching lists.
+Match any list with subsequent elements matched by all LPATS in
+order.
+
+The idea is to be able to search for pieces of code (i.e. lists)
+with very brief input by using a specialized syntax.
+
+An LPAT can take the following forms:
+
+SYMBOL  Matches any symbol S matched by SYMBOL's name interpreted
+        as a regexp.  Matches also 'S and #'S for any such S.
+STRING  Matches any string matched by STRING interpreted as a
+        regexp
+_       Matches any list element
+__      Matches any number of list elements (including zero)
+^       Matches zero elements, but only at the beginning of a list
+$       Matches zero elements, but only at the end of a list
+PAT     Anything else is interpreted as a normal pcase pattern, and
+        matches one list element matched by it
+
+^ is only valid as the first, $ as the last of the LPATS.
+
+Example: To match defuns that contain \"hl\" in their name and
+have at least one mandatory, but also optional arguments, you
+could use this pattern:
+
+    (l ^ 'defun hl (l _ &optional))"
+  (let ((match-start nil) (match-end nil))
+    (when (eq (car-safe lpats) '^)
+      (setq match-start t)
+      (cl-callf cdr lpats))
+    (when (eq (car-safe (last lpats)) '$)
+      (setq match-end t)
+      (cl-callf butlast lpats 1))
+    `(append ,@(if match-start '() '(_))
+             ,@(mapcar
+                (lambda (elt)
+                  (pcase elt
+                    ('__ '_)
+                    ('_ '`(,_))
+                    ('_? '(or '() `(,_))) ;FIXME: useful - document? or should 
we provide a (? PAT)
+                                          ;thing?
+                    (_ ``(,,(el-search--transform-nontrivial-lpat elt)))))
+                lpats)
+             ,@(if match-end '() '(_)))))
+
+
 ;;;; `change', `changed'
 
 (defvar diff-hl-reference-revision)
 (declare-function diff-hl-changes "diff-hl")
+(declare-function vc-git-command "vc-git")
 (defvar-local el-search--cached-changes nil)
 
+
+(defvar el-search-change-revision-transformer-function #'identity)
+
 (defun el-search--changes-from-diff-hl (revision)
   "Return a list of changed regions (as conses of positions) since REVISION.
 Use variable `el-search--cached-changes' for caching."
@@ -51,25 +167,35 @@ Use variable `el-search--cached-changes' for caching."
       (cdr el-search--cached-changes)
     (when (buffer-modified-p)
       (error "Buffer is modified - please save"))
+    (require 'vc)
     (require 'diff-hl)
     ;; `diff-hl-changes' returns line numbers.  We must convert them into 
positions.
     (save-restriction
       (widen)
       (save-excursion
-        (let ((diff-hl-reference-revision revision)
+        (let ((diff-hl-reference-revision
+               (funcall el-search-change-revision-transformer-function 
revision))
               (current-line-nbr 1) change-beg)
           (goto-char 1)
           (cdr (setq el-search--cached-changes
                      (cons (list revision (visited-file-modtime))
-                           (delq nil (mapcar (pcase-lambda (`(,start-line 
,nbr-lines ,kind))
-                                               (if (eq kind 'delete) nil
-                                                 (forward-line (- start-line 
current-line-nbr))
-                                                 (setq change-beg (point))
-                                                 (forward-line (1- nbr-lines))
-                                                 (setq current-line-nbr (+ 
start-line nbr-lines -1))
-                                                 (cons (copy-marker change-beg)
-                                                       (copy-marker 
(line-end-position)))))
-                                             (diff-hl-changes)))))))))))
+                           (and
+                            (let ((file-name buffer-file-name))
+                              (with-temp-buffer
+                                (vc-git-command
+                                 (current-buffer) 128 file-name
+                                 "log" "--ignore-missing" "-1"
+                                 diff-hl-reference-revision "--" file-name)
+                                (> (point-max) 1)))
+                            (delq nil (mapcar (pcase-lambda (`(,start-line 
,nbr-lines ,kind))
+                                                (if (eq kind 'delete) nil
+                                                  (forward-line (- start-line 
current-line-nbr))
+                                                  (setq change-beg (point))
+                                                  (forward-line (1- nbr-lines))
+                                                  (setq current-line-nbr (+ 
start-line nbr-lines -1))
+                                                  (cons (copy-marker 
change-beg)
+                                                        (copy-marker 
(line-end-position)))))
+                                              (ignore-errors 
(diff-hl-changes)))))))))))))
 
 (defun el-search--change-p (posn &optional revision)
   ;; Non-nil when sexp after POSN is part of a change
@@ -109,6 +235,75 @@ repository's HEAD commit."
   `(guard (el-search--changed-p (point) ,revision)))
 
 
+;;;; `keys'
+
+(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 descriptions of the KEY-SEQUENCE.
+KEY-SEQUENCE is a string or vector representing a key sequence,
+or an expression of the form (kbd STRING).
+
+Match any description of the same key sequence in any of these
+formats.
+
+Example: the pattern
+
+    (keys (kbd \"C-s\"))
+
+matches any of these expressions:
+
+    \"\\C-s\"
+    \"\C-s\"
+    (kbd \"C-s\")
+    [(control ?s)]"
+  (when (eq (car-safe key-sequence) 'kbd)
+    (setq key-sequence (kbd (cadr key-sequence))))
+  (el-search-defpattern--check-args
+   "keys" (list key-sequence) (lambda (x) (or (stringp x) (vectorp x))) 
"argument not a string or vector")
+  `(pred (el-search--match-key-sequence ,key-sequence)))
+
+
+;;;; `but-not-parent' and `top-level'
+
+(el-search-defpattern but-not-parent (pattern &optional not-pattern)
+    "Matches when PATTERN matches but the parent sexp does not.
+For toplevel expressions, this is equivalent to PATTERN.
+
+Optional NOT-PATTERN defaults to PATTERN; when given, match when
+PATTERN matches but the parent sexp is not matched by
+NOT-PATTERN.
+
+
+This pattern is useful to match only the outermost expression
+when subexpressions would match recursively.  For
+example, (but-not-parent _) matches only top-level expressions.
+Another example: For the `change' pattern, any subexpression of a
+match is typically also an according change.  Wrapping the
+`change' pattern into `but-not-parent' prevents el-search from
+descending into any found expression - only the outermost
+expression matching the `change' pattern will be matched."
+    `(and ,pattern
+          (not (guard (save-excursion
+                        (condition-case nil
+                            (progn
+                              (backward-up-list)
+                              (el-search--match-p
+                               ',(el-search--matcher (or not-pattern pattern))
+                               (save-excursion (read (current-buffer)))))
+                          (scan-error)))))))
+
+(el-search-defpattern top-level ()
+  "Matches any toplevel expression."
+  '(but-not-parent _))
+
+
 ;;; Patterns for stylistic rewriting
 
 ;;;; Iffy `if's
diff --git a/packages/el-search/el-search.el b/packages/el-search/el-search.el
index f88297e..ae00661 100644
--- a/packages/el-search/el-search.el
+++ b/packages/el-search/el-search.el
@@ -1,4 +1,4 @@
-;;; el-search.el --- Expression based incremental search for emacs-lisp-mode 
-*- lexical-binding: t -*-
+;;; el-search.el --- Expression based interactive search for emacs-lisp-mode 
-*- lexical-binding: t -*-
 
 ;; Copyright (C) 2015, 2016 Free Software Foundation, Inc
 
@@ -7,8 +7,8 @@
 ;; Created: 29 Jul 2015
 ;; Keywords: lisp
 ;; Compatibility: GNU Emacs 25
-;; Version: 0.2.3
-;; Package-Requires: ((emacs "25"))
+;; Version: 1.0
+;; Package-Requires: ((emacs "25") (stream "2.2.3"))
 
 
 ;; This file is not part of GNU Emacs.
@@ -29,10 +29,41 @@
 
 ;;; Commentary:
 
+;; Suggested key bindings
+;; ======================
+;;
+;; You can eval the following key definitions to try things out while
+;; reading this introduction.  These are the bindings I use
+;; personally:
+;;
+;;   (define-key emacs-lisp-mode-map [(control ?S)] #'el-search-pattern)
+;;   (define-key emacs-lisp-mode-map [(control ?%)] #'el-search-query-replace)
+;;   (define-key global-map          [(control ?J)] 
#'el-search-jump-to-search-head)
+;;   (define-key global-map          [(control ?N)] 
#'el-search-continue-in-next-buffer)
+;;
+;;   (define-key el-search-read-expression-map [(control ?S)] 
#'exit-minibuffer)
+;;
+;;   (define-key isearch-mode-map [(control ?S)] 
#'el-search-search-from-isearch)
+;;
+;;   (with-eval-after-load 'dired
+;;     (define-key dired-mode-map [(control ?S)] 
#'el-search-dired-marked-files))
+;;
+;; These bindings may not work in a console (if you have an idea for
+;; official bindings that fit better into the Emacs ecosystem, please
+;; mail me).
+;;
+;; The binding in `isearch-mode-map' lets you switch to "el-search"
+;; from isearch reusing already the given input.  The binding in
+;; `el-search-read-expression-map' allows you to hit C-S twice to
+;; start a search using the last search pattern, similar to isearch.
+;;
+;; Don't be afraid of the long introduction, it's only verbose (sorry)
+;; but not complicated.
+;;
+;;
 ;; Introduction
 ;; ============
 ;;
-;;
 ;; The main user entry point is `el-search-pattern'.  This command
 ;; prompts for a `pcase' pattern and searches the current buffer for
 ;; matching expressions by iteratively `read'ing buffer contents.  For
@@ -45,9 +76,9 @@
 ;; pattern types and to combine them with other patterns in a natural
 ;; and transparent way out of the box.
 ;;
-;; It doesn't matter how the code is actually formatted.  Comments are
-;; ignored, and strings are treated as atomic objects, their contents
-;; are not being searched.
+;; When searching, it doesn't matter how code is actually formatted.
+;; Comments are ignored, and strings are treated as atomic objects,
+;; their contents are not being searched.
 ;;
 ;;
 ;; Example 1: if you enter
@@ -66,15 +97,12 @@
 ;;
 ;; you search for all defvar forms that don't specify an init value.
 ;;
-;; The following will search for defvar forms with a docstring whose
-;; first line is longer than 70 characters:
+;; The following pattern will search for defvar forms with a docstring
+;; whose first line is longer than 70 characters:
 ;;
 ;;   `(defvar ,_ ,_
-;;      ,(and s (guard (< 70 (length (car (split-string s "\n")))))))
-;;
-;;
-;; When a search pattern is processed, the searched buffer is current
-;; with point at the beginning of the currently tested expression.
+;;      ,(and (pred stringp) s
+;;            (guard (< 70 (length (car (split-string s "\n")))))))
 ;;
 ;;
 ;; Convenience
@@ -89,8 +117,8 @@
 ;;
 ;; Example 3:
 ;;
-;; If you want to search a buffer for symbols that are defined in
-;; "cl-lib", you can use this pattern
+;; If you wanted to search a buffer for symbols that are defined in
+;; "cl-lib", you could use this pattern
 ;;
 ;;   (guard (and (symbolp exp)
 ;;               (when-let ((file (symbol-file exp)))
@@ -109,13 +137,14 @@
 ;;
 ;; It's cumbersome to write out the same complicated pattern
 ;; constructs in the minibuffer again and again.  You can define your
-;; own pcase pattern types for the purpose of el-search with
+;; own `pcase' pattern types for the purpose of el-searching with
 ;; `el-search-defpattern'.  It is just like `pcase-defmacro', but the
-;; effect is limited to this package.  See C-h f `el-search-pattern'
-;; for a list of predefined additional pattern forms.
+;; effect is limited to this package (i.e. it uses a separate name
+;; space).  See C-h f `el-search-pattern' for a list of predefined
+;; pattern forms.
 ;;
 ;; Some additional pattern definitions can be found in the file
-;; "el-search-x.el".
+;; "el-search-x" which is part of this package.
 ;;
 ;;
 ;; Replacing
@@ -125,7 +154,7 @@
 ;; You are queried for a (pcase) pattern and a replacement expression.
 ;; For each match of the pattern, the replacement expression is
 ;; evaluated with the bindings created by the pcase matching in
-;; effect, and printed to produce the replacement string.
+;; effect, and printed to a string to produce the replacement.
 ;;
 ;; Example: In some buffer you want to swap the two expressions at the
 ;; places of the first two arguments in all calls of function `foo',
@@ -156,29 +185,126 @@
 ;; in a `el-search-query-replace' session.
 ;;
 ;;
-;; Suggested key bindings
-;; ======================
+;; Multi Searching
+;; ===============
 ;;
-;;    (define-key emacs-lisp-mode-map [(control ?S)] #'el-search-pattern)
-;;    (define-key emacs-lisp-mode-map [(control ?%)] #'el-search-query-replace)
+;; "el-search" is capable of performing "multi searches" - searches
+;; spanning multiple files or buffers.  When no more matches can be
+;; found in the current buffer, the search automatically switches to
+;; the next buffer.  Examples for search commands starting a multi
+;; search are `el-search-buffers' (search all living elisp mode
+;; buffers), `el-search-directory' (search all elisp files in a
+;; specified directory), `el-search-emacs-elisp-sources' (search all
+;; Emacs elisp sources) and `el-search-dired-marked-files'.  Actually,
+;; every search is internally a multi search.
 ;;
-;;    (define-key isearch-mode-map [(control ?S)] 
#'el-search-search-from-isearch)
-;;    (define-key isearch-mode-map [(control ?%)] 
#'el-search-replace-from-isearch)
+;; You can pause any (multi) search by just doing something different,
+;; the state of the search is automatically saved. You can continue
+;; searching by calling `el-search-jump-to-search-head': this command
+;; jumps to the last match and re-activates the search.
+
+;; `el-search-continue-in-next-buffer' skips all remaining matches in
+;; the current buffer and continues searching in the next buffer.
 ;;
-;;    (define-key el-search-read-expression-map [(control ?S)] 
#'exit-minibuffer)
+;; Matches found in the current buffer are recorded; use
+;; `el-search-previous-match' to revisit them in reverse order (this
+;; is actually the poor-man's version of a backward search, since a
+;; real backward el-search would be slow).
+;;
+;; There is no multi query-replace currently implemented; I don't know
+;; if it would be that useful as a separate command anyway.  If you
+;; want to query-replace in multiple buffers or files, call an
+;; appropriate multi-search command, and every time a first match is
+;; found in any buffer, start an ordinary `el-search-query-replace';
+;; after finishing, check that everything is ok, save etc, and resume
+;; the multi search with one of the above commands.
+;;
+;; There is currently nothing like `occur' for el-search.  However,
+;; you can get a list of matches in the form
+;; (buffer-or-file-name . match-position) with
+;;
+;;    (el-search-all-matches
+;;      (el-search-make-search (el-search--matcher pattern) stream))
+;;
+;; where PATTERN is the search pattern and STREAM is a stream of
+;; buffers or files.  For example,
+;;
+;;   (el-search-all-matches
+;;    (el-search-make-search
+;;     (el-search--matcher ''require)
+;;     (seq-filter
+;;      (lambda (buffer)
+;;         (with-current-buffer buffer (derived-mode-p 'emacs-lisp-mode)))
+;;      (stream (buffer-list)))))
+;;
+;; would return a list of matches for the symbol require in all elisp
+;; mode buffers.
+;;
+;;
+;; Multiple multi searches
+;; =======================
+;;
+;; Every search is collected in a history.  You can resume older
+;; searches from the position of the last match by calling
+;; `el-search-jump-to-search-head' with a prefix argument.  That let's
+;; you select an older search to resume and switches to the buffer and
+;; position where this search had been suspended.
+;;
+;; There is no special command to restart a prior search from the
+;; beginning.  I suggest to use `repeat-complex-command'.
+;;
+;;
+;; Writing replacement rules for semi-automatic code rewriting
+;; ===========================================================
+;;
+;; When you want to rewrite larger code parts programmatically, it is
+;; often useful to define dedicated patterns for performing the
+;; replacement.  Here is an example:
+;;
+;; You heard that in many situations, `dolist' is faster than an
+;; equivalent `mapc'.  You use `mapc' quite often in your code and
+;; want to query-replace many occurrences in your stuff.  Instead of
+;; using an ad hoc replacing rule, it's cleaner to define a dedicated
+;; named pattern using `el-search-defpattern'.  Make this pattern
+;; accept an argument and use this argument to bind the replacement
+;; expression to a variable you specify.  In our case, the pattern
+;; could look like this:
+;;
+;;   (el-search-defpattern el-search-mapc->dolist (new)
+;;     (let ((var  (make-symbol "var"))
+;;           (body (make-symbol "body"))
+;;           (list (make-symbol "list")))
+;;       `(and `(mapc (lambda (,,var) . ,,body) ,,list)
+;;             (let ,new `(dolist (,,var ,,list) . ,,body)))))
+;;
+;; The first condition in the `and' performs the matching and binds
+;; the essential parts of the `mapc' form to variables.  The second,
+;; the `let' part, binds the pattern specified argument NEW (as said,
+;; typically just a variable to receive the rewritten code) to the
+;; rewritten expression - in our case, a `dolist' form is constructed
+;; with the remembered code parts filled in.
+;;
+;; Now, in `el-search-query-replace', you just specify the following
+;; rule:
+;;
+;;   (el-search-mapc->dolist replacement) -> replacement
+;;
+;; And when you want to replace in multiple buffers or files, call an
+;; appropriate multi el-search command, e.g. `el-search-directory',
+;; and specify
+;;
+;;   (el-search-mapc->dolist replacement)
+;;
+;; as search pattern.
 ;;
-;; The bindings in `isearch-mode-map' let you conveniently switch to
-;; "el-search" searching from isearch.  The binding in
-;; `el-search-read-expression-map' allows you to hit C-S twice to
-;; start a search for the last search pattern.
 ;;
 ;;
 ;; Bugs, Known Limitations
 ;; =======================
 ;;
-;; - Replacing: in some cases the reader syntax of forms
-;; is changing due to reading+printing.  "Some" because we can treat
-;; that problem in most cases.
+;; - Replacing: in some cases the reader syntax of forms is changing
+;; due to reading+printing.  "Some" because we can handle this problem
+;; in most cases.
 ;;
 ;; - Similarly: Comments are normally preserved (where it makes
 ;; sense).  But when replacing like `(foo ,a ,b) -> `(foo ,b ,a)
@@ -187,13 +313,11 @@
 ;;
 ;;   (foo
 ;;     a
-;;     ;;a comment
+;;     ;; comment
 ;;     b)
 ;;
 ;; the comment will be lost.
 ;;
-;; FIXME: when we have resumable sessions, pause and warn about this case.
-;;
 ;;
 ;;  Acknowledgments
 ;;  ===============
@@ -201,35 +325,53 @@
 ;; Thanks to Stefan Monnier for corrections and advice.
 ;;
 ;;
+;;  Notes for developers
+;;  ====================
+;;
+;; We use the following model for multi buffer/file searches: a search
+;; (object) is represented by a struct "el-search-object" that
+;; contains a stream of matches M and a search head object H that
+;; contains a compiled matcher according to the given search pattern,
+;; the buffer and buffer position where to continue searching, and the
+;; stream of buffers or files yet to search.
+;;
+;; When elements are requested from M, H is updated accordingly.  H
+;; can be manipulated directly to influence how M will find further
+;; elements when requested (useful for skipping buffers on the fly).
+;;
+;;
 ;; TODO:
 ;;
-;; - implement backward searching
+;; - The default keys are not available in the terminal
+;;
+;; - Handle buffers killed/files closed when resuming a search
+;;
+;; - Make el-search-previous-match behave correctly when a buffer has
+;;   been modified and data is outdated
 ;;
-;; - Make `el-search-pattern' accept an &optional limit, at least for
-;;   the non-interactive use case?
+;; - Make the non-command `el-search-forward' accept an &optional
+;;   LIMIT argument
 ;;
-;; - improve docstrings
+;; - Make searching work in comments, too? (->
+;;   `parse-sexp-ignore-comments').  Related: should the pattern
+;;   `symbol' also match strings that contain matches for a symbol so
+;;   that it's possible to replace also occurrences of a symbol in
+;;   docstrings?
 ;;
-;; - Make it work in comments, too? (-> `parse-sexp-ignore-comments').
-;;   Related: should the pattern `symbol' also match strings that
-;;   contain matches for a symbol so that it's possible to replace
-;;   also occurrences of a symbol in docstrings?
+;; - Implement an occur like interface?
 ;;
-;; - handle more reader syntaxes, e.g. #n, #n#
+;; - Port this to non Emacs Lisp modes?  How?  Would it already
+;;   work using only syntax tables, sexp scanning and font-lock?
 ;;
-;; - Implement sessions; add multi-file support based on iterators.  A
-;;   file list is read in (or the user can specify an iterator as a
-;;   variable).  The state in the current buffer is just (buffer
-;;   . marker).  Or should this be abstracted into an own lib?  Could
-;;   be named "files-session" or so.
+;; - For query-replace, maybe we should save the original buffer
+;;   string in a buffer-local variable, and make that ediff'able
+;;   against the new version.  Or should we even save multiple
+;;   versions when appropriate?
 ;;
-;; - Make `el-search--format-replacement' work non-heuristically.
-;;   Idea: When replacing, for every variable V bound by the search
-;;   pattern that directly corresponds to some text T, provide some
-;;   "match data" V -> T.  Use this when formatting the replacement.
-;;   Maybe use a special marker to "paste" in expressions, like (paste
-;;   V), whereby the `paste' flag lands in the replacement and can be
-;;   replaced textually afterwards.
+;; - Replace: pause and warn when replacement might be wrong
+;;   (ambiguous reader syntaxes; lost comments, comments that can't
+;;   non-ambiguously be assigned to rewritten code)
+
 
 
 
@@ -243,7 +385,9 @@
 (require 'cl-lib)
 (require 'elisp-mode)
 (require 'thingatpt)
+(require 'stream)
 (require 'help-fns) ;el-search--make-docstring
+(require 'ring)     ;el-search-history
 
 
 ;;;; Configuration stuff
@@ -254,76 +398,108 @@
 
 (defcustom el-search-this-expression-identifier 'exp
   "Identifier ID referring to the current expression in pattern input.
-When entering a PATTERN in an interactive \"el-search\" command,
-the pattern actually used will be (and ID PATTERN).
-The default value is `exp'."
+When entering a pattern in an interactive \"el-search\" command,
+the pattern actually used will be (and ID PATTERN).  The default
+value is `exp'."
   :type 'symbol)
 
-(defface el-search-match '((((background dark)) (:background "#0000A0"))
-                          (t                   (:background "DarkSlateGray3")))
+(defface el-search-match '((((background dark))
+                            ;; (:background "#0000A0")
+                            (:background "#600000"))
+                          (t (:background "DarkSlateGray3")))
   "Face for highlighting the current match.")
 
-(defface el-search-other-match '((((background dark)) (:background "#202060"))
-                                 (t                   (:background 
"DarkSlateGray1")))
+(defface el-search-other-match '((((background dark))
+                                  ;; (:background "#202060")
+                                  (:background "#603030"))
+                                 (t (:background "DarkSlateGray1")))
   "Face for highlighting the other matches.")
 
-(defcustom el-search-smart-case-fold-search t
-  "Whether to use smart case folding in pattern matching.
-When an \"el-search\" pattern involves regexp matching (like for
-\"string\" or \"source\") and this option is non-nil,
-case-fold-search will be temporarily bound to t if the according
-regexp contains any upper case letter, and nil else.  This is
-done independently for every single matching operation.
+(defvar el-search-display-buffer-action
+  '((display-buffer-reuse-window display-buffer-pop-up-frame)
+    (reusable-frames . visible))
+  "The `display-buffer' action used by `el-search-jump-to-search-head'.")
+
+(defcustom el-search-ignored-directory-regexps
+  (mapcar
+   (lambda (name) (format "\\`%s\\'" (regexp-quote name)))
+   ;; this is just the default value of `grep-find-ignored-directories':
+   '("SCCS" "RCS" "CVS" "MCVS" ".src" ".svn" ".git" ".hg" ".bzr" "_MTN" 
"_darcs" "{arch}"))
+  "List of regexps defining directories that el-search should ignore.
+
+The value influences the behavior of the commands that perform
+directory searches like `el-search-directory' or
+`el-search-dired-marked-files'.  It is consulted by all streams
+`el-search-stream-of-directory-files' returns."
+  :type '(choice (repeat :tag "Ignored directories" regexp)
+                (const  :tag "No ignored directories" nil)))
+
+(defvar el-search-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [(control ?D)] #'el-search-skip-directory)
+    (define-key map [(control ?N)] #'el-search-continue-in-next-buffer)
+    (define-key map [(control ?R)] #'el-search-previous-match)
+    map)
+  "Used by search commands as transient-map.")
 
-If nil, the value of `case-fold-search' is decisive."
-  :type 'boolean)
+(defvar el-search-read-expression-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map read-expression-map)
+    (define-key map [(control ?g)] #'abort-recursive-edit)
+    (define-key map [up]   nil)
+    (define-key map [down] nil)
+    (define-key map [(control ?j)] #'newline)
+    map)
+  "Map for reading input with `el-search-read-expression'.")
 
-(defcustom el-search-use-sloppy-strings nil
-  "Whether to allow the usage of \"sloppy strings\".
-When this option is turned on, for faster typing you are allowed
-to specify symbols instead of strings as arguments to an
-\"el-search\" pattern type that would otherwise accept only
-strings, and their names will be used as input (with other words,
-this spares you to type the string delimiters in many cases).
 
-For example,
+;;;; Helpers and Definitions
 
-  \(source ^cl\)
+(defvar el-search--current-pattern nil
+  "Current search pattern or nil when no search in progress.")
 
-is then equivalent to
+(defvar el-search--current-search nil
+  "The currently active search, an `el-search-object'.")
 
-  \(source \"^cl\"\)
+(defvar el-search--current-matcher nil
+  "The matcher according to `el-search--current-pattern'.")
 
-When this option is off, the first form would just signal an
-error."
-  :type 'boolean)
+(defvar-local el-search--temp-buffer-flag nil
+  "Non-nil tags (file) buffers as temporarily opened for searching.")
 
+(defvar el-search--success nil
+  "Non-nil when last search command was successful.")
 
-;;;; Helpers
+(defvar-local el-search--this-buffer-matches (stream-empty)
+  "Stream of the matches that were found in current buffer."
+  ;; Internally, simply the substream of the matches of the current
+  ;; search with all matches before current buffer cut off
+  )
 
-(defun el-search--smart-string-match-p (regexp string)
-  "`string-match-p' taking `el-search-smart-case-fold-search' into account."
-  (let ((case-fold-search (if el-search-smart-case-fold-search
-                              (not (let ((case-fold-search nil))
-                                     (string-match-p "[[:upper:]]" regexp)))
-                            case-fold-search)))
-    (string-match-p regexp string)))
+
+(defun el-search--string-match-p (eregexp string)
+  "Non-nil when extended regexp EREGEXP matches the STRING."
+  (let ((match-bindings ()) regexp)
+    (pcase eregexp
+      ((pred stringp) (setq regexp eregexp))
+      (`(,binds ,real-regexp)
+       (setq regexp real-regexp)
+       (setq match-bindings binds)))
+    (setq match-bindings
+          (mapcar (lambda (binding)
+                    (pcase binding
+                      ((pred symbolp)                  (list binding nil))
+                      (`(,(and (pred symbolp) symbol)) (list symbol nil))
+                      (_                               binding)))
+                  match-bindings))
+    (cl-progv (mapcar #'car match-bindings) (mapcar #'cadr match-bindings)
+      (string-match-p regexp string))))
 
 (defun el-search--pp-to-string (expr)
   (let ((print-length nil)
         (print-level nil))
     (pp-to-string expr)))
 
-(defvar el-search-read-expression-map
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map read-expression-map)
-    (define-key map [(control ?g)] #'abort-recursive-edit)
-    (define-key map [up]   nil)
-    (define-key map [down] nil)
-    (define-key map [(control ?j)] #'newline)
-    map)
-  "Map for reading input with `el-search-read-expression'.")
-
 (defun el-search--setup-minibuffer ()
   (let ((inhibit-read-only t))
     (put-text-property 1 (minibuffer-prompt-end) 'font-lock-face 
'minibuffer-prompt))
@@ -343,17 +519,21 @@ error."
                             (append minibuffer-default more-defaults)
                           (cons minibuffer-default more-defaults)))))))
 
-;; $$$$$FIXME: this should be in Emacs!  There is only a helper 
`read--expression'.
 (defun el-search-read-expression (prompt &optional initial-contents hist 
default read)
-  "Read expression for `my-eval-expression'."
+  "Conveniently read an expression from the minibuffer."
   (minibuffer-with-setup-hook #'el-search--setup-minibuffer
     (read-from-minibuffer prompt initial-contents 
el-search-read-expression-map read
                           (or hist 'read-expression-history) default)))
 
-(defvar el-search-history '()
-  "List of search input strings.")
+(defvar el-search-pattern-history ()
+  "List of search pattern input strings.")
 
-(defvar el-search-query-replace-history '()
+(defvar el-search-history (make-ring 10) ;$$$$FIXME: Make `10' customizable?
+  "History of previous searches."
+  ;; Elements have the form (search-object pattern)
+  )
+
+(defvar el-search-query-replace-history ()
   "List of input strings from `el-search-query-replace'.")
 
 (defvar el-search--initial-mb-contents nil)
@@ -373,23 +553,64 @@ error."
               input)
             (symbol-value histvar)))))
 
+(defun el-search--maybe-warn-about-unquoted-symbol (pattern)
+  (when (and (symbolp pattern)
+             (not (eq pattern '_))
+             (or (not (boundp pattern))
+                 (not (eq (symbol-value pattern) pattern))))
+    (error "Unbound symbol: %S" pattern)))
+
 (defun el-search--read-pattern (prompt &optional default histvar)
-  (cl-callf or histvar 'el-search-history)
-  (let ((input (el-search-read-expression
-                prompt el-search--initial-mb-contents histvar default)))
+  (cl-callf or histvar 'el-search-pattern-history)
+  (let ((input (el-search-read-expression prompt 
el-search--initial-mb-contents histvar default)))
     (el-search--pushnew-to-history input histvar)
     (if (not (string= input "")) input (car (symbol-value histvar)))))
 
+(defun el-search--read-pattern-for-interactive ()
+  (let* ((input (el-search--read-pattern "Find pcase pattern: " (car 
el-search-pattern-history)))
+         (pattern (read input)))
+    ;; A very common mistake: input "foo" instead of "'foo"
+    (el-search--maybe-warn-about-unquoted-symbol pattern)
+    (setq this-command 'el-search-pattern) ;in case we come from isearch
+    ;; Make input available also in query-replace history
+    (el-search--pushnew-to-history input 'el-search-query-replace-history)
+    (el-search--wrap-pattern pattern)))
+
+
 (defun el-search--end-of-sexp ()
-  ;;Point must be at sexp beginning
+  ;; Assumes point is at sexp beginning
   (or (scan-sexps (point) 1) (point-max)))
 
+(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 under these conditions.  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 equal the (read) expression at point, but with READ
+  ;; non-nil, ignore the first argument and use the read expression at
+  ;; point instead.
+  (when read (setq expression (save-excursion (read (current-buffer)))))
+  (cond
+   ((eq '@ expression) ;bug#24542
+    (forward-char))
+   ((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))))
+
 (defun el-search--ensure-sexp-start ()
   "Move point to the next sexp beginning position.
-Don't move if already at beginning of a sexp.  Point must not be
-inside a string or comment.  `read' the expression at that point
-and return it."
-  ;; This doesn't catch end-of-buffer to keep the return value non-ambiguous
+Do nothing if already at beginning of a sexp.  `read' the
+expression at that point and return it.  Point must not be inside
+a string or comment."
+  ;; We donn't catch end-of-buffer to keep the return value
+  ;; non-ambiguous
   (let ((not-done t) res)
     (while not-done
       (let ((stop-here nil)
@@ -420,13 +641,14 @@ and return it."
         (error (forward-char))))
     res))
 
+
 (defvar el-search--pcase-macros '()
   "List of additional \"el-search\" pcase macros.")
 
 (defun el-search--make-docstring (name)
   ;; code mainly from `pcase--make-docstring'
   (let* ((main (documentation (symbol-function name) 'raw))
-         (ud (help-split-fundoc main 'pcase)))
+         (ud (help-split-fundoc main name)))
     (with-temp-buffer
       (insert (or (cdr ud) main))
       (mapc
@@ -440,12 +662,11 @@ and return it."
       (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 
'el-search-pattern))
-
 (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\".
+of the definitions is limited to \"el-search\", using a separate
+name space.
 
 \(fn NAME ARGLIST &optional DOCSTRING &rest BODY)"
   (declare (indent 2) (debug defun))
@@ -453,14 +674,13 @@ of the definitions is limited to \"el-search\".
          (lambda ,args ,@body)))
 
 (defmacro el-search--with-additional-pcase-macros (&rest body)
-  `(cl-letf ,(mapcar (pcase-lambda (`(,symbol . ,fun))
-                       `((get ',symbol 'pcase-macroexpander) #',fun))
+  `(cl-letf ,(mapcar (pcase-lambda (`(,symbol . ,fun)) `((get ',symbol 
'pcase-macroexpander) #',fun))
                      el-search--pcase-macros)
      ,@body))
 
 (defun el-search--macroexpand-1 (pattern)
   "Expand \"el-search\" PATTERN.
-This is like `pcase--macroexpand', but expands only patterns
+This is like `pcase--macroexpand' but expands only patterns
 defined with `el-search-defpattern' and performs only one
 expansion step.
 
@@ -492,263 +712,253 @@ Return PATTERN if this pattern type was not defined with
 (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
-   ((eq '@ expression) ;bug#24542
-    (forward-char))
-   ((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))))
 
 (defun el-search--search-pattern-1 (matcher &optional noerror)
-  (let ((match-beg nil) (opoint (point)) current-expr)
-
-    ;; when inside a string or comment, move past it
-    (let ((syntax-here (syntax-ppss)))
-      (when (nth 3 syntax-here) ;inside a string
-        (goto-char (nth 8 syntax-here))
-        (forward-sexp))
-      (when (nth 4 syntax-here) ;inside a comment
-        (forward-line 1)
-        (while (and (not (eobp)) (looking-at (rx (and (* space) ";"))))
-          (forward-line 1))))
-
-    (if (catch 'no-match
-          (while (not match-beg)
-            (condition-case nil
-                (setq current-expr (el-search--ensure-sexp-start))
-              (end-of-buffer
-               (goto-char opoint)
-               (throw 'no-match t)))
-            (if (el-search--match-p matcher current-expr)
-                (setq match-beg (point)
-                      opoint (point))
-              (el-search--skip-expression current-expr))))
-        (if noerror nil (signal 'end-of-buffer nil)))
-    match-beg))
-
-(defun el-search--search-pattern (pattern &optional noerror)
-  "Search for el-search PATTERN in current buffer.
-Set point to the beginning of the occurrence found and return
-point.  Optional second argument, if non-nil, means if fail just
-return nil (no error)."
+  (if (not (derived-mode-p 'emacs-lisp-mode))
+      (if noerror nil (error "Buffer not in emacs-lisp-mode: %s" 
(buffer-name)))
+    (let ((match-beg nil) (opoint (point)) current-expr)
+
+      ;; when inside a string or comment, move past it
+      (let ((syntax-here (syntax-ppss)))
+        (when (nth 3 syntax-here) ;inside a string
+          (goto-char (nth 8 syntax-here))
+          (forward-sexp))
+        (when (nth 4 syntax-here) ;inside a comment
+          (forward-line 1)
+          (while (and (not (eobp)) (looking-at (rx (and (* space) ";"))))
+            (forward-line 1))))
+
+      (if (catch 'no-match
+            (while (not match-beg)
+              (condition-case nil
+                  (setq current-expr (el-search--ensure-sexp-start))
+                (end-of-buffer
+                 (goto-char opoint)
+                 (throw 'no-match t)))
+              (if (el-search--match-p matcher current-expr)
+                  (setq match-beg (point)
+                        opoint (point))
+                (el-search--skip-expression current-expr))))
+          (if noerror nil (signal 'end-of-buffer nil))
+        match-beg))))
+
+(defun el-search-forward (pattern &optional noerror)
+  "Search for el-search PATTERN in current buffer from point.
+Set point to the beginning of the occurrence found and return the
+new value of point.  Optional second argument, if non-nil, means
+if fail just return nil (no error)."
   (el-search--search-pattern-1 (el-search--matcher pattern) noerror))
 
-(defun el-search--replace-hunk (region to-insert)
-  "Replace the text in REGION in current buffer with string TO-INSERT.
-Add line breaks before and after TO-INSERT when appropriate and
-reindent."
-  (atomic-change-group
-    (let* ((inhibit-message t)
-           (opoint (point))
-           (original-text (prog1 (apply #'buffer-substring-no-properties 
region)
-                            (goto-char (car region))
-                            (apply #'delete-region region)))
-           ;; care about other sexps in this line
-           (sexp-before-us (not (looking-back "\(\\|^\\s-*" 
(line-beginning-position))))
-           (sexp-after-us  (not (looking-at "\\s-*[;\)]\\|$")))
-           (insert-newline-before
-            (or
-             (and (string-match-p "\n" to-insert)
-                  (not (string-match-p "\n" original-text))
-                  (or (and sexp-before-us sexp-after-us)
-                      (looking-back
-                       (rx (or (syntax word) (syntax symbol))
-                           (+ blank)
-                           (or (syntax word) (syntax symbol))
-                           (* any))
-                       (line-beginning-position))))
-             ;; (and sexp-before-us
-             ;;      (> (+ (apply #'max (mapcar #'length (split-string 
to-insert "\n")))
-             ;;            (- (point) (line-beginning-position)))
-             ;;         fill-column))
-             ))
-           (insert-newline-after (and insert-newline-before sexp-after-us)))
-      (when insert-newline-before
-        (when (looking-back "\\s-+" (line-beginning-position))
-          (delete-region (match-beginning 0) (match-end 0)))
-        (insert "\n"))
-      (insert to-insert)
-      (when insert-newline-after
-        (insert "\n"))
-      (indent-region opoint (1+ (point))))))
-
-(defun el-search--format-replacement (replacement original replace-expr-input 
splice)
-  ;; Return a printed representation of REPLACEMENT.  Try to reuse the
-  ;; layout of subexpressions shared with the original (replaced)
-  ;; expression and the replace expression.
-  (if (and splice (not (listp replacement)))
-      (error "Expression to splice in is an atom")
-    (let ((orig-buffer (generate-new-buffer "orig-expr")))
-      (with-current-buffer orig-buffer
-        (emacs-lisp-mode)
-        (insert original)
-        (when replace-expr-input (insert "\n\n" replace-expr-input)))
-      (unwind-protect
-          (with-temp-buffer
-            (emacs-lisp-mode)
-            (insert (if splice
-                        (mapconcat #'el-search--pp-to-string replacement " ")
-                      (el-search--pp-to-string replacement)))
-            (goto-char 1)
-            (let (start this-sexp end orig-match-start orig-match-end done)
-              (while (and (< (point) (point-max))
-                          (condition-case nil
-                              (progn
-                                (setq start (point)
-                                      this-sexp (read (current-buffer))
-                                      end   (point))
-                                t)
-                            (end-of-buffer nil)))
-                (setq done nil orig-match-start nil)
-                (with-current-buffer orig-buffer
-                  (goto-char 1)
-                  (if (el-search--search-pattern `',this-sexp t)
-                      (setq orig-match-start (point)
-                            orig-match-end (progn (forward-sexp) (point)))
-                    (setq done t)))
-                ;; find out whether we have a sequence of equal expressions
-                (while (and (not done)
-                            (condition-case nil
-                                (progn (setq this-sexp (read 
(current-buffer))) t)
-                              ((invalid-read-syntax end-of-buffer end-of-file) 
nil)))
-                  (if (with-current-buffer orig-buffer
-                        (condition-case nil
-                            (if (not (equal this-sexp (read (current-buffer))))
-                                nil
-                              (setq orig-match-end (point))
-                              t)
-                          ((invalid-read-syntax end-of-buffer end-of-file) 
nil)))
-                      (setq end (point))
-                    (setq done t)))
-                ;; FIXME: there could be another occurrence of THIS-SEXP in 
ORIG-BUFFER with more
-                ;; subsequent equal expressions after it
-                (if orig-match-start
-                    (el-search--replace-hunk
-                     (list start end)
-                     (with-current-buffer orig-buffer
-                       (buffer-substring-no-properties orig-match-start 
orig-match-end)))
-                  (goto-char start)
-                  (el-search--skip-expression nil t))
-                (condition-case nil
-                    (el-search--ensure-sexp-start)
-                  (end-of-buffer (goto-char (point-max))))))
-            (goto-char 1)
-            (forward-sexp (if splice (length replacement) 1))
-            (let ((result (buffer-substring 1 (point))))
-              (if (equal replacement (read (if splice (format "(%s)" result) 
result)))
-                  result
-                (error "Error in `el-search--format-replacement' - please make 
a bug report"))))
-        (kill-buffer orig-buffer)))))
 
-(defun el-search--check-pattern-args (type args predicate &optional message)
+(defun el-search-defpattern--check-args (type args predicate &optional message)
   "Check whether all ARGS fulfill PREDICATE.
 Raise an error if not.  The string arguments TYPE and optional
 MESSAGE are used to construct the error message."
-  (mapc (lambda (arg)
-          (unless (funcall predicate arg)
-            (error (concat "Pattern `%s': "
-                           (or message (format "argument doesn't fulfill %S" 
predicate))
-                           ": %S")
-                   type arg)))
-        args))
-
-(defvar el-search-current-pattern nil)
-
-(defvar el-search-success nil)
+  (dolist (arg args)
+    (unless (funcall predicate arg)
+      (error (concat "Pattern `%s': "
+                     (or message (format "argument doesn't fulfill %S" 
predicate))
+                     ": %S")
+             type arg))))
+
+(defun el-search--elisp-file-name-p (file)
+  (and (string-match-p "\\.el\\'" file)
+       (file-exists-p file)
+       (not (file-directory-p file))))
+
+(cl-defstruct el-search-object
+  head       ;an `el-search-head' instance, modified ("moved") while searching
+  matches    ;the stream of matches in the form (buffer position file)
+  last-match ;position of last match found
+  )
+
+(cl-defstruct el-search-head
+  matcher  ;for the current search pattern
+  buffer   ;currently searched buffer, or nil meaning "continue in next buffer"
+  file     ;name of currently searched file, or nil
+  position ;where to continue search in this buffer
+  buffers  ;stream of buffers and/or files yet to search
+  )
+
+(defun el-search-kill-left-over-search-buffers (&optional not-current-buffer)
+  "Kill all buffers that were opened for searching."
+  (interactive)
+  (dolist (buffer (buffer-list))
+    (when (with-current-buffer buffer el-search--temp-buffer-flag)
+      (unless (or (buffer-modified-p buffer)
+                  (and not-current-buffer (eq buffer (current-buffer))))
+        (kill-buffer buffer)))))
+
+(defvar warning-minimum-level)
+(defun el-search--next-buffer (search &optional predicate)
+  ;; Prepare to continue SEARCH in the next buffer in line.  Move
+  ;; SEARCH's head accordingly.  When specified, PREDICATE should accept
+  ;; a file name or a buffer, and we skip all buffers and files not
+  ;; fulfilling it.  Return the new buffer to search in or nil if done.
+  (el-search-hl-remove)
+  (el-search-kill-left-over-search-buffers t)
+  (let ((head (el-search-object-head search)))
+    (let ((buffer-stream (el-search-head-buffers head))
+          (buffer-list-before (buffer-list))
+          (done nil)  next  buffer)
+      (while (and (not done) (not (stream-empty-p buffer-stream)))
+        (setq next          (stream-first buffer-stream)
+              buffer-stream (stream-rest buffer-stream)
+              done          (or (not predicate) (funcall predicate next))))
+      (if (not done)
+          (progn
+            (setf (el-search-head-buffer   head) nil
+                  (el-search-head-buffers  head) buffer-stream) ;i.e. the 
empty stream
+            nil)
+        (setf (el-search-head-buffers  head) buffer-stream
+              (el-search-head-position head) 1)
+        (if (bufferp next)
+            (setq buffer next)
+          (setf (el-search-head-file head) next)
+          (setq buffer (let ((warning-minimum-level :error)) 
(find-file-noselect next))))
+        (unless (memq buffer buffer-list-before)
+          (with-current-buffer buffer
+            (setq-local el-search--temp-buffer-flag t)))
+        (setf (el-search-head-buffer head) buffer)
+        (when (bufferp buffer)
+          (with-current-buffer buffer
+            (setq-local el-search--this-buffer-matches
+                        (el-search-object-matches search))
+            (when (and (buffer-narrowed-p)
+                       (y-or-n-p (format "Widen buffer \"%s\"? "
+                                         (buffer-name))))
+              ;;FIXME: Is this always appropriate?
+              (widen))))
+        buffer))))
+
+(defun el-search--skip-to-next-buffer (&optional predicate)
+  ;; Find next buffer fulfilling the PREDICATE and continue search there
+  (el-search--next-buffer el-search--current-search predicate)
+  (el-search-continue-search))
+
+(defun el-search-make-search (matcher stream)
+  "Create and return a new `el-search-object' instance.
+MATCHER is the result of calling `el-search--matcher' on the
+pattern to search.  STREAM is a stream of buffers and/or files to
+search."
+  (let ((head (make-el-search-head :matcher matcher :buffers stream)))
+    (letrec ((search
+              (make-el-search-object
+               :head head
+               :matches
+               (seq-filter
+                #'identity ;we use `nil' as a "skip" tag
+                (funcall
+                 (letrec ((get-stream
+                           (lambda ()
+                             (stream-make
+                              (if-let ((buffer (or (el-search-head-buffer head)
+                                                   (el-search--next-buffer 
search))))
+                                  (with-current-buffer buffer
+                                    (save-excursion
+                                      ;; Widening already happens in 
`el-search-continue-search'
+                                      (goto-char (el-search-head-position 
head))
+                                      (message "Searching in %s"
+                                               (or (el-search-head-file head)
+                                                   (el-search-head-buffer 
head)))
+                                      (if-let ((match 
(el-search--search-pattern-1
+                                                       (el-search-head-matcher 
head) t)))
+                                          (progn
+                                            (setf (el-search-object-last-match 
search)
+                                                  (copy-marker (point)))
+                                            (el-search--skip-expression nil t)
+                                            (setf (el-search-head-position 
head)
+                                                  (copy-marker (point)))
+                                            (cons
+                                             (list (el-search-head-buffer head)
+                                                   match
+                                                   (el-search-head-file head))
+                                             (funcall get-stream)))
+                                        (setf (el-search-head-buffer head) nil
+                                              (el-search-head-file   head) nil)
+                                        (el-search--next-buffer search)
+                                        ;; retry with the next buffer
+                                        (cons nil (funcall get-stream)))))
+                                ;; end of stream (no buffers left to search in)
+                                nil)))))
+                   get-stream))))))
+      search)))
+
+(defun el-search-setup-search (pattern stream &optional from-here)
+  "Create and start a new search.
+PATTERN is the search pattern.  STREAM is a stream of buffers
+and/or files (i.e. file names) to search in.
+
+With optional FROM-HERE non-nil, the first buffer in STREAM
+should be the current buffer, and searching will start at the
+current buffer's point instead of its beginning."
+  (setq el-search--success nil)
+  (let ((matcher (el-search--matcher pattern)))
+    (setq el-search--current-search (el-search-make-search matcher stream))
+    (setq el-search--current-matcher matcher))
+  (setq el-search--current-pattern pattern)
+  (ring-insert el-search-history (list el-search--current-search pattern))
+  (when from-here (setq el-search--temp-buffer-flag nil))
+  (el-search-continue-search from-here))
+
+(defun el-search-stream-of-directory-files (&optional directory recurse)
+  "Return a stream of emacs-lisp files in DIRECTORY.
+DIRECTORY defaults to `default-directory'.  The returned stream
+will recurse into DIRECTORY's subdirectories when RECURSE is
+non-nil unless their name is matched by one of the
+`el-search-ignored-directory-regexps'."
+  (stream-of-directory-files
+   (or directory default-directory)
+   t nil
+   (and recurse
+        (lambda (dir-name)
+          (not (cl-some (lambda (regexp) (string-match-p regexp dir-name))
+                        el-search-ignored-directory-regexps))))
+   t #'el-search--elisp-file-name-p))
 
 
 ;;;; Additional pattern type definitions
 
-(defun el-search--split (matcher1 matcher2 list)
-  "Helper for the append pattern type.
-
-When a splitting of LIST into two lists L1, L2 exist so that Li
-is matched by MATCHERi, return (L1 L2) for such Li, else return
-nil."
-  (let ((try-match (lambda (list1 list2)
-                     (when (and (el-search--match-p matcher1 list1)
-                                (el-search--match-p matcher2 list2))
-                       (list list1 list2))))
-        (list1 list) (list2 '()) (match nil))
-    ;; don't use recursion, this could hit `max-lisp-eval-depth'
-    (while (and (not (setq match (funcall try-match list1 list2)))
-                (consp list1))
-      (let ((last-list1 (last list1)))
-        (if-let ((cdr-last-list1 (cdr last-list1)))
-            ;; list1 is a dotted list.  Then list2 must be empty.
-            (progn (setcdr last-list1 nil)
-                   (setq list2 cdr-last-list1))
-          (setq list1 (butlast list1 1)
-                list2 (cons (car last-list1) list2)))))
-    match))
-
-(el-search-defpattern append (&rest patterns)
-  "Matches any list factorable into lists matched by PATTERNS in order.
-
-PATTERNS is a list of patterns P1..Pn.  Match any list L for that
-lists L1..Ln exist that are matched by P1..Pn in order and L is
-equal to the concatenation of L1..Ln.  Ln is allowed to be no
-list.
-
-When different ways of matching are possible, it is unspecified
-which one is chosen.
-
-Example: the pattern
-
-   (append '(1 2 3) x (app car-safe 7))
-
-matches the list (1 2 3 4 5 6 7 8 9) and binds `x' to (4 5 6)."
-  (if (null patterns)
-      '(pred null)
-    (pcase-let ((`(,pattern . ,more-patterns) patterns))
-      (cond
-       ((null more-patterns)  pattern)
-       ((null (cdr more-patterns))
-        `(and (pred listp)
-              (app ,(apply-partially #'el-search--split
-                                     (el-search--matcher pattern)
-                                     (el-search--matcher (car more-patterns)))
-                   `(,,pattern ,,(car more-patterns)))))
-       (t `(append ,pattern (append ,@more-patterns)))))))
-
-(defun el-search--stringish-p (thing)
-  (or (stringp thing) (and el-search-use-sloppy-strings (symbolp thing))))
+(defun el-search--eregexp-p (thing)
+  ;; Return non-nil when THING is an "extended regexp" in the sense of
+  ;; the "string" pattern type
+  (pcase thing
+    ((pred stringp) t)
+    (`(,(and (pred listp) bindings)
+       ,(pred stringp))
+     (cl-every
+      (lambda (binding) (pcase binding ((or (pred symbolp) `(,(pred symbolp)) 
`(,(pred symbolp) ,_)) t)))
+      bindings))))
 
 (el-search-defpattern string (&rest regexps)
-  "Matches any string that is matched by all REGEXPS."
-  (el-search--check-pattern-args "string" regexps #'el-search--stringish-p
-                                 "Argument not a string")
+  "Matches any string that is matched by all REGEXPS.
+
+Any of the REGEXPS can also be an \"extended\" regexp of the form
+
+  \(bindings regexp\)
+
+where REGEXP is the actual regexp to match and BINDINGS is a
+let-style list of variable bindings.
+
+Example: (((case-fold-search nil)) \"foo\") is an extended regexp
+matching \"foo\", but not \"Foo\" even when `case-fold-search' is
+currently enabled."
+  (el-search-defpattern--check-args "string" regexps #'el-search--eregexp-p
+                                    "argument not a regexp")
   `(and (pred stringp)
-        ,@(mapcar (lambda (thing) `(pred (el-search--smart-string-match-p
-                                     ,(if (symbolp thing) (symbol-name thing) 
thing))))
+        ,@(mapcar (lambda (thing) `(pred (el-search--string-match-p ',thing)))
                   regexps)))
 
 (el-search-defpattern symbol (&rest regexps)
-  "Matches any symbol whose name is matched by all REGEXPS."
-  (el-search--check-pattern-args "symbol" regexps #'el-search--stringish-p
-                                 "Argument not a string")
-  `(and (pred symbolp)
-        (app symbol-name (string ,@regexps))))
+  "Matches any symbol whose name is matched by all REGEXPS.
+Any of the REGEXPS can be an extended regexp of the form
+\(bindings regexp\) like in the \"string\" pattern."
+  (el-search-defpattern--check-args "symbol" regexps #'el-search--eregexp-p
+                                    "argument not a regexp")
+  `(and (pred symbolp) (app symbol-name (string ,@regexps))))
 
 (defun el-search--contains-p (matcher exp)
-  "Return non-nil when tree EXP contains a match for MATCHER.
+  "Return non-nil when expression tree EXP contains a match for MATCHER.
 Recurse on all types of sequences.  In the positive case the
 return value is (t elt), where ELT is a matching element found in
 EXP."
@@ -762,11 +972,18 @@ EXP."
              (cl-some try-match exp))))))
 
 (el-search-defpattern contains (&rest patterns)
-  "Matches trees that contain a match for all PATTERNs.
-Searches any tree of sequences recursively for matches.  Objects
-of any kind matched by all PATTERNs are also matched.
+  "Matches expressions that contain a match for all PATTERNs.
+
+Example:
+
+  \(contains (string \"H\") 17)
+
+matches
+
+  \((\"Hallo\") x (5 [1 17])).
 
-  Example: (contains (string \"H\") 17) matches ((\"Hallo\") x (5 [1 17]))"
+The expression itself is included, so for example `1' is matched
+by \(contains 1\)."
   (cond
    ((null patterns) '_)
    ((null (cdr patterns))
@@ -780,117 +997,24 @@ of any kind matched by all PATTERNs are also matched.
   `(app ,(apply-partially #'el-search--match-p (el-search--matcher pattern))
         (pred not)))
 
-(defun el-search--match-symbol-file (regexp symbol)
-  (when-let ((symbol-file (and (symbolp symbol)
-                               (symbol-file symbol))))
-    (el-search--smart-string-match-p
-     (if (symbolp regexp) (concat "\\`" (symbol-name regexp) "\\'") regexp)
+(defun el-search--match-symbol-file (eregexp symbol)
+  (when-let ((symbol-file (and (symbolp symbol) (symbol-file symbol))))
+    (el-search--string-match-p
+     eregexp
      (file-name-sans-extension (file-name-nondirectory symbol-file)))))
 
-(el-search-defpattern source (regexp)
+(el-search-defpattern symbol-file (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."
-  (el-search--check-pattern-args "source" (list regexp) 
#'el-search--stringish-p
-                                 "Argument not a string")
-  `(pred (el-search--match-symbol-file ,(if (symbolp regexp) (symbol-name 
regexp) 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 descriptions of the KEY-SEQUENCE.
-KEY-SEQUENCE is a string or vector representing a key sequence,
-or an expression of the form (kbd STRING).
-
-Match any description of the same key sequence in any of these
-formats.
-
-Example: the pattern
-
-    (keys (kbd \"C-s\"))
-
-matches any of these expressions:
-
-    \"\\C-s\"
-    \"\C-s\"
-    (kbd \"C-s\")
-    [(control ?s)]"
-  (when (eq (car-safe key-sequence) 'kbd)
-    (setq key-sequence (kbd (cadr key-sequence))))
-  (el-search--check-pattern-args "keys" (list key-sequence) (lambda (x) (or 
(stringp x) (vectorp x)))
-                                 "argument not a string or vector")
-  `(pred (el-search--match-key-sequence ,key-sequence)))
-
-(defun el-search--transform-nontrivial-lpat (expr)
-  (pcase expr
-    ((and (pred symbolp) (let symbol-name (symbol-name expr)))
-     `(or (symbol ,symbol-name)
-          `',(symbol  ,symbol-name)
-          `#',(symbol ,symbol-name)))
-    ((pred stringp) `(string ,expr))
-    (_ expr)))
-
-(el-search-defpattern l (&rest lpats)
-  "Alternative pattern type for matching lists.
-Match any list with subsequent elements matched by all LPATS in
-order.
-
-The idea is to be able to search for pieces of code (i.e. lists)
-with very brief input by using a specialized syntax.
-
-An LPAT can take the following forms:
-
-SYMBOL  Matches any symbol S matched by SYMBOL's name interpreted
-        as a regexp.  Matches also 'S and #'S for any such S.
-STRING  Matches any string matched by STRING interpreted as a
-        regexp
-_       Matches any list element
-__      Matches any number of list elements (including zero)
-^       Matches zero elements, but only at the beginning of a list
-$       Matches zero elements, but only at the end of a list
-PAT     Anything else is interpreted as a normal pcase pattern, and
-        matches one list element matched by it
-
-^ is only valid as the first, $ as the last of the LPATS.
-
-Example: To match defuns that contain \"hl\" in their name and
-have at least one mandatory, but also optional arguments, you
-could use this pattern:
-
-    (l ^ 'defun hl (l _ &optional))"
-  (let ((match-start nil) (match-end nil))
-    (when (eq (car-safe lpats) '^)
-      (setq match-start t)
-      (cl-callf cdr lpats))
-    (when (eq (car-safe (last lpats)) '$)
-      (setq match-end t)
-      (cl-callf butlast lpats 1))
-    `(append ,@(if match-start '() '(_))
-             ,@(mapcar
-                (lambda (elt)
-                  (pcase elt
-                    ('__ '_)
-                    ('_ '`(,_))
-                    ('_? '(or '() `(,_))) ;FIXME: useful - document? or should 
we provide a (? PAT)
-                                          ;thing?
-                    (_ ``(,,(el-search--transform-nontrivial-lpat elt)))))
-                lpats)
-             ,@(if match-end '() '(_)))))
+`symbol-file' returns a (non-nil) FILE-NAME so that
+
+   (file-name-sans-extension (file-name-nondirectory FILENAME)))
+
+is matched by the REGEXP."
+  (el-search-defpattern--check-args "symbol-file" (list regexp) 
#'el-search--eregexp-p
+                                    "argument not a regexp")
+  `(pred (el-search--match-symbol-file ',regexp)))
 
 (el-search-defpattern char-prop (property)
   "Matches the object if completely covered with PROPERTY.
@@ -898,9 +1022,9 @@ This pattern matches the object if its representation in 
the
 search buffer is completely covered with the character property
 PROPERTY.
 
-This pattern always tests the complete expression in the search
-buffer, it is not possible to test subexpressions calculated in
-the search pattern."
+This pattern should only be used to match against the current
+object (so it can't be used inside an `app' pattern for
+example)."
   `(guard (and (get-char-property (point) ',property)
                ,(macroexp-let2 nil limit '(scan-sexps (point) 1)
                   `(= (next-single-char-property-change
@@ -913,9 +1037,9 @@ This pattern matches the object if its representation in 
the
 search buffer is partly covered with the character property
 PROPERTY.
 
-This pattern always tests the complete expression in the search
-buffer, it is not possible to test subexpressions calculated in
-the search pattern."
+This pattern should only be used to match against the current
+object (so it can't be used inside an `app' pattern for
+example)."
   `(guard (or (get-char-property (point) ',property)
               ,(macroexp-let2 nil limit '(scan-sexps (point) 1)
                  `(not (= (next-single-char-property-change
@@ -929,24 +1053,55 @@ the search pattern."
 
 (defvar-local el-search-hl-other-overlays '())
 
-(defvar el-search-keep-hl nil)
+(defvar el-search-keep-hl nil
+  "Non-nil indicates we should not remove any highlighting.")
 
 (defun el-search-hl-sexp (&optional bounds)
-  (let ((bounds (or bounds
-                    (list (point) (el-search--end-of-sexp)))))
+  (let ((bounds (or bounds (list (point) (el-search--end-of-sexp)))))
     (if (overlayp el-search-hl-overlay)
         (apply #'move-overlay el-search-hl-overlay bounds)
       (overlay-put (setq el-search-hl-overlay (apply #'make-overlay bounds))
                    'face 'el-search-match))
-    (overlay-put el-search-hl-overlay 'priority 1002))
+    (overlay-put el-search-hl-overlay 'priority 1002)
+
+    ;; Vertically scroll the current sexp into view when appropriate -- we
+    ;; must redisplay to get updated window bounds.  The selected frame
+    ;; must apparently be displayed for this to work.
+    (while (not (eq t (frame-visible-p (selected-frame))))
+      (sleep-for .1))
+    (redisplay)
+    (let ((wheight (window-height)))
+      ;; FIXME: make the following integer constants customizable,
+      ;; presumably, named in analogy to the scroll- options?
+      (unless (pos-visible-in-window-p
+               (save-excursion (goto-char (cadr bounds))
+                               (line-end-position (max +3 (/ wheight 25)))))
+        (scroll-up (min
+                    (max
+                     ;; make at least sexp end + a small margin visible
+                     (- (line-number-at-pos (cadr bounds))
+                        (line-number-at-pos (window-end))
+                        (- (max 2 (/ wheight 4))))
+                     ;; also try to center current sexp
+                     (- (/ ( + (line-number-at-pos (car bounds))
+                               (line-number-at-pos (cadr bounds)))
+                           2)
+                        (/ (+ (line-number-at-pos (window-start))
+                              (line-number-at-pos (window-end)))
+                           2)))
+                    ;; but also ensure at least a small margin is left between 
point and window start
+                    (- (line-number-at-pos (car  bounds))
+                       (line-number-at-pos (window-start))
+                       3))))))
+
   (add-hook 'post-command-hook #'el-search-hl-post-command-fun t t))
 
-(defun el-search--hl-other-matches-1 (pattern from to)
+(defun el-search--hl-other-matches-1 (matcher from to)
+  ;; Highlight all matches between FROM and TO with face
+  ;; `el-search-other-match'.
   (mapc #'delete-overlay el-search-hl-other-overlays)
   (setq el-search-hl-other-overlays '())
-  (let ((matcher (el-search--matcher pattern))
-        this-match-beg this-match-end
-        (done nil))
+  (let (this-match-beg this-match-end (done nil))
     (save-excursion
       (goto-char from)
       (while (not done)
@@ -962,9 +1117,13 @@ the search pattern."
             (goto-char this-match-end)
             (when (>= (point) to) (setq done t))))))))
 
-(defun el-search-hl-other-matches (pattern)
-  "Highlight all matches visible in the selected window."
-  (el-search--hl-other-matches-1 pattern
+(defun el-search-hl-other-matches (matcher)
+  "Highlight all visible matches.
+
+Highlight all matches visible in the selected window with face
+`el-search-other-match' and add `el-search--after-scroll' to the
+local binding of `window-scroll-functions'."
+  (el-search--hl-other-matches-1 matcher
                                  (save-excursion
                                    (goto-char (window-start))
                                    (beginning-of-defun-raw)
@@ -973,7 +1132,7 @@ the search pattern."
   (add-hook 'window-scroll-functions #'el-search--after-scroll t t))
 
 (defun el-search--after-scroll (_win start)
-  (el-search--hl-other-matches-1 el-search-current-pattern
+  (el-search--hl-other-matches-1 el-search--current-matcher
                                  (save-excursion
                                    (goto-char start)
                                    (beginning-of-defun-raw)
@@ -992,20 +1151,137 @@ the search pattern."
               (eq this-command 'el-search-query-replace)
               (eq this-command 'el-search-pattern))
     (el-search-hl-remove)
-    (remove-hook 'post-command-hook 'el-search-hl-post-command-fun t)))
+    (remove-hook 'post-command-hook 'el-search-hl-post-command-fun t)
+    (setq el-search--temp-buffer-flag nil)))
 
 
 ;;;; Core functions
 
+(defun el-search-continue-in-next-buffer ()
+  "Skip current search buffer and continue with the next."
+  (interactive)
+  (el-search--skip-to-next-buffer
+   (lambda (buffer-or-file)
+     (not (if (bufferp buffer-or-file)
+              (eq buffer-or-file (current-buffer))
+            (file-equal-p buffer-or-file buffer-file-name))))))
+
+(defun el-search-all-matches (search)
+  "Perform SEARCH non-interactively and return a list of all matches.
+
+SEARCH is an `el-search-object'.  Execute SEARCH
+non-interactively until finished and return a list of matches in
+the form \(buffer-or-file-name . match-position)."
+  (mapcar
+   (pcase-lambda (`(,buffer ,position ,file))
+     (cons (if (buffer-live-p buffer) buffer file) position))
+   (seq-into-sequence (el-search-object-matches search))))
+
+(defun el-search-jump-to-search-head (&optional previous-search)
+  (interactive "P")
+  "Switch to current search buffer and go to the last match.
+With prefix arg, prompt for a prior search to resume, and make
+that the current search."
+  (when previous-search
+    (setq el-search--current-search
+          (car (ring-ref
+                el-search-history
+                (string-to-number
+                 (let ((completion-extra-properties
+                        `(:annotation-function
+                          ,(lambda (elt)
+                             (format "  Search in %S for %S"
+                                     (el-search-head-buffer
+                                      (el-search-object-head
+                                       (car (ring-ref el-search-history 
(string-to-number elt)))))
+                                     (cadr (ring-ref el-search-history 
(string-to-number elt))))))))
+                   (completing-read
+                    "Resume previous search: "
+                    (mapcar #'prin1-to-string (number-sequence 0 (1- 
(ring-length el-search-history))))))))))
+    (setq el-search--current-matcher
+          (el-search-head-matcher (el-search-object-head 
el-search--current-search))))
+  (if-let ((search el-search--current-search)
+           (current-head (el-search-object-head search))
+           (current-search-buffer (el-search-head-buffer current-head)))
+      (progn
+        (setq this-command 'el-search-pattern)
+        (let ((win (display-buffer current-search-buffer 
el-search-display-buffer-action)))
+          (select-frame-set-input-focus (window-frame win))
+          (select-window win))
+        (let ((last-match (el-search-object-last-match search)))
+          (if (not (and last-match
+                        (eq (marker-buffer last-match) (current-buffer))))
+              (goto-char (el-search-head-position current-head)) ;this should 
normally not happpen
+            (goto-char last-match)
+            (el-search-hl-sexp)
+            (el-search-hl-other-matches el-search--current-matcher)
+            (set-transient-map el-search-map))))
+    (error "Last search finished")))
+
+(defun el-search-continue-search (&optional from-here)
+  "Continue the current search."
+  (interactive "P")
+  (setq this-command 'el-search-pattern)
+  (setq el-search--current-matcher
+        (el-search-head-matcher (el-search-object-head 
el-search--current-search)))
+  (let ((old-current-buffer (current-buffer)))
+    (when from-here
+      (let* ((head (el-search-object-head el-search--current-search))
+             (current-search-buffer
+              (or (el-search-head-buffer head)
+                  (el-search--next-buffer el-search--current-search)))) ; 
search not yet started
+        (cond
+         ((eq (current-buffer) current-search-buffer)
+          (setf (el-search-head-position head) (copy-marker (point))))
+         ((and current-search-buffer (buffer-live-p current-search-buffer))
+          (error "Please resume from buffer %s" (buffer-name 
current-search-buffer)))
+         (t (error "Invalid search head")))))
+    (unwind-protect
+        (let ((stream-of-matches (el-search-object-matches 
el-search--current-search)))
+          (if (not (stream-empty-p stream-of-matches))
+              (let ((match (stream-first stream-of-matches)))
+                (switch-to-buffer (car match))
+                (goto-char (cadr match))
+                (cl-callf stream-rest (el-search-object-matches 
el-search--current-search))
+                (el-search-hl-sexp)
+                (unless (and (eq this-command last-command)
+                             el-search--success
+                             (eq (current-buffer) old-current-buffer))
+                  (el-search-hl-other-matches el-search--current-matcher))
+                (set-transient-map el-search-map)
+                (setq el-search--success t))
+            (setq el-search--success nil)
+            (set-transient-map nil)
+            (el-search-kill-left-over-search-buffers)
+            (message "Done!")))
+      (unless el-search--success (setq el-search--current-pattern nil)))))
+
+(defun el-search-skip-directory (directory)
+  "Skip all subsequent matches in files located under DIRECTORY."
+  (interactive
+   (list (read-directory-name "Skip all files under directory: " nil
+                              (if-let ((search el-search--current-search)
+                                       (current-head (el-search-object-head 
search))
+                                       (current-file (el-search-head-file 
current-head)))
+                                  (file-name-directory current-file)
+                                default-directory)
+                              t)))
+  (el-search--skip-to-next-buffer
+   (lambda (buffer-or-file-name)
+     (or (bufferp buffer-or-file-name)
+         ;; `file-in-directory-p' would be perfect here, but it calls
+         ;; file-truename on both args, so we use this:
+         (string-match-p "\\`\\.\\." (file-relative-name buffer-or-file-name 
directory))))))
+
 ;;;###autoload
-(defun el-search-pattern (pattern &optional no-error)
-  "Start new or resume last elisp search.
+(defun el-search-pattern (pattern)
+  "Start new or resume last elisp buffer search.
 
 Search current buffer for expressions that are matched by `pcase'
 PATTERN.  Use `read' to transform buffer contents into
 expressions.
 
-Use `emacs-lisp-mode' for reading input.  Some keys in the
+Uses `emacs-lisp-mode' for reading input.  Some keys in the
 minibuffer have a special binding: to make it possible to edit
 multi line input, C-j inserts a newline, and up and down move the
 cursor vertically - see `el-search-read-expression-map' for more
@@ -1017,40 +1293,214 @@ be defined with `el-search-defpattern'.
 
 The following additional pattern types are currently defined:"
   (interactive (list (if (and (eq this-command last-command)
-                              el-search-success)
-                         el-search-current-pattern
-                       (let* ((input (el-search--read-pattern "Find pcase 
pattern: "
-                                                              (car 
el-search-history)))
-                              (pattern (read input)))
-                         ;; A very common mistake: input "foo" instead of 
"'foo"
-                         (when (and (symbolp pattern)
-                                    (not (eq pattern '_))
-                                    (or (not (boundp pattern))
-                                        (not (eq (symbol-value pattern) 
pattern))))
-                           (error "Please don't forget the quote when 
searching for a symbol"))
-                         ;; Make input available also in query-replace history
-                         (el-search--pushnew-to-history input 
'el-search-query-replace-history)
-                         ;; and wrap the PATTERN
-                         (el-search--wrap-pattern pattern)))))
-  (if (not (called-interactively-p 'any))
-      (el-search--search-pattern pattern no-error)
-    (setq this-command 'el-search-pattern) ;in case we come from isearch
-    (setq el-search-current-pattern pattern)
-    (let ((opoint (point)))
-      (when (and (eq this-command last-command) el-search-success)
-        (el-search--skip-expression nil t))
-      (setq el-search-success nil)
-      (when (condition-case nil
-                (el-search--search-pattern pattern)
-              (end-of-buffer (message "No match")
-                             (goto-char opoint)
-                             (el-search-hl-remove)
-                             (ding)
-                             nil))
-        (setq el-search-success t)
-        (el-search-hl-sexp)
-        (unless (eq this-command last-command)
-          (el-search-hl-other-matches pattern))))))
+                              el-search--success)
+                         el-search--current-pattern
+                       (el-search--read-pattern-for-interactive))))
+  (cond
+   ((and (eq this-command last-command)
+         (eq pattern el-search--current-pattern))
+    (progn
+      (el-search--skip-expression nil t)
+      (el-search-continue-search 'from-here)))
+   ((and (equal pattern el-search--current-pattern)
+         (eq (current-buffer)
+             (el-search-head-buffer (el-search-object-head 
el-search--current-search))))
+    (el-search-continue-search 'from-here))
+   (t ;; create a new search single-buffer search
+    (el-search-setup-search pattern (stream (list (current-buffer))) 
'from-here))))
+
+(put 'el-search-pattern 'function-documentation '(el-search--make-docstring 
'el-search-pattern))
+
+(defun el-search-previous-match ()
+  "Revisit found matches in the current buffer in reverse order."
+  ;; Implementing backward el-searching is very hard (or very slow,
+  ;; alternatively), so let's use this for now...
+  (interactive)
+  (if (not (el-search-head-buffer (el-search-object-head 
el-search--current-search)))
+      ;; This case is hard; the user would expect that when he hits C-S
+      ;; then, the search is restored with the old matches merged
+      (error "Last search completed, please start a new search")
+    (setq this-command 'el-search-pattern)
+    (let ((last-match-beg (el-search-object-last-match 
el-search--current-search)))
+      (if (< last-match-beg (point))
+          (el-search-jump-to-search-head)
+        (let ((go-there nil) (matches el-search--this-buffer-matches))
+          (catch 'done
+            (while (and (not (stream-empty-p matches))
+                        (< (cadr (stream-first matches)) (point)))
+              (setq go-there (cadr (stream-first matches)))
+              (stream-pop matches)))
+          (if (not go-there)
+              (message "[Beginning of this buffer's recorded matches]")
+            (goto-char go-there)
+            (el-search-hl-sexp)
+            (set-transient-map el-search-map)))))))
+
+;;;###autoload
+(defun el-search-buffers (pattern)
+  "Search all live elisp buffers for PATTERN."
+  (interactive (list (el-search--read-pattern-for-interactive)))
+  (el-search-setup-search
+   pattern
+   (seq-filter
+    (lambda (buffer) (with-current-buffer buffer (derived-mode-p 
'emacs-lisp-mode)))
+    (stream (buffer-list)))))
+
+;;;###autoload
+(defun el-search-directory (pattern directory &optional search-recursively)
+  "Search all elisp files in DIRECTORY for PATTERN.
+With prefix arg SEARCH-RECURSIVELY non-nil, search subdirectories recursively."
+  (interactive (list (el-search--read-pattern-for-interactive)
+                     (read-directory-name (format "el-search directory%s: "
+                                                  (if current-prefix-arg " 
recursively" ""))
+                                          nil default-directory t)
+                     current-prefix-arg))
+  (el-search-setup-search
+   pattern
+   (el-search-stream-of-directory-files directory search-recursively)))
+
+;;;###autoload
+(defun el-search-emacs-elisp-sources (pattern)
+  "Search Emacs elisp sources for PATTERN.
+This command recursively searches all elisp files under
+\(expand-file-name \"lisp/\" source-directory\)."
+  (interactive (list (el-search--read-pattern-for-interactive)))
+  (el-search-setup-search
+   pattern
+   (el-search-stream-of-directory-files
+    (expand-file-name "lisp/" source-directory)
+    t)))
+
+(declare-function dired-get-marked-files "dired")
+
+;;;###autoload
+(defun el-search-dired-marked-files (pattern &optional recursively)
+  "el-search marked files and directories in dired.
+With RECURSIVELY given (the prefix arg in an interactive call),
+search directories recursively."
+  (interactive (list (el-search--read-pattern-for-interactive) 
current-prefix-arg))
+  (el-search-setup-search
+   pattern
+   (stream-concatenate
+    (seq-map
+     (lambda (file)
+       (if (file-directory-p file)
+           (el-search-stream-of-directory-files file recursively)
+         (stream (list file))))
+     (stream (dired-get-marked-files))))))
+
+
+;;;; Query-replace
+
+(defun el-search--replace-hunk (region to-insert)
+  "Replace the text in REGION in current buffer with string TO-INSERT.
+Add line breaks before and after TO-INSERT when appropriate and
+reindent."
+  (atomic-change-group
+    (let* ((inhibit-message t)
+           (opoint (point))
+           (original-text (prog1 (apply #'buffer-substring-no-properties 
region)
+                            (goto-char (car region))
+                            (apply #'delete-region region)))
+           ;; care about other sexps in this line
+           (sexp-before-us (not (looking-back "\(\\|^\\s-*" 
(line-beginning-position))))
+           (sexp-after-us  (not (looking-at "\\s-*[;\)]\\|$")))
+           (insert-newline-before
+            (or
+             (and (string-match-p "\n" to-insert)
+                  (not (string-match-p "\n" original-text))
+                  (or (and sexp-before-us sexp-after-us)
+                      (looking-back
+                       (rx (or (syntax word) (syntax symbol))
+                           (+ blank)
+                           (or (syntax word) (syntax symbol))
+                           (* any))
+                       (line-beginning-position))))
+             ;; (and sexp-before-us
+             ;;      (> (+ (apply #'max (mapcar #'length (split-string 
to-insert "\n")))
+             ;;            (- (point) (line-beginning-position)))
+             ;;         fill-column))
+             ))
+           (insert-newline-after (and insert-newline-before sexp-after-us)))
+      (when insert-newline-before
+        (when (looking-back "\\s-+" (line-beginning-position))
+          (delete-region (match-beginning 0) (match-end 0)))
+        (insert "\n"))
+      (insert to-insert)
+      (when insert-newline-after
+        (insert "\n"))
+      (save-excursion
+        ;; the whole enclosing sexp might need re-indenting
+        (condition-case nil (up-list)  (scan-error))
+        (indent-region opoint (1+ (point)))))))
+
+(defun el-search--format-replacement (replacement original replace-expr-input 
splice)
+  ;; Return a printed representation of REPLACEMENT.  Try to reuse the
+  ;; layout of subexpressions shared with the original (replaced)
+  ;; expression and the replace expression.
+  (if (and splice (not (listp replacement)))
+      (error "Expression to splice in is an atom")
+    (let ((orig-buffer (generate-new-buffer "orig-expr")))
+      (with-current-buffer orig-buffer
+        (emacs-lisp-mode)
+        (insert original)
+        (when replace-expr-input (insert "\n\n" replace-expr-input)))
+      (unwind-protect
+          (with-temp-buffer
+            (emacs-lisp-mode)
+            (insert (if splice
+                        (mapconcat #'el-search--pp-to-string replacement " ")
+                      (el-search--pp-to-string replacement)))
+            (goto-char 1)
+            (let (start this-sexp end orig-match-start orig-match-end done)
+              (while (and (< (point) (point-max))
+                          (condition-case nil
+                              (progn
+                                (setq start (point)
+                                      this-sexp (read (current-buffer))
+                                      end   (point))
+                                t)
+                            (end-of-buffer nil)))
+                (setq done nil orig-match-start nil)
+                (with-current-buffer orig-buffer
+                  (goto-char 1)
+                  (if (el-search-forward `',this-sexp t)
+                      (setq orig-match-start (point)
+                            orig-match-end (progn (forward-sexp) (point)))
+                    (setq done t)))
+                ;; find out whether we have a sequence of equal expressions
+                (while (and (not done)
+                            (condition-case nil
+                                (progn (setq this-sexp (read 
(current-buffer))) t)
+                              ((invalid-read-syntax end-of-buffer end-of-file) 
nil)))
+                  (if (with-current-buffer orig-buffer
+                        (condition-case nil
+                            (if (not (equal this-sexp (read (current-buffer))))
+                                nil
+                              (setq orig-match-end (point))
+                              t)
+                          ((invalid-read-syntax end-of-buffer end-of-file) 
nil)))
+                      (setq end (point))
+                    (setq done t)))
+                ;; FIXME: there could be another occurrence of THIS-SEXP in 
ORIG-BUFFER with more
+                ;; subsequent equal expressions after it
+                (if orig-match-start
+                    (el-search--replace-hunk
+                     (list start end)
+                     (with-current-buffer orig-buffer
+                       (buffer-substring-no-properties orig-match-start 
orig-match-end)))
+                  (goto-char start)
+                  (el-search--skip-expression nil t))
+                (condition-case nil
+                    (el-search--ensure-sexp-start)
+                  (end-of-buffer (goto-char (point-max))))))
+            (goto-char 1)
+            (forward-sexp (if splice (length replacement) 1))
+            (let ((result (buffer-substring 1 (point))))
+              (if (equal replacement (read (if splice (format "(%s)" result) 
result)))
+                  result
+                (error "Error in `el-search--format-replacement' - please make 
a bug report"))))
+        (kill-buffer orig-buffer)))))
 
 (defvar el-search-search-and-replace-help-string
   "\
@@ -1072,95 +1522,104 @@ Hit any key to proceed."
   (let ((replace-all nil) (nbr-replaced 0) (nbr-skipped 0) (done nil)
         (el-search-keep-hl t) (opoint (point))
         (get-replacement (el-search--matcher pattern replacement))
-        (skip-matches-in-replacement 'ask))
+        (skip-matches-in-replacement 'ask)
+        (matcher (setq el-search--current-matcher (el-search--matcher 
pattern))))
     (unwind-protect
-        (while (and (not done) (el-search--search-pattern pattern t))
-          (setq opoint (point))
-          (unless replace-all
-            (el-search-hl-sexp)
-            (unless (eq this-command last-command)
-              (el-search-hl-other-matches pattern)))
-          (let* ((region (list (point) (el-search--end-of-sexp)))
-                 (original-text (apply #'buffer-substring-no-properties 
region))
-                 (expr      (read original-text))
-                 (replaced-this nil)
-                 (new-expr  (funcall get-replacement expr))
-                 (get-replacement-string
-                  (lambda () (el-search--format-replacement new-expr 
original-text to-input-string splice)))
-                 (to-insert (funcall get-replacement-string))
-                 (replacement-contains-another-match
-                  (with-temp-buffer
-                    (emacs-lisp-mode)
-                    (insert to-insert)
-                    (goto-char 1)
-                    (el-search--skip-expression new-expr)
-                    (condition-case nil
-                        (progn (el-search--ensure-sexp-start)
-                               (el-search--search-pattern pattern t))
-                      (end-of-buffer nil))))
-                 (do-replace
-                  (lambda ()
-                    (save-excursion
-                      (save-restriction
-                        (widen)
-                        (el-search--replace-hunk (list (point) 
(el-search--end-of-sexp)) to-insert)))
-                    (el-search--ensure-sexp-start) ;skip potentially newly 
added whitespace
-                    (el-search-hl-sexp (list opoint (point)))
-                    (cl-incf nbr-replaced)
-                    (setq replaced-this t))))
-            (if replace-all
-                (funcall do-replace)
-              (while (not (pcase (if replaced-this
-                                     (read-char-choice "[SPC ! q]  (? for 
help)"
-                                                       '(?\ ?! ?q ?\C-g ?n ??))
-                                   (read-char-choice
-                                    (concat "Replace this occurrence"
-                                            (if (or (string-match-p "\n" 
to-insert)
-                                                    (< 40 (length to-insert)))
-                                                "" (format " with `%s'" 
to-insert))
-                                            "? "
-                                            (if splice "{splice} " "")
-                                            "[y SPC r ! s q]  (? for help)" )
-                                    '(?y ?n ?r ?\ ?! ?q ?\C-g ?s ??)))
-                            (?r (funcall do-replace)
-                                nil)
-                            (?y (funcall do-replace)
-                                t)
-                            ((or ?\ ?n)
-                             (unless replaced-this (cl-incf nbr-skipped))
-                             t)
-                            (?! (unless replaced-this
-                                  (funcall do-replace))
-                                (setq replace-all t)
-                                t)
-                            (?s (cl-callf not splice)
-                                (setq to-insert (funcall 
get-replacement-string))
-                                nil)
-                            ((or ?q ?\C-g)
-                             (setq done t)
-                             t)
-                            (?? (ignore (read-char 
el-search-search-and-replace-help-string))
-                                nil)))))
-            (unless (or done (eobp))
-              (cond
-               ((not (and replaced-this replacement-contains-another-match))
-                (el-search--skip-expression nil t))
-               ((eq skip-matches-in-replacement 'ask)
-                (if (setq skip-matches-in-replacement
-                          (yes-or-no-p "Match in replacement - always skip? "))
-                    (forward-sexp)
+        (progn
+
+          ;; Try to avoid to call time consuming `el-search-hl-other-matches' 
in the loop
+          (el-search-hl-other-matches matcher)
+          (add-hook 'window-scroll-functions #'el-search--after-scroll t t)
+
+          (while (and (not done) (el-search--search-pattern-1 matcher t))
+            (setq opoint (point))
+            (unless replace-all
+              (el-search-hl-sexp))
+            (let* ((region (list (point) (el-search--end-of-sexp)))
+                   (original-text (apply #'buffer-substring-no-properties 
region))
+                   (expr      (read original-text))
+                   (replaced-this nil)
+                   (new-expr  (funcall get-replacement expr))
+                   (get-replacement-string
+                    (lambda () (el-search--format-replacement
+                           new-expr original-text to-input-string splice)))
+                   (to-insert (funcall get-replacement-string))
+                   (replacement-contains-another-match
+                    (with-temp-buffer
+                      (emacs-lisp-mode)
+                      (insert to-insert)
+                      (goto-char 1)
+                      (el-search--skip-expression new-expr)
+                      (condition-case nil
+                          (progn (el-search--ensure-sexp-start)
+                                 (el-search-forward pattern t))
+                        (end-of-buffer nil))))
+                   (do-replace
+                    (lambda ()
+                      (save-excursion
+                        (save-restriction
+                          (widen)
+                          (el-search--replace-hunk (list (point) 
(el-search--end-of-sexp)) to-insert)))
+                      (el-search--ensure-sexp-start) ;skip potentially newly 
added whitespace
+                      (unless replace-all (el-search-hl-sexp (list opoint 
(point))))
+                      (cl-incf nbr-replaced)
+                      (setq replaced-this t))))
+              (if replace-all
+                  (funcall do-replace)
+                (redisplay) ;FIXME: why is this necessary?  Without this, 
read-char-choice recenters!?!
+                (while (not (pcase (if replaced-this
+                                       (read-char-choice "[SPC ! q]  (? for 
help)"
+                                                         '(?\ ?! ?q ?\C-g ?n 
??))
+                                     (read-char-choice
+                                      (concat "Replace this occurrence"
+                                              (if (or (string-match-p "\n" 
to-insert)
+                                                      (< 40 (length 
to-insert)))
+                                                  "" (format " with `%s'" 
to-insert))
+                                              "? "
+                                              (if splice "{splice} " "")
+                                              "[y SPC r ! s q]  (? for help)" )
+                                      '(?y ?n ?r ?\ ?! ?q ?\C-g ?s ??)))
+                              (?r (funcall do-replace)
+                                  nil)
+                              (?y (funcall do-replace)
+                                  t)
+                              ((or ?\ ?n)
+                               (unless replaced-this (cl-incf nbr-skipped))
+                               t)
+                              (?! (unless replaced-this
+                                    (funcall do-replace))
+                                  (setq replace-all t)
+                                  t)
+                              (?s (cl-callf not splice)
+                                  (setq to-insert (funcall 
get-replacement-string))
+                                  nil)
+                              ((or ?q ?\C-g)
+                               (setq done t)
+                               t)
+                              (?? (ignore (read-char 
el-search-search-and-replace-help-string))
+                                  nil)))))
+              (when replacement-contains-another-match
+                (el-search-hl-other-matches matcher))
+              (unless (or done (eobp))
+                (cond
+                 ((not (and replaced-this replacement-contains-another-match))
+                  (el-search--skip-expression nil t))
+                 ((eq skip-matches-in-replacement 'ask)
+                  (if (setq skip-matches-in-replacement
+                            (yes-or-no-p "Match in replacement - always skip? 
"))
+                      (forward-sexp)
+                    (el-search--skip-expression nil t)
+                    (when replace-all
+                      (setq replace-all nil)
+                      (message "Falling back to interactive mode")
+                      (sit-for 3.))))
+                 (skip-matches-in-replacement (forward-sexp))
+                 (t
                   (el-search--skip-expression nil t)
-                  (when replace-all
-                    (setq replace-all nil)
-                    (message "Falling back to interactive mode")
-                    (sit-for 3.))))
-               (skip-matches-in-replacement (forward-sexp))
-               (t
-                (el-search--skip-expression nil t)
-                (message "Replacement contains another match%s"
-                         (if replace-all " - falling back to interactive mode" 
""))
-                (setq replace-all nil)
-                (sit-for 2.)))))))
+                  (message "Replacement contains another match%s"
+                           (if replace-all " - falling back to interactive 
mode" ""))
+                  (setq replace-all nil)
+                  (sit-for 2.))))))))
     (el-search-hl-remove)
     (goto-char opoint)
     (message "Replaced %d matches%s"
@@ -1173,10 +1632,13 @@ Hit any key to proceed."
   (let ((from-input (let ((el-search--initial-mb-contents
                            (or el-search--initial-mb-contents
                                (and (eq last-command 'el-search-pattern)
-                                    (car el-search-history)))))
+                                    (if (equal (read (car 
el-search-pattern-history))
+                                               (read (car 
el-search-query-replace-history)))
+                                        (car el-search-query-replace-history)
+                                      (car el-search-pattern-history))))))
                       (el-search--read-pattern "Query replace pattern: " nil
                                                
'el-search-query-replace-history)))
-        from to)
+        from to read-from read-to)
     (with-temp-buffer
       (emacs-lisp-mode)
       (insert from-input)
@@ -1184,11 +1646,11 @@ Hit any key to proceed."
       (forward-sexp)
       (skip-chars-forward " \t\n")
       ;; FIXME: maybe more sanity tests here...
-      (if (not (looking-at "->"))
+      (if (not (looking-at "->\\|=>\\|>"))
           (setq from from-input
                 to (let ((el-search--initial-mb-contents nil))
                      (el-search--read-pattern "Replace with result of 
evaluation of: " from)))
-        (delete-char 2)
+        (delete-region (point) (match-end 0))
         (goto-char 1)
         (forward-sexp)
         (setq from (buffer-substring 1 (point)))
@@ -1210,8 +1672,14 @@ Hit any key to proceed."
               (indent-region 1 (point-max))
               (buffer-string))
             el-search-query-replace-history))
-    (el-search--pushnew-to-history from 'el-search-history)
-    (list (el-search--wrap-pattern (read from)) (read to) to)))
+    (el-search--pushnew-to-history from 'el-search-pattern-history)
+    (setq read-from (read from))
+    (setq read-to   (read to))
+    (el-search--maybe-warn-about-unquoted-symbol read-from)
+    (when (and (symbolp read-to)
+               (not (el-search--contains-p (el-search--matcher `',read-to) 
read-from)))
+      (el-search--maybe-warn-about-unquoted-symbol read-to))
+    (list (el-search--wrap-pattern read-from) read-to to)))
 
 ;;;###autoload
 (defun el-search-query-replace (from-pattern to-expr &optional textual-to)
@@ -1230,11 +1698,12 @@ you can also give an input of the form
 
    FROM-PATTERN -> TO-EXPR
 
-to the first prompt and specify both expressions at once.  This
-format is also used for history entries."
+\(\">\" and \"=>\" are also allowed as a separator) to the first
+prompt and specify both expressions at once.  This format is also
+used for history entries."
   (interactive (el-search-query-replace--read-args))
   (setq this-command 'el-search-query-replace) ;in case we come from isearch
-  (setq el-search-current-pattern from-pattern)
+  (setq el-search--current-pattern from-pattern)
   (barf-if-buffer-read-only)
   (el-search--search-and-replace-pattern from-pattern to-expr nil textual-to))
 
@@ -1246,22 +1715,23 @@ format is also used for history entries."
       (goto-char other-end))
     input))
 
+
+;;;; Invoking from Isearch
+
 ;;;###autoload
 (defun el-search-search-from-isearch ()
-  ;; FIXME: an interesting alternative would be to really integrate it
-  ;; with Isearch, using `isearch-search-fun-function'.
-  ;; Alas, this is not trivial if we want to transfer our optimizations.
+  "Switch from isearch to an el-search session."
   (interactive)
   (let ((el-search--initial-mb-contents (concat "'" 
(el-search--take-over-from-isearch))))
     ;; use `call-interactively' so we get recorded in 
`extended-command-history'
     (call-interactively #'el-search-pattern)))
 
-;;;###autoload
-(defun el-search-replace-from-isearch ()
-  (interactive)
-  (let ((el-search--initial-mb-contents (concat "'" 
(el-search--take-over-from-isearch t))))
-    (call-interactively #'el-search-query-replace)))
-
+;; Is this useful at all?
+;; ;;;###autoload
+;; (defun el-search-replace-from-isearch ()
+;;   (interactive)
+;;   (let ((el-search--initial-mb-contents (concat "'" 
(el-search--take-over-from-isearch t))))
+;;     (call-interactively #'el-search-query-replace)))
 
 
 (provide 'el-search)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]