summaryrefslogtreecommitdiff
path: root/.emacs.d/initlibs/elisp-slime-nav.el
blob: bce845d326537a1a0a3ed6e908a51b9fc4d9be47 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
;;; elisp-slime-nav.el --- Make M-. and M-, work in elisp like they do in slime  -*- lexical-binding: t -*-

;; Copyright (C) 2016-2020  Steve Purcell

;; Author: Steve Purcell <steve@sanityinc.com>
;; Keywords: languages navigation slime elisp emacs-lisp
;; URL: https://github.com/purcell/elisp-slime-nav
;; Package-Version: 0
;; Package-Requires: ((emacs "24.1") (cl-lib "0.2"))

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:
;;
;; This package provides Slime's convenient "M-." and "M-," navigation
;; in `emacs-lisp-mode', together with an elisp equivalent of
;; `slime-describe-symbol', bound by default to `C-c C-d d`.
;;
;; When the main functions are given a prefix argument, they will
;; prompt for the symbol upon which to operate.
;;
;; Usage:
;;
;; Enable the package in elisp and ielm modes as follows:
;;
;;   (require 'elisp-slime-nav) ;; optional if installed via package.el
;;   (dolist (hook '(emacs-lisp-mode-hook ielm-mode-hook))
;;     (add-hook hook 'turn-on-elisp-slime-nav-mode))
;;
;; Known issues:
;;
;;   When navigating into Emacs' C source, "M-," will not be bound to
;;   the same command, but "M-*" will typically do the trick.
;;
;;; Code:

(require 'etags)
(require 'help-mode)

(defvar elisp-slime-nav-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "M-.")         'elisp-slime-nav-find-elisp-thing-at-point)
    (define-key map (kbd "M-,")         'pop-tag-mark)
    (define-key map (kbd "C-c C-d d")   'elisp-slime-nav-describe-elisp-thing-at-point)
    (define-key map (kbd "C-c C-d C-d") 'elisp-slime-nav-describe-elisp-thing-at-point)
    map))

;;;###autoload
(define-minor-mode elisp-slime-nav-mode
  "Enable Slime-style navigation of elisp symbols using M-. and M-,"
  nil " SliNav" elisp-slime-nav-mode-map)

;;;###autoload
(define-obsolete-function-alias 'turn-on-elisp-slime-nav-mode 'elisp-slime-nav-mode
  "2020-01-30")

(defun elisp-slime-nav--all-navigable-symbol-names ()
  "Return a list of strings for the symbols to which navigation is possible."
  (let ((result '()))
    (mapatoms
     (lambda (x)
       (when (or (fboundp x) (boundp x) (symbol-plist x) (facep x))
         (push (symbol-name x) result))))
    result))

(defun elisp-slime-nav--read-symbol-at-point ()
  "Return the symbol at point as a string.
If `current-prefix-arg' is not nil, the user is prompted for the symbol."
  (let* ((sym-at-point (symbol-at-point))
         (at-point (and sym-at-point (symbol-name sym-at-point))))
    (if (or current-prefix-arg (null at-point))
        (completing-read "Symbol: "
                         (elisp-slime-nav--all-navigable-symbol-names)
                         nil t nil nil at-point)
      at-point)))

;;;###autoload
(defun elisp-slime-nav-find-elisp-thing-at-point (sym-name)
  "Find the elisp thing at point, be it a function, variable, library or face.

With a prefix arg, or if there is no thing at point, prompt for
the symbol to jump to.

Argument SYM-NAME is the thing to find."
  (interactive (list (elisp-slime-nav--read-symbol-at-point)))
  (when sym-name
    (let ((sym (intern sym-name)))
      (message "Searching for %s..." sym-name)
      (if (fboundp 'xref-push-marker-stack)
          (xref-push-marker-stack)
        (with-no-warnings
          (ring-insert find-tag-marker-ring (point-marker))))
      (cond
       ((fboundp sym)
        (find-function sym))
       ((boundp sym)
        (find-variable sym))
       ((or (featurep sym) (locate-library sym-name))
        (find-library sym-name))
       ((facep sym)
        (find-face-definition sym))
       (t
        (pop-tag-mark)
        (error "Don't know how to find '%s'" sym))))))

;;;###autoload
(defun elisp-slime-nav-describe-elisp-thing-at-point (sym-name)
  "Display the full documentation of the elisp thing at point.

The named subject may be a function, variable, library or face.

With a prefix arg, or if there is not \"thing\" at point, prompt
for the symbol to jump to.

Argument SYM-NAME is the thing to find."
  (interactive (list (elisp-slime-nav--read-symbol-at-point)))
  (if (fboundp 'describe-symbol)
      (describe-symbol (intern sym-name))
    (with-no-warnings
      (help-xref-interned (intern sym-name)))))


(provide 'elisp-slime-nav)
;; Local Variables:
;; indent-tabs-mode: nil
;; End:
;;; elisp-slime-nav.el ends here