GNU ELPA - el-search


Expression based interactive search for Emacs Lisp
el-search-1.7.15.tar, 2018-Nov-06, 280kB
Michael Heerdegen <>
Home page
Browse repository
CGit or Gitweb

To install this package, run in Emacs:

M-x package-install RET el-search RET

Full description

This package implements an expression based interactive search tool
for Emacs Lisp files and buffers.  The pattern language used is a
superset of `pcase' patterns.

"el-search" is multi file/buffer search capable.  It is designed to
be fast and easy to use.  It offers an occur-like overview of
matches and can do query-replace based on the same set of patterns.
All searches are added to a history and can be resumed or restarted
later.  Finally, it allows you to define your own kinds of search
patterns and your own multi-search commands.

Key bindings

Loading this file doesn't install any key bindings - but you
probably want some.  There are two predefined installable schemes
of key bindings.  The first scheme defines bindings mostly of the
form "Control-Shift-Letter", e.g. C-S, C-R, C-% etc.  These can be
installed by calling (el-search-install-shift-bindings) - typically
from your init file.  For console users (and others), the function
`el-search-install-bindings-under-prefix' installs bindings of the
form PREFIX LETTER.  If you call

  (el-search-install-bindings-under-prefix [(meta ?s) ?e])

you install bindings M-s e s, M-s e r, M-s e % etc.  When using
this function to install key bindings, installed bindings are
"repeatable" where it makes sense so that you can for example hit
M-s e j s s s a % to reactive the last search, go to the next match
three times, then go back to the first match in the current buffer,
and finally invoke query-replace.

