Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/slime/contrib/slime-autodoc.el
990 views
1
2
(define-slime-contrib slime-autodoc
3
"Show fancy arglist in echo area."
4
(:gnu-emacs-only t)
5
(:license "GPL")
6
(:authors "Luke Gorrie <[email protected]>"
7
"Lawrence Mitchell <[email protected]>"
8
"Matthias Koeppe <[email protected]>"
9
"Tobias C. Rittweiler <[email protected]>")
10
(:slime-dependencies slime-parse)
11
(:swank-dependencies swank-arglists)
12
(:on-load
13
(dolist (h '(slime-mode-hook slime-repl-mode-hook sldb-mode-hook))
14
(add-hook h 'slime-autodoc-maybe-enable)))
15
(:on-unload
16
;; FIXME: This doesn't disable eldoc-mode in existing buffers.
17
(setq slime-echo-arglist-function 'slime-show-arglist)
18
(dolist (h '(slime-mode-hook slime-repl-mode-hook sldb-mode-hook))
19
(remove-hook h 'slime-autodoc-maybe-enable))))
20
21
(defun slime-autodoc-maybe-enable ()
22
(when slime-use-autodoc-mode
23
(slime-autodoc-mode 1)
24
(setq slime-echo-arglist-function
25
(lambda ()
26
(if slime-autodoc-mode
27
(eldoc-message (slime-autodoc))
28
(slime-show-arglist))))))
29
30
(defcustom slime-use-autodoc-mode t
31
"When non-nil always enable slime-autodoc-mode in slime-mode.")
32
33
(defcustom slime-autodoc-use-multiline-p nil
34
"If non-nil, allow long autodoc messages to resize echo area display."
35
:type 'boolean
36
:group 'slime-ui)
37
38
(defcustom slime-autodoc-delay 0.3
39
"*Delay before autodoc messages are fetched and displayed, in seconds."
40
:type 'number
41
:group 'slime-ui)
42
43
(defcustom slime-autodoc-accuracy-depth 10
44
"Number of paren levels that autodoc takes into account for
45
context-sensitive arglist display (local functions. etc)")
46
47
48
49
(defun slime-arglist (name)
50
"Show the argument list for NAME."
51
(interactive (list (slime-read-symbol-name "Arglist of: " t)))
52
(let ((arglist (slime-retrieve-arglist name)))
53
(if (eq arglist :not-available)
54
(error "Arglist not available")
55
(message "%s" (slime-fontify-string arglist)))))
56
57
(defun slime-retrieve-arglist (name)
58
(let ((name (etypecase name
59
(string name)
60
(symbol (symbol-name name)))))
61
(slime-eval `(swank:autodoc '(,name ,slime-cursor-marker)))))
62
63
64
;;;; Autodocs (automatic context-sensitive help)
65
66
(defun slime-make-autodoc-rpc-form ()
67
"Return a cache key and a swank form."
68
(let* ((levels slime-autodoc-accuracy-depth)
69
(buffer-form (slime-parse-form-upto-point levels)))
70
(when buffer-form
71
(values buffer-form
72
`(swank:autodoc ',buffer-form
73
:print-right-margin
74
,(window-width (minibuffer-window)))))))
75
76
(defun slime-autodoc-global-at-point ()
77
"Return the global variable name at point, if any."
78
(when-let (name (slime-symbol-at-point))
79
(and (slime-global-variable-name-p name) name)))
80
81
(defcustom slime-global-variable-name-regexp "^\\(.*:\\)?\\([*+]\\).+\\2$"
82
"Regexp used to check if a symbol name is a global variable.
83
84
Default value assumes +this+ or *that* naming conventions."
85
:type 'regexp
86
:group 'slime)
87
88
(defun slime-global-variable-name-p (name)
89
"Is NAME a global variable?
90
Globals are recognised purely by *this-naming-convention*."
91
(and (< (length name) 80) ; avoid overflows in regexp matcher
92
(string-match slime-global-variable-name-regexp name)))
93
94
95
;;;; Autodoc cache
96
97
(defvar slime-autodoc-last-buffer-form nil)
98
(defvar slime-autodoc-last-autodoc nil)
99
100
(defun slime-get-cached-autodoc (buffer-form)
101
"Return the cached autodoc documentation for `buffer-form', or nil."
102
(when (equal buffer-form slime-autodoc-last-buffer-form)
103
slime-autodoc-last-autodoc))
104
105
(defun slime-store-into-autodoc-cache (buffer-form autodoc)
106
"Update the autodoc cache for SYMBOL with DOCUMENTATION.
107
Return DOCUMENTATION."
108
(setq slime-autodoc-last-buffer-form buffer-form)
109
(setq slime-autodoc-last-autodoc autodoc))
110
111
112
;;;; Formatting autodoc
113
114
(defun slime-format-autodoc (doc multilinep)
115
(let ((doc (slime-fontify-string doc)))
116
(if multilinep
117
doc
118
(slime-oneliner (replace-regexp-in-string "[ \n\t]+" " " doc)))))
119
120
(defun slime-fontify-string (string)
121
"Fontify STRING as `font-lock-mode' does in Lisp mode."
122
(with-current-buffer (get-buffer-create (slime-buffer-name :fontify 'hidden))
123
(erase-buffer)
124
(unless (eq major-mode 'lisp-mode)
125
;; Just calling (lisp-mode) will turn slime-mode on in that buffer,
126
;; which may interfere with this function
127
(setq major-mode 'lisp-mode)
128
(lisp-mode-variables t))
129
(insert string)
130
(let ((font-lock-verbose nil))
131
(font-lock-fontify-buffer))
132
(goto-char (point-min))
133
(when (re-search-forward "===> \\(\\(.\\|\n\\)*\\) <===" nil t)
134
(let ((highlight (match-string 1)))
135
;; Can't use (replace-match highlight) here -- broken in Emacs 21
136
(delete-region (match-beginning 0) (match-end 0))
137
(slime-insert-propertized '(face highlight) highlight)))
138
(buffer-substring (point-min) (point-max))))
139
140
141
;;;; slime-autodoc-mode
142
143
(defun* slime-autodoc (&optional (multilinep slime-autodoc-use-multiline-p)
144
cache-multiline)
145
"Returns the cached arglist information as string, or nil.
146
If it's not in the cache, the cache will be updated asynchronously."
147
(interactive)
148
(save-excursion
149
;; Save match data just in case. This is automatically run in
150
;; background, so it'd be rather disastrous if it touched match
151
;; data.
152
(save-match-data
153
(unless (if (fboundp 'slime-repl-inside-string-or-comment-p)
154
(slime-repl-inside-string-or-comment-p)
155
(slime-inside-string-or-comment-p))
156
(multiple-value-bind (cache-key retrieve-form)
157
(slime-make-autodoc-rpc-form)
158
(let* (cached
159
(multilinep (or (slime-autodoc-multiline-cached (car cache-key))
160
multilinep)))
161
(slime-autodoc-cache-multiline (car cache-key) cache-multiline)
162
(cond
163
((not cache-key) nil)
164
((setq cached (slime-get-cached-autodoc cache-key))
165
(slime-format-autodoc cached multilinep))
166
(t
167
;; If nothing is in the cache, we first decline (by
168
;; returning nil), and fetch the arglist information
169
;; asynchronously.
170
(slime-eval-async retrieve-form
171
(lexical-let ((cache-key cache-key)
172
(multilinep multilinep))
173
(lambda (doc)
174
(unless (eq doc :not-available)
175
(slime-store-into-autodoc-cache cache-key doc)
176
;; Now that we've got our information,
177
;; get it to the user ASAP.
178
(eldoc-message
179
(slime-format-autodoc doc multilinep))))))
180
nil))))))))
181
182
(defvar slime-autodoc-cache-car nil)
183
184
(defun slime-autodoc-multiline-cached (cache-key)
185
(equal cache-key
186
slime-autodoc-cache-car))
187
188
(defun slime-autodoc-cache-multiline (cache-key cache-new-p)
189
(cond (cache-new-p
190
(setq slime-autodoc-cache-car
191
cache-key))
192
((not (equal cache-key
193
slime-autodoc-cache-car))
194
(setq slime-autodoc-cache-car nil))))
195
196
(defun slime-autodoc-manually ()
197
"Like slime-autodoc, but when called twice,
198
or after slime-autodoc was already automatically called,
199
display multiline arglist"
200
(interactive)
201
(eldoc-message (slime-autodoc (or slime-autodoc-use-multiline-p
202
slime-autodoc-mode)
203
t)))
204
205
(make-variable-buffer-local (defvar slime-autodoc-mode nil))
206
207
(defun slime-autodoc-mode (&optional arg)
208
(interactive (list (or current-prefix-arg 'toggle)))
209
(make-local-variable 'eldoc-documentation-function)
210
(make-local-variable 'eldoc-idle-delay)
211
(make-local-variable 'eldoc-minor-mode-string)
212
(setq eldoc-documentation-function 'slime-autodoc)
213
(setq eldoc-idle-delay slime-autodoc-delay)
214
(setq eldoc-minor-mode-string " Autodoc")
215
(setq slime-autodoc-mode (eldoc-mode arg))
216
(when (interactive-p)
217
(message (format "Slime autodoc mode %s."
218
(if slime-autodoc-mode "enabled" "disabled")))))
219
220
(defadvice eldoc-display-message-no-interference-p
221
(after slime-autodoc-message-ok-p)
222
(when slime-autodoc-mode
223
(setq ad-return-value
224
(and ad-return-value
225
;; Display arglist only when the minibuffer is
226
;; inactive, e.g. not on `C-x C-f'.
227
(not (active-minibuffer-window))
228
;; Display arglist only when inferior Lisp will be able
229
;; to cope with the request.
230
(slime-background-activities-enabled-p)))
231
(slime-bind-keys slime-doc-map t '((?A slime-autodoc-manually))))
232
ad-return-value)
233
234
235
;;;; Initialization
236
237
238
239
;;;; Test cases
240
241
(defun slime-check-autodoc-at-point (arglist)
242
(let ((slime-autodoc-use-multiline-p nil))
243
(slime-test-expect (format "Autodoc in `%s' (at %d) is as expected"
244
(buffer-string) (point))
245
arglist
246
(slime-eval (second (slime-make-autodoc-rpc-form)))
247
'equal)))
248
249
(def-slime-test autodoc.1
250
(buffer-sexpr wished-arglist &optional skip-trailing-test-p)
251
""
252
'(
253
;; Test basics
254
("(swank::emacs-connected*HERE*" "(emacs-connected)")
255
("(swank::emacs-connected *HERE*" "(emacs-connected)")
256
("(swank::create-socket*HERE*" "(create-socket host port)")
257
("(swank::create-socket *HERE*" "(create-socket ===> host <=== port)")
258
("(swank::create-socket foo *HERE*" "(create-socket host ===> port <===)")
259
260
;; Test that autodoc differentiates between exported and unexported symbols.
261
("(swank:create-socket*HERE*" :not-available)
262
263
;; Test if cursor is on non-existing required parameter
264
("(swank::create-socket foo bar *HERE*" "(create-socket host port)")
265
266
;; Test cursor in front of opening parenthesis
267
("(swank::with-struct *HERE*(foo. x y) *struct* body1)"
268
"(with-struct (conc-name &rest names) obj &body body)"
269
t)
270
271
;; Test variable content display
272
("(progn swank::default-server-port*HERE*" "DEFAULT-SERVER-PORT => 4005")
273
274
;; Test that "variable content display" is not triggered for trivial constants.
275
("(swank::create-socket t*HERE*" "(create-socket ===> host <=== port)")
276
("(swank::create-socket :foo*HERE*" "(create-socket ===> host <=== port)")
277
278
;; Test with syntactic sugar
279
("#'(lambda () (swank::create-socket*HERE*" "(create-socket host port)")
280
("`(lambda () ,(swank::create-socket*HERE*" "(create-socket host port)")
281
("(remove-if #'(lambda () (swank::create-socket*HERE*" "(create-socket host port)")
282
("`(remove-if #'(lambda () ,@(swank::create-socket*HERE*" "(create-socket host port)")
283
284
;; Test &optional
285
("(swank::symbol-status foo *HERE*"
286
"(symbol-status symbol &optional ===> (package (symbol-package symbol)) <===)")
287
288
;; Test context-sensitive autodoc (DEFMETHOD)
289
("(defmethod swank::arglist-dispatch (*HERE*"
290
"(defmethod arglist-dispatch (===> operator <=== arguments) &body body)")
291
("(defmethod swank::arglist-dispatch :before (*HERE*"
292
"(defmethod arglist-dispatch :before (===> operator <=== arguments) &body body)")
293
294
;; Test context-sensitive autodoc (APPLY)
295
("(apply 'swank::eval-for-emacs*HERE*"
296
"(apply 'eval-for-emacs &optional form buffer-package id &rest args)")
297
("(apply #'swank::eval-for-emacs*HERE*"
298
"(apply #'eval-for-emacs &optional form buffer-package id &rest args)")
299
("(apply 'swank::eval-for-emacs foo *HERE*"
300
"(apply 'eval-for-emacs &optional form ===> buffer-package <=== id &rest args)")
301
("(apply #'swank::eval-for-emacs foo *HERE*"
302
"(apply #'eval-for-emacs &optional form ===> buffer-package <=== id &rest args)")
303
304
;; Test context-sensitive autodoc (ERROR, CERROR)
305
("(error 'simple-condition*HERE*"
306
"(error 'simple-condition &rest arguments &key format-arguments format-control)")
307
("(cerror \"Foo\" 'simple-condition*HERE*"
308
"(cerror \"Foo\" 'simple-condition &rest arguments &key format-arguments format-control)")
309
310
;; Test &KEY and nested arglists
311
("(swank::with-retry-restart (:msg *HERE*"
312
"(with-retry-restart (&key ===> (msg \"Retry.\") <===) &body body)")
313
("(swank::start-server \"/tmp/foo\" :coding-system *HERE*"
314
"(start-server port-file &key (style swank:*communication-style*) (dont-close swank:*dont-close*) ===> (coding-system swank::*coding-system*) <===)")
315
316
;; Test declarations and type specifiers
317
("(declare (string *HERE*"
318
"(declare (string &rest ===> variables <===))")
319
("(declare ((string *HERE*"
320
"(declare ((string &optional ===> size <===) &rest variables))")
321
("(declare (type (string *HERE*"
322
"(declare (type (string &optional ===> size <===) &rest variables))")
323
324
;; Test local functions
325
("(flet ((foo (x y) (+ x y))) (foo *HERE*" "(foo ===> x <=== y)")
326
("(macrolet ((foo (x y) `(+ ,x ,y))) (foo *HERE*" "(foo ===> x <=== y)")
327
("(labels ((foo (x y) (+ x y))) (foo *HERE*" "(foo ===> x <=== y)")
328
("(labels ((foo (x y) (+ x y))
329
(bar (y) (foo *HERE*"
330
"(foo ===> x <=== y)"))
331
(slime-check-top-level)
332
(with-temp-buffer
333
(setq slime-buffer-package "COMMON-LISP-USER")
334
(lisp-mode)
335
(insert buffer-sexpr)
336
(search-backward "*HERE*")
337
(delete-region (match-beginning 0) (match-end 0))
338
(slime-check-autodoc-at-point wished-arglist)
339
(unless skip-trailing-test-p
340
(insert ")") (backward-char)
341
(slime-check-autodoc-at-point wished-arglist))
342
))
343
344
(provide 'slime-autodoc)
345
346