(require 'slime) (require 'advice) (require 'slime-compiler-notes-tree) ; FIXME: actually only uses the tree bits, so that should be a library. (define-slime-contrib slime-references "Clickable references to documentation (SBCL only)." (:authors "Christophe Rhodes " "Luke Gorrie " "Tobias C. Rittweiler ") (:license "GPL") (:on-load (ad-enable-advice 'slime-note.message 'after 'slime-note.message+references) (ad-activate 'slime-note.message) (setq slime-tree-printer 'slime-tree-print-with-references) (add-hook 'sldb-extras-hooks 'sldb-maybe-insert-references)) (:on-unload (ad-disable-advice 'slime-note.message 'after 'slime-note.message+references) (ad-deactivate 'slime-note.message) (setq slime-tree-printer 'slime-tree-default-printer) (remove-hook 'sldb-extras-hooks 'sldb-maybe-insert-references))) (defcustom slime-sbcl-manual-root "http://www.sbcl.org/manual/" "*The base URL of the SBCL manual, for documentation lookup." :type '(choice (string :tag "HTML Documentation") (const :tag "Info Documentation" :info)) :group 'slime-mode) (defface sldb-reference-face (list (list t '(:underline t))) "Face for references." :group 'slime-debugger) ;;;;; SBCL-style references (defvar slime-references-local-keymap (let ((map (make-sparse-keymap "local keymap for slime references"))) (define-key map [mouse-2] 'slime-lookup-reference-at-mouse) (define-key map [return] 'slime-lookup-reference-at-point) map)) (defun slime-reference-properties (reference) "Return the properties for a reference. Only add clickability to properties we actually know how to lookup." (cl-destructuring-bind (where type what) reference (if (or (and (eq where :sbcl) (eq type :node)) (and (eq where :ansi-cl) (memq type '(:function :special-operator :macro :type :system-class :section :glossary :issue)))) `(slime-reference ,reference font-lock-face sldb-reference-face follow-link t mouse-face highlight help-echo "mouse-2: visit documentation." keymap ,slime-references-local-keymap)))) (defun slime-insert-reference (reference) "Insert documentation reference from a condition. See SWANK-BACKEND:CONDITION-REFERENCES for the datatype." (cl-destructuring-bind (where type what) reference (insert "\n" (slime-format-reference-source where) ", ") (slime-insert-propertized (slime-reference-properties reference) (slime-format-reference-node what)) (insert (format " [%s]" type)))) (defun slime-insert-references (references) (when references (insert "\nSee also:") (slime-with-rigid-indentation 2 (mapc #'slime-insert-reference references)))) (defun slime-format-reference-source (where) (cl-case where (:amop "The Art of the Metaobject Protocol") (:ansi-cl "Common Lisp Hyperspec") (:sbcl "SBCL Manual") (t (format "%S" where)))) (defun slime-format-reference-node (what) (if (listp what) (mapconcat #'prin1-to-string what ".") what)) (defun slime-lookup-reference-at-point () "Browse the documentation reference at point." (interactive) (let ((refs (get-text-property (point) 'slime-reference))) (if (null refs) (error "No references at point") (cl-destructuring-bind (where type what) refs (cl-case where (:ansi-cl (cl-case type (:section (browse-url (funcall common-lisp-hyperspec-section-fun what))) (:glossary (browse-url (funcall common-lisp-hyperspec-glossary-function what))) (:issue (browse-url (common-lisp-issuex what))) (:special-operator (browse-url (common-lisp-special-operator (downcase name)))) (t (hyperspec-lookup what)))) (t (case slime-sbcl-manual-root (:info (info (format "(sbcl)%s" what))) (t (browse-url (format "%s#%s" slime-sbcl-manual-root (subst-char-in-string ?\ ?\- what))))))))))) (defun slime-lookup-reference-at-mouse (event) "Invoke the action pointed at by the mouse." (interactive "e") (cl-destructuring-bind (mouse-1 (w pos . _) . _) event (save-excursion (goto-char pos) (slime-lookup-reference-at-point)))) ;;;;; Hook into *SLIME COMPILATION* (defun slime-note.references (note) (plist-get note :references)) ;;; FIXME: `compilation-mode' will swallow the `mouse-face' ;;; etc. properties. (defadvice slime-note.message (after slime-note.message+references) (setq ad-return-value (concat ad-return-value (with-temp-buffer (slime-insert-references (slime-note.references (ad-get-arg 0))) (buffer-string))))) ;;;;; Hook into slime-compiler-notes-tree (defun slime-tree-print-with-references (tree) ;; for SBCL-style references (slime-tree-default-printer tree) (let ((note (plist-get (slime-tree.plist tree) 'note))) (when note (let ((references (slime-note.references note))) (when references (terpri (current-buffer)) (slime-insert-references references)))))) ;;;;; Hook into SLDB (defun sldb-maybe-insert-references (extra) (slime-dcase extra ((:references references) (slime-insert-references references) t) (t nil))) (provide 'slime-references)