lispcommon-lispslimeswank

How to get the filename where a function is defined in Common Lisp?


I would like to get the name of the file where a particular function or macro definition was last written, for various documentation & testing purposes?

I have posted an answer that works well from me, after the help of PJB on #CommonLisp (IRC.LIBERA.CHAT), but will accept any answers which provide a portable solution that does not depend on SWANK or provides additional detail on how to achieve the same in other IDEs like LispWorks, ACL, etc.


Solution

  • If what you seek is a portable solution – one that is written in portable CL – then the answer to that is to define wrappers for defining forms and then use the wrappers.

    (defvar *flocs* (make-hash-table :test #'equal))
    
    (defgeneric function-location (f/name)
      (:method ((name t))
       (values (gethash name *flocs* nil) t))
      (:method ((f function))
       (multiple-value-bind (le cp nm) (function-lambda-expression f)
         (declare (ignore le cp))
         (if nm
             (function-location nm)
           (values nil nil)))))
    
    (defmacro define-function (f args &body doc/decls/forms)
      (when (or *load-pathname* *compile-file-pathname*)
        ;; Prefer *load-pathname*
        (setf (gethash f *flocs*) (or *load-pathname* *compile-file-pathname*)))
      `(defun ,f ,args ,@doc/decls/forms))
    

    In real life you'd call define-function defun of course, and similarly with define-variable etc, and then construct a conduit package for CL which exported all the CL symbols while replacing the defining forms with these ones.

    If what you seek is a portable solution in the sense that it exports some standard interface but has varying implementation-dependent backends, then probably looking at what SLY or SWANK do is a good start. In the case of LW you would want the backend to use DSPECs which are how it deals with location information:

    > (dspec:dspec-definition-locations '(defun foo))
    (((defun foo) :listener))
    
    > (dspec:dspec-definition-locations '(defun needs))
    (((defmacro needs)
      #P"..."))
    
    > (defclass foo () ())
    #<standard-class foo 402000B763>
    
    > (dspec:name-definition-locations dspec:*dspec-classes* 'foo)
    (((defclass foo) :listener) ((defun foo) :listener))