Helpful is a fantastic Emacs package that drastically improves the builtin help-mode.One of the particularly nice features is finding references to a particular symbol.Unfortunately it can be painfully slow in practice due to actually parsing every loaded Elisp file: Emacs Lisp (require 'benchmark)(benchmark-elapse (elisp-refs-function #'car))24.001221Instead of walking the ASTs of every file, why not do a regex search?It sacrifices correctness (and completely ignores the type of the symbol), but it turns out to be orders of magnitude faster in practice.Bonus points for using a fast tool like ripgrep and extra bonus points for completing the work asynchronously so as not to block Emacs’s main thread.I don’t mean to denigrate elisp-refs; the author clearly has put a lot of thought into performance and it’s only natural that using an approach that heavily cuts corners together with a tool implemented in optimized machine code instead of Elisp (which is especially hampered by GC performance) will lead to faster results.I’m a ripgrep junkie and I prefer it for grokking most codebases, but the speed comes at the cost of having to sift through many false positives.I use the wonderful deadgrep package to do just that: Emacs Lisp (when (locate-library "deadgrep") (require 'deadgrep) (fset 'deadgrep--arguments-orig (symbol-function #'deadgrep--arguments)) (define-advice helpful--all-references (:override (button) fysh/use-deadgrep) (cl-letf* (((symbol-function 'deadgrep--arguments) (lambda (&rest args) `("--follow" "--type=elisp" "--type=gzip" "--search-zip" ,@(butlast (apply #'deadgrep--arguments-orig args)) ,lisp-directory ,(-first (lambda (p) (string-suffix-p "/share/emacs/site-lisp" p)) load-path))))) (deadgrep (symbol-name (button-get button 'symbol)) default-directory))))The code is quite hacky because deadgrep is not designed to allow passing multiple directories in a single search, but this gets the job done. Emacs Lisp (with-temp-buffer (let ((button (make-button (point-min) (point-max))) (time-to-draw) (time-to-completion)) (button-put button 'symbol #'car) (setq time-to-completion (benchmark-elapse (setq time-to-draw (benchmark-elapse (helpful--all-references button))) (while deadgrep--running (sit-for 0.1 'nodisp)))) (format "Time to first draw: %s\nTime to completion: %s" time-to-draw time-to-completion)))Time to first draw: 0.084542Time to completion: 38.184606In this pathological case the total time is slower than using elisp-refs-function because there are almost four times as many matches because of comments/docstrings and partial matches like mapcar or car-safe (including symbols that aren’t functions like byte-car).The major difference is that while the performance of elisp-refs-* functions1 are roughly constant regardless of the total number of references to a symbol, using ripgrep is significantly faster for terms with fewer than 10k matches (not to mention that you can browse the results immediately).If you want to remove the partial matches, you could use the following advice instead: Emacs Lisp (define-advice helpful--all-references (:override (button) fysh/use-deadgrep) (cl-letf* (((symbol-function 'deadgrep--arguments) (lambda (&rest args) `("--follow" "--type=elisp" "--type=gzip" "--search-zip" ,@(butlast (apply #'deadgrep--arguments-orig args) 3) "--no-fixed-strings" "--" ,(car args) ,lisp-directory ,(-first (lambda (p) (string-suffix-p "/share/emacs/site-lisp" p)) load-path))))) (deadgrep (format "(\\(|#?'| )(%s) " (symbol-name (button-get button 'symbol))) default-directory)))This unfortunately will highlight the entire match instead of just the capturing group, so I prefer not to use it (althgough the speed is mostly the same, if not a bit faster).elisp-refs-symbol is faster than its counterparts due to reduced implementation complexity ↩︎