Code Monkey home page Code Monkey logo

elisp-refs's Introduction

elisp-refs

Coverage Status MELPA

elisp-refs is an intelligent code search for Emacs lisp.

It can find references to functions, macros or variables. Unlike a dumb text search, elisp-refs actually parses the code, so it's never confused by comments or variables with the same name as functions.

screenshot

This is particularly useful for finding all the places a function is used, or finding examples of usage.

Interested readers may enjoy my blog post: Searching A Million Lines Of Lisp.

Installation

Install from MELPA (recommended) or just add elisp-refs to your load-path.

Commands available

  • elisp-refs-function (find function calls)
  • elisp-refs-macro (find macro calls)
  • elisp-refs-variable (find variable references)
  • elisp-refs-special (find special form calls)
  • elisp-refs-symbol (find all references to a symbol)

These command search all the files currently loaded in your Emacs instance.

If called with a prefix, you can limit search results to specific directories. For example:

C-u M-x elisp-refs-macro RET pcase RET ~/.emacs.d/elpa/magit-20160927.510 RET

will search for uses of pcase in magit:

filtering screenshot

Semantic analysis

elisp-refs has street smarts: given (defun foo (bar) (baz)), it understands that bar is a variable and baz is a function.

elisp-refs understands the following forms:

  • defun defsubst defmacro cl-defun
  • lambda
  • let let*
  • funcall apply
  • sharp quoted expressions (e.g. #'some-func)

Limitations

elisp-refs understands elisp special forms, and a few common macros. However, it cannot understand arbitrary macros.

Therefore elisp-refs will assume that (other-macro (foo bar)) is a function call to foo. If this is incorrect, you may wish to use the command elisp-refs-symbol to find all references to the foo symbol.

If other-macro is a common macro, please consider submitting a patch to elisp-refs--function-p to make elisp-refs smarter.

elisp-refs also does not support indirect calls.

;; Since we do a simple syntax tree walk, this isn't treated as a
;; call to foo.
(let ((x (symbol-function 'foo)))
  (funcall x))

;; Similarly, indirect function calls are not treated as
;; function calls.
(defun call-func (x)
  (funcall x))
(call-func 'foo)

;; However, if you use sharp quoting, elisp-refs knows it's a function
reference!
(call-func #'foo)

Running tests

You can run the tests with:

$ cask install
$ cask exec ert-runner

Performance

elisp-refs is CPU-intensive elisp and has been carefully optimised. You can run the benchmark script with:

$ cask install
$ ./bench.sh

New features are carefully measured to ensure performance does not get worse.

See elisp-refs-bench.el for more details.

Alternative Projects

xref-find-references: This command is included in Emacs 25.1, but it's based on a text search. It is confused by comments and strings, and cannot distinguish between functions and variables.

xrefs-find-references is also line oriented, so it does not show the whole sexp that matched your search. Since it requires text files, it doesn't search built-in .el.gz files.

TAGS: It is possible to record function references in TAGS files. Whilst universal-ctags (formerly known as exuberant-ctags) does provide the ability to find references, it is not supported in its lisp parser.

etags, the TAGS implementation shipped with Emacs, cannot find references (to my knowledge).

el-search allows you to search for arbitrary forms in elisp files. It's slower, but a much more general tool. Its design greatly influenced elisp-refs.

elisp-slime-nav finds definitions, not references. It's a great complementary tool.

elisp-refs's People

Contributors

amno1 avatar bestlem avatar jrblevin avatar lebensterben avatar oylenshpeegul avatar pestctrl avatar tarsius avatar tpeacock19 avatar wbolster avatar wilfred avatar wyuenho avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

elisp-refs's Issues

Found 0 references to variable fill-column.

When running (elisp-refs-variable 'fill-column) on the latest Emacs with native compilation, no results are returned.

I can't find any variables or functions which have any references.

Emacs 27.2 works fine.

elisp-refs without dash.el & s.el

Hi Wilfred,

I am not sure if you are interested, but I have removed both dash and s from elisp-refs; can give you a PR if you would like to have it. Can be seen on:

https://github.com/amno1/elisp-refs/tree/remove-dash

Interestingly I didn't think I would see any differences in running time, but I can see some very slight speedup without dash. But really nothing spectacular, ~0.1 to ~0.5 sec, depending on benchmark and run (those benchmarks included with elisp-refs.el), but I do see the difference consistently; I runned quite a few times with each version. For any practical use, that speed difference is too small to matter of course.

By the way, I have removed dash and s, so I don't depend on any extra dependencies. I intend to include elisp-refs.el with my project so the project itself is self-contained.

elisp-refs-function and elisp-refs-macro returns the same matches

For instance,
(elisp-refs-function 'cl-loop)
=>
Found 70 references to function cl-loop in 16 files.

(elisp-refs-macro 'cl-loop)
=>
Found 70 references to macro cl-loop in 16 files.

Do do you need two functions?
Why not to define `elisp-refs-function-or-macro'?

What about to make `elisp-refs-function' just
returns matches for actual functions, i.e., to not return
matches for macro calls?

Wrong type argument: number-or-marker-p, nil

In Erefs mode when I press RET on Line: or on an empty line the following error occurs:

funcall-interactively: Wrong type argument: number-or-marker-p, nil

The reason is that elisp-refs-visit-match assumes that the text has always properties attached.

(unindent (get-text-property (point) 'elisp-refs-unindented))
(target-offset (+ column-offset unindent))

Maybe this will do the trick:

(target-offset (if unindent (+ column-offset unindent)))

declare-function's ARGLIST should not be searched

Found 8 references to function record in 3 files. Searched all 838
files loaded in Emacs.

File: ~/src/emacs-mac/lisp/emacs-lisp/cl-preloaded.el
(record 'cl-slot-descriptor
        name initform type props)))

File: ~/src/emacs-mac/lisp/emacs-lisp/cl-macs.el
(,(or type #'record) ,@make))

File: ~/src/emacs-mac/lisp/org/org-bbdb.el
(declare-function bbdb-record-field "ext:bbdb" (record field))
(declare-function bbdb-record-getprop "ext:bbdb" (record property))
(declare-function bbdb-record-name "ext:bbdb" (record))
(declare-function bbdb-record-get-field "ext:bbdb" (record field))
(declare-function bbdb-record-note "ext:bbdb" (record label))
(declare-function bbdb-record-xfield "ext:bbdb" (record label))

Reducing the files to search using grep

Hi,

Thanks for this great tool.

I was wondering if you have considered speeding it up further by reducing the files that elisp-refs needs to actually search using something grep/zgrep. The idea is use grep to find out files where the symbol requested occurs (in whatever form) and them as a second step apply elisp-refs's semantic search only on those (limited) set of files.

If this seems a good idea to you, I can try implementing it

Thanks

Lazy by default

Just search files in the current directory where the symbol is defined, with the option of searching further.

This would cover most use cases, and be much faster.

elisp-refs-next-match test fails with Emacs 27.1 (release candidate)

Hi,

I'm testing elisp-refs 1.3 with the Emacs 27.1 release candidate. The elisp-refs-next-match test fails with the following log:

[…]
uncompressing subr.el.gz...done
uncompressing backquote.el.gz...
uncompressing backquote.el.gz...done
uncompressing byte-run.el.gz...
uncompressing byte-run.el.gz...done
Searched 0/151 files
Searched 10/151 files
Searched 20/151 files
Searched 30/151 files
Searched 40/151 files
Searched 50/151 files
Searched 60/151 files
Searched 70/151 files
Searched 80/151 files
Searched 90/151 files
Searched 100/151 files
Searched 110/151 files
Searched 120/151 files
Searched 130/151 files
Searched 140/151 files
Searched 150/151 files
Searched 151/151 files
......................................Test elisp-refs-next-match backtrace:


Test elisp-refs-next-match condition:

    (end-of-buffer)

F...

Ran 42 tests in 13.097 seconds
1 unexpected results:
   FAILED  elisp-refs-next-match
command "ert-runner" failed with status 1

Best,
Jack

Accessing the last character in a result line can crash

Traceback:

Debugger entered--Lisp error: (wrong-type-argument number-or-marker-p nil)
  +(33 nil)
  (let* ((path (get-text-property (point) (quote elisp-refs-path))) (pos (get-text-property (point) (quote elisp-refs-start-pos))) (unindent (get-text-property (point) (quote elisp-refs-unindented))) (column-offset (current-column)) (target-offset (+ column-offset unindent)) (line-offset -1)) (if (null path) (progn (user-error "No match here"))) (save-excursion (while (equal pos (get-text-property (point) (quote elisp-refs-start-pos))) (forward-line -1) (setq line-offset (1+ line-offset)))) (find-file path) (goto-char pos) (forward-line line-offset) (beginning-of-line) (let ((i 0)) (while (< i target-offset) (if (looking-at "   ") (setq i (+ i tab-width)) (setq i (1+ i))) (forward-char 1))))
  elisp-refs-visit-match()
  funcall-interactively(elisp-refs-visit-match)
  call-interactively(elisp-refs-visit-match nil nil)
  command-execute(elisp-refs-visit-match)

This seems to only occur when the last character in the line is not part of the highlighted sexp, e.g. (searching-for-this-func 1 2))))

Navigation of references via previous-error and next-error

elisp-refs-symbol and similar should support navigation of references via Emacs standard functions previous-error and next-error similar to what Emacs standard packages xref (providing xref-find-references), grep, locate etc support.

The whole idea is to visit match automatically in a "preview" window when navigating the hits via next-match and previous-match without hiding the *xref* buffer window.

This style of navigation is becoming standard behaviour in large extra packages. For instance, ivy binds such navigation (with preview in other window) to C-M-n for next match and C-M-p for previous match.

Missing results in M-x elisp-refs-macro cl-assert

Currently I get

File: ~/src/emacs/nextstep/Emacs.app/Contents/Resources/lisp/emacs-lisp/eieio-core.el.gz
(cl-assert (eq newc oldc))
(unless (eq d eieio-unbound)
  (eieio--perform-slot-validation-for-default new skipnil)
  (setf (cl--slot-descriptor-initform old) d))

The file does contains two uses of cl-assert. The correct result should be.

File: ~/src/emacs/nextstep/Emacs.app/Contents/Resources/lisp/emacs-lisp/eieio-core.el.gz
(cl-assert (eq newc oldc))
(cl-assert (eq (cl--slot-descriptor-name old) (cl--slot-descriptor-name new)))

Consider indenting the results

When there's a large number of results (e.g. searching for all references to defadvice), it becomes a little difficult to see what's what.

void-variable read-symbol-positions-list error on emacs-29 HEAD

looks like read-with-symbol-position and read-symbol-positions-list were removed in emacs commit emacs-mirror/emacs@dfae76c

if: Unexpected error whilst reading ~/.emacs.d/elpa/company-20220110.2248/company.el position 2497: (void-variable read-symbol-positions-list)

I took a quick look and I'm not sure what the replacement would be for this, but I'd assume there is one (read-positioning-symbols maybe?)

thx for this great package btw !

Please add a prefix to test-helper.el to avoid conflicts with 68 other packages

There exist at least 69 packages that contain a file named test-helper.el that also provides the feature test-helper.

This leads to issues for users who have at least two of these packages installed. It is unlikely that such a user would be able to run the tests of all of those packages. If the primary test file of one of those packages does (require 'test-helper), then it is undefined which of the various test-helper.el files gets loaded. Which it is, depends on the order of the load-path.

To avoid this conflicts, you should rename your test-helper.el to <your-package>-test-helper.el and adjust the feature accordingly.

Also don't forget to update the require form in your primary test file and/or update references to the library/feature elsewhere. Also, if your primary test file is named something like test.el, then please consider renaming that too (same for any other utility elisp files your repositoroy may contain).

Thanks!

PS: This issue is a bit generic because I had to open 69 issues.

Mention by name and link to references, not just containing files

Currently the files that contain references are shown and linkified, and the calling expressions are also shown.

However clicking on the link takes one to the beginning of the file instead of to the first reference (as mentioned in #15), there are no direct links to the other references either, and the names of the functions that contain references are not even shown at all.

Without that I can answer questions like "how am I supposed to use this function?", but in order to answer questions "in what contexts is this function used?". To do the latter I still have to manually jump to each file and then do something like an isearch to get the full context. If I know that a function isn't used by Emacs itself and would have to jump to every reference anyway to get the full context, then doing even a rgrep is faster.

Could you please add those things?

Consider contributing this to Emacs?

Any tools directly related to elisp would be (especially) valuable to have as default functionality in Emacs.

Please do consider contributing this, while you still have only yourself to get permission from?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.