Here is a complete list of key bindings installed when
you call
  (el-search-install-bindings-under-prefix [(meta ?s) ?e])


  C-S, M-s e s (el-search-pattern)
    Start a search in the current buffer/go to the next match.

    While searching, the searched buffer is current (not the
    minibuffer).  All commands that are not search or scrolling
    commands terminate the search, while the state of the search is
    always automatically saved.  Like in isearch you can also just
    hit RET to exit.

  C-R, M-s e r (el-search-pattern-backward)
    Search backward.

  C-%, M-s e % (el-search-query-replace)
    Do a query-replace.

  M-x el-search-directory
    Prompt for a directory name and start a multi el-search for all
    Emacs-Lisp files in that directory.  With prefix arg,
    recursively search files in subdirectories.

  C-S, M-s e s in Dired (el-search-dired-marked-files)
    Like above but uses the marked files and directories.

  C-S, M-s e s in Ibuffer (el-search-ibuffer-marked-buffers)
    Search marked buffers in *Ibuffer*.

  C-O, M-s e o (el-search-occur)
    Pop up an occur buffer for the current search.

  C-O or M-RET (from a search pattern prompt)
    Execute this search command as occur.

  C-N, M-s e n (el-search-continue-in-next-buffer)
    Skip over current buffer or file.

  C-D, M-s e d (el-search-skip-directory)
    Prompt for a directory name and skip all subsequent files
    located under this directory.

  C-A, M-s e a, M-s e < (el-search-from-beginning)
    Go back to the first match in this buffer or (with positive
    prefix arg) completely restart the current search from the
    first file or buffer.

    With negative prefix arg, or with >, go to the last match in
    the current buffer.

  C-J, M-s e j (el-search-jump-to-search-head)
    Resume the last search from the position of the last visited
    With prefix arg 0, resume from the position of the match
    following point instead.  With prefix arg 1 or -1, jump to the
    first or last match visible in the selected window.  This can
    be useful even when a search is current, e.g. after scrolling
    the searched buffer.
    With a plain C-u prefix arg, prompt for a former search to

  C-S-next, v   when search is active (el-search-scroll-down)
  C-S-prior, V  when search is active (el-search-scroll-up)
    Scrolling by matches: Select the first match after
    `window-end', or select the first match before `window-start',

  C-H, M-s e h (el-search-this-sexp)
    Grab the symbol or sexp under point and initiate an el-search
    for other occurrences.

  M-x el-search-to-register
    Save the current search to an Emacs register.  Use C-x r j
    (`jump-to-register') to make that search current and jump to
    the latest position.

The setup you need for your init file is trivial: you only need to
install the key bindings you want to use.  All important commands
are autoloaded.


The main user entry point `el-search-pattern' (C-S or M-s e s) is
analogue to `isearch-forward'.  You are prompted for a
`pcase'-style search pattern using an `emacs-lisp-mode' minibuffer.
After hitting RET it searches the current buffer from point for
matching expressions.  For any match, point is put at the beginning
of the expression found (unlike isearch which puts point at the end
of matches).  Hit C-S or s again to go to the next match etc.

Syntax and semantics of search patterns are identical to that of
`pcase' patterns, plus additionally defined pattern types
especially useful for matching parts of programs.

It doesn't matter how code is formatted.  Comments are
ignored, and strings are treated as atomic objects (their contents
are not being searched).

Example 1: if you enter


at the prompt, el-search will find any occurrence of the integer 97
in the code, but not 97.0 or 977 or (+ 90 7) or "My string
containing 97" or symbol_97.  OTOH it will find any printed
representation of 97, e.g. #x61 or ?a.

Example 2: If you enter the pattern

  `(defvar ,_)

you search for all `defvar' forms that don't specify an init value.

The following pattern will search for `defvar's with a docstring
whose first line is longer than 70 characters:

  `(defvar ,_ ,_
     ,(and (pred stringp)
           (guard (< 70 (length (car (split-string s "\n")))))))

Put simply, el-search is a tool for matching representations of
symbolic expressions written in a buffer or file.  Most of the
time, but not necessarily, this is Elisp code.  El-search has no
semantic understanding of the meaning of these s-exps as a program
per se.  If you define a macro `my-defvar' that expands to `defvar'
forms, the pattern `(defvar ,_) will not match any equivalent
`my-defvar' form, it just matches any lists of two elements with
the first element being the symbol `defvar'.

You can define your own pattern types with macro
`el-search-defpattern' which is analogue to `defmacro' (and
`pcase-defmacro').  See C-h f `el-search-defined-patterns' for a
list of predefined additional pattern types, and C-h f pcase for
the basic pcase patterns.

Some additional pattern definitions can be found in the file
"el-search-x.el" which is part of this package but not
automatically loaded.

Multi Searching

"el-search" is capable of performing "multi searches" - searches
spanning multiple files or buffers.  When no more matches can be
found in the current file or buffer, the search automatically
switches to the next one.  Examples for search commands that start
a multi search are `el-search-buffers' (search all live elisp mode
buffers), `el-search-directory' (search all elisp files in a
specified directory), `el-search-emacs-elisp-sources' and
`el-search-dired-marked-files'.  Actually, every search is
internally a multi search.

You can pause any search by just doing something different (no
explicit quitting needed); the state of the search is automatically
saved.  You can later continue searching by calling
`el-search-jump-to-search-head' (C-J; M-s e j): this command jumps
to the last match and re-activates the search.

`el-search-continue-in-next-buffer' (C-N; n) skips all remaining
matches in the current buffer and continues searching in the next
buffer.  `el-search-skip-directory' (C-D; d) even skips all
subsequent files under a specified directory.


To get an occur-like overview you can use the usual commands.  You
can either hit C-O or M-RET from the pattern prompt instead of RET
to confirm your input and start the search as noninteractive occur
search in the first place.  Alternatively, you can always call
`el-search-occur' (C-O or o) to start an occur for the latest
started search.

The *El Occur* buffer uses an adjusted emacs-lisp-mode.  RET on a
match gives you a pop-up window displaying the position of the
match in that buffer or file.  With S-tab you can (un)collapse all
file sections like in `org-mode' to see only file names and the
number of matches, or everything.  Tab folds and unfolds
expressions (this uses hideshow) and also sections at the beginning
of headlines.

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' (C-J; M-s e j) 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.  Like any search you can restart the search driving an
`el-search-query-replace' with C-u C-A or C-u M-s e a respectively.


You can replace expressions with command `el-search-query-replace'.
You are queried for a pattern and a replacement expression.  For
each match of the pattern, the replacement expression is evaluated
with the bindings created by pattern matching in 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',
so that e.g.

  (foo 'a (* 2 (+ 3 4)) t)


  (foo (* 2 (+ 3 4)) 'a t).

This will do it:

   C-%  (or M-s e %)
   `(foo ,a ,b . ,rest) RET
   `(foo ,b ,a . ,rest) RET

Type y to replace a match and go to the next one, r to replace
without moving (hitting r again restores that match), n to go to
the next match without replacing and ! to replace all remaining
matches automatically.  q quits.  ? shows a quick help summarizing
all of these keys.

It is possible to replace a match with an arbitrary number of
expressions using "splicing mode".  When it is active, the
replacement expression must evaluate to a list, and is spliced into
the buffer for any match.  Hit s from the prompt to toggle splicing
mode in an `el-search-query-replace' session.

Multi query-replace

To query-replace in multiple files or buffers at once, call
`el-search-query-replace' directly after starting a search whose
search domain is the set of files and buffers you want to treat.
Answer "yes" to the prompt asking whether you want the started
search to drive the query-replace.  The user interface is

It is always possible to resume an aborted query-replace session
even if you did other stuff in the meantime (including other
`el-search-query-replace' invocations).  Since internally every
query-replace is driven by a search, call
`el-search-jump-to-search-head' to make that search current, and
invoke `el-search-query-replace'.  This will continue the
query-replace session from where you left.

Advanced usage: Replacement rules for semi-automatic code rewriting

When you want to rewrite larger code parts programmatically, it can
often be useful to define a dedicated pattern type to perform 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 type using `el-search-defpattern'.  Make this pattern
accept an argument and use it to bind a replacement expression to a
variable you specify.  In query-replace, specify that variable as
replacement expression.

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 helper variables.  The
second, the `let', part, binds the specified variable NEW to the
rewritten expression - in our case, a `dolist' form is constructed
with the remembered code parts filled in.

Now after this preparatory work, for `el-search-query-replace' you
can simply specify (literally!) the following rule:

  (el-search-mapc->dolist repl) -> repl


Thanks to Manuela for our review sessions.
Thanks to Stefan Monnier for corrections and advice.

Known Limitations and Bugs

- Replacing: in some cases the read syntax of forms is changing due
  to reading-printing.  "Some" because we can handle this problem
  in most cases.

- Similar: comments are normally preserved (where it makes sense).
  But when replacing like `(foo ,a ,b) -> `(foo ,b ,a)

  in a content like

      ;; comment

  the comment will be lost.

- Something like (1 #1#) is unmatchable (because it is un`read'able
  without context).

- In el-search-query-replace, replacements are not allowed to
  contain uninterned symbols.

- The `l' pattern type is very slow for very long lists.
  E.g. C-S-e (l "test")

- Emacs bug#30132: 27.0.50; "scan-sexps and ##": Occurrences of the
  syntax "##" (a syntax for an interned symbol whose name is the
  empty string) can lead to errors while searching.


- There should be a way to go back to the starting position, like
  in Isearch, which does this with (push-mark isearch-opoint t) in

- Add a help command that can be called while searching.

- 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 occurrences of a symbol in

- Port this package to non Emacs Lisp modes?  How?  Would it
  already suffice using only syntax tables, sexp scanning and

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

- There could be something much better than pp to format the
  replacement, or pp should be improved.


NEWS are listed in the separate NEWS file.

Old versions

el-search-1.7.9.tar2018-Sep-12 270kB
el-search-1.7.8.tar2018-Sep-09 270kB
el-search-1.7.7.tar2018-Sep-01 270kB
el-search-1.7.6.tar2018-Aug-07 270kB
el-search-1.7.5.tar2018-Aug-01 260kB
el-search-1.7.4.tar2018-Jul-30 260kB
el-search-1.7.3.tar2018-Jul-22 260kB
el-search-1.7.2.tar2018-Jul-14 260kB
el-search-1.7.14.tar2018-Nov-04 280kB
el-search-1.7.11.tar2018-Nov-03 270kB
el-search-1.7.10.tar2018-Nov-01 270kB
el-search-1.7.1.tar2018-Jun-23 260kB
el-search-1.6.tar2018-Feb-24 240kB
el-search-1.6.9.tar2018-May-21 250kB
el-search-1.6.8.tar2018-May-16 240kB
el-search-1.6.7.tar2018-May-15 240kB
el-search-1.6.6.tar2018-May-11 240kB
el-search-1.6.5.tar2018-Apr-07 240kB
el-search-1.6.3.tar2018-Mar-24 240kB
el-search-1.6.2.tar2018-Mar-18 240kB
el-search-1.6.10.tar2018-May-27 250kB
el-search-1.6.1.tar2018-Mar-13 240kB
el-search-1.5.4.tar2018-Feb-05 230kB
el-search-1.5.3.tar2018-Jan-28 230kB
el-search-1.5.1.tar2018-Jan-21 220kB
el-search-1.4.tar2017-Nov-15 200kB
el-search- 210kB
el-search- 210kB
el-search- 200kB
el-search- 200kB
el-search- 200kB
el-search- 200kB
el-search- 210kB
el-search- 210kB
el-search- 210kB
el-search- 210kB
el-search- 200kB
el-search-1.3.tar2017-Mar-09 160kB
el-search-1.3.2.tar2017-Apr-23 160kB
el-search-1.3.1.tar2017-Apr-15 160kB
el-search-1.2.tar2016-Dec-15 130kB
el-search-1.2.3.tar2017-Jan-02 130kB
el-search-1.2.2.tar2017-Jan-01 130kB
el-search-1.2.1.tar2016-Dec-20 130kB
el-search-1.1.tar2016-Nov-07 120kB
el-search-1.1.2.tar2016-Nov-16 120kB
el-search-1.1.1.tar2016-Nov-10 120kB
el-search-1.0.1.tar2016-Oct-17 110kB


Some of the user visible news were:

Version: 1.7.15

  *El Occur* buffers are now initially unfolded.

Version: 1.7.8

  Similar to isearch, el-search now opens invisible text.

Version: 1.7.7

  The new scroll commands `el-search-scroll-down' and
  `el-search-scroll-up', bound to C-S-next and C-S-prior, or v and V
  respectively, perform by-match scrolling: `el-search-scroll-down'
  scrolls the next matches after `window-end' into view, i.e. it
  selects the first match after `window-end'.  Likewise,
  `el-search-scroll-up' selects the last match before `window-start'.

  You can now explicitly terminate (pause) search and query-replace
  sessions by hitting RET.

Version: 1.7.5

  The meaning of the prefix argument of
  `el-search-jump-to-search-head' (C-J or M-s e j with the default
  bindings) has been extended: A numeric prefix N jumps to the Nth
  match after `window-start', while a negative prefix -N jumps to the
  Nth match before `window-end'.  Prefix 0 jumps to the match
  following point, which is also useful to resume the current search
  from any buffer position.  A former search can now be made current
  with a plain C-u prefix arg.

Version: 1.7.3

  Match highlighting faces have been improved to look better on text
  terminals.  Matches in *El Occur* buffers are now highlighted with a
  separate face.

Version: 1.7

  Signature and semantics of non-interactive function
  `el-search-forward' have been further adapted to that of the vanilla
  search function `search-forward'.  The counterpart
  `el-search-backward' has been added.

  The new key bindings < and > let you directly jump to the first and
  to the last match in a buffer.

Version: 1.6.5

  When the new user option `el-search-allow-scroll' is enabled (the
  default), scrolling doesn't deactivate the current el-search.
  Unlike isearch you can scroll the current match offscreen - use
  `el-search-jump-to-search-head' (C-J or M-s e j when using the
  suggested key bindings) to jump back to the current match.

Version: 1.6.1

  New function `el-search-looking-at', the el-search version of

Version: 1.5.2

  The new command `el-search-to-register' allows to save the current
  search (including its state) to a register and later make that
  search current again with `jump-to-register' (C-x r j).

Version: 1.5.1

  The new command `el-search-ibuffer-marked-buffers' el-searches the
  marked buffers in *Ibuffer*.

Version: 1.5

  The new function `el-search-install-bindings-under-prefix' can be
  used to install repeatable versions of the el-search commands under
  a prefix key.


  The new option value 'ask-multi for el-search-auto-save-buffers,
  which is also the new default, makes el-search only prompt for
  whether to save buffers for multi-buffer query-replace sessions.
  For single buffer sessions, no prompt, and you can/should save
  yourself.  I find that behavior slightly more convenient than 'ask
  in most cases.