2

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.

Ashok Khanna
  • 325
  • 2
  • 9

2 Answers2

4

PJB on #CommonLisp (IRC.LIBERA.CHAT) greatly assisted and provided the below answer (which I have paraphrased so any mistakes are on my end):

Firstly, CL is a lisp-2 (actually, lisp-∞) and thus there may not be a single definition attached to a symbol. For example, which foo is being referred to in the below?

(deftype foo () (member foo))

(defvar foo 'foo)

(defun foo () foo)

In addition, as an example, symbols may be defined in the REPL (no associated filename), so this is not a straightforward question that can be answered without making assumptions on the intent.

Now, when the behaviour of M-. is satisfactory for your requirements, you could look in slime/swank what the implementation specific API is to do it.

This should point you to swank:find-definition-for-emacs, which may be what you are after:

(swank:find-definitions-for-emacs "foo") #| -->

(("#'foo"
 (:location (:file "/private/tmp/foo.lisp")
 (:position 50)
 (:snippet "(defun foo () foo)")))
 
 ("(type foo)"
 (:location (:file "/private/tmp/foo.lisp")
 (:position 1)
 (:snippet "(deftype foo () '(member foo))")))
 
 ("(variable foo)"
 (:location (:file "/private/tmp/foo.lisp")
 (:position 32)
 (:snippet "(defvar foo 'foo)")))) 

Make sure to load swank as a dependency in your .asd files if you want to rely on the above.

EDIT: I also found the below very useful (and swank has a similar file for most implementations, so just go through each to see their equivalents):

https://github.com/slime/slime/blob/68c58c0194ff03cd147fcec99f0ee90ba9178875/swank/sbcl.lisp#L811

The function call (sb-introspect:find-definition-sources-by-name name type) (name is a symbol, type is a keyword, e.g. :function - refer above link) returns the file in which a definition is stored, assuming you are using SBCL. More (SBCL) details also in:

https://github.com/sbcl/sbcl/blob/master/contrib/sb-introspect/introspect.lisp

Ashok Khanna
  • 325
  • 2
  • 9
4

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))
ignis volens
  • 7,040
  • 2
  • 12