Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Make M-. be able to jump right into the C source for ECL.

	Because it's based on TAGS files, M-. and M-* will DTRT once in a
	.c file.

	* swank-ecl.lisp (assert-TAGS-file): New helper.
	(classify-definition-name): Ditto.
	(find-definitions-for-type): Ditto. Understands Lisp and C
	functions.
	(find-definitions): Use them.
	(source-location): New helper. Extracted from FIND-SOURCE-LOCATION.
	(find-source-location): Use it.
	(swank-compile-string): Only try to delete temporary files if they
	exist.
  • Loading branch information...
commit 3d76a5c095e65ab451af1a52773c99bf9cb1ade4 1 parent 7cabbfd
@trittweiler trittweiler authored
Showing with 138 additions and 26 deletions.
  1. +35 −0 ChangeLog
  2. +103 −26 swank-ecl.lisp
View
35 ChangeLog
@@ -1,3 +1,38 @@
+2010-02-22 Tobias C. Rittweiler <tcr@freebits.de>
+
+ Make M-. be able to jump right into the C source for ECL.
+
+ Because it's based on TAGS files, M-. and M-* will DTRT once in a
+ .c file.
+
+ * swank-ecl.lisp (assert-TAGS-file): New helper.
+ (classify-definition-name): Ditto.
+ (find-definitions-for-type): Ditto. Understands Lisp and C
+ functions.
+ (find-definitions): Use them.
+ (source-location): New helper. Extracted from FIND-SOURCE-LOCATION.
+ (find-source-location): Use it.
+ (swank-compile-string): Only try to delete temporary files if they
+ exist.
+
+2010-02-22 Tobias C. Rittweiler <tcr@freebits.de>
+
+ Make it possible for SWANK backends to specify locations based on
+ a TAGS file.
+
+ * slime.el (slime-postprocess-xref, slime-postprocess-xrefs): New
+ functions. They convert TAGS based locations from SWANK into
+ file+position based locations because the rest of Slime expects
+ and works with those.
+ (slime-find-definitions): Call slime-postprocess-xrefs.
+ (slime-xref): Ditto.
+ (slime-etags-to-locations): The function which does the actual
+ conversion. Extracted from `slime-etags-definitions'.
+ (slime-etags-definitions): Use it.
+
+ * swank-backend (defimplementation): Add implicit BLOCK.
+ (:etags-file, :tag): Mentioned for possible values in :LOCATION.
+
2010-02-20 Tobias C. Rittweiler <tcr@freebits.de>
More work on ECL's swank-backend.
View
129 swank-ecl.lisp
@@ -213,7 +213,9 @@
(defun make-file-location (file file-position)
;; File positions in CL start at 0, but Emacs' buffer positions
- ;; start at 1.
+ ;; start at 1. We specify (:ALIGN T) because the positions comming
+ ;; from ECL point at right after the toplevel form appearing before
+ ;; the actual target toplevel form; (:ALIGN T) will DTRT in that case.
(make-location `(:file ,(namestring file))
`(:position ,(1+ file-position))
`(:align t)))
@@ -249,15 +251,22 @@
(with-compilation-hooks ()
(let ((*buffer-name* buffer) ; for compilation hooks
(*buffer-start-position* position))
- (let ((file (si:mkstemp "TMP:ECL-SWANK-")))
+ (let ((file (si:mkstemp "TMP:ECL-SWANK-"))
+ (fasl-file)
+ (warnings-p)
+ (failure-p))
(unwind-protect
(with-open-file (file-stream file :direction :output
:if-exists :supersede)
(write-string string file-stream)
(finish-output file-stream)
- (not (nth-value 2 (compile-file file :load t))))
- (delete-file file)
- (delete-file (compile-file-pathname file)))))))
+ (multiple-value-setq (fasl-file warnings-p failure-p)
+ (compile-file file :load t)))
+ (when (probe-file file)
+ (delete-file file))
+ (when fasl-file
+ (delete-file fasl-file)))
+ (not failure-p)))))
;;;; Documentation
@@ -548,28 +557,96 @@
;;;; Definitions
+(defconstant +TAGS+ #P"SYS:TAGS")
+
+;;; FIXME: this depends on a patch not yet merged into ECL upstream.
+;;; When it's in, remove this.
+
+(defun get-source-pathname ()
+ #+#. (swank-backend::with-symbol 'get-source-pathname 'si)
+ (si:get-source-pathname))
+
+(defun assert-TAGS-file (fail)
+ (flet ((fail (x)
+ (funcall fail x)))
+ (let ((ecl-src-dir (get-source-pathname)))
+ (unless ecl-src-dir
+ (fail (make-error-location "Do not know where ECL's source directory ~
+ is. You can set the environment variable ~
+ `ECLSRCDIR' for that purpose.")))
+ (unless (probe-file ecl-src-dir)
+ (fail (make-error-location "ECL's source directory ~S does not ~
+ seem to exist." ecl-src-dir)))
+ (unless (probe-file +TAGS+)
+ (fail (make-error-location "No TAGS file ~A. You can create it by ~
+ the command `make TAGS'"
+ (truename +TAGS+)))))))
+
+(defun classify-definition-name (name)
+ (let ((types '()))
+ (when (fboundp name)
+ (cond ((special-operator-p name)
+ (push :special-operator types))
+ ((macro-function name)
+ (push :macro types))
+ ((typep (fdefinition name) 'generic-function)
+ (push :generic-function types))
+ ((si:mangle-name name t)
+ (push :c-function types))
+ (t
+ (push :lisp-function types))))
+ types))
+
+(defun find-definitions-for-type (name type)
+ (ecase type
+ (:lisp-function
+ (list `((defun ,name) ,(source-location (symbol-function name)))))
+ (:c-function
+ (assert-TAGS-file #'(lambda (x) (return-from find-definitions-for-type x)))
+ (multiple-value-bind (flag c-name) (si:mangle-name name t)
+ (assert flag)
+ ;; In ECL's code base sometimes the mangled name is used
+ ;; directly, sometimes ECL's DPP magic of @LISP:SYMBOL is used.
+ ;; We cannot predict here, so we just provide two candidates.
+ (let* ((candidate1 c-name)
+ (candidate2 (format nil "~A::~A"
+ (package-name (symbol-package name))
+ (symbol-name name)))
+ (loc (make-location `(:etags-file ,(namestring (truename +TAGS+)))
+ `(:tag ,candidate1 ,candidate2))))
+ (list `((c-function ,name) ,loc)))))
+ (:generic-function
+ (loop for method in (clos:generic-function-methods (fdefinition name))
+ for specs = (clos:method-specializers method)
+ for loc = (source-location method)
+ when loc
+ collect `((defmethod ,name ,specs) ,loc)))
+ (:macro
+ (values 'defmacro (source-location (macro-function name))))
+ (:special-operator)))
+
(defimplementation find-definitions (name)
- (if (fboundp name)
- (let ((tmp (find-source-location (symbol-function name))))
- `(((defun ,name) ,tmp)))))
-
-;;; FIXME: BC-FILE may return "/tmp/ECLXXXXXXKMOXtm" which are the
-;;; temporary files comming from C-c C-c.
-(defimplementation find-source-location (obj)
- (or
- (typecase obj
- (function
- (multiple-value-bind (file pos) (ignore-errors (si::bc-file obj))
- (if (and file pos)
- (make-location
- `(:file ,(namestring file))
- `(:position ,pos)
- `(:snippet
- ,(with-open-file (s file)
- (file-position s pos)
- (skip-comments-and-whitespace s)
- (read-snippet s))))))))
- `(:error ,(format nil "Source definition of ~S not found" obj))))
+ (mapcan #'(lambda (type) (find-definitions-for-type name type))
+ (classify-definition-name name)))
+
+(defun source-location (object)
+ (typecase object
+ (function
+ ;; FIXME: EXT:C-F-FILE may return "/tmp/ECL_SWANK_KMOXtm" which
+ ;; are the temporary files stemming from C-c C-c.
+ (multiple-value-bind (file pos) (ext:compiled-function-file object)
+ (when file
+ (assert (probe-file file))
+ (assert (not (minusp pos)))
+ (make-file-location file pos))))
+ (method
+ ;; FIXME: This will always return NIL at the moment; ECL does not
+ ;; store debug information for methods yet.
+ (source-location (clos:method-function object)))))
+
+(defimplementation find-source-location (object)
+ (or (source-location object)
+ (make-error-location "Source definition of ~S not found" object)))
;;;; Profiling
Please sign in to comment.
Something went wrong with that request. Please try again.