13.4.1 Finding Definitions

Tools such as Emacs’s built-in help command C-h f (see Help in The GNU Emacs Manual) can find the definition site of functions and other Lisp objects in the source code. To do this, they use the variable find-function-regexp-alist.

The alist find-function-regexp-alist associates object types with a regexp or a function that finds the definition of that object in its source file. Each element’s car is a symbol that describes the type of object, or nil, which means a function defined with defun. Each element’s cdr can be one of the following:

A regexp string is actually a format string, and %s will be substituted with the name of the symbol we are looking for.

A function will be called with one argument, the (symbol for) the object we are searching for.

The form-matcher function in a cons cell value is called with one argument (the symbol being sought) and should return a function; that function should take a form and return non-nil if the form defines the sought symbol. This is useful for finding definitions that are created by expansion of macros.

If the function to be found is defined by a macro, it may be hard for Emacs to find the definition site in the source code. A macro call may have an unexpected look to it, and find-function-regexp-alist will fail to identify the definition.

For example, a define-derived-mode (see Defining Derived Modes) might define a mode-specific function or a variable implicitly; or your Lisp program might generate a run-time call to defun to define a function. In these and similar cases, the definition-name property of the symbol should be another symbol whose definition can be found by textual search and whose code defines the original symbol. In the example with define-derived-mode, the value of this property of the functions and variables it defines should be the mode symbol.

In some cases, the definition cannot be found by looking for the definition of another symbol. For example, a test file might use a macro to generate calls to ert-deftest (see How to Write Tests in ERT: Emacs Lisp Regression Testing) where the code is boiler plate and only varying data need to be passed in. In such cases, the find-function-type-alist property of the symbol can be an alist that augments find-function-regexp-alist telling how to find the definition of symbols of this type.

The find-function-regexp-alist property is most easily maintained with the convenience function find-function-update-type-alist.

Function: find-function-update-type-alist symbol type variable

Update property find-function-type-alist of symbol with a new element containing key type and value variable.

In the example of a macro defining calls to ert-deftest, the macro could put the property find-function-type-alist on each test defined, associating ert--test (the internal type of ERT tests) with the name of a regexp or function that can find the correct macro call. The file defining the macro would also have to provide that definition-finding function or regexp. Here is an example using a function to find the definition:

(defmacro define-foo-test (data)
  "Define a test of the foo system using DATA."
  (declare (debug (&rest sexp)))
  (let ((test-name (intern (concat ...))))
    `(progn
       (find-function-update-type-alist
        ',test-name 'ert--test 'foo-find-test-def-function)
       (ert-deftest ,test-name ()
         ,(concat "Test foo with " ...)
         ...))))

(defun foo-find-test-def-function (test-name)
  "Search for the `define-foo-test' call defining TEST-NAME.
Return non-nil if the definition is found."
  (let ((regexp ...))
    (re-search-forward regexp nil t)))