-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathquicklisp-apropos.el
235 lines (199 loc) · 10 KB
/
quicklisp-apropos.el
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
;;; quicklisp-apropos.el --- Commands for quicklisp-apropos -*- lexical-binding: t -*-
;; Copyright (C) 2023 Mariano Montone
;; 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:
;; Install:
;; Emacs side: just put this file in your load-path and load it on init.
;; Lisp side: (load "quicklisp-apropos.lisp") in your init file (i.e. .sbclrc).
;; Use:
;; M-x quicklisp-apropos
;; Customize max results with: M-x customize-variable RET quicklisp-apropos-max-results RET
;;; Code:
(require 'slime)
(defgroup quicklisp-apropos nil
"Quicklisp-apropos settings."
:group 'slime)
(defcustom quicklisp-apropos-max-results 50
"Maximum number of results to be returned by quicklisp-apropos."
:type 'integer
:group 'quicklisp-apropos)
(defcustom quicklisp-apropos-query-results-function
'quicklisp-apropos--query-results
"Internal function to use for fetching and showing quicklisp-apropos results."
:type 'symbol
:group 'quicklisp-apropos)
(defun quicklisp-apropos-update-index ()
"Download and update quicklisp-apropos index."
(interactive)
(message "Downloding quicklisp-apropos index ...")
(slime-eval '(quicklisp-apropos:download-index))
(message "quicklisp-apropos index updated."))
;; Taken from elisp-mode, after elisp-mode--docstring-first-line.
;; Note that any leading `*' in the docstring (which indicates the variable
;; is a user option) is removed.
(defun quicklisp-apropos--docstring-first-line (doc)
"Return first line of DOC."
(and (stringp doc)
(substitute-command-keys
(save-match-data
;; Don't use "^" in the regexp below since it may match
;; anywhere in the doc-string.
(let ((start (if (string-match "\\`\\*" doc) (match-end 0) 0)))
(cond ((string-match "\n" doc)
(substring doc start (match-beginning 0)))
((zerop start) doc)
(t (substring doc start))))))))
(defun quicklisp-apropos--open-buffer-with-results (buffer-name results)
"Open a buffer named with BUFFER-NAME and show the list of apropos RESULTS."
(let ((buffer (get-buffer-create buffer-name)))
(with-current-buffer buffer
(dolist (result results)
(let ((name (cdr (assoc-string "name" result)))
(type (cdr (assoc-string "type" result)))
(doc (cdr (assoc-string "doc" result)))
(system (cdr (assoc-string "system" result))))
(if (string= type "system")
(insert-button (upcase name)
'follow-link t
'help-echo "Load system."
'face 'slime-apropos-symbol
'action (lambda (_)
(when (yes-or-no-p (format "Load %s system?" name))
(slime-eval `(ql:quickload ,name)))))
;; else
(insert-button name
'follow-link t
'help-echo "Load system and edit definition."
'face 'slime-apropos-symbol
'action (lambda (_)
(when (yes-or-no-p (format "Load %s system?" system))
(slime-eval `(ql:quickload ,system))
(slime-edit-definition name)))))
(when system
(insert " in system ")
(insert-button system
'follow-link t
'help-echo "Load system"
'action (lambda (_)
(when (yes-or-no-p (format "Load %s system?" system))
(slime-eval `(ql:quickload ,system))))))
(newline)
(insert " " (propertize (capitalize type) 'face 'underline) ": ")
(if doc
(insert (quicklisp-apropos--docstring-first-line doc))
(insert "Not documented"))
(newline)))
(local-set-key "q" 'kill-buffer)
(setq buffer-read-only t)
(buffer-disable-undo)
(goto-char 0)
(pop-to-buffer buffer))))
(defun quicklisp-apropos--open-buffer-with-printed-results (buffer-name results)
"Open a buffer named with BUFFER-NAME and show the printed apropos RESULTS."
(let ((buffer (get-buffer-create buffer-name)))
(with-current-buffer buffer
(insert results)
(local-set-key "q" 'kill-buffer)
(setq buffer-read-only t)
(buffer-disable-undo)
(goto-char 0)
(pop-to-buffer buffer))))
(defun quicklisp-apropos--query-printed-results (apropos-function query)
"Call APROPOS-FUNCTION with QUERY.
The printed results are show in an Emacs buffer."
(let* ((results
(slime-eval `(cl:with-output-to-string
(cl:*standard-output*)
(,apropos-function ,query :count ,quicklisp-apropos-max-results))))
(buffer-name (format "*quicklisp-apropos: %s*" query)))
(quicklisp-apropos--open-buffer-with-printed-results buffer-name results)))
(defun quicklisp-apropos--query-results (apropos-function query)
"Call APROPOS-FUNCTION with QUERY. Show result in an Emacs buffer."
(let* ((results
(slime-eval `(,apropos-function ,query :count ,quicklisp-apropos-max-results :print-results nil)))
(buffer-name (format "*quicklisp-apropos: %s*" query)))
(quicklisp-apropos--open-buffer-with-results buffer-name
(mapcar #'car results))))
(defun quicklisp-apropos (query)
"Apropos quicklisp using a generic QUERY.
If QUERY contains a ?: color character, then interpret the query
as a Montezuma query string.
Otherwise, build a proper Montezuma query with the term,
one that looks into 'name' and 'doc' fields."
(interactive "sQuicklisp apropos: ")
(funcall quicklisp-apropos-query-results-function
'quicklisp-apropos:apropos query))
(defun quicklisp-apropos-system (query)
"Search across ASDF systems in Quicklisp libraries that match the QUERY."
(interactive "sQuicklisp apropos system: ")
(funcall quicklisp-apropos-query-results-function
'quicklisp-apropos:apropos-system query))
(defun quicklisp-apropos-package (query)
"Search across Lisp packages in Quicklisp libraries that match the QUERY."
(interactive "sQuicklisp apropos package: ")
(funcall quicklisp-apropos-query-results-function
'quicklisp-apropos:apropos-package query))
(defun quicklisp-apropos-variable (query)
"Search across Lisp variables exported in Quicklisp libraries that match the QUERY."
(interactive "sQuicklisp apropos variable: ")
(funcall quicklisp-apropos-query-results-function
'quicklisp-apropos:apropos-variable query))
(defun quicklisp-apropos-class (query)
"Search across CLOS classes exported in Quicklisp libraries that match the QUERY."
(interactive "sQuicklisp apropos class: ")
(funcall quicklisp-apropos-query-results-function
'quicklisp-apropos:apropos-class query))
(defun quicklisp-apropos-function (query)
"Search across Lisp functions exported in Quicklisp libraries that match the QUERY."
(interactive "sQuicklisp apropos function: ")
(funcall quicklisp-apropos-query-results-function
'quicklisp-apropos:apropos-function query))
(defun quicklisp-apropos-macro (query)
"Search across Lisp macros exported in Quicklisp libraries that match the QUERY."
(interactive "sQuicklisp apropos macro: ")
(funcall quicklisp-apropos-query-results-function
'quicklisp-apropos:apropos-macro query))
(defun quicklisp-apropos-generic-function (query)
"Search across CLOS generic functions exported in Quicklisp libraries that match the QUERY."
(interactive "sQuicklisp apropos generic function: ")
(funcall quicklisp-apropos-query-results-function
'quicklisp-apropos:apropos-generic-function query))
;;---- SLIME integration ------------------------------------------------------
(defun quicklisp-apropos--add-to-slime-menu ()
"Add quicklisp-apropos menu to SLIME menu."
(easy-menu-add-item 'menubar-slime nil '("---"))
(easy-menu-add-item 'menubar-slime nil
'("Quicklisp apropos"
["Apropos" quicklisp-apropos
:help "Apropos across Quicklisp libraries."]
["Apropos function" quicklisp-apropos-function
:help "Apropos functions exported across Quicklisp libraries."]
["Apropos variable" quicklisp-apropos-variable
:help "Apropos variables exported across Quicklisp libraries."]
["Apropos class" quicklisp-apropos-class
:help "Apropos classes exported across Quicklisp libraries."]
["Apropos system" quicklisp-apropos-system
:help "Apropos ASDF systems across Quicklisp libraries."]
["Apropos package" quicklisp-apropos-package
:help "Apropos packages across Quicklisp libraries."]
["Update index" quicklisp-apropos-update-index
:help "Download and update quicklisp-apropos index."]
)))
(define-slime-contrib quicklisp-apropos
"Apropos across Quicklisp libraries."
(:authors "Mariano Montone")
(:license "GPL")
(:swank-dependencies quicklisp-apropos)
(:on-load
(quicklisp-apropos--add-to-slime-menu)))
(provide 'quicklisp-apropos)
;;; quicklisp-apropos.el ends here