Go to the definition of the symbol at point. Supports global definitions, local definitions, and even macro-heavy code!
elisp-def
statically analyses your code, and falls back to
heuristics where that's not possible. It should work 99% of the time,
so please file bugs if it can't find definitions for your code.
Install from MELPA, then add the following to your Emacs configuration:
(dolist (hook '(emacs-lisp-mode-hook ielm-mode-hook))
(add-hook hook #'elisp-def-mode))
elisp-def
will find the definition of global functions and global
variables at point.
(defun demo/foo ()
1)
(defun demo/bar ()
;; M-x eval-buffer, then elisp-def on this:
(demo/foo))
It will also use edebug information to find function definitions, so it finds definitions more often than xref.
elisp-def
understands the difference between symbols and functions
and jumps to the correct definition.
(require 'cc-mode)
;; `c-version' is both a variable and a function.
(defun demo/foo ()
;; `elisp-def` will find the function here.
(c-version))
(defun demo/foo ()
;; `elisp-def` will find the variable here.
(setq c-version t))
elisp-def
understands macros, so it can accurately detect function
references.
(require 'dash)
(defvar demo/foo nil)
(defun demo/foo (x)
x)
(defun demo/bar ()
(->> 123
;; `elisp-def' knows that this is a function, even though there are
;; no parens.
demo/foo))
It can also understand macros that define functions or variables.
(define-derived-mode demo/foo-mode fundamental-mode "demo")
;; `elisp-def' will expand macros to discover where major mode hooks
;; are defined.
demo/foo-mode-hook
(cl-defstruct demo/point x y)
;; `elisp-def' can find this function even though the defstruct
;; call doesn't contain this symbol name.
(make-demo/point 1 2)
elisp-def
will find libraries, displaying the provide
declarations
if possible.
;; `elisp-def' will open python.el here.
(require 'python)
;; Unlike `xref-find-definition', `elisp-def' will not confuse this
;; library name with the macro named `use-package'.
(require 'use-package)
;; `elisp-def' will even find python.el here, because the macro
;; expands to a call to `require'.
(use-package python
:config
(setq python-indent-guess-indent-offset-verbose nil))
elisp-def
understands local bindings and parameters.
(defun demo/foo (bar)
(let ((foo 1))
;; `elisp-def' on the FOO below will move point to the let
;; binding.
(setq foo 2)
;; `elisp-def' on the BAR below will move point to the function
;; parameters line.
(setq bar 3)))
(defun demo/bar ()
(let* ((foo 1)
(bar 2)
(foo 3)
;; `elisp-def' on the second FOO on the following line will
;; move point to the relevant binding, which is the line
;; immediately above.
(foo (+ foo 1))
(foo 5))
nil))
This even works with macros that introduce bindings.
(require 'dash)
(eval-when-compile
(require 'cl-lib))
(defun demo/foo (items)
(cl-destructuring-bind (first second) items
;; `elisp-def' knowns that FIRST is bound on line above.
(message "first is %s" first))
(-let [(first . rest) items]
;; `elisp-def' knowns that FIRST is bound on line above.
(message "first is %s" first)))
elisp-def
allows you to put point on quoted symbols, docstring
symbols or backquoted symbols.
(defun demo/foo (x)
;; `elisp-def' on X in the docstring will find the parameter.
"Adds one to X and returns it."
(1+ x))
(defun demo/bar ()
;; `elisp-def' can find demo/foo even when point is on the #.
(funcall #'demo/foo 1)
;; `elisp-def' on demo/foo below will find the function.
;;
;; See `demo/foo' for more information.
nil)
(defun demo/baz (foo)
;; `elisp-def' understands that @ is not part of foo here.
`(blah ,@foo))
When it finds the symbol, elisp-def will also temporarily highlight it for visibility.
elisp-def
is limited in its ability to analyse quoted symbols.
;; `elisp-def' is able to find these quoted symbols because they're
;; only globally bound in one namespace.
(mapcar 'symbol-name '(foo bar baz))
(add-to-list 'auto-mode-alist '("\\.java\\'" . java-mode))
(require 'cc-mode)
(defun demo/calls-fn (sym)
(funcall sym))
;; Since `c-version' is both a function and a variable, and we're not
;; using a sharp-quote #'c-version, we have to prompt the user.
(demo/calls-fn 'c-version)
(defun demo/foo (c-version)
;; Here we have no idea whether we're using `c-version' as a
;; function (e.g. funcall), as a variable (e.g. set) or as a
;; parameter (e.g. eval).
(bar 'c-version nil))
elisp-def
cannot find definitions in macros with let*
semantics
and duplicated variables.
(require 'dash)
(defun demo/foo ()
(-let ((x 1)
(x 2))
;; `elisp-def' on X below will move to the first X binding, rather
;; than the second.
x))
elisp-def
also cannot handle macros that rewrite forms such that the
symbol disappears entirely.
(eval-when-compile (require 'cl-lib))
(cl-labels ((foo (x y) (+ x y)))
;; `cl-labels' completely rewrites this body to (--cl-foo-- 1 2), so
;; `elisp-def' can't find the definition of FOO.
(foo 1 2))
elisp-slime-nav-find-elisp-thing-at-point
from elisp-slime-navxref-find-definitions
inemacs-lisp-mode
(part of Emacs core)semantic-ia-fast-jump
fromsemantic/ia.el
(included in Emacs)
The fine folks on #emacs
for answering my questions on elisp
esoterica, particularly Wasamasa.
The compliment library for Clojure completion has a notion of context which is very similar to how elisp-def extracts and analyses forms.
Hacklang has a similar notion of a placeholder for analysing completions at a point in the code.
GPLv3+.
I am providing code in the repository to you under an open source license. Because this is my personal repository, the license you receive to my code is from me and not my employer.