Searching the load-path

Here’s another tip for taming your Emacs config. In particular, how to search the huge body of elisp that is present with most Emacs installs - the load-path. While the load-path is a very important collection of directories, searching it isn’t as easy and accessible as it should be. As usual, Emacs compensates with plugins:

  • elisp-refs - this excellent package is useful for canned searches that are often present in most IDE’s. Quite useful whenever your search fits into one of the use case supported.

  • el-search - this is quite an advanced package that allows you to write queries with a deep understanding of elisp itself. An extremely useful plugin, but overkill when your query is simple.

A problem that is shared by both of the options above is that they’re a bit sluggish. Something that is fast, allows us to use familiar PCRE’s, is a missing piece.

Luckily, such a missing piece is glued together with a bit of elisp and the silver searcher:

(defun region-or-symbol-at-point ()
  (if mark-active
      (buffer-substring-no-properties (region-beginning) (region-end))
    (or (symbol-name (symbol-at-point)) "")))

(defun rg/grep-load-path (query)
  (interactive
   (let ((query (read-string
                 (format "Grep (default %s): " (region-or-symbol-at-point))
                 nil 'git-grep (region-or-symbol-at-point))))
     (list query)))
  (grep-find
   (mapconcat #'shell-quote-argument
                       `("ag" "--search-zip" "--no-heading" "--no-color"
                         ,query ,@(-filter #'f-exists? load-path))
                       " ")))

This gives us an interactive command where we can either the query manually, or automatically insert the active region. Note that We’d like to use ag specifically because of its support for searching gzip files. This is common for elisp code in your load-path.

Folding Ergonomic with Origami

Not bad, but since the load-path can be quite huge, the command inserted in the grep output is a bit of an eye-sore. My favorite folding mode is origami, but surely this can be done with something like hideshow as well. First, let’s define the folding functions:

(defun rg/find-grep-command-bounds ()
  (save-excursion
    (goto-char (point-min))
    (let ((start (re-search-forward "^Grep started at")))
      (progn
        (forward-line 3)
        (list start (- (point) 1))))))

;; make sure you have lexical scoping enabled for this function
(defun rg/origami-grep-command (create-fold)
  (lambda (content)
    (destructuring-bind
     (beg end) (rg/find-grep-command-bounds)
     (list (funcall create-fold beg end 20 nil)))))

(with-eval-after-load 'origami
  (add-to-list 'origami-parser-alist
               '(rg/origami-grep-command-style . rg/origami-grep-command)))

The gist of it being that we have a single fold spanning from our "Grep started at" marker to the next 3 lines.

The only thing that remains is turn on this folding more and close all folds after completing the fold:

(defun rg/grep-load-path (query)
  (interactive
   (let ((query (read-string
                 (format "Grep (default %s): " (region-or-symbol-at-point))
                 nil 'git-grep (region-or-symbol-at-point))))
     (list query)))
  (let ((myb (grep-find
              (mapconcat #'shell-quote-argument
                      `("ag" "--search-zip" "--no-heading" "--no-color"
                        ,query ,@(-filter #'f-exists? load-path))
                      " "))))
    (with-current-buffer myb
      (setq-local origami-fold-style 'rg/origami-grep-command-style)
      (origami-close-all-nodes myb))))

Limitations

One annoying limitation is that the our fold settings aren’t preserved through updating the search results with (recompile). I haven’t ran into this problem enough for this issue to matter yet.

Searching the load-path is nice for gauging all that code is available to Emacs, but what about searching what Emacs has actually loaded? This is where load-history comes in. It should be easy enough to only search these files with a similar command (or a prefix argument).

Comments

comments powered by Disqus