Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/slime/slime-1.2/slime.el
990 views
1
2
;;; slime.el --- Superior Lisp Interaction Mode for Emacs
3
;;
4
;;;; License
5
;; Copyright (C) 2003 Eric Marsden, Luke Gorrie, Helmut Eller
6
;; Copyright (C) 2004,2005,2006 Luke Gorrie, Helmut Eller
7
;; Copyright (C) 2007,2008,2009 Helmut Eller, Tobias C. Rittweiler
8
;;
9
;; For a detailed list of contributors, see the manual.
10
;;
11
;; This program is free software; you can redistribute it and/or
12
;; modify it under the terms of the GNU General Public License as
13
;; published by the Free Software Foundation; either version 2 of
14
;; the License, or (at your option) any later version.
15
;;
16
;; This program is distributed in the hope that it will be useful,
17
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
;; GNU General Public License for more details.
20
;;
21
;; You should have received a copy of the GNU General Public
22
;; License along with this program; if not, write to the Free
23
;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
24
;; MA 02111-1307, USA.
25
26
27
;;;; Commentary
28
;;
29
;; This file contains extensions for programming in Common Lisp. The
30
;; main features are:
31
;;
32
;; A socket-based communication/RPC interface between Emacs and
33
;; Lisp, enabling introspection and remote development.
34
;;
35
;; The `slime-mode' minor-mode complementing `lisp-mode'. This new
36
;; mode includes many commands for interacting with the Common Lisp
37
;; process.
38
;;
39
;; A Common Lisp debugger written in Emacs Lisp. The debugger pops up
40
;; an Emacs buffer similar to the Emacs/Elisp debugger.
41
;;
42
;; A Common Lisp inspector to interactively look at run-time data.
43
;;
44
;; Trapping compiler messages and creating annotations in the source
45
;; file on the appropriate forms.
46
;;
47
;; SLIME should work with Emacs 22 and 23. If it works on XEmacs,
48
;; consider yourself lucky.
49
;;
50
;; In order to run SLIME, a supporting Lisp server called Swank is
51
;; required. Swank is distributed with slime.el and will automatically
52
;; be started in a normal installation.
53
54
55
;;;; Dependencies and setup
56
57
(eval-and-compile
58
(when (<= emacs-major-version 20)
59
(error "Slime requires an Emacs version of 21, or above")))
60
61
(eval-and-compile
62
(require 'cl)
63
(when (locate-library "hyperspec")
64
(require 'hyperspec)))
65
(require 'thingatpt)
66
(require 'comint)
67
(require 'timer)
68
(require 'pp)
69
(require 'hideshow)
70
(require 'font-lock)
71
(when (featurep 'xemacs)
72
(require 'overlay))
73
(require 'easymenu)
74
(eval-when (compile)
75
(require 'arc-mode)
76
(require 'apropos)
77
(require 'outline)
78
(require 'etags)
79
(require 'compile)
80
(require 'gud))
81
82
(eval-and-compile
83
(defvar slime-path
84
(let ((path (or (locate-library "slime") load-file-name)))
85
(and path (file-name-directory path)))
86
"Directory containing the Slime package.
87
This is used to load the supporting Common Lisp library, Swank.
88
The default value is automatically computed from the location of the
89
Emacs Lisp package."))
90
91
(defvar slime-lisp-modes '(lisp-mode))
92
(defvar slime-setup-contribs nil)
93
94
(defun slime-setup (&optional contribs)
95
"Setup Emacs so that lisp-mode buffers always use SLIME.
96
CONTRIBS is a list of contrib packages to load."
97
(when (member 'lisp-mode slime-lisp-modes)
98
(add-hook 'lisp-mode-hook 'slime-lisp-mode-hook))
99
(setq slime-setup-contribs contribs)
100
(slime-setup-contribs))
101
102
(defun slime-setup-contribs ()
103
"Load and initialize contribs."
104
(when slime-setup-contribs
105
(add-to-list 'load-path (expand-file-name "contrib" slime-path))
106
(dolist (c slime-setup-contribs)
107
(require c)
108
(let ((init (intern (format "%s-init" c))))
109
(when (fboundp init)
110
(funcall init))))))
111
112
(defun slime-lisp-mode-hook ()
113
(slime-mode 1)
114
(set (make-local-variable 'lisp-indent-function)
115
'common-lisp-indent-function))
116
117
(eval-and-compile
118
(defun slime-changelog-date (&optional interactivep)
119
"Return the datestring of the latest entry in the ChangeLog file.
120
Return nil if the ChangeLog file cannot be found."
121
(interactive "p")
122
(let ((changelog (concat slime-path "ChangeLog"))
123
(date nil))
124
(when (file-exists-p changelog)
125
(with-temp-buffer
126
(insert-file-contents-literally changelog nil 0 100)
127
(goto-char (point-min))
128
(setq date (symbol-name (read (current-buffer))))))
129
(when interactivep
130
(message "Slime ChangeLog dates %s." date))
131
date)))
132
133
(defvar slime-protocol-version nil)
134
(setq slime-protocol-version
135
(eval-when-compile (slime-changelog-date)))
136
137
138
;;;; Customize groups
139
;;
140
;;;;; slime
141
142
(defgroup slime nil
143
"Interaction with the Superior Lisp Environment."
144
:prefix "slime-"
145
:group 'applications)
146
147
;;;;; slime-ui
148
149
(defgroup slime-ui nil
150
"Interaction with the Superior Lisp Environment."
151
:prefix "slime-"
152
:group 'slime)
153
154
(defcustom slime-truncate-lines t
155
"Set `truncate-lines' in popup buffers.
156
This applies to buffers that present lines as rows of data, such as
157
debugger backtraces and apropos listings."
158
:type 'boolean
159
:group 'slime-ui)
160
161
(defcustom slime-kill-without-query-p nil
162
"If non-nil, kill SLIME processes without query when quitting Emacs.
163
This applies to the *inferior-lisp* buffer and the network connections."
164
:type 'boolean
165
:group 'slime-ui)
166
167
;;;;; slime-lisp
168
169
(defgroup slime-lisp nil
170
"Lisp server configuration."
171
:prefix "slime-"
172
:group 'slime)
173
174
(defcustom slime-backend "swank-loader.lisp"
175
"The name of the Lisp file that loads the Swank server.
176
This name is interpreted relative to the directory containing
177
slime.el, but could also be set to an absolute filename."
178
:type 'string
179
:group 'slime-lisp)
180
181
(defcustom slime-connected-hook nil
182
"List of functions to call when SLIME connects to Lisp."
183
:type 'hook
184
:group 'slime-lisp)
185
186
(defcustom slime-enable-evaluate-in-emacs nil
187
"*If non-nil, the inferior Lisp can evaluate arbitrary forms in Emacs.
188
The default is nil, as this feature can be a security risk."
189
:type '(boolean)
190
:group 'slime-lisp)
191
192
(defcustom slime-lisp-host "127.0.0.1"
193
"The default hostname (or IP address) to connect to."
194
:type 'string
195
:group 'slime-lisp)
196
197
(defcustom slime-port 4005
198
"Port to use as the default for `slime-connect'."
199
:type 'integer
200
:group 'slime-lisp)
201
202
(defvar slime-net-valid-coding-systems
203
'((iso-latin-1-unix nil "iso-latin-1-unix")
204
(iso-8859-1-unix nil "iso-latin-1-unix")
205
(binary nil "iso-latin-1-unix")
206
(utf-8-unix t "utf-8-unix")
207
(emacs-mule-unix t "emacs-mule-unix")
208
(euc-jp-unix t "euc-jp-unix"))
209
"A list of valid coding systems.
210
Each element is of the form: (NAME MULTIBYTEP CL-NAME)")
211
212
(defun slime-find-coding-system (name)
213
"Return the coding system for the symbol NAME.
214
The result is either an element in `slime-net-valid-coding-systems'
215
of nil."
216
(let ((probe (assq name slime-net-valid-coding-systems)))
217
(when (and probe (if (fboundp 'check-coding-system)
218
(ignore-errors (check-coding-system (car probe)))
219
(eq (car probe) 'binary)))
220
probe)))
221
222
(defcustom slime-net-coding-system
223
(car (find-if 'slime-find-coding-system
224
slime-net-valid-coding-systems :key 'car))
225
"Coding system used for network connections.
226
See also `slime-net-valid-coding-systems'."
227
:type (cons 'choice
228
(mapcar (lambda (x)
229
(list 'const (car x)))
230
slime-net-valid-coding-systems))
231
:group 'slime-lisp)
232
233
;;;;; slime-mode
234
235
(defgroup slime-mode nil
236
"Settings for slime-mode Lisp source buffers."
237
:prefix "slime-"
238
:group 'slime)
239
240
(defcustom slime-find-definitions-function 'slime-find-definitions-rpc
241
"Function to find definitions for a name.
242
The function is called with the definition name, a string, as its
243
argument."
244
:type 'function
245
:group 'slime-mode
246
:options '(slime-find-definitions-rpc
247
slime-etags-definitions
248
(lambda (name)
249
(append (slime-find-definitions-rpc name)
250
(slime-etags-definitions name)))
251
(lambda (name)
252
(or (slime-find-definitions-rpc name)
253
(and tags-table-list
254
(slime-etags-definitions name))))))
255
256
(defcustom slime-complete-symbol-function 'slime-simple-complete-symbol
257
"*Function to perform symbol completion."
258
:group 'slime-mode
259
:type '(choice (const :tag "Simple" slime-simple-complete-symbol)
260
(const :tag "Compound" slime-complete-symbol*)
261
(const :tag "Fuzzy" slime-fuzzy-complete-symbol)))
262
263
;;;;; slime-mode-faces
264
265
(defgroup slime-mode-faces nil
266
"Faces in slime-mode source code buffers."
267
:prefix "slime-"
268
:group 'slime-mode)
269
270
(defun slime-underline-color (color)
271
"Return a legal value for the :underline face attribute based on COLOR."
272
;; In XEmacs the :underline attribute can only be a boolean.
273
;; In GNU it can be the name of a colour.
274
(if (featurep 'xemacs)
275
(if color t nil)
276
color))
277
278
(defface slime-error-face
279
`((((class color) (background light))
280
(:underline ,(slime-underline-color "red")))
281
(((class color) (background dark))
282
(:underline ,(slime-underline-color "red")))
283
(t (:underline t)))
284
"Face for errors from the compiler."
285
:group 'slime-mode-faces)
286
287
(defface slime-warning-face
288
`((((class color) (background light))
289
(:underline ,(slime-underline-color "orange")))
290
(((class color) (background dark))
291
(:underline ,(slime-underline-color "coral")))
292
(t (:underline t)))
293
"Face for warnings from the compiler."
294
:group 'slime-mode-faces)
295
296
(defface slime-style-warning-face
297
`((((class color) (background light))
298
(:underline ,(slime-underline-color "brown")))
299
(((class color) (background dark))
300
(:underline ,(slime-underline-color "gold")))
301
(t (:underline t)))
302
"Face for style-warnings from the compiler."
303
:group 'slime-mode-faces)
304
305
(defface slime-note-face
306
`((((class color) (background light))
307
(:underline ,(slime-underline-color "brown4")))
308
(((class color) (background dark))
309
(:underline ,(slime-underline-color "light goldenrod")))
310
(t (:underline t)))
311
"Face for notes from the compiler."
312
:group 'slime-mode-faces)
313
314
(defun slime-face-inheritance-possible-p ()
315
"Return true if the :inherit face attribute is supported."
316
(assq :inherit custom-face-attributes))
317
318
(defface slime-highlight-face
319
(if (slime-face-inheritance-possible-p)
320
'((t (:inherit highlight :underline nil)))
321
'((((class color) (background light))
322
(:background "darkseagreen2"))
323
(((class color) (background dark))
324
(:background "darkolivegreen"))
325
(t (:inverse-video t))))
326
"Face for compiler notes while selected."
327
:group 'slime-mode-faces)
328
329
;;;;; sldb
330
331
(defgroup slime-debugger nil
332
"Backtrace options and fontification."
333
:prefix "sldb-"
334
:group 'slime)
335
336
(defmacro define-sldb-faces (&rest faces)
337
"Define the set of SLDB faces.
338
Each face specifiation is (NAME DESCRIPTION &optional PROPERTIES).
339
NAME is a symbol; the face will be called sldb-NAME-face.
340
DESCRIPTION is a one-liner for the customization buffer.
341
PROPERTIES specifies any default face properties."
342
`(progn ,@(loop for face in faces
343
collect `(define-sldb-face ,@face))))
344
345
(defmacro define-sldb-face (name description &optional default)
346
(let ((facename (intern (format "sldb-%s-face" (symbol-name name)))))
347
`(defface ,facename
348
(list (list t ,default))
349
,(format "Face for %s." description)
350
:group 'slime-debugger)))
351
352
(define-sldb-faces
353
(topline "the top line describing the error")
354
(condition "the condition class")
355
(section "the labels of major sections in the debugger buffer")
356
(frame-label "backtrace frame numbers")
357
(restart-type "restart names."
358
(if (slime-face-inheritance-possible-p)
359
'(:inherit font-lock-keyword-face)))
360
(restart "restart descriptions")
361
(restart-number "restart numbers (correspond to keystrokes to invoke)"
362
'(:bold t))
363
(frame-line "function names and arguments in the backtrace")
364
(restartable-frame-line
365
"frames which are surely restartable"
366
'(:foreground "lime green"))
367
(non-restartable-frame-line
368
"frames which are surely not restartable")
369
(detailed-frame-line
370
"function names and arguments in a detailed (expanded) frame")
371
(local-name "local variable names")
372
(local-value "local variable values")
373
(catch-tag "catch tags"))
374
375
376
;;;; Minor modes
377
378
;;;;; slime-mode
379
380
(defvar slime-mode-indirect-map (make-sparse-keymap)
381
"Empty keymap which has `slime-mode-map' as it's parent.
382
This is a hack so that we can reinitilize the real slime-mode-map
383
more easily. See `slime-init-keymaps'.")
384
385
(define-minor-mode slime-mode
386
"\\<slime-mode-map>\
387
SLIME: The Superior Lisp Interaction Mode for Emacs (minor-mode).
388
389
Commands to compile the current buffer's source file and visually
390
highlight any resulting compiler notes and warnings:
391
\\[slime-compile-and-load-file] - Compile and load the current buffer's file.
392
\\[slime-compile-file] - Compile (but not load) the current buffer's file.
393
\\[slime-compile-defun] - Compile the top-level form at point.
394
395
Commands for visiting compiler notes:
396
\\[slime-next-note] - Goto the next form with a compiler note.
397
\\[slime-previous-note] - Goto the previous form with a compiler note.
398
\\[slime-remove-notes] - Remove compiler-note annotations in buffer.
399
400
Finding definitions:
401
\\[slime-edit-definition] - Edit the definition of the function called at point.
402
\\[slime-pop-find-definition-stack] - Pop the definition stack to go back from a definition.
403
404
Documentation commands:
405
\\[slime-describe-symbol] - Describe symbol.
406
\\[slime-apropos] - Apropos search.
407
\\[slime-disassemble-symbol] - Disassemble a function.
408
409
Evaluation commands:
410
\\[slime-eval-defun] - Evaluate top-level from containing point.
411
\\[slime-eval-last-expression] - Evaluate sexp before point.
412
\\[slime-pprint-eval-last-expression] - Evaluate sexp before point, pretty-print result.
413
414
Full set of commands:
415
\\{slime-mode-map}"
416
nil
417
nil
418
slime-mode-indirect-map
419
(slime-setup-command-hooks)
420
(slime-recompute-modelines))
421
422
423
;;;;;; Modeline
424
425
;; For XEmacs only
426
(make-variable-buffer-local
427
(defvar slime-modeline-string nil
428
"The string that should be displayed in the modeline."))
429
430
(add-to-list 'minor-mode-alist
431
`(slime-mode ,(if (featurep 'xemacs)
432
'slime-modeline-string
433
'(:eval (slime-modeline-string)))))
434
435
(defun slime-modeline-string ()
436
"Return the string to display in the modeline.
437
\"Slime\" only appears if we aren't connected. If connected,
438
include package-name, connection-name, and possibly some state
439
information."
440
(let ((conn (slime-current-connection)))
441
;; Bail out early in case there's no connection, so we won't
442
;; implicitly invoke `slime-connection' which may query the user.
443
(if (not conn)
444
(and slime-mode " Slime")
445
(let ((local (eq conn slime-buffer-connection))
446
(pkg (slime-current-package)))
447
(concat " "
448
(if local "{" "[")
449
(if pkg (slime-pretty-package-name pkg) "?")
450
" "
451
;; ignore errors for closed connections
452
(ignore-errors (slime-connection-name conn))
453
(slime-modeline-state-string conn)
454
(if local "}" "]"))))))
455
456
(defun slime-pretty-package-name (name)
457
"Return a pretty version of a package name NAME."
458
(cond ((string-match "^#?:\\(.*\\)$" name)
459
(match-string 1 name))
460
((string-match "^\"\\(.*\\)\"$" name)
461
(match-string 1 name))
462
(t name)))
463
464
(defun slime-modeline-state-string (conn)
465
"Return a string possibly describing CONN's state."
466
(cond ((not (eq (process-status conn) 'open))
467
(format " %s" (process-status conn)))
468
((let ((pending (length (slime-rex-continuations conn)))
469
(sldbs (length (sldb-buffers conn))))
470
(cond ((and (zerop sldbs) (zerop pending)) nil)
471
((zerop sldbs) (format " %s" pending))
472
(t (format " %s/%s" pending sldbs)))))))
473
474
(defun slime-recompute-modelines ()
475
(when (featurep 'xemacs)
476
(dolist (buffer (buffer-list))
477
(with-current-buffer buffer
478
(when (or slime-mode slime-popup-buffer-mode)
479
(setq slime-modeline-string (slime-modeline-string)))))
480
(force-mode-line-update t)))
481
482
483
;;;;; Key bindings
484
485
(defvar slime-parent-map nil
486
"Parent keymap for shared between all Slime related modes.")
487
488
(defvar slime-parent-bindings
489
'(("\M-." slime-edit-definition)
490
("\M-," slime-pop-find-definition-stack)
491
("\M-_" slime-edit-uses) ; for German layout
492
("\M-?" slime-edit-uses) ; for USian layout
493
("\C-x4." slime-edit-definition-other-window)
494
("\C-x5." slime-edit-definition-other-frame)
495
("\C-x\C-e" slime-eval-last-expression)
496
("\C-\M-x" slime-eval-defun)
497
;; Include PREFIX keys...
498
("\C-c" slime-prefix-map)))
499
500
(defvar slime-prefix-map nil
501
"Keymap for commands prefixed with `slime-prefix-key'.")
502
503
(defvar slime-prefix-bindings
504
'(("\C-r" slime-eval-region)
505
(":" slime-interactive-eval)
506
("\C-e" slime-interactive-eval)
507
("E" slime-edit-value)
508
("\C-l" slime-load-file)
509
("\C-b" slime-interrupt)
510
("\M-d" slime-disassemble-symbol)
511
("\C-t" slime-toggle-trace-fdefinition)
512
("I" slime-inspect)
513
("\C-xt" slime-list-threads)
514
("\C-xn" slime-cycle-connections)
515
("\C-xc" slime-list-connections)
516
("<" slime-list-callers)
517
(">" slime-list-callees)
518
;; Include DOC keys...
519
("\C-d" slime-doc-map)
520
;; Include XREF WHO-FOO keys...
521
("\C-w" slime-who-map)
522
))
523
524
(defvar slime-editing-map nil
525
"These keys are useful for buffers where the user can insert and
526
edit s-exprs, e.g. for source buffers and the REPL.")
527
528
(defvar slime-editing-keys
529
`(;; Arglist display & completion
530
("\M-\t" slime-complete-symbol)
531
(" " slime-space)
532
;; Evaluating
533
;;("\C-x\M-e" slime-eval-last-expression-display-output :inferior t)
534
("\C-c\C-p" slime-pprint-eval-last-expression)
535
;; Macroexpand
536
("\C-c\C-m" slime-macroexpand-1)
537
("\C-c\M-m" slime-macroexpand-all)
538
;; Misc
539
("\C-c\C-u" slime-undefine-function)
540
(,(kbd "C-M-.") slime-next-location)
541
(,(kbd "C-M-,") slime-previous-location)
542
;; Obsolete, redundant bindings
543
("\C-c\C-i" slime-complete-symbol)
544
;;("\M-*" pop-tag-mark) ; almost to clever
545
))
546
547
(defvar slime-mode-map nil
548
"Keymap for slime-mode.")
549
550
(defvar slime-keys
551
'( ;; Compiler notes
552
("\M-p" slime-previous-note)
553
("\M-n" slime-next-note)
554
("\C-c\M-c" slime-remove-notes)
555
("\C-c\C-k" slime-compile-and-load-file)
556
("\C-c\M-k" slime-compile-file)
557
("\C-c\C-c" slime-compile-defun)))
558
559
(defun slime-nop ()
560
"The null command. Used to shadow currently-unused keybindings."
561
(interactive)
562
(call-interactively 'undefined))
563
564
(defvar slime-doc-map nil
565
"Keymap for documentation commands. Bound to a prefix key.")
566
567
(defvar slime-doc-bindings
568
'((?a slime-apropos)
569
(?z slime-apropos-all)
570
(?p slime-apropos-package)
571
(?d slime-describe-symbol)
572
(?f slime-describe-function)
573
(?h slime-documentation-lookup)
574
(?~ common-lisp-hyperspec-format)
575
(?# common-lisp-hyperspec-lookup-reader-macro)))
576
577
(defvar slime-who-map nil
578
"Keymap for who-xref commands. Bound to a prefix key.")
579
580
(defvar slime-who-bindings
581
'((?c slime-who-calls)
582
(?w slime-calls-who)
583
(?r slime-who-references)
584
(?b slime-who-binds)
585
(?s slime-who-sets)
586
(?m slime-who-macroexpands)
587
(?a slime-who-specializes)))
588
589
(defun slime-init-keymaps ()
590
"(Re)initialize the keymaps for `slime-mode'."
591
(interactive)
592
(slime-init-keymap 'slime-doc-map t t slime-doc-bindings)
593
(slime-init-keymap 'slime-who-map t t slime-who-bindings)
594
(slime-init-keymap 'slime-prefix-map t nil slime-prefix-bindings)
595
(slime-init-keymap 'slime-parent-map nil nil slime-parent-bindings)
596
(slime-init-keymap 'slime-editing-map nil nil slime-editing-keys)
597
(set-keymap-parent slime-editing-map slime-parent-map)
598
(slime-init-keymap 'slime-mode-map nil nil slime-keys)
599
(set-keymap-parent slime-mode-map slime-editing-map)
600
(set-keymap-parent slime-mode-indirect-map slime-mode-map))
601
602
(defun slime-init-keymap (keymap-name prefixp bothp bindings)
603
(set keymap-name (make-sparse-keymap))
604
(when prefixp (define-prefix-command keymap-name))
605
(slime-bind-keys (eval keymap-name) bothp bindings))
606
607
(defun slime-bind-keys (keymap bothp bindings)
608
"Add BINDINGS to KEYMAP.
609
If BOTHP is true also add bindings with control modifier."
610
(loop for (key command) in bindings do
611
(cond (bothp
612
(define-key keymap `[,key] command)
613
(unless (equal key ?h) ; But don't bind C-h
614
(define-key keymap `[(control ,key)] command)))
615
(t (define-key keymap key command)))))
616
617
(slime-init-keymaps)
618
619
(define-minor-mode slime-editing-mode
620
"Minor mode which makes slime-editing-map available.
621
\\{slime-editing-map}"
622
nil
623
nil
624
slime-editing-map)
625
626
627
;;;; Setup initial `slime-mode' hooks
628
629
(make-variable-buffer-local
630
(defvar slime-pre-command-actions nil
631
"List of functions to execute before the next Emacs command.
632
This list of flushed between commands."))
633
634
(defun slime-pre-command-hook ()
635
"Execute all functions in `slime-pre-command-actions', then NIL it."
636
(dolist (undo-fn slime-pre-command-actions)
637
(funcall undo-fn))
638
(setq slime-pre-command-actions nil))
639
640
(defun slime-post-command-hook ()
641
(when (null pre-command-hook) ; sometimes this is lost
642
(add-hook 'pre-command-hook 'slime-pre-command-hook)))
643
644
(defun slime-setup-command-hooks ()
645
"Setup a buffer-local `pre-command-hook' to call `slime-pre-command-hook'."
646
(slime-add-local-hook 'pre-command-hook 'slime-pre-command-hook)
647
(slime-add-local-hook 'post-command-hook 'slime-post-command-hook))
648
649
650
;;;; Framework'ey bits
651
;;;
652
;;; This section contains some standard SLIME idioms: basic macros,
653
;;; ways of showing messages to the user, etc. All the code in this
654
;;; file should use these functions when applicable.
655
;;;
656
;;;;; Syntactic sugar
657
658
(defmacro* when-let ((var value) &rest body)
659
"Evaluate VALUE, if the result is non-nil bind it to VAR and eval BODY.
660
661
\(fn (VAR VALUE) &rest BODY)"
662
`(let ((,var ,value))
663
(when ,var ,@body)))
664
665
(put 'when-let 'lisp-indent-function 1)
666
667
(defmacro destructure-case (value &rest patterns)
668
"Dispatch VALUE to one of PATTERNS.
669
A cross between `case' and `destructuring-bind'.
670
The pattern syntax is:
671
((HEAD . ARGS) . BODY)
672
The list of patterns is searched for a HEAD `eq' to the car of
673
VALUE. If one is found, the BODY is executed with ARGS bound to the
674
corresponding values in the CDR of VALUE."
675
(let ((operator (gensym "op-"))
676
(operands (gensym "rand-"))
677
(tmp (gensym "tmp-")))
678
`(let* ((,tmp ,value)
679
(,operator (car ,tmp))
680
(,operands (cdr ,tmp)))
681
(case ,operator
682
,@(mapcar (lambda (clause)
683
(if (eq (car clause) t)
684
`(t ,@(cdr clause))
685
(destructuring-bind ((op &rest rands) &rest body) clause
686
`(,op (destructuring-bind ,rands ,operands
687
. ,body)))))
688
patterns)
689
,@(if (eq (caar (last patterns)) t)
690
'()
691
`((t (error "Elisp destructure-case failed: %S" ,tmp))))))))
692
693
(put 'destructure-case 'lisp-indent-function 1)
694
695
(defmacro slime-define-keys (keymap &rest key-command)
696
"Define keys in KEYMAP. Each KEY-COMMAND is a list of (KEY COMMAND)."
697
`(progn . ,(mapcar (lambda (k-c) `(define-key ,keymap . ,k-c))
698
key-command)))
699
700
(put 'slime-define-keys 'lisp-indent-function 1)
701
702
(defmacro* with-struct ((conc-name &rest slots) struct &body body)
703
"Like with-slots but works only for structs.
704
\(fn (CONC-NAME &rest SLOTS) STRUCT &body BODY)"
705
(flet ((reader (slot) (intern (concat (symbol-name conc-name)
706
(symbol-name slot)))))
707
(let ((struct-var (gensym "struct")))
708
`(let ((,struct-var ,struct))
709
(symbol-macrolet
710
,(mapcar (lambda (slot)
711
(etypecase slot
712
(symbol `(,slot (,(reader slot) ,struct-var)))
713
(cons `(,(first slot) (,(reader (second slot))
714
,struct-var)))))
715
slots)
716
. ,body)))))
717
718
(put 'with-struct 'lisp-indent-function 2)
719
720
;;;;; Very-commonly-used functions
721
722
(defvar slime-message-function 'message)
723
724
;; Interface
725
(defun slime-buffer-name (type &optional hidden)
726
(assert (keywordp type))
727
(concat (if hidden " " "")
728
(format "*slime-%s*" (substring (symbol-name type) 1))))
729
730
;; Interface
731
(defun slime-message (format &rest args)
732
"Like `message' but with special support for multi-line messages.
733
Single-line messages use the echo area."
734
(apply slime-message-function format args))
735
736
(defun slime-display-warning (message &rest args)
737
(display-warning '(slime warning) (apply #'format message args)))
738
739
(defvar slime-background-message-function 'slime-display-oneliner)
740
741
;; Interface
742
(defun slime-background-message (format-string &rest format-args)
743
"Display a message in passing.
744
This is like `slime-message', but less distracting because it
745
will never pop up a buffer or display multi-line messages.
746
It should be used for \"background\" messages such as argument lists."
747
(apply slime-background-message-function format-string format-args))
748
749
(defun slime-display-oneliner (format-string &rest format-args)
750
(let* ((msg (apply #'format format-string format-args)))
751
(unless (minibuffer-window-active-p (minibuffer-window))
752
(message "%s" (slime-oneliner msg)))))
753
754
(defun slime-oneliner (string)
755
"Return STRING truncated to fit in a single echo-area line."
756
(substring string 0 (min (length string)
757
(or (position ?\n string) most-positive-fixnum)
758
(1- (frame-width)))))
759
760
;; Interface
761
(defun slime-set-truncate-lines ()
762
"Apply `slime-truncate-lines' to the current buffer."
763
(when slime-truncate-lines
764
(set (make-local-variable 'truncate-lines) t)))
765
766
;; Interface
767
(defun slime-read-package-name (prompt &optional initial-value)
768
"Read a package name from the minibuffer, prompting with PROMPT."
769
(let ((completion-ignore-case t))
770
(completing-read prompt (slime-bogus-completion-alist
771
(slime-eval
772
`(swank:list-all-package-names t)))
773
nil t initial-value)))
774
775
;; Interface
776
(defun slime-read-symbol-name (prompt &optional query)
777
"Either read a symbol name or choose the one at point.
778
The user is prompted if a prefix argument is in effect, if there is no
779
symbol at point, or if QUERY is non-nil."
780
(cond ((or current-prefix-arg query (not (slime-symbol-at-point)))
781
(slime-read-from-minibuffer prompt (slime-symbol-at-point)))
782
(t (slime-symbol-at-point))))
783
784
;; Interface
785
(defmacro slime-propertize-region (props &rest body)
786
"Execute BODY and add PROPS to all the text it inserts.
787
More precisely, PROPS are added to the region between the point's
788
positions before and after executing BODY."
789
(let ((start (gensym)))
790
`(let ((,start (point)))
791
(prog1 (progn ,@body)
792
(add-text-properties ,start (point) ,props)))))
793
794
(put 'slime-propertize-region 'lisp-indent-function 1)
795
796
(defun slime-add-face (face string)
797
(add-text-properties 0 (length string) (list 'face face) string)
798
string)
799
800
(put 'slime-add-face 'lisp-indent-function 1)
801
802
;; Interface
803
(defsubst slime-insert-propertized (props &rest args)
804
"Insert all ARGS and then add text-PROPS to the inserted text."
805
(slime-propertize-region props (apply #'insert args)))
806
807
(defmacro slime-with-rigid-indentation (level &rest body)
808
"Execute BODY and then rigidly indent its text insertions.
809
Assumes all insertions are made at point."
810
(let ((start (gensym)) (l (gensym)))
811
`(let ((,start (point)) (,l ,(or level '(current-column))))
812
(prog1 (progn ,@body)
813
(slime-indent-rigidly ,start (point) ,l)))))
814
815
(put 'slime-with-rigid-indentation 'lisp-indent-function 1)
816
817
(defun slime-indent-rigidly (start end column)
818
;; Similar to `indent-rigidly' but doesn't inherit text props.
819
(let ((indent (make-string column ?\ )))
820
(save-excursion
821
(goto-char end)
822
(beginning-of-line)
823
(while (and (<= start (point))
824
(progn
825
(insert-before-markers indent)
826
(zerop (forward-line -1))))))))
827
828
(defun slime-insert-indented (&rest strings)
829
"Insert all arguments rigidly indented."
830
(slime-with-rigid-indentation nil
831
(apply #'insert strings)))
832
833
(defun slime-property-bounds (prop)
834
"Return two the positions of the previous and next changes to PROP.
835
PROP is the name of a text property."
836
(assert (get-text-property (point) prop))
837
(let ((end (next-single-char-property-change (point) prop)))
838
(list (previous-single-char-property-change end prop) end)))
839
840
(defun slime-curry (fun &rest args)
841
"Partially apply FUN to ARGS. The result is a new function.
842
This idiom is preferred over `lexical-let'."
843
`(lambda (&rest more) (apply ',fun (append ',args more))))
844
845
(defun slime-rcurry (fun &rest args)
846
"Like `slime-curry' but ARGS on the right are applied."
847
`(lambda (&rest more) (apply ',fun (append more ',args))))
848
849
850
;;;;; Temporary popup buffers
851
852
(defvar slime-popup-restore-data nil
853
"Data needed when closing popup windows.
854
This is used as buffer local variable.
855
The format is (POPUP-WINDOW SELECTED-WINDOW OLD-BUFFER).
856
POPUP-WINDOW is the window used to display the temp buffer.
857
That window may have been reused or freshly created.
858
SELECTED-WINDOW is the window that was selected before displaying
859
the popup buffer.
860
OLD-BUFFER is the buffer that was previously displayed in POPUP-WINDOW.
861
OLD-BUFFER is nil if POPUP-WINDOW was newly created.
862
863
See `view-return-to-alist' for a similar idea.")
864
865
;; keep compiler quiet
866
(defvar slime-buffer-package)
867
(defvar slime-buffer-connection)
868
869
;; Interface
870
(defmacro* slime-with-popup-buffer ((name &key package connection select mode)
871
&body body)
872
"Similar to `with-output-to-temp-buffer'.
873
Bind standard-output and initialize some buffer-local variables.
874
Restore window configuration when closed.
875
876
NAME is the name of the buffer to be created.
877
PACKAGE is the value `slime-buffer-package'.
878
CONNECTION is the value for `slime-buffer-connection'.
879
MODE is the name of a major mode which will be enabled.
880
If nil, no explicit connection is associated with
881
the buffer. If t, the current connection is taken.
882
"
883
`(let* ((vars% (list ,(if (eq package t) '(slime-current-package) package)
884
,(if (eq connection t) '(slime-connection) connection)))
885
(standard-output (slime-make-popup-buffer ,name vars% ,mode)))
886
(with-current-buffer standard-output
887
(prog1 (progn ,@body)
888
(assert (eq (current-buffer) standard-output))
889
(setq buffer-read-only t)
890
(set-window-point (slime-display-popup-buffer ,(or select nil))
891
(point))))))
892
893
(put 'slime-with-popup-buffer 'lisp-indent-function 1)
894
895
(defun slime-make-popup-buffer (name buffer-vars mode)
896
"Return a temporary buffer called NAME.
897
The buffer also uses the minor-mode `slime-popup-buffer-mode'."
898
(with-current-buffer (get-buffer-create name)
899
(kill-all-local-variables)
900
(when mode
901
(funcall mode))
902
(setq buffer-read-only nil)
903
(erase-buffer)
904
(set-syntax-table lisp-mode-syntax-table)
905
(slime-init-popup-buffer buffer-vars)
906
(current-buffer)))
907
908
(defun slime-init-popup-buffer (buffer-vars)
909
(slime-popup-buffer-mode 1)
910
(multiple-value-setq (slime-buffer-package slime-buffer-connection)
911
buffer-vars))
912
913
(defun slime-display-popup-buffer (select)
914
"Display the current buffer.
915
Save the selected-window in a buffer-local variable, so that we
916
can restore it later."
917
(let ((selected-window (selected-window))
918
(old-windows))
919
(walk-windows (lambda (w) (push (cons w (window-buffer w)) old-windows))
920
nil t)
921
(let ((new-window (display-buffer (current-buffer))))
922
(unless slime-popup-restore-data
923
(set (make-local-variable 'slime-popup-restore-data)
924
(list new-window
925
selected-window
926
(cdr (find new-window old-windows :key #'car)))))
927
(when select
928
(select-window new-window))
929
new-window)))
930
931
(defun slime-close-popup-window ()
932
(when slime-popup-restore-data
933
(destructuring-bind (popup-window selected-window old-buffer)
934
slime-popup-restore-data
935
(kill-local-variable 'slime-popup-restore-data)
936
(bury-buffer)
937
(when (eq popup-window (selected-window))
938
(cond ((and (not old-buffer) (not (one-window-p)))
939
(delete-window popup-window))
940
((and old-buffer (buffer-live-p old-buffer))
941
(set-window-buffer popup-window old-buffer))))
942
(when (window-live-p selected-window)
943
(select-window selected-window)))))
944
945
(defmacro slime-save-local-variables (vars &rest body)
946
(let ((vals (make-symbol "vals")))
947
`(let ((,vals (mapcar (lambda (var)
948
(if (slime-local-variable-p var)
949
(cons var (eval var))))
950
',vars)))
951
(prog1 (progn . ,body)
952
(mapc (lambda (var+val)
953
(when (consp var+val)
954
(set (make-local-variable (car var+val)) (cdr var+val))))
955
,vals)))))
956
957
(put 'slime-save-local-variables 'lisp-indent-function 1)
958
959
(define-minor-mode slime-popup-buffer-mode
960
"Mode for displaying read only stuff"
961
nil
962
nil
963
'(("q" . slime-popup-buffer-quit-function)
964
;;("\C-c\C-z" . slime-switch-to-output-buffer)
965
("\M-." . slime-edit-definition)))
966
967
(add-to-list 'minor-mode-alist
968
`(slime-popup-buffer-mode
969
,(if (featurep 'xemacs)
970
'slime-modeline-string
971
'(:eval (unless slime-mode
972
(slime-modeline-string))))))
973
974
(set-keymap-parent slime-popup-buffer-mode-map slime-parent-map)
975
976
(make-variable-buffer-local
977
(defvar slime-popup-buffer-quit-function 'slime-popup-buffer-quit
978
"The function that is used to quit a temporary popup buffer."))
979
980
(defun slime-popup-buffer-quit-function (&optional kill-buffer-p)
981
"Wrapper to invoke the value of `slime-popup-buffer-quit-function'."
982
(interactive)
983
(funcall slime-popup-buffer-quit-function kill-buffer-p))
984
985
;; Interface
986
(defun slime-popup-buffer-quit (&optional kill-buffer-p)
987
"Get rid of the current (temp) buffer without asking.
988
Restore the window configuration unless it was changed since we
989
last activated the buffer."
990
(interactive)
991
(let ((buffer (current-buffer)))
992
(slime-close-popup-window)
993
(when kill-buffer-p
994
(kill-buffer buffer))))
995
996
;;;;; Filename translation
997
;;;
998
;;; Filenames passed between Emacs and Lisp should be translated using
999
;;; these functions. This way users who run Emacs and Lisp on separate
1000
;;; machines have a chance to integrate file operations somehow.
1001
1002
(defvar slime-to-lisp-filename-function #'convert-standard-filename
1003
"Function to translate Emacs filenames to CL namestrings.")
1004
(defvar slime-from-lisp-filename-function #'identity
1005
"Function to translate CL namestrings to Emacs filenames.")
1006
1007
(defun slime-to-lisp-filename (filename)
1008
"Translate the string FILENAME to a Lisp filename."
1009
(funcall slime-to-lisp-filename-function filename))
1010
1011
(defun slime-from-lisp-filename (filename)
1012
"Translate the Lisp filename FILENAME to an Emacs filename."
1013
(funcall slime-from-lisp-filename-function filename))
1014
1015
1016
;;;; Starting SLIME
1017
;;;
1018
;;; This section covers starting an inferior-lisp, compiling and
1019
;;; starting the server, initiating a network connection.
1020
1021
;;;;; Entry points
1022
1023
;; We no longer load inf-lisp, but we use this variable for backward
1024
;; compatibility.
1025
(defvar inferior-lisp-program "lisp"
1026
"*Program name for invoking an inferior Lisp with for Inferior Lisp mode.")
1027
1028
(defvar slime-lisp-implementations nil
1029
"*A list of known Lisp implementations.
1030
The list should have the form:
1031
((NAME (PROGRAM PROGRAM-ARGS...) &key KEYWORD-ARGS) ...)
1032
1033
NAME is a symbol for the implementation.
1034
PROGRAM and PROGRAM-ARGS are strings used to start the Lisp process.
1035
For KEYWORD-ARGS see `slime-start'.
1036
1037
Here's an example:
1038
((cmucl (\"/opt/cmucl/bin/lisp\" \"-quiet\") :init slime-init-command)
1039
(acl (\"acl7\") :coding-system emacs-mule))")
1040
1041
(defvar slime-default-lisp nil
1042
"*The name of the default Lisp implementation.
1043
See `slime-lisp-implementations'")
1044
1045
;; dummy definitions for the compiler
1046
(defvar slime-net-processes)
1047
(defvar slime-default-connection)
1048
1049
(defun slime (&optional command coding-system)
1050
"Start an inferior^_superior Lisp and connect to its Swank server."
1051
(interactive)
1052
(let ((inferior-lisp-program (or command inferior-lisp-program))
1053
(slime-net-coding-system (or coding-system slime-net-coding-system)))
1054
(slime-start* (cond ((and command (symbolp command))
1055
(slime-lisp-options command))
1056
(t (slime-read-interactive-args))))))
1057
1058
(defvar slime-inferior-lisp-program-history '()
1059
"History list of command strings. Used by `slime'.")
1060
1061
(defun slime-read-interactive-args ()
1062
"Return the list of args which should be passed to `slime-start'.
1063
1064
The rules for selecting the arguments are rather complicated:
1065
1066
- In the most common case, i.e. if there's no prefix-arg in
1067
effect and if `slime-lisp-implementations' is nil, use
1068
`inferior-lisp-program' as fallback.
1069
1070
- If the table `slime-lisp-implementations' is non-nil use the
1071
implementation with name `slime-default-lisp' or if that's nil
1072
the first entry in the table.
1073
1074
- If the prefix-arg is `-', prompt for one of the registered
1075
lisps.
1076
1077
- If the prefix-arg is positive, read the command to start the
1078
process."
1079
(let ((table slime-lisp-implementations))
1080
(cond ((not current-prefix-arg) (slime-lisp-options))
1081
((eq current-prefix-arg '-)
1082
(let ((key (completing-read
1083
"Lisp name: " (mapcar (lambda (x)
1084
(list (symbol-name (car x))))
1085
table)
1086
nil t)))
1087
(slime-lookup-lisp-implementation table (intern key))))
1088
(t
1089
(destructuring-bind (program &rest program-args)
1090
(split-string (read-string
1091
"Run lisp: " inferior-lisp-program
1092
'slime-inferior-lisp-program-history))
1093
(let ((coding-system
1094
(if (eq 16 (prefix-numeric-value current-prefix-arg))
1095
(read-coding-system "set slime-coding-system: "
1096
slime-net-coding-system)
1097
slime-net-coding-system)))
1098
(list :program program :program-args program-args
1099
:coding-system coding-system)))))))
1100
1101
(defun slime-lisp-options (&optional name)
1102
(let ((table slime-lisp-implementations))
1103
(assert (or (not name) table))
1104
(cond (table (slime-lookup-lisp-implementation slime-lisp-implementations
1105
(or name slime-default-lisp
1106
(car (car table)))))
1107
(t (destructuring-bind (program &rest args)
1108
(split-string inferior-lisp-program)
1109
(list :program program :program-args args))))))
1110
1111
(defun slime-lookup-lisp-implementation (table name)
1112
(destructuring-bind (name (prog &rest args) &rest keys) (assoc name table)
1113
(list* :name name :program prog :program-args args keys)))
1114
1115
(defun* slime-start (&key (program inferior-lisp-program) program-args
1116
directory
1117
(coding-system slime-net-coding-system)
1118
(init 'slime-init-command)
1119
name
1120
(buffer "*inferior-lisp*")
1121
init-function
1122
env)
1123
"Start a Lisp process and connect to it.
1124
This function is intended for programmatic use if `slime' is not
1125
flexible enough.
1126
1127
PROGRAM and PROGRAM-ARGS are the filename and argument strings
1128
for the subprocess.
1129
INIT is a function that should return a string to load and start
1130
Swank. The function will be called with the PORT-FILENAME and ENCODING as
1131
arguments. INIT defaults to `slime-init-command'.
1132
CODING-SYSTEM a symbol for the coding system. The default is
1133
slime-net-coding-system
1134
ENV environment variables for the subprocess (see `process-environment').
1135
INIT-FUNCTION function to call right after the connection is established.
1136
BUFFER the name of the buffer to use for the subprocess.
1137
NAME a symbol to describe the Lisp implementation
1138
DIRECTORY change to this directory before starting the process.
1139
"
1140
(let ((args (list :program program :program-args program-args :buffer buffer
1141
:coding-system coding-system :init init :name name
1142
:init-function init-function :env env)))
1143
(slime-check-coding-system coding-system)
1144
(when (slime-bytecode-stale-p)
1145
(slime-urge-bytecode-recompile))
1146
(let ((proc (slime-maybe-start-lisp program program-args env
1147
directory buffer)))
1148
(slime-inferior-connect proc args)
1149
(pop-to-buffer (process-buffer proc)))))
1150
1151
(defun slime-start* (options)
1152
(apply #'slime-start options))
1153
1154
(defun slime-connect (host port &optional coding-system)
1155
"Connect to a running Swank server. Return the connection."
1156
(interactive (list (read-from-minibuffer "Host: " slime-lisp-host)
1157
(read-from-minibuffer "Port: " (format "%d" slime-port)
1158
nil t)))
1159
(when (and (interactive-p) slime-net-processes
1160
(y-or-n-p "Close old connections first? "))
1161
(slime-disconnect-all))
1162
(message "Connecting to Swank on port %S.." port)
1163
(let ((coding-system (or coding-system slime-net-coding-system)))
1164
(slime-check-coding-system coding-system)
1165
(message "Connecting to Swank on port %S.." port)
1166
(let* ((process (slime-net-connect host port coding-system))
1167
(slime-dispatching-connection process))
1168
(slime-setup-connection process))))
1169
1170
;; FIXME: seems redundant
1171
(defun slime-start-and-init (options fun)
1172
(let* ((rest (plist-get options :init-function))
1173
(init (cond (rest `(lambda () (funcall ',rest) (funcall ',fun)))
1174
(t fun))))
1175
(slime-start* (plist-put (copy-list options) :init-function init))))
1176
1177
;;;;; Start inferior lisp
1178
;;;
1179
;;; Here is the protocol for starting SLIME:
1180
;;;
1181
;;; 0. Emacs recompiles/reloads slime.elc if it exists and is stale.
1182
;;; 1. Emacs starts an inferior Lisp process.
1183
;;; 2. Emacs tells Lisp (via stdio) to load and start Swank.
1184
;;; 3. Lisp recompiles the Swank if needed.
1185
;;; 4. Lisp starts the Swank server and writes its TCP port to a temp file.
1186
;;; 5. Emacs reads the temp file to get the port and then connects.
1187
;;; 6. Emacs prints a message of warm encouragement for the hacking ahead.
1188
;;;
1189
;;; Between steps 2-5 Emacs polls for the creation of the temp file so
1190
;;; that it can make the connection. This polling may continue for a
1191
;;; fair while if Swank needs recompilation.
1192
1193
(defvar slime-connect-retry-timer nil
1194
"Timer object while waiting for an inferior-lisp to start.")
1195
1196
;;; Recompiling bytecode:
1197
1198
(defun slime-bytecode-stale-p ()
1199
"Return true if slime.elc is older than slime.el."
1200
(when-let (libfile (locate-library "slime"))
1201
(let* ((basename (file-name-sans-extension libfile))
1202
(sourcefile (concat basename ".el"))
1203
(bytefile (concat basename ".elc")))
1204
(and (file-exists-p bytefile)
1205
(file-newer-than-file-p sourcefile bytefile)))))
1206
1207
(defun slime-recompile-bytecode ()
1208
"Recompile and reload slime.
1209
Warning: don't use this in XEmacs, it seems to crash it!"
1210
(interactive)
1211
(let ((sourcefile (concat (file-name-sans-extension (locate-library "slime"))
1212
".el")))
1213
(byte-compile-file sourcefile t)))
1214
1215
(defun slime-urge-bytecode-recompile ()
1216
"Urge the user to recompile slime.elc.
1217
Return true if we have been given permission to continue."
1218
(cond ((featurep 'xemacs)
1219
;; My XEmacs crashes and burns if I recompile/reload an elisp
1220
;; file from itself. So they have to do it themself.
1221
(or (y-or-n-p "slime.elc is older than source. Continue? ")
1222
(signal 'quit nil)))
1223
((y-or-n-p "slime.elc is older than source. Recompile first? ")
1224
(slime-recompile-bytecode))
1225
(t)))
1226
1227
(defun slime-abort-connection ()
1228
"Abort connection the current connection attempt."
1229
(interactive)
1230
(cond (slime-connect-retry-timer
1231
(slime-cancel-connect-retry-timer)
1232
(message "Cancelled connection attempt."))
1233
(t (error "Not connecting"))))
1234
1235
;;; Starting the inferior Lisp and loading Swank:
1236
1237
(defun slime-maybe-start-lisp (program program-args env directory buffer)
1238
"Return a new or existing inferior lisp process."
1239
(cond ((not (comint-check-proc buffer))
1240
(slime-start-lisp program program-args env directory buffer))
1241
((slime-reinitialize-inferior-lisp-p program program-args env buffer)
1242
(when-let (conn (find (get-buffer-process buffer) slime-net-processes
1243
:key #'slime-inferior-process))
1244
(slime-net-close conn))
1245
(get-buffer-process buffer))
1246
(t (slime-start-lisp program program-args env directory
1247
(generate-new-buffer-name buffer)))))
1248
1249
(defun slime-reinitialize-inferior-lisp-p (program program-args env buffer)
1250
(let ((args (slime-inferior-lisp-args (get-buffer-process buffer))))
1251
(and (equal (plist-get args :program) program)
1252
(equal (plist-get args :program-args) program-args)
1253
(equal (plist-get args :env) env)
1254
(not (y-or-n-p "Create an additional *inferior-lisp*? ")))))
1255
1256
(defvar slime-inferior-process-start-hook nil
1257
"Hook called whenever a new process gets started.")
1258
1259
(defun slime-start-lisp (program program-args env directory buffer)
1260
"Does the same as `inferior-lisp' but less ugly.
1261
Return the created process."
1262
(with-current-buffer (get-buffer-create buffer)
1263
(when directory
1264
(cd (expand-file-name directory)))
1265
(comint-mode)
1266
(let ((process-environment (append env process-environment))
1267
(process-connection-type nil))
1268
(comint-exec (current-buffer) "inferior-lisp" program nil program-args))
1269
(lisp-mode-variables t)
1270
(let ((proc (get-buffer-process (current-buffer))))
1271
(slime-set-query-on-exit-flag proc)
1272
(run-hooks 'slime-inferior-process-start-hook)
1273
proc)))
1274
1275
(defun slime-inferior-connect (process args)
1276
"Start a Swank server in the inferior Lisp and connect."
1277
(slime-delete-swank-port-file 'quiet)
1278
(slime-start-swank-server process args)
1279
(slime-read-port-and-connect process nil))
1280
1281
(defvar slime-inferior-lisp-args nil
1282
"A buffer local variable in the inferior proccess.
1283
See `slime-start'.")
1284
1285
(defun slime-start-swank-server (process args)
1286
"Start a Swank server on the inferior lisp."
1287
(destructuring-bind (&key coding-system init &allow-other-keys) args
1288
(with-current-buffer (process-buffer process)
1289
(make-local-variable 'slime-inferior-lisp-args)
1290
(setq slime-inferior-lisp-args args)
1291
(let ((str (funcall init (slime-swank-port-file) coding-system)))
1292
(goto-char (process-mark process))
1293
(insert-before-markers str)
1294
(process-send-string process str)))))
1295
1296
(defun slime-inferior-lisp-args (process)
1297
"Return the initial process arguments.
1298
See `slime-start'."
1299
(with-current-buffer (process-buffer process)
1300
slime-inferior-lisp-args))
1301
1302
;; XXX load-server & start-server used to be separated. maybe that was better.
1303
(defun slime-init-command (port-filename coding-system)
1304
"Return a string to initialize Lisp."
1305
(let ((loader (if (file-name-absolute-p slime-backend)
1306
slime-backend
1307
(concat slime-path slime-backend)))
1308
(encoding (slime-coding-system-cl-name coding-system)))
1309
;; Return a single form to avoid problems with buffered input.
1310
(format "%S\n\n"
1311
`(progn
1312
(load ,(slime-to-lisp-filename (expand-file-name loader))
1313
:verbose t)
1314
(funcall (read-from-string "swank-loader:init"))
1315
(funcall (read-from-string "swank:start-server")
1316
,(slime-to-lisp-filename port-filename)
1317
:coding-system ,encoding)))))
1318
1319
(defun slime-swank-port-file ()
1320
"Filename where the SWANK server writes its TCP port number."
1321
(concat (file-name-as-directory (slime-temp-directory))
1322
(format "slime.%S" (emacs-pid))))
1323
1324
(defun slime-temp-directory ()
1325
(cond ((fboundp 'temp-directory) (temp-directory))
1326
((boundp 'temporary-file-directory) temporary-file-directory)
1327
(t "/tmp/")))
1328
1329
(defun slime-delete-swank-port-file (&optional quiet)
1330
(condition-case data
1331
(delete-file (slime-swank-port-file))
1332
(error
1333
(ecase quiet
1334
((nil) (signal (car data) (cdr data)))
1335
(quiet)
1336
(message (message "Unable to delete swank port file %S"
1337
(slime-swank-port-file)))))))
1338
1339
(defun slime-read-port-and-connect (inferior-process retries)
1340
(slime-cancel-connect-retry-timer)
1341
(slime-attempt-connection inferior-process retries 1))
1342
1343
(defun slime-attempt-connection (process retries attempt)
1344
;; A small one-state machine to attempt a connection with
1345
;; timer-based retries.
1346
(let ((file (slime-swank-port-file)))
1347
(unless (active-minibuffer-window)
1348
(message "Polling %S.. (Abort with `M-x slime-abort-connection'.)" file))
1349
(cond ((and (file-exists-p file)
1350
(> (nth 7 (file-attributes file)) 0)) ; file size
1351
(slime-cancel-connect-retry-timer)
1352
(let ((port (slime-read-swank-port))
1353
(args (slime-inferior-lisp-args process)))
1354
(slime-delete-swank-port-file 'message)
1355
(let ((c (slime-connect slime-lisp-host port
1356
(plist-get args :coding-system))))
1357
(slime-set-inferior-process c process))))
1358
((and retries (zerop retries))
1359
(slime-cancel-connect-retry-timer)
1360
(message "Gave up connecting to Swank after %d attempts." attempt))
1361
((eq (process-status process) 'exit)
1362
(slime-cancel-connect-retry-timer)
1363
(message "Failed to connect to Swank: inferior process exited."))
1364
(t
1365
(when (and (file-exists-p file)
1366
(zerop (nth 7 (file-attributes file))))
1367
(message "(Zero length port file)")
1368
;; the file may be in the filesystem but not yet written
1369
(unless retries (setq retries 3)))
1370
(unless slime-connect-retry-timer
1371
(setq slime-connect-retry-timer
1372
(run-with-timer
1373
0.3 0.3
1374
#'slime-timer-call #'slime-attempt-connection
1375
process (and retries (1- retries))
1376
(1+ attempt))))))))
1377
1378
(defun slime-timer-call (fun &rest args)
1379
"Call function FUN with ARGS, reporting all errors.
1380
1381
The default condition handler for timer functions (see
1382
`timer-event-handler') ignores errors."
1383
(condition-case data
1384
(apply fun args)
1385
(error (debug nil (list "Error in timer" fun args data)))))
1386
1387
(defun slime-cancel-connect-retry-timer ()
1388
(when slime-connect-retry-timer
1389
(cancel-timer slime-connect-retry-timer)
1390
(setq slime-connect-retry-timer nil)))
1391
1392
(defun slime-read-swank-port ()
1393
"Read the Swank server port number from the `slime-swank-port-file'."
1394
(save-excursion
1395
(with-temp-buffer
1396
(insert-file-contents (slime-swank-port-file))
1397
(goto-char (point-min))
1398
(let ((port (read (current-buffer))))
1399
(assert (integerp port))
1400
port))))
1401
1402
(defun slime-toggle-debug-on-swank-error ()
1403
(interactive)
1404
(if (slime-eval `(swank:toggle-debug-on-swank-error))
1405
(message "Debug on SWANK error enabled.")
1406
(message "Debug on SWANK error disabled.")))
1407
1408
;;; Words of encouragement
1409
1410
(defun slime-user-first-name ()
1411
(let ((name (if (string= (user-full-name) "")
1412
(user-login-name)
1413
(user-full-name))))
1414
(string-match "^[^ ]*" name)
1415
(capitalize (match-string 0 name))))
1416
1417
(defvar slime-words-of-encouragement
1418
`("Let the hacking commence!"
1419
"Hacks and glory await!"
1420
"Hack and be merry!"
1421
"Your hacking starts... NOW!"
1422
"May the source be with you!"
1423
"Take this REPL, brother, and may it serve you well."
1424
"Lemonodor-fame is but a hack away!"
1425
,(format "%s, this could be the start of a beautiful program."
1426
(slime-user-first-name)))
1427
"Scientifically-proven optimal words of hackerish encouragement.")
1428
1429
(defun slime-random-words-of-encouragement ()
1430
"Return a string of hackerish encouragement."
1431
(eval (nth (random (length slime-words-of-encouragement))
1432
slime-words-of-encouragement)))
1433
1434
1435
;;;; Networking
1436
;;;
1437
;;; This section covers the low-level networking: establishing
1438
;;; connections and encoding/decoding protocol messages.
1439
;;;
1440
;;; Each SLIME protocol message beings with a 3-byte length header
1441
;;; followed by an S-expression as text. The sexp must be readable
1442
;;; both by Emacs and by Common Lisp, so if it contains any embedded
1443
;;; code fragments they should be sent as strings.
1444
;;;
1445
;;; The set of meaningful protocol messages are not specified
1446
;;; here. They are defined elsewhere by the event-dispatching
1447
;;; functions in this file and in swank.lisp.
1448
1449
(defvar slime-net-processes nil
1450
"List of processes (sockets) connected to Lisps.")
1451
1452
(defvar slime-net-process-close-hooks '()
1453
"List of functions called when a slime network connection closes.
1454
The functions are called with the process as their argument.")
1455
1456
(defun slime-secret ()
1457
"Find the magic secret from the user's home directory.
1458
Return nil if the file doesn't exist or is empty; otherwise the
1459
first line of the file."
1460
(condition-case err
1461
(with-temp-buffer
1462
(insert-file-contents "~/.slime-secret")
1463
(goto-char (point-min))
1464
(buffer-substring (point-min) (line-end-position)))
1465
(file-error nil)))
1466
1467
;;; Interface
1468
(defun slime-net-connect (host port coding-system)
1469
"Establish a connection with a CL."
1470
(let* ((inhibit-quit nil)
1471
(proc (open-network-stream "SLIME Lisp" nil host port))
1472
(buffer (slime-make-net-buffer " *cl-connection*")))
1473
(push proc slime-net-processes)
1474
(set-process-buffer proc buffer)
1475
(set-process-filter proc 'slime-net-filter)
1476
(set-process-sentinel proc 'slime-net-sentinel)
1477
(slime-set-query-on-exit-flag proc)
1478
(when (fboundp 'set-process-coding-system)
1479
(slime-check-coding-system coding-system)
1480
(set-process-coding-system proc coding-system coding-system))
1481
(when-let (secret (slime-secret))
1482
(slime-net-send secret proc))
1483
proc))
1484
1485
(defun slime-make-net-buffer (name)
1486
"Make a buffer suitable for a network process."
1487
(let ((buffer (generate-new-buffer name)))
1488
(with-current-buffer buffer
1489
(buffer-disable-undo)
1490
(set (make-local-variable 'kill-buffer-query-functions) nil))
1491
buffer))
1492
1493
(defun slime-set-query-on-exit-flag (process)
1494
"Set PROCESS's query-on-exit-flag to `slime-kill-without-query-p'."
1495
(when slime-kill-without-query-p
1496
;; avoid byte-compiler warnings
1497
(let ((fun (if (fboundp 'set-process-query-on-exit-flag)
1498
'set-process-query-on-exit-flag
1499
'process-kill-without-query)))
1500
(funcall fun process nil))))
1501
1502
;;;;; Coding system madness
1503
1504
(defun slime-check-coding-system (coding-system)
1505
"Signal an error if CODING-SYSTEM isn't a valid coding system."
1506
(interactive)
1507
(let ((props (slime-find-coding-system coding-system)))
1508
(unless props
1509
(error "Invalid slime-net-coding-system: %s. %s"
1510
coding-system (mapcar #'car slime-net-valid-coding-systems)))
1511
(when (and (second props) (boundp 'default-enable-multibyte-characters))
1512
(assert default-enable-multibyte-characters))
1513
t))
1514
1515
(defun slime-coding-system-mulibyte-p (coding-system)
1516
(second (slime-find-coding-system coding-system)))
1517
1518
(defun slime-coding-system-cl-name (coding-system)
1519
(third (slime-find-coding-system coding-system)))
1520
1521
;;; Interface
1522
(defun slime-net-send (sexp proc)
1523
"Send a SEXP to Lisp over the socket PROC.
1524
This is the lowest level of communication. The sexp will be READ and
1525
EVAL'd by Lisp."
1526
(let* ((msg (concat (slime-prin1-to-string sexp) "\n"))
1527
(string (concat (slime-net-encode-length (length msg)) msg))
1528
(coding-system (cdr (process-coding-system proc))))
1529
(slime-log-event sexp)
1530
(cond ((slime-safe-encoding-p coding-system string)
1531
(process-send-string proc string))
1532
(t (error "Coding system %s not suitable for %S"
1533
coding-system string)))))
1534
1535
(defun slime-safe-encoding-p (coding-system string)
1536
"Return true iff CODING-SYSTEM can safely encode STRING."
1537
(if (featurep 'xemacs)
1538
;; FIXME: XEmacs encodes non-encodeable chars as ?~ automatically
1539
t
1540
(or (let ((candidates (find-coding-systems-string string))
1541
(base (coding-system-base coding-system)))
1542
(or (equal candidates '(undecided))
1543
(memq base candidates)))
1544
(and (not (multibyte-string-p string))
1545
(not (slime-coding-system-mulibyte-p coding-system))))))
1546
1547
(defun slime-net-close (process &optional debug)
1548
(setq slime-net-processes (remove process slime-net-processes))
1549
(when (eq process slime-default-connection)
1550
(setq slime-default-connection nil))
1551
(cond (debug
1552
(set-process-sentinel process 'ignore)
1553
(set-process-filter process 'ignore)
1554
(delete-process process))
1555
(t
1556
(run-hook-with-args 'slime-net-process-close-hooks process)
1557
;; killing the buffer also closes the socket
1558
(kill-buffer (process-buffer process)))))
1559
1560
(defun slime-net-sentinel (process message)
1561
(message "Lisp connection closed unexpectedly: %s" message)
1562
(slime-net-close process))
1563
1564
;;; Socket input is handled by `slime-net-filter', which decodes any
1565
;;; complete messages and hands them off to the event dispatcher.
1566
1567
(defun slime-net-filter (process string)
1568
"Accept output from the socket and process all complete messages."
1569
(with-current-buffer (process-buffer process)
1570
(goto-char (point-max))
1571
(insert string))
1572
(slime-process-available-input process))
1573
1574
(defun slime-process-available-input (process)
1575
"Process all complete messages that have arrived from Lisp."
1576
(with-current-buffer (process-buffer process)
1577
(while (slime-net-have-input-p)
1578
(let ((event (slime-net-read-or-lose process))
1579
(ok nil))
1580
(slime-log-event event)
1581
(unwind-protect
1582
(save-current-buffer
1583
(slime-dispatch-event event process)
1584
(setq ok t))
1585
(unless ok
1586
(slime-run-when-idle 'slime-process-available-input process)))))))
1587
1588
(defun slime-net-have-input-p ()
1589
"Return true if a complete message is available."
1590
(goto-char (point-min))
1591
(and (>= (buffer-size) 6)
1592
(>= (- (buffer-size) 6) (slime-net-decode-length))))
1593
1594
(defun slime-run-when-idle (function &rest args)
1595
"Call FUNCTION as soon as Emacs is idle."
1596
(apply #'run-at-time
1597
(if (featurep 'xemacs) itimer-short-interval 0)
1598
nil function args))
1599
1600
(defun slime-net-read-or-lose (process)
1601
(condition-case error
1602
(slime-net-read)
1603
(error
1604
(debug 'error error)
1605
(slime-net-close process t)
1606
(error "net-read error: %S" error))))
1607
1608
(defun slime-net-read ()
1609
"Read a message from the network buffer."
1610
(goto-char (point-min))
1611
(let* ((length (slime-net-decode-length))
1612
(start (+ 6 (point)))
1613
(end (+ start length)))
1614
(assert (plusp length))
1615
(prog1 (save-restriction
1616
(narrow-to-region start end)
1617
(read (current-buffer)))
1618
(delete-region (point-min) end))))
1619
1620
(defun slime-net-decode-length ()
1621
"Read a 24-bit hex-encoded integer from buffer."
1622
(string-to-number (buffer-substring-no-properties (point) (+ (point) 6)) 16))
1623
1624
(defun slime-net-encode-length (n)
1625
"Encode an integer into a 24-bit hex string."
1626
(format "%06x" n))
1627
1628
(defun slime-prin1-to-string (sexp)
1629
"Like `prin1-to-string' but don't octal-escape non-ascii characters.
1630
This is more compatible with the CL reader."
1631
(with-temp-buffer
1632
(let (print-escape-nonascii
1633
print-escape-newlines
1634
print-length
1635
print-level)
1636
(prin1 sexp (current-buffer))
1637
(buffer-string))))
1638
1639
1640
;;;; Connections
1641
;;;
1642
;;; "Connections" are the high-level Emacs<->Lisp networking concept.
1643
;;;
1644
;;; Emacs has a connection to each Lisp process that it's interacting
1645
;;; with. Typically there would only be one, but a user can choose to
1646
;;; connect to many Lisps simultaneously.
1647
;;;
1648
;;; A connection consists of a control socket, optionally an extra
1649
;;; socket dedicated to receiving Lisp output (an optimization), and a
1650
;;; set of connection-local state variables.
1651
;;;
1652
;;; The state variables are stored as buffer-local variables in the
1653
;;; control socket's process-buffer and are used via accessor
1654
;;; functions. These variables include things like the *FEATURES* list
1655
;;; and Unix Pid of the Lisp process.
1656
;;;
1657
;;; One connection is "current" at any given time. This is:
1658
;;; `slime-dispatching-connection' if dynamically bound, or
1659
;;; `slime-buffer-connection' if this is set buffer-local, or
1660
;;; `slime-default-connection' otherwise.
1661
;;;
1662
;;; When you're invoking commands in your source files you'll be using
1663
;;; `slime-default-connection'. This connection can be interactively
1664
;;; reassigned via the connection-list buffer.
1665
;;;
1666
;;; When a command creates a new buffer it will set
1667
;;; `slime-buffer-connection' so that commands in the new buffer will
1668
;;; use the connection that the buffer originated from. For example,
1669
;;; the apropos command creates the *Apropos* buffer and any command
1670
;;; in that buffer (e.g. `M-.') will go to the same Lisp that did the
1671
;;; apropos search. REPL buffers are similarly tied to their
1672
;;; respective connections.
1673
;;;
1674
;;; When Emacs is dispatching some network message that arrived from a
1675
;;; connection it will dynamically bind `slime-dispatching-connection'
1676
;;; so that the event will be processed in the context of that
1677
;;; connection.
1678
;;;
1679
;;; This is mostly transparent. The user should be aware that he can
1680
;;; set the default connection to pick which Lisp handles commands in
1681
;;; Lisp-mode source buffers, and slime hackers should be aware that
1682
;;; they can tie a buffer to a specific connection. The rest takes
1683
;;; care of itself.
1684
1685
(defvar slime-dispatching-connection nil
1686
"Network process currently executing.
1687
This is dynamically bound while handling messages from Lisp; it
1688
overrides `slime-buffer-connection' and `slime-default-connection'.")
1689
1690
(make-variable-buffer-local
1691
(defvar slime-buffer-connection nil
1692
"Network connection to use in the current buffer.
1693
This overrides `slime-default-connection'."))
1694
1695
(defvar slime-default-connection nil
1696
"Network connection to use by default.
1697
Used for all Lisp communication, except when overridden by
1698
`slime-dispatching-connection' or `slime-buffer-connection'.")
1699
1700
(defun slime-current-connection ()
1701
"Return the connection to use for Lisp interaction.
1702
Return nil if there's no connection."
1703
(or slime-dispatching-connection
1704
slime-buffer-connection
1705
slime-default-connection))
1706
1707
(defun slime-connection ()
1708
"Return the connection to use for Lisp interaction.
1709
Signal an error if there's no connection."
1710
(let ((conn (slime-current-connection)))
1711
(cond ((and (not conn) slime-net-processes)
1712
(or (slime-auto-select-connection)
1713
(error "No default connection selected.")))
1714
((not conn)
1715
(or (slime-auto-connect)
1716
(error "Not connected.")))
1717
((not (eq (process-status conn) 'open))
1718
(error "Connection closed."))
1719
(t conn))))
1720
1721
;; FIXME: should be called auto-start
1722
(defcustom slime-auto-connect 'never
1723
"Controls auto connection when information from lisp process is needed.
1724
This doesn't mean it will connect right after Slime is loaded."
1725
:group 'slime-mode
1726
:type '(choice (const never)
1727
(const always)
1728
(const ask)))
1729
1730
(defun slime-auto-connect ()
1731
(cond ((or (eq slime-auto-connect 'always)
1732
(and (eq slime-auto-connect 'ask)
1733
(y-or-n-p "No connection. Start Slime? ")))
1734
(save-window-excursion
1735
(slime)
1736
(while (not (slime-current-connection))
1737
(sleep-for 1))
1738
(slime-connection)))
1739
(t nil)))
1740
1741
(defcustom slime-auto-select-connection 'ask
1742
"Controls auto selection after the default connection was closed."
1743
:group 'slime-mode
1744
:type '(choice (const never)
1745
(const always)
1746
(const ask)))
1747
1748
(defun slime-auto-select-connection ()
1749
(let* ((c0 (car slime-net-processes))
1750
(c (cond ((eq slime-auto-select-connection 'always) c0)
1751
((and (eq slime-auto-select-connection 'ask)
1752
(y-or-n-p
1753
(format "No default connection selected. %s %s? "
1754
"Switch to" (slime-connection-name c0))))
1755
c0))))
1756
(when c
1757
(slime-select-connection c)
1758
(message "Switching to connection: %s" (slime-connection-name c))
1759
c)))
1760
1761
(defun slime-select-connection (process)
1762
"Make PROCESS the default connection."
1763
(setq slime-default-connection process))
1764
1765
(defun slime-cycle-connections ()
1766
"Change current slime connection, cycling through all connections."
1767
(interactive)
1768
(let* ((tail (or (cdr (member (slime-current-connection)
1769
slime-net-processes))
1770
slime-net-processes))
1771
(p (car tail)))
1772
(slime-select-connection p)
1773
(message "Lisp: %s %s" (slime-connection-name p) (process-contact p))))
1774
1775
(defmacro* slime-with-connection-buffer ((&optional process) &rest body)
1776
"Execute BODY in the process-buffer of PROCESS.
1777
If PROCESS is not specified, `slime-connection' is used.
1778
1779
\(fn (&optional PROCESS) &body BODY))"
1780
`(with-current-buffer
1781
(process-buffer (or ,process (slime-connection)
1782
(error "No connection")))
1783
,@body))
1784
1785
(put 'slime-with-connection-buffer 'lisp-indent-function 1)
1786
1787
;;; Connection-local variables:
1788
1789
(defmacro slime-def-connection-var (varname &rest initial-value-and-doc)
1790
"Define a connection-local variable.
1791
The value of the variable can be read by calling the function of the
1792
same name (it must not be accessed directly). The accessor function is
1793
setf-able.
1794
1795
The actual variable bindings are stored buffer-local in the
1796
process-buffers of connections. The accessor function refers to
1797
the binding for `slime-connection'."
1798
(let ((real-var (intern (format "%s:connlocal" varname))))
1799
`(progn
1800
;; Variable
1801
(make-variable-buffer-local
1802
(defvar ,real-var ,@initial-value-and-doc))
1803
;; Accessor
1804
(defun ,varname (&optional process)
1805
(slime-with-connection-buffer (process) ,real-var))
1806
;; Setf
1807
(defsetf ,varname (&optional process) (store)
1808
`(slime-with-connection-buffer (,process)
1809
(setq (\, (quote (\, real-var))) (\, store))
1810
(\, store)))
1811
'(\, varname))))
1812
1813
(put 'slime-def-connection-var 'lisp-indent-function 2)
1814
(put 'slime-indulge-pretty-colors 'slime-def-connection-var t)
1815
1816
(slime-def-connection-var slime-connection-number nil
1817
"Serial number of a connection.
1818
Bound in the connection's process-buffer.")
1819
1820
(slime-def-connection-var slime-lisp-features '()
1821
"The symbol-names of Lisp's *FEATURES*.
1822
This is automatically synchronized from Lisp.")
1823
1824
(slime-def-connection-var slime-lisp-modules '()
1825
"The strings of Lisp's *MODULES*.")
1826
1827
(slime-def-connection-var slime-pid nil
1828
"The process id of the Lisp process.")
1829
1830
(slime-def-connection-var slime-lisp-implementation-type nil
1831
"The implementation type of the Lisp process.")
1832
1833
(slime-def-connection-var slime-lisp-implementation-version nil
1834
"The implementation type of the Lisp process.")
1835
1836
(slime-def-connection-var slime-lisp-implementation-name nil
1837
"The short name for the Lisp implementation.")
1838
1839
(slime-def-connection-var slime-lisp-implementation-program nil
1840
"The argv[0] of the process running the Lisp implementation.")
1841
1842
(slime-def-connection-var slime-connection-name nil
1843
"The short name for connection.")
1844
1845
(slime-def-connection-var slime-inferior-process nil
1846
"The inferior process for the connection if any.")
1847
1848
(slime-def-connection-var slime-communication-style nil
1849
"The communication style.")
1850
1851
(slime-def-connection-var slime-machine-instance nil
1852
"The name of the (remote) machine running the Lisp process.")
1853
1854
;;;;; Connection setup
1855
1856
(defvar slime-connection-counter 0
1857
"The number of SLIME connections made. For generating serial numbers.")
1858
1859
;;; Interface
1860
(defun slime-setup-connection (process)
1861
"Make a connection out of PROCESS."
1862
(let ((slime-dispatching-connection process))
1863
(slime-init-connection-state process)
1864
(slime-select-connection process)
1865
process))
1866
1867
(defun slime-init-connection-state (proc)
1868
"Initialize connection state in the process-buffer of PROC."
1869
;; To make life simpler for the user: if this is the only open
1870
;; connection then reset the connection counter.
1871
(when (equal slime-net-processes (list proc))
1872
(setq slime-connection-counter 0))
1873
(slime-with-connection-buffer ()
1874
(setq slime-buffer-connection proc))
1875
(setf (slime-connection-number proc) (incf slime-connection-counter))
1876
;; We do the rest of our initialization asynchronously. The current
1877
;; function may be called from a timer, and if we setup the REPL
1878
;; from a timer then it mysteriously uses the wrong keymap for the
1879
;; first command.
1880
(let ((slime-current-thread t))
1881
(slime-eval-async '(swank:connection-info)
1882
(slime-curry #'slime-set-connection-info proc))))
1883
1884
(defun slime-set-connection-info (connection info)
1885
"Initialize CONNECTION with INFO received from Lisp."
1886
(let ((slime-dispatching-connection connection)
1887
(slime-current-thread t))
1888
(destructuring-bind (&key pid style lisp-implementation machine
1889
features package version modules
1890
&allow-other-keys) info
1891
(slime-check-version version connection)
1892
(setf (slime-pid) pid
1893
(slime-communication-style) style
1894
(slime-lisp-features) features
1895
(slime-lisp-modules) modules)
1896
(destructuring-bind (&key type name version program) lisp-implementation
1897
(setf (slime-lisp-implementation-type) type
1898
(slime-lisp-implementation-version) version
1899
(slime-lisp-implementation-name) name
1900
(slime-lisp-implementation-program) program
1901
(slime-connection-name) (slime-generate-connection-name name)))
1902
(destructuring-bind (&key instance type version) machine
1903
(setf (slime-machine-instance) instance)))
1904
(let ((args (when-let (p (slime-inferior-process))
1905
(slime-inferior-lisp-args p))))
1906
(when-let (name (plist-get args ':name))
1907
(unless (string= (slime-lisp-implementation-name) name)
1908
(setf (slime-connection-name)
1909
(slime-generate-connection-name (symbol-name name)))))
1910
(slime-load-contribs)
1911
(run-hooks 'slime-connected-hook)
1912
(when-let (fun (plist-get args ':init-function))
1913
(funcall fun)))
1914
(message "Connected. %s" (slime-random-words-of-encouragement))))
1915
1916
(defun slime-check-version (version conn)
1917
(or (equal version slime-protocol-version)
1918
(equal slime-protocol-version 'ignore)
1919
(y-or-n-p
1920
(format "Versions differ: %s (slime) vs. %s (swank). Continue? "
1921
slime-protocol-version version))
1922
(slime-net-close conn)
1923
(top-level)))
1924
1925
(defun slime-generate-connection-name (lisp-name)
1926
(loop for i from 1
1927
for name = lisp-name then (format "%s<%d>" lisp-name i)
1928
while (find name slime-net-processes
1929
:key #'slime-connection-name :test #'equal)
1930
finally (return name)))
1931
1932
(defun slime-connection-close-hook (process)
1933
(when (eq process slime-default-connection)
1934
(when slime-net-processes
1935
(slime-select-connection (car slime-net-processes))
1936
(message "Default connection closed; switched to #%S (%S)"
1937
(slime-connection-number)
1938
(slime-connection-name)))))
1939
1940
(add-hook 'slime-net-process-close-hooks 'slime-connection-close-hook)
1941
1942
;;;;; Commands on connections
1943
1944
(defun slime-disconnect ()
1945
"Close the current connection."
1946
(interactive)
1947
(slime-net-close (slime-connection)))
1948
1949
(defun slime-disconnect-all ()
1950
"Disconnect all connections."
1951
(interactive)
1952
(mapc #'slime-net-close slime-net-processes))
1953
1954
(defun slime-connection-port (connection)
1955
"Return the remote port number of CONNECTION."
1956
(if (featurep 'xemacs)
1957
(car (process-id connection))
1958
(cadr (process-contact connection))))
1959
1960
(defun slime-process (&optional connection)
1961
"Return the Lisp process for CONNECTION (default `slime-connection').
1962
Return nil if there's no process object for the connection."
1963
(let ((proc (slime-inferior-process connection)))
1964
(if (and proc
1965
(memq (process-status proc) '(run stop)))
1966
proc)))
1967
1968
;; Non-macro version to keep the file byte-compilable.
1969
(defun slime-set-inferior-process (connection process)
1970
(setf (slime-inferior-process connection) process))
1971
1972
(defun slime-use-sigint-for-interrupt (&optional connection)
1973
(let ((c (or connection (slime-connection))))
1974
(ecase (slime-communication-style c)
1975
((:fd-handler nil) t)
1976
((:spawn :sigio) nil))))
1977
1978
(defvar slime-inhibit-pipelining t
1979
"*If true, don't send background requests if Lisp is already busy.")
1980
1981
(defun slime-background-activities-enabled-p ()
1982
(and (let ((con (slime-current-connection)))
1983
(and con
1984
(eq (process-status con) 'open)))
1985
(or (not (slime-busy-p))
1986
(not slime-inhibit-pipelining))))
1987
1988
1989
;;;; Communication protocol
1990
1991
;;;;; Emacs Lisp programming interface
1992
;;;
1993
;;; The programming interface for writing Emacs commands is based on
1994
;;; remote procedure calls (RPCs). The basic operation is to ask Lisp
1995
;;; to apply a named Lisp function to some arguments, then to do
1996
;;; something with the result.
1997
;;;
1998
;;; Requests can be either synchronous (blocking) or asynchronous
1999
;;; (with the result passed to a callback/continuation function). If
2000
;;; an error occurs during the request then the debugger is entered
2001
;;; before the result arrives -- for synchronous evaluations this
2002
;;; requires a recursive edit.
2003
;;;
2004
;;; You should use asynchronous evaluations (`slime-eval-async') for
2005
;;; most things. Reserve synchronous evaluations (`slime-eval') for
2006
;;; the cases where blocking Emacs is really appropriate (like
2007
;;; completion) and that shouldn't trigger errors (e.g. not evaluate
2008
;;; user-entered code).
2009
;;;
2010
;;; We have the concept of the "current Lisp package". RPC requests
2011
;;; always say what package the user is making them from and the Lisp
2012
;;; side binds that package to *BUFFER-PACKAGE* to use as it sees
2013
;;; fit. The current package is defined as the buffer-local value of
2014
;;; `slime-buffer-package' if set, and otherwise the package named by
2015
;;; the nearest IN-PACKAGE as found by text search (first backwards,
2016
;;; then forwards).
2017
;;;
2018
;;; Similarly we have the concept of the current thread, i.e. which
2019
;;; thread in the Lisp process should handle the request. The current
2020
;;; thread is determined solely by the buffer-local value of
2021
;;; `slime-current-thread'. This is usually bound to t meaning "no
2022
;;; particular thread", but can also be used to nominate a specific
2023
;;; thread. The REPL and the debugger both use this feature to deal
2024
;;; with specific threads.
2025
2026
(make-variable-buffer-local
2027
(defvar slime-current-thread t
2028
"The id of the current thread on the Lisp side.
2029
t means the \"current\" thread;
2030
:repl-thread the thread that executes REPL requests;
2031
fixnum a specific thread."))
2032
2033
(make-variable-buffer-local
2034
(defvar slime-buffer-package nil
2035
"The Lisp package associated with the current buffer.
2036
This is set only in buffers bound to specific packages."))
2037
2038
;;; `slime-rex' is the RPC primitive which is used to implement both
2039
;;; `slime-eval' and `slime-eval-async'. You can use it directly if
2040
;;; you need to, but the others are usually more convenient.
2041
2042
(defmacro* slime-rex ((&rest saved-vars)
2043
(sexp &optional
2044
(package '(slime-current-package))
2045
(thread 'slime-current-thread))
2046
&rest continuations)
2047
"(slime-rex (VAR ...) (SEXP &optional PACKAGE THREAD) CLAUSES ...)
2048
2049
Remote EXecute SEXP.
2050
2051
VARs are a list of saved variables visible in the other forms. Each
2052
VAR is either a symbol or a list (VAR INIT-VALUE).
2053
2054
SEXP is evaluated and the princed version is sent to Lisp.
2055
2056
PACKAGE is evaluated and Lisp binds *BUFFER-PACKAGE* to this package.
2057
The default value is (slime-current-package).
2058
2059
CLAUSES is a list of patterns with same syntax as
2060
`destructure-case'. The result of the evaluation of SEXP is
2061
dispatched on CLAUSES. The result is either a sexp of the
2062
form (:ok VALUE) or (:abort). CLAUSES is executed
2063
asynchronously.
2064
2065
Note: don't use backquote syntax for SEXP, because various Emacs
2066
versions cannot deal with that."
2067
(let ((result (gensym)))
2068
`(lexical-let ,(loop for var in saved-vars
2069
collect (etypecase var
2070
(symbol (list var var))
2071
(cons var)))
2072
(slime-dispatch-event
2073
(list :emacs-rex ,sexp ,package ,thread
2074
(lambda (,result)
2075
(destructure-case ,result
2076
,@continuations)))))))
2077
2078
(put 'slime-rex 'lisp-indent-function 2)
2079
2080
;;; Interface
2081
(defun slime-current-package ()
2082
"Return the Common Lisp package in the current context.
2083
If `slime-buffer-package' has a value then return that, otherwise
2084
search for and read an `in-package' form."
2085
(or slime-buffer-package
2086
(save-restriction
2087
(widen)
2088
(slime-find-buffer-package))))
2089
2090
(defvar slime-find-buffer-package-function 'slime-search-buffer-package
2091
"*Function to use for `slime-find-buffer-package'.
2092
The result should be the package-name (a string)
2093
or nil if nothing suitable can be found.")
2094
2095
(defun slime-find-buffer-package ()
2096
"Figure out which Lisp package the current buffer is associated with."
2097
(funcall slime-find-buffer-package-function))
2098
2099
;; When modifing this code consider cases like:
2100
;; (in-package #.*foo*)
2101
;; (in-package #:cl)
2102
;; (in-package :cl)
2103
;; (in-package "CL")
2104
;; (in-package |CL|)
2105
;; (in-package #+ansi-cl :cl #-ansi-cl 'lisp)
2106
(defun slime-search-buffer-package ()
2107
(let ((case-fold-search t)
2108
(regexp (concat "^(\\(cl:\\|common-lisp:\\)?in-package\\>[ \t']*"
2109
"\\([^)]+\\)[ \t]*)")))
2110
(save-excursion
2111
(when (or (re-search-backward regexp nil t)
2112
(re-search-forward regexp nil t))
2113
(match-string-no-properties 2)))))
2114
2115
;;; Synchronous requests are implemented in terms of asynchronous
2116
;;; ones. We make an asynchronous request with a continuation function
2117
;;; that `throw's its result up to a `catch' and then enter a loop of
2118
;;; handling I/O until that happens.
2119
2120
(defvar slime-stack-eval-tags nil
2121
"List of stack-tags of continuations waiting on the stack.")
2122
2123
(defun slime-eval (sexp &optional package)
2124
"Evaluate EXPR on the superior Lisp and return the result."
2125
(when (null package) (setq package (slime-current-package)))
2126
(let* ((tag (gensym (format "slime-result-%d-"
2127
(1+ (slime-continuation-counter)))))
2128
(slime-stack-eval-tags (cons tag slime-stack-eval-tags)))
2129
(apply
2130
#'funcall
2131
(catch tag
2132
(slime-rex (tag sexp)
2133
(sexp package)
2134
((:ok value)
2135
(unless (member tag slime-stack-eval-tags)
2136
(error "Reply to canceled synchronous eval request tag=%S sexp=%S"
2137
tag sexp))
2138
(throw tag (list #'identity value)))
2139
((:abort)
2140
(throw tag (list #'error "Synchronous Lisp Evaluation aborted"))))
2141
(let ((debug-on-quit t)
2142
(inhibit-quit nil)
2143
(conn (slime-connection)))
2144
(while t
2145
(unless (eq (process-status conn) 'open)
2146
(error "Lisp connection closed unexpectedly"))
2147
(slime-accept-process-output nil 0.01)))))))
2148
2149
(defun slime-eval-async (sexp &optional cont package)
2150
"Evaluate EXPR on the superior Lisp and call CONT with the result."
2151
(slime-rex (cont (buffer (current-buffer)))
2152
(sexp (or package (slime-current-package)))
2153
((:ok result)
2154
(when cont
2155
(set-buffer buffer)
2156
(funcall cont result)))
2157
((:abort)
2158
(message "Evaluation aborted.")))
2159
;; Guard against arbitrary return values which once upon a time
2160
;; showed up in the minibuffer spuriously (due to a bug in
2161
;; slime-autodoc.) If this ever happens again, returning the
2162
;; following will make debugging much easier:
2163
:slime-eval-async)
2164
2165
(put 'slime-eval-async 'lisp-indent-function 1)
2166
2167
;;; These functions can be handy too:
2168
2169
(defun slime-connected-p ()
2170
"Return true if the Swank connection is open."
2171
(not (null slime-net-processes)))
2172
2173
(defun slime-check-connected ()
2174
"Signal an error if we are not connected to Lisp."
2175
(unless (slime-connected-p)
2176
(error "Not connected. Use `%s' to start a Lisp."
2177
(substitute-command-keys "\\[slime]"))))
2178
2179
;; UNUSED
2180
(defun slime-debugged-connection-p (conn)
2181
;; This previously was (AND (SLDB-DEBUGGED-CONTINUATIONS CONN) T),
2182
;; but an SLDB buffer may exist without having continuations
2183
;; attached to it, e.g. the one resulting from `slime-interrupt'.
2184
(loop for b in (sldb-buffers)
2185
thereis (with-current-buffer b
2186
(eq slime-buffer-connection conn))))
2187
2188
(defun slime-busy-p (&optional conn)
2189
"True if Lisp has outstanding requests.
2190
Debugged requests are ignored."
2191
(let ((debugged (sldb-debugged-continuations (or conn (slime-connection)))))
2192
(remove-if (lambda (id)
2193
(memq id debugged))
2194
(slime-rex-continuations)
2195
:key #'car)))
2196
2197
(defun slime-sync ()
2198
"Block until the most recent request has finished."
2199
(when (slime-rex-continuations)
2200
(let ((tag (caar (slime-rex-continuations))))
2201
(while (find tag (slime-rex-continuations) :key #'car)
2202
(slime-accept-process-output nil 0.1)))))
2203
2204
(defun slime-ping ()
2205
"Check that communication works."
2206
(interactive)
2207
(message "%s" (slime-eval "PONG")))
2208
2209
;;;;; Protocol event handler (the guts)
2210
;;;
2211
;;; This is the protocol in all its glory. The input to this function
2212
;;; is a protocol event that either originates within Emacs or arrived
2213
;;; over the network from Lisp.
2214
;;;
2215
;;; Each event is a list beginning with a keyword and followed by
2216
;;; arguments. The keyword identifies the type of event. Events
2217
;;; originating from Emacs have names starting with :emacs- and events
2218
;;; from Lisp don't.
2219
2220
(slime-def-connection-var slime-rex-continuations '()
2221
"List of (ID . FUNCTION) continuations waiting for RPC results.")
2222
2223
(slime-def-connection-var slime-continuation-counter 0
2224
"Continuation serial number counter.")
2225
2226
(defvar slime-event-hooks)
2227
2228
(defun slime-dispatch-event (event &optional process)
2229
(let ((slime-dispatching-connection (or process (slime-connection))))
2230
(or (run-hook-with-args-until-success 'slime-event-hooks event)
2231
(destructure-case event
2232
((:emacs-rex form package thread continuation)
2233
(when (and (slime-use-sigint-for-interrupt) (slime-busy-p))
2234
(slime-display-oneliner "; pipelined request... %S" form))
2235
(let ((id (incf (slime-continuation-counter))))
2236
(slime-send `(:emacs-rex ,form ,package ,thread ,id))
2237
(push (cons id continuation) (slime-rex-continuations))
2238
(slime-recompute-modelines)))
2239
((:return value id)
2240
(let ((rec (assq id (slime-rex-continuations))))
2241
(cond (rec (setf (slime-rex-continuations)
2242
(remove rec (slime-rex-continuations)))
2243
(slime-recompute-modelines)
2244
(funcall (cdr rec) value))
2245
(t
2246
(error "Unexpected reply: %S %S" id value)))))
2247
((:debug-activate thread level &optional select)
2248
(assert thread)
2249
(sldb-activate thread level select))
2250
((:debug thread level condition restarts frames conts)
2251
(assert thread)
2252
(sldb-setup thread level condition restarts frames conts))
2253
((:debug-return thread level stepping)
2254
(assert thread)
2255
(sldb-exit thread level stepping))
2256
((:emacs-interrupt thread)
2257
(slime-send `(:emacs-interrupt ,thread)))
2258
((:channel-send id msg)
2259
(slime-channel-send (or (slime-find-channel id)
2260
(error "Invalid channel id: %S %S" id msg))
2261
msg))
2262
((:emacs-channel-send id msg)
2263
(slime-send `(:emacs-channel-send ,id ,msg)))
2264
((:read-from-minibuffer thread tag prompt initial-value)
2265
(slime-read-from-minibuffer-for-swank thread tag prompt initial-value))
2266
((:y-or-n-p thread tag question)
2267
(slime-y-or-n-p thread tag question))
2268
((:emacs-return-string thread tag string)
2269
(slime-send `(:emacs-return-string ,thread ,tag ,string)))
2270
((:new-features features)
2271
(setf (slime-lisp-features) features))
2272
((:indentation-update info)
2273
(slime-handle-indentation-update info))
2274
((:eval-no-wait fun args)
2275
(apply (intern fun) args))
2276
((:eval thread tag form-string)
2277
(slime-check-eval-in-emacs-enabled)
2278
(slime-eval-for-lisp thread tag form-string))
2279
((:emacs-return thread tag value)
2280
(slime-send `(:emacs-return ,thread ,tag ,value)))
2281
((:ed what)
2282
(slime-ed what))
2283
((:inspect what wait-thread wait-tag)
2284
(let ((hook (when (and wait-thread wait-tag)
2285
(lexical-let ((thread wait-thread)
2286
(tag wait-tag))
2287
(lambda ()
2288
(slime-send `(:emacs-return ,thread ,tag nil)))))))
2289
(slime-open-inspector what nil hook)))
2290
((:background-message message)
2291
(slime-background-message "%s" message))
2292
((:debug-condition thread message)
2293
(assert thread)
2294
(message "%s" message))
2295
((:ping thread tag)
2296
(slime-send `(:emacs-pong ,thread ,tag)))
2297
((:reader-error packet condition)
2298
(slime-with-popup-buffer ((slime-buffer-name :error))
2299
(princ (format "Invalid protocol message:\n%s\n\n%S"
2300
condition packet))
2301
(goto-char (point-min)))
2302
(error "Invalid protocol message"))
2303
((:invalid-rpc id message)
2304
(setf (slime-rex-continuations)
2305
(remove* id (slime-rex-continuations) :key #'car))
2306
(error "Invalid rpc: %s" message))))))
2307
2308
(defun slime-send (sexp)
2309
"Send SEXP directly over the wire on the current connection."
2310
(slime-net-send sexp (slime-connection)))
2311
2312
(defun slime-reset ()
2313
"Clear all pending continuations and erase connection buffer."
2314
(interactive)
2315
(setf (slime-rex-continuations) '())
2316
(mapc #'kill-buffer (sldb-buffers))
2317
(slime-with-connection-buffer ()
2318
(erase-buffer)))
2319
2320
(defun slime-send-sigint ()
2321
(interactive)
2322
(signal-process (slime-pid) 'SIGINT))
2323
2324
;;;;; Channels
2325
2326
;;; A channel implements a set of operations. Those operations can be
2327
;;; invoked by sending messages to the channel. Channels are used for
2328
;;; protocols which can't be expressed naturally with RPCs, e.g. for
2329
;;; streaming data over the wire.
2330
;;;
2331
;;; A channel can be "remote" or "local". Remote channels are
2332
;;; represented by integers. Local channels are structures. Messages
2333
;;; sent to a closed (remote) channel are ignored.
2334
2335
(slime-def-connection-var slime-channels '()
2336
"Alist of the form (ID . CHANNEL).")
2337
2338
(slime-def-connection-var slime-channels-counter 0
2339
"Channel serial number counter.")
2340
2341
(defstruct (slime-channel (:conc-name slime-channel.)
2342
(:constructor
2343
slime-make-channel% (operations name id plist)))
2344
operations name id plist)
2345
2346
(defun slime-make-channel (operations &optional name)
2347
(let* ((id (incf (slime-channels-counter)))
2348
(ch (slime-make-channel% operations name id nil)))
2349
(push (cons id ch) (slime-channels))
2350
ch))
2351
2352
(defun slime-close-channel (channel)
2353
(setf (slime-channel.operations channel) 'closed-channel)
2354
(let ((probe (assq (slime-channel.id channel) (slime-channels))))
2355
(cond (probe (setf (slime-channels) (delete probe (slime-channels))))
2356
(t (error "Invalid channel: %s" channel)))))
2357
2358
(defun slime-find-channel (id)
2359
(cdr (assq id (slime-channels))))
2360
2361
(defun slime-channel-send (channel message)
2362
(apply (or (gethash (car message) (slime-channel.operations channel))
2363
(error "Unsupported operation: %S %S" message channel))
2364
channel (cdr message)))
2365
2366
(defun slime-channel-put (channel prop value)
2367
(setf (slime-channel.plist channel)
2368
(plist-put (slime-channel.plist channel) prop value)))
2369
2370
(defun slime-channel-get (channel prop)
2371
(plist-get (slime-channel.plist channel) prop))
2372
2373
(eval-and-compile
2374
(defun slime-channel-method-table-name (type)
2375
(intern (format "slime-%s-channel-methods" type))))
2376
2377
(defmacro slime-define-channel-type (name)
2378
(let ((tab (slime-channel-method-table-name name)))
2379
`(progn
2380
(defvar ,tab)
2381
(setq ,tab (make-hash-table :size 10)))))
2382
2383
(put 'slime-indulge-pretty-colors 'slime-define-channel-type t)
2384
2385
(defmacro slime-define-channel-method (type method args &rest body)
2386
`(puthash ',method
2387
(lambda (self . ,args) . ,body)
2388
,(slime-channel-method-table-name type)))
2389
2390
(put 'slime-define-channel-method 'lisp-indent-function 3)
2391
(put 'slime-indulge-pretty-colors 'slime-define-channel-method t)
2392
2393
(defun slime-send-to-remote-channel (channel-id msg)
2394
(slime-dispatch-event `(:emacs-channel-send ,channel-id ,msg)))
2395
2396
;;;;; Event logging to *slime-events*
2397
;;;
2398
;;; The *slime-events* buffer logs all protocol messages for debugging
2399
;;; purposes. Optionally you can enable outline-mode in that buffer,
2400
;;; which is convenient but slows things down significantly.
2401
2402
(defvar slime-log-events t
2403
"*Log protocol events to the *slime-events* buffer.")
2404
2405
(defvar slime-outline-mode-in-events-buffer nil
2406
"*Non-nil means use outline-mode in *slime-events*.")
2407
2408
(defvar slime-event-buffer-name (slime-buffer-name :events)
2409
"The name of the slime event buffer.")
2410
2411
(defun slime-log-event (event)
2412
"Record the fact that EVENT occurred."
2413
(when slime-log-events
2414
(with-current-buffer (slime-events-buffer)
2415
;; trim?
2416
(when (> (buffer-size) 100000)
2417
(goto-char (/ (buffer-size) 2))
2418
(re-search-forward "^(" nil t)
2419
(delete-region (point-min) (point)))
2420
(goto-char (point-max))
2421
(save-excursion
2422
(slime-pprint-event event (current-buffer)))
2423
(when (and (boundp 'outline-minor-mode)
2424
outline-minor-mode)
2425
(hide-entry))
2426
(goto-char (point-max)))))
2427
2428
(defun slime-pprint-event (event buffer)
2429
"Pretty print EVENT in BUFFER with limited depth and width."
2430
(let ((print-length 20)
2431
(print-level 6)
2432
(pp-escape-newlines t))
2433
(pp event buffer)))
2434
2435
(defun slime-events-buffer ()
2436
"Return or create the event log buffer."
2437
(or (get-buffer slime-event-buffer-name)
2438
(let ((buffer (get-buffer-create slime-event-buffer-name)))
2439
(with-current-buffer buffer
2440
(buffer-disable-undo)
2441
(set (make-local-variable 'outline-regexp) "^(")
2442
(set (make-local-variable 'comment-start) ";")
2443
(set (make-local-variable 'comment-end) "")
2444
(when slime-outline-mode-in-events-buffer
2445
(outline-minor-mode)))
2446
buffer)))
2447
2448
2449
;;;;; Cleanup after a quit
2450
2451
(defun slime-restart-inferior-lisp ()
2452
"Kill and restart the Lisp subprocess."
2453
(interactive)
2454
(assert (slime-inferior-process) () "No inferior lisp process")
2455
(slime-quit-lisp-internal (slime-connection) 'slime-restart-sentinel t))
2456
2457
(defun slime-restart-sentinel (process message)
2458
"Restart the inferior lisp process.
2459
Also rearrange windows."
2460
(assert (process-status process) 'closed)
2461
(let* ((proc (slime-inferior-process process))
2462
(args (slime-inferior-lisp-args proc))
2463
(buffer (buffer-name (process-buffer proc)))
2464
(buffer-window (get-buffer-window buffer))
2465
(new-proc (slime-start-lisp (plist-get args :program)
2466
(plist-get args :program-args)
2467
(plist-get args :env)
2468
nil
2469
buffer)))
2470
(slime-net-close process)
2471
(slime-inferior-connect new-proc args)
2472
(switch-to-buffer buffer)
2473
(goto-char (point-max))))
2474
2475
;; FIXME: move to slime-repl
2476
(defun slime-kill-all-buffers ()
2477
"Kill all the slime related buffers.
2478
This is only used by the repl command sayoonara."
2479
(dolist (buf (buffer-list))
2480
(when (or (string= (buffer-name buf) slime-event-buffer-name)
2481
(string-match "^\\*inferior-lisp*" (buffer-name buf))
2482
(string-match "^\\*slime-repl .*\\*$" (buffer-name buf))
2483
(string-match "^\\*sldb .*\\*$" (buffer-name buf))
2484
(string-match "^\\*SLIME.*\\*$" (buffer-name buf)))
2485
(kill-buffer buf))))
2486
2487
2488
;;;; Compilation and the creation of compiler-note annotations
2489
2490
(defvar slime-highlight-compiler-notes t
2491
"*When non-nil annotate buffers with compilation notes etc.")
2492
2493
(defvar slime-before-compile-functions nil
2494
"A list of function called before compiling a buffer or region.
2495
The function receive two arguments: the beginning and the end of the
2496
region that will be compiled.")
2497
2498
;; FIXME: remove some of the options
2499
(defcustom slime-compilation-finished-hook 'slime-maybe-show-compilation-log
2500
"Hook called with a list of compiler notes after a compilation."
2501
:group 'slime-mode
2502
:type 'hook
2503
:options '(slime-maybe-show-compilation-log
2504
slime-create-compilation-log
2505
slime-show-compilation-log
2506
slime-maybe-list-compiler-notes
2507
slime-list-compiler-notes
2508
slime-maybe-show-xrefs-for-notes
2509
slime-goto-first-note))
2510
2511
;; FIXME: I doubt that anybody uses this directly and it seems to be
2512
;; only an ugly way to pass arguments.
2513
(defvar slime-compilation-policy nil
2514
"When non-nil compile with these optimization settings.")
2515
2516
(defun slime-compute-policy (arg)
2517
"Return the policy for the prefix argument ARG."
2518
(flet ((between (min n max)
2519
(if (< n min)
2520
min
2521
(if (> n max) max n))))
2522
(let ((n (prefix-numeric-value arg)))
2523
(cond ((not arg) slime-compilation-policy)
2524
((plusp n) `((cl:debug . ,(between 0 n 3))))
2525
((eq arg '-) `((cl:speed . 3)))
2526
(t `((cl:speed . ,(between 0 (abs n) 3))))))))
2527
2528
(defstruct (slime-compilation-result
2529
(:type list)
2530
(:conc-name slime-compilation-result.)
2531
(:constructor nil)
2532
(:copier nil))
2533
tag notes successp duration)
2534
2535
(defvar slime-last-compilation-result nil
2536
"The result of the most recently issued compilation.")
2537
2538
(defun slime-compiler-notes ()
2539
"Return all compiler notes, warnings, and errors."
2540
(slime-compilation-result.notes slime-last-compilation-result))
2541
2542
(defun slime-compile-and-load-file (&optional policy)
2543
"Compile and load the buffer's file and highlight compiler notes.
2544
2545
With (positive) prefix argument the file is compiled with maximal
2546
debug settings (`C-u'). With negative prefix argument it is compiled for
2547
speed (`M--'). If a numeric argument is passed set debug or speed settings
2548
to it depending on its sign.
2549
2550
Each source location that is the subject of a compiler note is
2551
underlined and annotated with the relevant information. The commands
2552
`slime-next-note' and `slime-previous-note' can be used to navigate
2553
between compiler notes and to display their full details."
2554
(interactive "P")
2555
(slime-compile-file t (slime-compute-policy policy)))
2556
2557
;;; FIXME: This should become a DEFCUSTOM
2558
(defvar slime-compile-file-options '()
2559
"Plist of additional options that C-c C-k should pass to Lisp.
2560
Currently only :fasl-directory is supported.")
2561
2562
(defun slime-compile-file (&optional load policy)
2563
"Compile current buffer's file and highlight resulting compiler notes.
2564
2565
See `slime-compile-and-load-file' for further details."
2566
(interactive)
2567
(unless buffer-file-name
2568
(error "Buffer %s is not associated with a file." (buffer-name)))
2569
(check-parens)
2570
(when (and (buffer-modified-p)
2571
(y-or-n-p (format "Save file %s? " (buffer-file-name))))
2572
(save-buffer))
2573
(run-hook-with-args 'slime-before-compile-functions (point-min) (point-max))
2574
(let ((file (slime-to-lisp-filename (buffer-file-name)))
2575
(options (slime-simplify-plist `(,@slime-compile-file-options
2576
:policy ,policy))))
2577
(slime-eval-async
2578
`(swank:compile-file-for-emacs ,file ,(if load t nil)
2579
. ,(slime-hack-quotes options))
2580
#'slime-compilation-finished)
2581
(message "Compiling %s..." file)))
2582
2583
(defun slime-hack-quotes (arglist)
2584
;; eval is the wrong primitive, we really want funcall
2585
(loop for arg in arglist collect `(quote ,arg)))
2586
2587
(defun slime-simplify-plist (plist)
2588
(loop for (key val) on plist by #'cddr
2589
append (cond ((null val) '())
2590
(t (list key val)))))
2591
2592
(defun slime-compile-defun (&optional raw-prefix-arg)
2593
"Compile the current toplevel form.
2594
2595
With (positive) prefix argument the form is compiled with maximal
2596
debug settings (`C-u'). With negative prefix argument it is compiled for
2597
speed (`M--'). If a numeric argument is passed set debug or speed settings
2598
to it depending on its sign."
2599
(interactive "P")
2600
(let ((slime-compilation-policy (slime-compute-policy raw-prefix-arg)))
2601
(if (use-region-p)
2602
(slime-compile-region (region-beginning) (region-end))
2603
(apply #'slime-compile-region (slime-region-for-defun-at-point)))))
2604
2605
(defun slime-compile-region (start end)
2606
"Compile the region."
2607
(interactive "r")
2608
(slime-flash-region start end)
2609
(run-hook-with-args 'slime-before-compile-functions start end)
2610
(slime-compile-string (buffer-substring-no-properties start end) start))
2611
2612
(defun slime-flash-region (start end &optional timeout)
2613
"Temporarily highlight region from START to END."
2614
(let ((overlay (make-overlay start end)))
2615
(overlay-put overlay 'face 'secondary-selection)
2616
(run-with-timer (or timeout 0.2) nil 'delete-overlay overlay)))
2617
2618
(defun slime-compile-string (string start-offset)
2619
(slime-eval-async
2620
`(swank:compile-string-for-emacs
2621
,string
2622
,(buffer-name)
2623
,start-offset
2624
,(if (buffer-file-name) (slime-to-lisp-filename (buffer-file-name)))
2625
',slime-compilation-policy)
2626
#'slime-compilation-finished))
2627
2628
(defun slime-compilation-finished (result)
2629
(with-struct (slime-compilation-result. notes duration successp) result
2630
(setf slime-last-compilation-result result)
2631
(slime-show-note-counts notes duration successp)
2632
(when slime-highlight-compiler-notes
2633
(slime-highlight-notes notes))
2634
(run-hook-with-args 'slime-compilation-finished-hook notes)))
2635
2636
(defun slime-show-note-counts (notes secs successp)
2637
(message (concat
2638
(cond (successp "Compilation finished")
2639
(t (slime-add-face 'font-lock-warning-face
2640
"Compilation failed")))
2641
(if (null notes) ". (No warnings)" ": ")
2642
(mapconcat
2643
(lambda (messages)
2644
(destructuring-bind (sev . notes) messages
2645
(let ((len (length notes)))
2646
(format "%d %s%s" len (slime-severity-label sev)
2647
(if (= len 1) "" "s")))))
2648
(sort (slime-alistify notes #'slime-note.severity #'eq)
2649
(lambda (x y) (slime-severity< (car y) (car x))))
2650
" ")
2651
(if secs (format " [%.2f secs]" secs)))))
2652
2653
(defun slime-highlight-notes (notes)
2654
"Highlight compiler notes, warnings, and errors in the buffer."
2655
(interactive (list (slime-compiler-notes)))
2656
(with-temp-message "Highlighting notes..."
2657
(save-excursion
2658
(save-restriction
2659
(widen) ; highlight notes on the whole buffer
2660
(slime-remove-old-overlays)
2661
(mapc #'slime-overlay-note (slime-merge-notes-for-display notes))))))
2662
2663
(defvar slime-note-overlays '()
2664
"List of overlays created by `slime-make-note-overlay'")
2665
2666
(defun slime-remove-old-overlays ()
2667
"Delete the existing note overlays."
2668
(mapc #'delete-overlay slime-note-overlays)
2669
(setq slime-note-overlays '()))
2670
2671
(defun slime-filter-buffers (predicate)
2672
"Return a list of where PREDICATE returns true.
2673
PREDICATE is executed in the buffer to test."
2674
(remove-if-not (lambda (%buffer)
2675
(with-current-buffer %buffer
2676
(funcall predicate)))
2677
(buffer-list)))
2678
2679
;;;;; Recompilation.
2680
2681
;; FIXME: This whole idea is questionable since it depends so
2682
;; crucially on precise source-locs.
2683
2684
(defun slime-recompile-location (location)
2685
(save-excursion
2686
(slime-goto-source-location location)
2687
(slime-compile-defun)))
2688
2689
(defun slime-recompile-locations (locations cont)
2690
(slime-eval-async
2691
`(swank:compile-multiple-strings-for-emacs
2692
',(loop for loc in locations collect
2693
(save-excursion
2694
(slime-goto-source-location loc)
2695
(destructuring-bind (start end)
2696
(slime-region-for-defun-at-point)
2697
(list (buffer-substring-no-properties start end)
2698
(buffer-name)
2699
(slime-current-package)
2700
start
2701
(if (buffer-file-name)
2702
(file-name-directory (buffer-file-name))
2703
nil)))))
2704
',slime-compilation-policy)
2705
cont))
2706
2707
2708
;;;;; Merging together compiler notes in the same location.
2709
2710
(defun slime-merge-notes-for-display (notes)
2711
"Merge together notes that refer to the same location.
2712
This operation is \"lossy\" in the broad sense but not for display purposes."
2713
(mapcar #'slime-merge-notes
2714
(slime-group-similar 'slime-notes-in-same-location-p notes)))
2715
2716
(defun slime-merge-notes (notes)
2717
"Merge NOTES together. Keep the highest severity, concatenate the messages."
2718
(let* ((new-severity (reduce #'slime-most-severe notes
2719
:key #'slime-note.severity))
2720
(new-message (mapconcat #'slime-note.message notes "\n")))
2721
(let ((new-note (copy-list (car notes))))
2722
(setf (getf new-note :message) new-message)
2723
(setf (getf new-note :severity) new-severity)
2724
new-note)))
2725
2726
(defun slime-notes-in-same-location-p (a b)
2727
(equal (slime-note.location a) (slime-note.location b)))
2728
2729
2730
;;;;; Compiler notes list
2731
2732
(defun slime-one-line-ify (string)
2733
"Return a single-line version of STRING.
2734
Each newlines and following indentation is replaced by a single space."
2735
(with-temp-buffer
2736
(insert string)
2737
(goto-char (point-min))
2738
(while (re-search-forward "\n[\n \t]*" nil t)
2739
(replace-match " "))
2740
(buffer-string)))
2741
2742
(defun slime-xrefs-for-notes (notes)
2743
(let ((xrefs))
2744
(dolist (note notes)
2745
(let* ((location (getf note :location))
2746
(fn (cadr (assq :file (cdr location))))
2747
(file (assoc fn xrefs))
2748
(node
2749
(cons (format "%s: %s"
2750
(getf note :severity)
2751
(slime-one-line-ify (getf note :message)))
2752
location)))
2753
(when fn
2754
(if file
2755
(push node (cdr file))
2756
(setf xrefs (acons fn (list node) xrefs))))))
2757
xrefs))
2758
2759
(defun slime-maybe-show-xrefs-for-notes (notes)
2760
"Show the compiler notes NOTES if they come from more than one file."
2761
(let ((xrefs (slime-xrefs-for-notes notes)))
2762
(when (slime-length> xrefs 1) ; >1 file
2763
(slime-show-xrefs
2764
xrefs 'definition "Compiler notes" (slime-current-package)))))
2765
2766
(defun slime-note-has-location-p (note)
2767
(not (eq ':error (car (slime-note.location note)))))
2768
2769
(defun slime-redefinition-note-p (note)
2770
(eq (slime-note.severity note) :redefinition))
2771
2772
(defun slime-create-compilation-log (notes)
2773
"Create a buffer for `next-error' to use."
2774
(with-current-buffer (get-buffer-create (slime-buffer-name :compilation))
2775
(let ((inhibit-read-only t))
2776
(erase-buffer))
2777
(slime-insert-compilation-log notes)
2778
(compilation-mode)))
2779
2780
(defun slime-maybe-show-compilation-log (notes)
2781
"Display the log on failed compilations or if NOTES is non-nil."
2782
(slime-create-compilation-log notes)
2783
(with-struct (slime-compilation-result. notes duration successp)
2784
slime-last-compilation-result
2785
(unless successp
2786
(with-current-buffer (slime-buffer-name :compilation)
2787
(let ((inhibit-read-only t))
2788
(goto-char (point-max))
2789
(insert "Compilation " (if successp "succeeded." "failed."))
2790
(goto-char (point-min))
2791
(display-buffer (current-buffer)))))))
2792
2793
(defun slime-show-compilation-log (notes)
2794
"Create and display the compilation log buffer."
2795
(interactive (list (slime-compiler-notes)))
2796
(slime-with-popup-buffer ((slime-buffer-name :compilation)
2797
:mode 'compilation-mode)
2798
(slime-insert-compilation-log notes)))
2799
2800
(defun slime-insert-compilation-log (notes)
2801
"Insert NOTES in format suitable for `compilation-mode'."
2802
(multiple-value-bind (grouped-notes canonicalized-locs-table)
2803
(slime-group-and-sort-notes notes)
2804
(with-temp-message "Preparing compilation log..."
2805
(let ((inhibit-read-only t)
2806
(inhibit-modification-hooks t)) ; inefficient font-lock-hook
2807
(insert (format "cd %s\n%d compiler notes:\n\n"
2808
default-directory (length notes)))
2809
(dolist (notes grouped-notes)
2810
(let ((loc (gethash (first notes) canonicalized-locs-table))
2811
(start (point)))
2812
(insert (slime-canonicalized-location-to-string loc) ":")
2813
(slime-insert-note-group notes)
2814
(insert "\n")
2815
(slime-make-note-overlay (first notes) start (1- (point))))))
2816
(set (make-local-variable 'compilation-skip-threshold) 0)
2817
(setq next-error-last-buffer (current-buffer)))))
2818
2819
(defun slime-insert-note-group (notes)
2820
"Insert a group of compiler messages."
2821
(insert "\n")
2822
(dolist (note notes)
2823
(insert " " (slime-severity-label (slime-note.severity note)) ": ")
2824
(let ((start (point)))
2825
(insert (slime-note.message note))
2826
(let ((ctx (slime-note.source-context note)))
2827
(if ctx (insert "\n" ctx)))
2828
(slime-indent-block start 4))
2829
(insert "\n")))
2830
2831
(defun slime-indent-block (start column)
2832
"If the region back to START isn't a one-liner indent it."
2833
(when (< start (line-beginning-position))
2834
(save-excursion
2835
(goto-char start)
2836
(insert "\n"))
2837
(slime-indent-rigidly start (point) column)))
2838
2839
(defun slime-canonicalized-location (location)
2840
"Return a list (FILE LINE COLUMN) for slime-location LOCATION.
2841
This is quite an expensive operation so use carefully."
2842
(save-excursion
2843
(slime-goto-location-buffer (slime-location.buffer location))
2844
(save-excursion
2845
(slime-goto-source-location location)
2846
(list (or (buffer-file-name) (buffer-name))
2847
(line-number-at-pos)
2848
(1+ (current-column))))))
2849
2850
(defun slime-canonicalized-location-to-string (loc)
2851
(if loc
2852
(destructuring-bind (filename line col) loc
2853
(format "%s:%d:%d"
2854
(cond ((not filename) "")
2855
((let ((rel (file-relative-name filename)))
2856
(if (< (length rel) (length filename))
2857
rel)))
2858
(t filename))
2859
line col))
2860
(format "Unknown location")))
2861
2862
(defun slime-goto-note-in-compilation-log (note)
2863
"Find `note' in the compilation log and display it."
2864
(with-current-buffer (get-buffer (slime-buffer-name :compilation))
2865
(let ((origin (point))
2866
(foundp nil))
2867
(goto-char (point-min))
2868
(let ((overlay))
2869
(while (and (setq overlay (slime-find-next-note))
2870
(not foundp))
2871
(let ((other-note (overlay-get overlay 'slime-note)))
2872
(when (slime-notes-in-same-location-p note other-note)
2873
(slime-show-buffer-position (overlay-start overlay) 'top)
2874
(setq foundp t)))))
2875
(unless foundp
2876
(goto-char origin)))))
2877
2878
(defun slime-group-and-sort-notes (notes)
2879
"First sort, then group NOTES according to their canonicalized locs."
2880
(let ((locs (make-hash-table :test #'eq)))
2881
(mapc (lambda (note)
2882
(let ((loc (slime-note.location note)))
2883
(when (slime-location-p loc)
2884
(puthash note (slime-canonicalized-location loc) locs))))
2885
notes)
2886
(values (slime-group-similar
2887
(lambda (n1 n2)
2888
(equal (gethash n1 locs nil) (gethash n2 locs t)))
2889
(let* ((bottom most-negative-fixnum)
2890
(+default+ (list "" bottom bottom)))
2891
(sort notes
2892
(lambda (n1 n2)
2893
(destructuring-bind (filename1 line1 col1)
2894
(gethash n1 locs +default+)
2895
(destructuring-bind (filename2 line2 col2)
2896
(gethash n2 locs +default+)
2897
(cond ((string-lessp filename1 filename2) t)
2898
((string-lessp filename2 filename1) nil)
2899
((< line1 line2) t)
2900
((> line1 line2) nil)
2901
(t (< col1 col2)))))))))
2902
locs)))
2903
2904
(defun slime-note.severity (note)
2905
(plist-get note :severity))
2906
2907
(defun slime-note.message (note)
2908
(plist-get note :message))
2909
2910
(defun slime-note.source-context (note)
2911
(plist-get note :source-context))
2912
2913
(defun slime-note.location (note)
2914
(plist-get note :location))
2915
2916
(defun slime-severity-label (severity)
2917
(subseq (symbol-name severity) 1))
2918
2919
2920
;;;;; Adding a single compiler note
2921
2922
(defun slime-overlay-note (note)
2923
"Add a compiler note to the buffer as an overlay.
2924
If an appropriate overlay for a compiler note in the same location
2925
already exists then the new information is merged into it. Otherwise a
2926
new overlay is created."
2927
(multiple-value-bind (start end) (slime-choose-overlay-region note)
2928
(when start
2929
(goto-char start)
2930
(let ((severity (plist-get note :severity))
2931
(message (plist-get note :message))
2932
(overlay (slime-note-at-point)))
2933
(if overlay
2934
(slime-merge-note-into-overlay overlay severity message)
2935
(slime-create-note-overlay note start end severity message))))))
2936
2937
(defun slime-make-note-overlay (note start end)
2938
(let ((overlay (make-overlay start end)))
2939
(overlay-put overlay 'slime-note note)
2940
(push overlay slime-note-overlays)
2941
overlay))
2942
2943
(defun slime-create-note-overlay (note start end severity message)
2944
"Create an overlay representing a compiler note.
2945
The overlay has several properties:
2946
FACE - to underline the relevant text.
2947
SEVERITY - for future reference :NOTE, :STYLE-WARNING, :WARNING, or :ERROR.
2948
MOUSE-FACE - highlight the note when the mouse passes over.
2949
HELP-ECHO - a string describing the note, both for future reference
2950
and for display as a tooltip (due to the special
2951
property name)."
2952
(let ((overlay (slime-make-note-overlay note start end)))
2953
(flet ((putp (name value) (overlay-put overlay name value)))
2954
(putp 'face (slime-severity-face severity))
2955
(putp 'severity severity)
2956
(putp 'mouse-face 'highlight)
2957
(putp 'help-echo message)
2958
overlay)))
2959
2960
;; XXX Obsolete due to `slime-merge-notes-for-display' doing the
2961
;; work already -- unless we decide to put several sets of notes on a
2962
;; buffer without clearing in between, which only this handles.
2963
(defun slime-merge-note-into-overlay (overlay severity message)
2964
"Merge another compiler note into an existing overlay.
2965
The help text describes both notes, and the highest of the severities
2966
is kept."
2967
(flet ((putp (name value) (overlay-put overlay name value))
2968
(getp (name) (overlay-get overlay name)))
2969
(putp 'severity (slime-most-severe severity (getp 'severity)))
2970
(putp 'face (slime-severity-face (getp 'severity)))
2971
(putp 'help-echo (concat (getp 'help-echo) "\n" message))))
2972
2973
(defun slime-choose-overlay-region (note)
2974
"Choose the start and end points for an overlay over NOTE.
2975
If the location's sexp is a list spanning multiple lines, then the
2976
region around the first element is used.
2977
Return nil if there's no useful source location."
2978
(let ((location (slime-note.location note)))
2979
(when location
2980
(destructure-case location
2981
((:error _) _ nil) ; do nothing
2982
((:location file pos _hints)
2983
(cond ((eq (car file) ':source-form) nil)
2984
((eq (slime-note.severity note) :read-error)
2985
(slime-choose-overlay-for-read-error location))
2986
((equal pos '(:eof))
2987
(list (1- (point-max)) (point-max)))
2988
(t
2989
(slime-choose-overlay-for-sexp location))))))))
2990
2991
(defun slime-choose-overlay-for-read-error (location)
2992
(let ((pos (slime-location-offset location)))
2993
(save-excursion
2994
(goto-char pos)
2995
(cond ((slime-symbol-at-point)
2996
;; package not found, &c.
2997
(values (slime-symbol-start-pos) (slime-symbol-end-pos)))
2998
(t
2999
(values pos (1+ pos)))))))
3000
3001
(defun slime-choose-overlay-for-sexp (location)
3002
(slime-goto-source-location location)
3003
(skip-chars-forward "'#`")
3004
(let ((start (point)))
3005
(ignore-errors (slime-forward-sexp))
3006
(if (slime-same-line-p start (point))
3007
(values start (point))
3008
(values (1+ start)
3009
(progn (goto-char (1+ start))
3010
(ignore-errors (forward-sexp 1))
3011
(point))))))
3012
3013
(defun slime-same-line-p (pos1 pos2)
3014
"Return t if buffer positions POS1 and POS2 are on the same line."
3015
(save-excursion (goto-char (min pos1 pos2))
3016
(<= (max pos1 pos2) (line-end-position))))
3017
3018
(defvar slime-severity-face-plist
3019
'(:error slime-error-face
3020
:read-error slime-error-face
3021
:warning slime-warning-face
3022
:redefinition slime-style-warning-face
3023
:style-warning slime-style-warning-face
3024
:note slime-note-face))
3025
3026
(defun slime-severity-face (severity)
3027
"Return the name of the font-lock face representing SEVERITY."
3028
(or (plist-get slime-severity-face-plist severity)
3029
(error "No face for: %S" severity)))
3030
3031
(defvar slime-severity-order
3032
'(:note :style-warning :redefinition :warning :error :read-error))
3033
3034
(defun slime-severity< (sev1 sev2)
3035
"Return true if SEV1 is less severe than SEV2."
3036
(< (position sev1 slime-severity-order)
3037
(position sev2 slime-severity-order)))
3038
3039
(defun slime-most-severe (sev1 sev2)
3040
"Return the most servere of two conditions."
3041
(if (slime-severity< sev1 sev2) sev2 sev1))
3042
3043
;; XXX: unused function
3044
(defun slime-visit-source-path (source-path)
3045
"Visit a full source path including the top-level form."
3046
(goto-char (point-min))
3047
(slime-forward-source-path source-path))
3048
3049
;;; The following two functions can be handy when inspecting
3050
;;; source-location while debugging `M-.'.
3051
;;;
3052
(defun slime-current-tlf-number ()
3053
"Return the current toplevel number."
3054
(interactive)
3055
(let ((original-pos (car (slime-region-for-defun-at-point)))
3056
(n 0))
3057
(save-excursion
3058
;; We use this and no repeated `beginning-of-defun's to get
3059
;; reader conditionals right.
3060
(goto-char (point-min))
3061
(while (progn (slime-forward-sexp)
3062
(< (point) original-pos))
3063
(incf n)))
3064
n))
3065
3066
;;; This is similiar to `slime-enclosing-form-paths' in the
3067
;;; `slime-parse' contrib except that this does not do any duck-tape
3068
;;; parsing, and gets reader conditionals right.
3069
(defun slime-current-form-path ()
3070
"Returns the path from the beginning of the current toplevel
3071
form to the atom at point, or nil if we're in front of a tlf."
3072
(interactive)
3073
(let ((source-path nil))
3074
(save-excursion
3075
;; Moving forward to get reader conditionals right.
3076
(loop for inner-pos = (point)
3077
for outer-pos = (nth-value 1 (slime-current-parser-state))
3078
while outer-pos do
3079
(goto-char outer-pos)
3080
(unless (eq (char-before) ?#) ; when at #(...) continue.
3081
(forward-char)
3082
(let ((n 0))
3083
(while (progn (slime-forward-sexp)
3084
(< (point) inner-pos))
3085
(incf n))
3086
(push n source-path)
3087
(goto-char outer-pos)))))
3088
source-path))
3089
3090
(defun slime-forward-positioned-source-path (source-path)
3091
"Move forward through a sourcepath from a fixed position.
3092
The point is assumed to already be at the outermost sexp, making the
3093
first element of the source-path redundant."
3094
(ignore-errors
3095
(slime-forward-sexp)
3096
(beginning-of-defun))
3097
(when-let (source-path (cdr source-path))
3098
(down-list 1)
3099
(slime-forward-source-path source-path)))
3100
3101
(defun slime-forward-source-path (source-path)
3102
(let ((origin (point)))
3103
(condition-case nil
3104
(progn
3105
(loop for (count . more) on source-path
3106
do (progn
3107
(slime-forward-sexp count)
3108
(when more (down-list 1))))
3109
;; Align at beginning
3110
(slime-forward-sexp)
3111
(beginning-of-sexp))
3112
(error (goto-char origin)))))
3113
3114
3115
;; FIXME: really fix this mess
3116
;; FIXME: the check shouln't be done here anyway but by M-. itself.
3117
3118
(defun slime-filesystem-toplevel-directory ()
3119
;; Windows doesn't have a true toplevel root directory, and all
3120
;; filenames look like "c:/foo/bar/quux.baz" from an Emacs
3121
;; perspective anyway.
3122
(if (memq system-type '(ms-dos windows-nt))
3123
""
3124
(file-name-as-directory "/")))
3125
3126
(defun slime-file-name-merge-source-root (target-filename buffer-filename)
3127
"Returns a filename where the source root directory of TARGET-FILENAME
3128
is replaced with the source root directory of BUFFER-FILENAME.
3129
3130
If no common source root could be determined, return NIL.
3131
3132
E.g. (slime-file-name-merge-source-root
3133
\"/usr/local/src/joe/upstream/sbcl/code/late-extensions.lisp\"
3134
\"/usr/local/src/joe/hacked/sbcl/compiler/deftype.lisp\")
3135
3136
==> \"/usr/local/src/joe/hacked/sbcl/code/late-extensions.lisp\"
3137
"
3138
(let ((target-dirs (slime-split-string (file-name-directory target-filename) "/" t))
3139
(buffer-dirs (slime-split-string (file-name-directory buffer-filename) "/" t)))
3140
;; Starting from the end, we look if one of the TARGET-DIRS exists
3141
;; in BUFFER-FILENAME---if so, it and everything left from that dirname
3142
;; is considered to be the source root directory of BUFFER-FILENAME.
3143
(loop with target-suffix-dirs = nil
3144
with buffer-dirs* = (reverse buffer-dirs)
3145
with target-dirs* = (reverse target-dirs)
3146
for target-dir in target-dirs*
3147
do (flet ((concat-dirs (dirs)
3148
(apply #'concat (mapcar #'file-name-as-directory dirs))))
3149
(let ((pos (position target-dir buffer-dirs* :test #'equal)))
3150
(if (not pos) ; TARGET-DIR not in BUFFER-FILENAME?
3151
(push target-dir target-suffix-dirs)
3152
(let* ((target-suffix (concat-dirs target-suffix-dirs)) ; PUSH reversed for us!
3153
(buffer-root (concat-dirs (reverse (nthcdr pos buffer-dirs*)))))
3154
(return (concat (slime-filesystem-toplevel-directory)
3155
buffer-root
3156
target-suffix
3157
(file-name-nondirectory target-filename))))))))))
3158
3159
(defun slime-highlight-differences-in-dirname (base-dirname contrast-dirname)
3160
"Returns a copy of BASE-DIRNAME where all differences between
3161
BASE-DIRNAME and CONTRAST-DIRNAME are propertized with a
3162
highlighting face."
3163
(setq base-dirname (file-name-as-directory base-dirname))
3164
(setq contrast-dirname (file-name-as-directory contrast-dirname))
3165
(flet ((insert-dir (dirname)
3166
(insert (file-name-as-directory dirname)))
3167
(insert-dir/propzd (dirname)
3168
(slime-insert-propertized '(face highlight) dirname)
3169
(insert "/"))) ; Not exactly portable (to VMS...)
3170
(let ((base-dirs (slime-split-string base-dirname "/" t))
3171
(contrast-dirs (slime-split-string contrast-dirname "/" t)))
3172
(with-temp-buffer
3173
(loop initially (insert (slime-filesystem-toplevel-directory))
3174
for base-dir in base-dirs do
3175
(let ((pos (position base-dir contrast-dirs :test #'equal)))
3176
(if (not pos)
3177
(insert-dir/propzd base-dir)
3178
(progn (insert-dir base-dir)
3179
(setq contrast-dirs (nthcdr (1+ pos) contrast-dirs))))))
3180
(buffer-substring (point-min) (point-max))))))
3181
3182
(defvar slime-warn-when-possibly-tricked-by-M-. t
3183
"When working on multiple source trees simultaneously, the way
3184
`slime-edit-definition' (M-.) works can sometimes be confusing:
3185
3186
`M-.' visits locations that are present in the current Lisp image,
3187
which works perfectly well as long as the image reflects the source
3188
tree that one is currently looking at.
3189
3190
In the other case, however, one can easily end up visiting a file
3191
in a different source root directory (the one corresponding to
3192
the Lisp image), and is thus easily tricked to modify the wrong
3193
source files---which can lead to quite some stressfull cursing.
3194
3195
If this variable is T, a warning message is issued to raise the
3196
user's attention whenever `M-.' is about opening a file in a
3197
different source root that also exists in the source root
3198
directory of the user's current buffer.
3199
3200
There's no guarantee that all possible cases are covered, but
3201
if you encounter such a warning, it's a strong indication that
3202
you should check twice before modifying.")
3203
3204
(defun slime-maybe-warn-for-different-source-root (target-filename buffer-filename)
3205
(let ((guessed-target (slime-file-name-merge-source-root target-filename
3206
buffer-filename)))
3207
(when (and guessed-target
3208
(not (equal guessed-target target-filename))
3209
(file-exists-p guessed-target))
3210
(slime-message "Attention: This is `%s'."
3211
(concat (slime-highlight-differences-in-dirname
3212
(file-name-directory target-filename)
3213
(file-name-directory guessed-target))
3214
(file-name-nondirectory target-filename))))))
3215
3216
(defun slime-check-location-filename-sanity (filename)
3217
(when slime-warn-when-possibly-tricked-by-M-.
3218
(flet ((file-truename-safe (filename) (and filename (file-truename filename))))
3219
(let ((target-filename (file-truename-safe filename))
3220
(buffer-filename (file-truename-safe (buffer-file-name))))
3221
(when buffer-filename
3222
(slime-maybe-warn-for-different-source-root
3223
target-filename buffer-filename))))))
3224
3225
(defun slime-check-location-buffer-name-sanity (buffer-name)
3226
(slime-check-location-filename-sanity
3227
(buffer-file-name (get-buffer buffer-name))))
3228
3229
3230
3231
(defun slime-goto-location-buffer (buffer)
3232
(destructure-case buffer
3233
((:file filename)
3234
(let ((filename (slime-from-lisp-filename filename)))
3235
(slime-check-location-filename-sanity filename)
3236
(set-buffer (or (get-file-buffer filename)
3237
(let ((find-file-suppress-same-file-warnings t))
3238
(find-file-noselect filename))))))
3239
((:buffer buffer-name)
3240
(slime-check-location-buffer-name-sanity buffer-name)
3241
(set-buffer buffer-name))
3242
((:source-form string)
3243
(set-buffer (get-buffer-create (slime-buffer-name :source)))
3244
(erase-buffer)
3245
(lisp-mode)
3246
(insert string)
3247
(goto-char (point-min)))
3248
((:zip file entry)
3249
(require 'arc-mode)
3250
(set-buffer (find-file-noselect file t))
3251
(goto-char (point-min))
3252
(re-search-forward (concat " " entry "$"))
3253
(let ((buffer (save-window-excursion
3254
(archive-extract)
3255
(current-buffer))))
3256
(set-buffer buffer)
3257
(goto-char (point-min))))))
3258
3259
(defun slime-goto-location-position (position)
3260
(destructure-case position
3261
((:position pos)
3262
(goto-char 1)
3263
(forward-char (- (1- pos) (slime-eol-conversion-fixup (1- pos)))))
3264
((:offset start offset)
3265
(goto-char start)
3266
(forward-char offset))
3267
((:line start &optional column)
3268
(goto-char (point-min))
3269
(beginning-of-line start)
3270
(cond (column (move-to-column column))
3271
(t (skip-chars-forward " \t"))))
3272
((:function-name name)
3273
(let ((case-fold-search t)
3274
(name (regexp-quote name)))
3275
(when (or
3276
(re-search-forward
3277
(format "\\s *(def\\(\\s_\\|\\sw\\)*\\s +%s\\S_" name) nil t)
3278
(re-search-forward
3279
(format "\\s *(def\\(\\s_\\|\\sw\\)*\\s +(*%s\\S_" name) nil t)
3280
(re-search-forward
3281
(format "[( \t]%s\\>\\(\\s \\|$\\)" name) nil t))
3282
(goto-char (match-beginning 0)))))
3283
((:method name specializers &rest qualifiers)
3284
(slime-search-method-location name specializers qualifiers))
3285
((:source-path source-path start-position)
3286
(cond (start-position
3287
(goto-char start-position)
3288
(slime-forward-positioned-source-path source-path))
3289
(t
3290
(slime-forward-source-path source-path))))
3291
((:eof)
3292
(goto-char (point-max)))))
3293
3294
(defun slime-eol-conversion-fixup (n)
3295
;; Return the number of \r\n eol markers that we need to cross when
3296
;; moving N chars forward. N is the number of chars but \r\n are
3297
;; counted as 2 separate chars.
3298
(case (coding-system-eol-type buffer-file-coding-system)
3299
((1)
3300
(save-excursion
3301
(do ((pos (+ (point) n))
3302
(count 0 (1+ count)))
3303
((>= (point) pos) (1- count))
3304
(forward-line)
3305
(decf pos))))
3306
(t 0)))
3307
3308
(defun slime-search-method-location (name specializers qualifiers)
3309
;; Look for a sequence of words (def<something> method name
3310
;; qualifers specializers don't look for "T" since it isn't requires
3311
;; (arg without t) as class is taken as such.
3312
(let* ((case-fold-search t)
3313
(name (regexp-quote name))
3314
(qualifiers (mapconcat (lambda (el) (concat ".+?\\<" el "\\>"))
3315
qualifiers ""))
3316
(specializers (mapconcat (lambda (el)
3317
(if (eql (aref el 0) ?\()
3318
(let ((spec (read el)))
3319
(if (eq (car spec) 'EQL)
3320
(concat ".*?\\n\\{0,1\\}.*?(EQL.*?'\\{0,1\\}"
3321
(format "%s" (second spec)) ")")
3322
(error "don't understand specializer: %s,%s" el (car spec))))
3323
(concat ".+?\n\\{0,1\\}.+?\\<" el "\\>")))
3324
(remove "T" specializers) ""))
3325
(regexp (format "\\s *(def\\(\\s_\\|\\sw\\)*\\s +%s\\s +%s%s" name
3326
qualifiers specializers)))
3327
(or (and (re-search-forward regexp nil t)
3328
(goto-char (match-beginning 0)))
3329
;; (slime-goto-location-position `(:function-name ,name))
3330
)))
3331
3332
(defun slime-search-call-site (fname)
3333
"Move to the place where FNAME called.
3334
Don't move if there are multiple or no calls in the current defun."
3335
(save-restriction
3336
(narrow-to-defun)
3337
(let ((start (point))
3338
(regexp (concat "(" fname "[\n \t]")))
3339
(cond ((and (re-search-forward regexp nil t)
3340
(not (re-search-forward regexp nil t)))
3341
(goto-char (match-beginning 0)))
3342
(t (goto-char start))))))
3343
3344
(defun slime-goto-source-location (location &optional noerror)
3345
"Move to the source location LOCATION. Several kinds of locations
3346
are supported:
3347
3348
<location> ::= (:location <buffer> <position> <hints>)
3349
| (:error <message>)
3350
3351
<buffer> ::= (:file <filename>)
3352
| (:buffer <buffername>)
3353
| (:source-form <string>)
3354
| (:zip <file> <entry>)
3355
3356
<position> ::= (:position <fixnum>) ; 1 based (for files)
3357
| (:offset <start> <offset>) ; start+offset (for C-c C-c)
3358
| (:line <line> [<column>])
3359
| (:function-name <string>)
3360
| (:source-path <list> <start-position>)
3361
| (:method <name string> <specializer strings> . <qualifiers strings>)"
3362
(destructure-case location
3363
((:location buffer position hints)
3364
(slime-goto-location-buffer buffer)
3365
(let ((pos (slime-location-offset location)))
3366
(cond ((and (<= (point-min) pos) (<= pos (point-max))))
3367
(widen-automatically (widen))
3368
(t (error "Location is outside accessible part of buffer")))
3369
(goto-char pos)))
3370
((:error message)
3371
(if noerror
3372
(slime-message "%s" message)
3373
(error "%s" message)))))
3374
3375
(defun slime-location-offset (location)
3376
"Return the position, as character number, of LOCATION."
3377
(save-restriction
3378
(widen)
3379
(slime-goto-location-position (slime-location.position location))
3380
(let ((hints (slime-location.hints location)))
3381
(when-let (snippet (getf hints :snippet))
3382
(slime-isearch snippet))
3383
(when-let (fname (getf hints :call-site))
3384
(slime-search-call-site fname))
3385
(when (getf hints :align)
3386
(slime-forward-sexp)
3387
(beginning-of-sexp)))
3388
(point)))
3389
3390
3391
;;;;; Incremental search
3392
;;
3393
;; Search for the longest match of a string in either direction.
3394
;;
3395
;; This is for locating text that is expected to be near the point and
3396
;; may have been modified (but hopefully not near the beginning!)
3397
3398
(defun slime-isearch (string)
3399
"Find the longest occurence of STRING either backwards of forwards.
3400
If multiple matches exist the choose the one nearest to point."
3401
(goto-char
3402
(let* ((start (point))
3403
(len1 (slime-isearch-with-function 'search-forward string))
3404
(pos1 (point)))
3405
(goto-char start)
3406
(let* ((len2 (slime-isearch-with-function 'search-backward string))
3407
(pos2 (point)))
3408
(cond ((and len1 len2)
3409
;; Have a match in both directions
3410
(cond ((= len1 len2)
3411
;; Both are full matches -- choose the nearest.
3412
(if (< (abs (- start pos1))
3413
(abs (- start pos2)))
3414
pos1 pos2))
3415
((> len1 len2) pos1)
3416
((> len2 len1) pos2)))
3417
(len1 pos1)
3418
(len2 pos2)
3419
(t start))))))
3420
3421
(defun slime-isearch-with-function (search-fn string)
3422
"Search for the longest substring of STRING using SEARCH-FN.
3423
SEARCH-FN is either the symbol `search-forward' or `search-backward'."
3424
(unless (string= string "")
3425
(loop for i from 1 to (length string)
3426
while (funcall search-fn (substring string 0 i) nil t)
3427
for match-data = (match-data)
3428
do (case search-fn
3429
(search-forward (goto-char (match-beginning 0)))
3430
(search-backward (goto-char (1+ (match-end 0)))))
3431
finally (return (if (null match-data)
3432
nil
3433
;; Finish based on the last successful match
3434
(store-match-data match-data)
3435
(goto-char (match-beginning 0))
3436
(- (match-end 0) (match-beginning 0)))))))
3437
3438
3439
;;;;; Visiting and navigating the overlays of compiler notes
3440
3441
(defun slime-next-note ()
3442
"Go to and describe the next compiler note in the buffer."
3443
(interactive)
3444
(let ((here (point))
3445
(note (slime-find-next-note)))
3446
(if note
3447
(slime-show-note note)
3448
(goto-char here)
3449
(message "No next note."))))
3450
3451
(defun slime-previous-note ()
3452
"Go to and describe the previous compiler note in the buffer."
3453
(interactive)
3454
(let ((here (point))
3455
(note (slime-find-previous-note)))
3456
(if note
3457
(slime-show-note note)
3458
(goto-char here)
3459
(message "No previous note."))))
3460
3461
(defun slime-goto-first-note (&rest ignore)
3462
"Go to the first note in the buffer."
3463
(let ((point (point)))
3464
(goto-char (point-min))
3465
(cond ((slime-find-next-note)
3466
(slime-show-note (slime-note-at-point)))
3467
(t (goto-char point)))))
3468
3469
(defun slime-remove-notes ()
3470
"Remove compiler-note annotations from the current buffer."
3471
(interactive)
3472
(slime-remove-old-overlays))
3473
3474
(defun slime-show-note (overlay)
3475
"Present the details of a compiler note to the user."
3476
(slime-temporarily-highlight-note overlay)
3477
(if (get-buffer-window (slime-buffer-name :compilation) t)
3478
(slime-goto-note-in-compilation-log (overlay-get overlay 'slime-note))
3479
(let ((message (get-char-property (point) 'help-echo)))
3480
(slime-message "%s" (if (zerop (length message)) "\"\"" message)))))
3481
3482
;; FIXME: could probably use flash region
3483
(defun slime-temporarily-highlight-note (overlay)
3484
"Temporarily highlight a compiler note's overlay.
3485
The highlighting is designed to both make the relevant source more
3486
visible, and to highlight any further notes that are nested inside the
3487
current one.
3488
3489
The highlighting is automatically undone with a timer."
3490
(run-with-timer 0.2 nil
3491
#'overlay-put overlay 'face (overlay-get overlay 'face))
3492
(overlay-put overlay 'face 'slime-highlight-face))
3493
3494
3495
;;;;; Overlay lookup operations
3496
3497
(defun slime-note-at-point ()
3498
"Return the overlay for a note starting at point, otherwise NIL."
3499
(find (point) (slime-note-overlays-at-point)
3500
:key 'overlay-start))
3501
3502
(defun slime-note-overlay-p (overlay)
3503
"Return true if OVERLAY represents a compiler note."
3504
(overlay-get overlay 'slime-note))
3505
3506
(defun slime-note-overlays-at-point ()
3507
"Return a list of all note overlays that are under the point."
3508
(remove-if-not 'slime-note-overlay-p (overlays-at (point))))
3509
3510
(defun slime-find-next-note ()
3511
"Go to the next position with the `slime-note' text property.
3512
Retuns the note overlay if such a position is found, otherwise nil."
3513
(slime-search-property 'slime-note nil #'slime-note-at-point))
3514
3515
(defun slime-find-previous-note ()
3516
"Go to the next position with the `slime-note' text property.
3517
Retuns the note overlay if such a position is found, otherwise nil."
3518
(slime-search-property 'slime-note t #'slime-note-at-point))
3519
3520
3521
;;;; Arglist Display
3522
3523
(defun slime-space (n)
3524
"Insert a space and print some relevant information (function arglist).
3525
Designed to be bound to the SPC key. Prefix argument can be used to insert
3526
more than one space."
3527
(interactive "p")
3528
(self-insert-command n)
3529
(when (slime-background-activities-enabled-p)
3530
(slime-echo-arglist)))
3531
3532
(put 'slime-space 'delete-selection t) ; for delete-section-mode & CUA
3533
3534
(defvar slime-echo-arglist-function 'slime-show-arglist)
3535
3536
(defun slime-echo-arglist ()
3537
"Display the arglist of the current form in the echo area."
3538
(funcall slime-echo-arglist-function))
3539
3540
(defun slime-show-arglist ()
3541
(let ((op (slime-operator-before-point)))
3542
(when op
3543
(slime-eval-async `(swank:operator-arglist ,op ,(slime-current-package))
3544
(lambda (arglist)
3545
(when arglist
3546
(slime-message "%s" arglist)))))))
3547
3548
(defun slime-operator-before-point ()
3549
(ignore-errors
3550
(save-excursion
3551
(backward-up-list 1)
3552
(down-list 1)
3553
(slime-symbol-at-point))))
3554
3555
3556
;;;; Completion
3557
3558
;; XXX those long names are ugly to read; long names an indicator for
3559
;; bad factoring?
3560
3561
(defvar slime-completions-buffer-name "*Completions*")
3562
3563
(make-variable-buffer-local
3564
(defvar slime-complete-saved-window-configuration nil
3565
"Window configuration before we show the *Completions* buffer.
3566
This is buffer local in the buffer where the completion is
3567
performed."))
3568
3569
(make-variable-buffer-local
3570
(defvar slime-completions-window nil
3571
"The window displaying *Completions* after saving window configuration.
3572
If this window is no longer active or displaying the completions
3573
buffer then we can ignore `slime-complete-saved-window-configuration'."))
3574
3575
(defun slime-complete-maybe-save-window-configuration ()
3576
"Maybe save the current window configuration.
3577
Return true if the configuration was saved."
3578
(unless (or slime-complete-saved-window-configuration
3579
(get-buffer-window slime-completions-buffer-name))
3580
(setq slime-complete-saved-window-configuration
3581
(current-window-configuration))
3582
t))
3583
3584
(defun slime-complete-delay-restoration ()
3585
(slime-add-local-hook 'pre-command-hook
3586
'slime-complete-maybe-restore-window-configuration))
3587
3588
(defun slime-complete-forget-window-configuration ()
3589
(setq slime-complete-saved-window-configuration nil)
3590
(setq slime-completions-window nil))
3591
3592
(defun slime-complete-restore-window-configuration ()
3593
"Restore the window config if available."
3594
(remove-hook 'pre-command-hook
3595
'slime-complete-maybe-restore-window-configuration)
3596
(when (and slime-complete-saved-window-configuration
3597
(slime-completion-window-active-p))
3598
;; XEmacs does not allow us to restore a window configuration from
3599
;; pre-command-hook, so we do it asynchronously.
3600
(slime-run-when-idle
3601
(lambda ()
3602
(save-excursion
3603
(set-window-configuration
3604
slime-complete-saved-window-configuration))
3605
(setq slime-complete-saved-window-configuration nil)
3606
(when (buffer-live-p slime-completions-buffer-name)
3607
(kill-buffer slime-completions-buffer-name))))))
3608
3609
(defun slime-complete-maybe-restore-window-configuration ()
3610
"Restore the window configuration, if the following command
3611
terminates a current completion."
3612
(remove-hook 'pre-command-hook
3613
'slime-complete-maybe-restore-window-configuration)
3614
(condition-case err
3615
(cond ((find last-command-char "()\"'`,# \r\n:")
3616
(slime-complete-restore-window-configuration))
3617
((not (slime-completion-window-active-p))
3618
(slime-complete-forget-window-configuration))
3619
(t
3620
(slime-complete-delay-restoration)))
3621
(error
3622
;; Because this is called on the pre-command-hook, we mustn't let
3623
;; errors propagate.
3624
(message "Error in slime-complete-restore-window-configuration: %S" err))))
3625
3626
(defun slime-completion-window-active-p ()
3627
"Is the completion window currently active?"
3628
(and (window-live-p slime-completions-window)
3629
(equal (buffer-name (window-buffer slime-completions-window))
3630
slime-completions-buffer-name)))
3631
3632
(defun slime-display-completion-list (completions base)
3633
(let ((savedp (slime-complete-maybe-save-window-configuration)))
3634
(with-output-to-temp-buffer slime-completions-buffer-name
3635
(display-completion-list completions)
3636
(let ((offset (- (point) 1 (length base))))
3637
(with-current-buffer standard-output
3638
(setq completion-base-size offset)
3639
(set-syntax-table lisp-mode-syntax-table))))
3640
(when savedp
3641
(setq slime-completions-window
3642
(get-buffer-window slime-completions-buffer-name)))))
3643
3644
(defun slime-display-or-scroll-completions (completions base)
3645
(cond ((and (eq last-command this-command)
3646
(slime-completion-window-active-p))
3647
(slime-scroll-completions))
3648
(t
3649
(slime-display-completion-list completions base)))
3650
(slime-complete-delay-restoration))
3651
3652
(defun slime-scroll-completions ()
3653
(let ((window slime-completions-window))
3654
(with-current-buffer (window-buffer window)
3655
(if (pos-visible-in-window-p (point-max) window)
3656
(set-window-start window (point-min))
3657
(save-selected-window
3658
(select-window window)
3659
(scroll-up))))))
3660
3661
(defun slime-complete-symbol ()
3662
"Complete the symbol at point.
3663
3664
Completion is performed by `slime-complete-symbol-function'."
3665
(interactive)
3666
(funcall slime-complete-symbol-function))
3667
3668
(defun slime-simple-complete-symbol ()
3669
"Complete the symbol at point.
3670
Perform completion more similar to Emacs' complete-symbol."
3671
(or (slime-maybe-complete-as-filename)
3672
(let* ((end (point))
3673
(beg (slime-symbol-start-pos))
3674
(prefix (buffer-substring-no-properties beg end))
3675
(result (slime-simple-completions prefix)))
3676
(destructuring-bind (completions partial) result
3677
(if (null completions)
3678
(progn (slime-minibuffer-respecting-message
3679
"Can't find completion for \"%s\"" prefix)
3680
(ding)
3681
(slime-complete-restore-window-configuration))
3682
(insert-and-inherit (substring partial (length prefix)))
3683
(cond ((slime-length= completions 1)
3684
(slime-minibuffer-respecting-message "Sole completion")
3685
(slime-complete-restore-window-configuration))
3686
;; Incomplete
3687
(t
3688
(slime-minibuffer-respecting-message
3689
"Complete but not unique")
3690
(slime-display-or-scroll-completions completions
3691
partial))))))))
3692
3693
(defun slime-maybe-complete-as-filename ()
3694
"If point is at a string starting with \", complete it as filename.
3695
Return nil if point is not at filename."
3696
(if (save-excursion (re-search-backward "\"[^ \t\n]+\\=" nil t))
3697
(let ((comint-completion-addsuffix '("/" . "\"")))
3698
(comint-replace-by-expanded-filename)
3699
t)
3700
nil))
3701
3702
(defun slime-minibuffer-respecting-message (format &rest format-args)
3703
"Display TEXT as a message, without hiding any minibuffer contents."
3704
(let ((text (format " [%s]" (apply #'format format format-args))))
3705
(if (minibuffer-window-active-p (minibuffer-window))
3706
(if (fboundp 'temp-minibuffer-message) ;; XEmacs
3707
(temp-minibuffer-message text)
3708
(minibuffer-message text))
3709
(message "%s" text))))
3710
3711
(defun slime-indent-and-complete-symbol ()
3712
"Indent the current line and perform symbol completion.
3713
First indent the line. If indenting doesn't move point, complete
3714
the symbol. If there's no symbol at the point, show the arglist
3715
for the most recently enclosed macro or function."
3716
(interactive)
3717
(let ((pos (point)))
3718
(unless (get-text-property (line-beginning-position) 'slime-repl-prompt)
3719
(lisp-indent-line))
3720
(when (= pos (point))
3721
(cond ((save-excursion (re-search-backward "[^() \n\t\r]+\\=" nil t))
3722
(slime-complete-symbol))
3723
((memq (char-before) '(?\t ?\ ))
3724
(slime-echo-arglist))))))
3725
3726
(defvar slime-minibuffer-map
3727
(let ((map (make-sparse-keymap)))
3728
(set-keymap-parent map minibuffer-local-map)
3729
(define-key map "\t" 'slime-complete-symbol)
3730
(define-key map "\M-\t" 'slime-complete-symbol)
3731
map)
3732
"Minibuffer keymap used for reading CL expressions.")
3733
3734
(defvar slime-minibuffer-history '()
3735
"History list of expressions read from the minibuffer.")
3736
3737
(defun slime-minibuffer-setup-hook ()
3738
(cons (lexical-let ((package (slime-current-package))
3739
(connection (slime-connection)))
3740
(lambda ()
3741
(setq slime-buffer-package package)
3742
(setq slime-buffer-connection connection)
3743
(set-syntax-table lisp-mode-syntax-table)))
3744
minibuffer-setup-hook))
3745
3746
(defun slime-read-from-minibuffer (prompt &optional initial-value history)
3747
"Read a string from the minibuffer, prompting with PROMPT.
3748
If INITIAL-VALUE is non-nil, it is inserted into the minibuffer before
3749
reading input. The result is a string (\"\" if no input was given)."
3750
(let ((minibuffer-setup-hook (slime-minibuffer-setup-hook)))
3751
(read-from-minibuffer prompt initial-value slime-minibuffer-map
3752
nil 'slime-minibuffer-history)))
3753
3754
(defun slime-bogus-completion-alist (list)
3755
"Make an alist out of list.
3756
The same elements go in the CAR, and nil in the CDR. To support the
3757
apparently very stupid `try-completions' interface, that wants an
3758
alist but ignores CDRs."
3759
(mapcar (lambda (x) (cons x nil)) list))
3760
3761
(defun slime-simple-completions (prefix)
3762
(let ((slime-current-thread t))
3763
(slime-eval
3764
`(swank:simple-completions ,prefix ',(slime-current-package)))))
3765
3766
3767
;;;; Edit definition
3768
3769
(defun slime-push-definition-stack ()
3770
"Add point to find-tag-marker-ring."
3771
(require 'etags)
3772
(cond ((featurep 'xemacs)
3773
(push-tag-mark))
3774
(t (ring-insert find-tag-marker-ring (point-marker)))))
3775
3776
(defun slime-pop-find-definition-stack ()
3777
"Pop the edit-definition stack and goto the location."
3778
(interactive)
3779
(cond ((featurep 'xemacs) (pop-tag-mark nil))
3780
(t (pop-tag-mark))))
3781
3782
(defstruct (slime-xref (:conc-name slime-xref.) (:type list))
3783
dspec location)
3784
3785
(defstruct (slime-location (:conc-name slime-location.) (:type list)
3786
(:constructor nil)
3787
(:copier nil))
3788
tag buffer position hints)
3789
3790
(defun slime-location-p (o) (and (consp o) (eq (car o) :location)))
3791
3792
(defun slime-xref-has-location-p (xref)
3793
(slime-location-p (slime-xref.location xref)))
3794
3795
(defun make-slime-buffer-location (buffer-name position &optional hints)
3796
`(:location (:buffer ,buffer-name) (:position ,position)
3797
,(when hints `(:hints ,hints))))
3798
3799
(defun make-slime-file-location (file-name position &optional hints)
3800
`(:location (:file ,file-name) (:position ,position)
3801
,(when hints `(:hints ,hints))))
3802
3803
;;; The hooks are tried in order until one succeeds, otherwise the
3804
;;; default implementation involving `slime-find-definitions-function'
3805
;;; is used. The hooks are called with the same arguments as
3806
;;; `slime-edit-definition'.
3807
(defvar slime-edit-definition-hooks)
3808
3809
(defun slime-edit-definition (name &optional where)
3810
"Lookup the definition of the name at point.
3811
If there's no name at point, or a prefix argument is given, then the
3812
function name is prompted."
3813
(interactive (list (slime-read-symbol-name "Edit Definition of: ")))
3814
(or (run-hook-with-args-until-success 'slime-edit-definition-hooks
3815
name where)
3816
(slime-edit-definition-cont (slime-find-definitions name)
3817
name where)))
3818
3819
(defun slime-edit-definition-cont (xrefs name where)
3820
(destructuring-bind (1loc file-alist) (slime-analyze-xrefs xrefs)
3821
(cond ((null xrefs)
3822
(error "No known definition for: %s (in %s)"
3823
name (slime-current-package)))
3824
(1loc
3825
(slime-push-definition-stack)
3826
(slime-pop-to-location (slime-xref.location (car xrefs)) where))
3827
((slime-length= xrefs 1) ; ((:error "..."))
3828
(error "%s" (cadr (slime-xref.location (car xrefs)))))
3829
(t
3830
(slime-push-definition-stack)
3831
(slime-show-xrefs file-alist 'definition name
3832
(slime-current-package))))))
3833
3834
(defvar slime-edit-uses-xrefs
3835
'(:calls :macroexpands :binds :references :sets :specializes))
3836
3837
;;; FIXME. TODO: Would be nice to group the symbols (in each
3838
;;; type-group) by their home-package.
3839
(defun slime-edit-uses (symbol)
3840
"Lookup all the uses of SYMBOL."
3841
(interactive (list (slime-read-symbol-name "Edit Uses of: ")))
3842
(slime-xrefs slime-edit-uses-xrefs
3843
symbol
3844
(lambda (xrefs type symbol package)
3845
(cond
3846
((null xrefs)
3847
(message "No xref information found for %s." symbol))
3848
((and (slime-length= xrefs 1) ; one group
3849
(slime-length= (cdar xrefs) 1)) ; one ref in group
3850
(destructuring-bind (_ (_ loc)) (first xrefs)
3851
(slime-push-definition-stack)
3852
(slime-pop-to-location loc)))
3853
(t
3854
(slime-push-definition-stack)
3855
(slime-show-xref-buffer xrefs type symbol package))))))
3856
3857
(defun slime-analyze-xrefs (xrefs)
3858
"Find common filenames in XREFS.
3859
Return a list (SINGLE-LOCATION FILE-ALIST).
3860
SINGLE-LOCATION is true if all xrefs point to the same location.
3861
FILE-ALIST is an alist of the form ((FILENAME . (XREF ...)) ...)."
3862
(list (and xrefs
3863
(let ((loc (slime-xref.location (car xrefs))))
3864
(and (slime-location-p loc)
3865
(every (lambda (x) (equal (slime-xref.location x) loc))
3866
(cdr xrefs)))))
3867
(slime-alistify xrefs #'slime-xref-group #'equal)))
3868
3869
(defun slime-xref-group (xref)
3870
(cond ((slime-xref-has-location-p xref)
3871
(destructure-case (slime-location.buffer (slime-xref.location xref))
3872
((:file filename) filename)
3873
((:buffer bufname)
3874
(let ((buffer (get-buffer bufname)))
3875
(if buffer
3876
(format "%S" buffer) ; "#<buffer foo.lisp>"
3877
(format "%s (previously existing buffer)" bufname))))
3878
((:source-form _) "(S-Exp)")
3879
((:zip zip entry) entry)))
3880
(t
3881
"(No location)")))
3882
3883
(defun slime-pop-to-location (location &optional where)
3884
(slime-goto-source-location location)
3885
(ecase where
3886
((nil) (switch-to-buffer (current-buffer)))
3887
(window (pop-to-buffer (current-buffer) t))
3888
(frame (let ((pop-up-frames t)) (pop-to-buffer (current-buffer) t)))))
3889
3890
(defun slime-postprocess-xref (original-xref)
3891
"Process (for normalization purposes) an Xref comming directly
3892
from SWANK before the rest of Slime sees it. In particular,
3893
convert ETAGS based xrefs to actual file+position based
3894
locations."
3895
(if (not (slime-xref-has-location-p original-xref))
3896
(list original-xref)
3897
(let ((loc (slime-xref.location original-xref)))
3898
(destructure-case (slime-location.buffer loc)
3899
((:etags-file tags-file)
3900
(destructure-case (slime-location.position loc)
3901
((:tag &rest tags)
3902
(visit-tags-table tags-file)
3903
(mapcar (lambda (xref)
3904
(let ((old-dspec (slime-xref.dspec original-xref))
3905
(new-dspec (slime-xref.dspec xref)))
3906
(setf (slime-xref.dspec xref)
3907
(format "%s: %s" old-dspec new-dspec))
3908
xref))
3909
(mapcan #'slime-etags-definitions tags)))))
3910
(t
3911
(list original-xref))))))
3912
3913
(defun slime-postprocess-xrefs (xrefs)
3914
(mapcan #'slime-postprocess-xref xrefs))
3915
3916
(defun slime-find-definitions (name)
3917
"Find definitions for NAME."
3918
(slime-postprocess-xrefs (funcall slime-find-definitions-function name)))
3919
3920
(defun slime-find-definitions-rpc (name)
3921
(slime-eval `(swank:find-definitions-for-emacs ,name)))
3922
3923
(defun slime-edit-definition-other-window (name)
3924
"Like `slime-edit-definition' but switch to the other window."
3925
(interactive (list (slime-read-symbol-name "Symbol: ")))
3926
(slime-edit-definition name 'window))
3927
3928
(defun slime-edit-definition-other-frame (name)
3929
"Like `slime-edit-definition' but switch to the other window."
3930
(interactive (list (slime-read-symbol-name "Symbol: ")))
3931
(slime-edit-definition name 'frame))
3932
3933
(defun slime-edit-definition-with-etags (name)
3934
(interactive (list (slime-read-symbol-name "Symbol: ")))
3935
(let ((xrefs (slime-etags-definitions name)))
3936
(cond (xrefs
3937
(message "Using tag file...")
3938
(slime-edit-definition-cont xrefs name nil))
3939
(t
3940
(error "No known definition for: %s" name)))))
3941
3942
(defun slime-etags-to-locations (name)
3943
"Search for definitions matching `name' in the currently active
3944
tags table. Return a possibly empty list of slime-locations."
3945
(let ((locs '()))
3946
(save-excursion
3947
(let ((first-time t))
3948
(while (visit-tags-table-buffer (not first-time))
3949
(setq first-time nil)
3950
(goto-char (point-min))
3951
(while (search-forward name nil t)
3952
(beginning-of-line)
3953
(destructuring-bind (hint line &rest pos) (etags-snarf-tag)
3954
(unless (eq hint t) ; hint==t if we are in a filename line
3955
(push `(:location (:file ,(expand-file-name (file-of-tag)))
3956
(:line ,line)
3957
(:snippet ,hint))
3958
locs))))))
3959
(nreverse locs))))
3960
3961
(defun slime-etags-definitions (name)
3962
"Search definitions matching NAME in the tags file.
3963
The result is a (possibly empty) list of definitions."
3964
(mapcar (lambda (loc)
3965
(make-slime-xref :dspec (second (slime-location.hints loc))
3966
:location loc))
3967
(slime-etags-to-locations name)))
3968
3969
;;;;; first-change-hook
3970
3971
(defun slime-first-change-hook ()
3972
"Notify Lisp that a source file's buffer has been modified."
3973
;; Be careful not to disturb anything!
3974
;; In particular if we muck up the match-data then query-replace
3975
;; breaks. -luke (26/Jul/2004)
3976
(save-excursion
3977
(save-match-data
3978
(when (and (buffer-file-name)
3979
(file-exists-p (buffer-file-name))
3980
(slime-background-activities-enabled-p))
3981
(let ((filename (slime-to-lisp-filename (buffer-file-name))))
3982
(slime-eval-async `(swank:buffer-first-change ,filename)))))))
3983
3984
(defun slime-setup-first-change-hook ()
3985
(add-hook (make-local-variable 'first-change-hook)
3986
'slime-first-change-hook))
3987
3988
(add-hook 'slime-mode-hook 'slime-setup-first-change-hook)
3989
3990
3991
;;;; Eval for Lisp
3992
3993
(defun slime-eval-for-lisp (thread tag form-string)
3994
(let ((ok nil)
3995
(value nil)
3996
(c (slime-connection)))
3997
(unwind-protect (progn
3998
(slime-check-eval-in-emacs-enabled)
3999
(setq value (eval (read form-string)))
4000
(setq ok t))
4001
(let ((result (if ok `(:ok ,value) `(:abort))))
4002
(slime-dispatch-event `(:emacs-return ,thread ,tag ,result) c)))))
4003
4004
(defun slime-check-eval-in-emacs-enabled ()
4005
"Raise an error if `slime-enable-evaluate-in-emacs' isn't true."
4006
(unless slime-enable-evaluate-in-emacs
4007
(error (concat "slime-eval-in-emacs disabled for security."
4008
"Set slime-enable-evaluate-in-emacs true to enable it."))))
4009
4010
4011
;;;; `ED'
4012
4013
(defvar slime-ed-frame nil
4014
"The frame used by `slime-ed'.")
4015
4016
(defcustom slime-ed-use-dedicated-frame t
4017
"*When non-nil, `slime-ed' will create and reuse a dedicated frame."
4018
:type 'boolean
4019
:group 'slime-mode)
4020
4021
(defun slime-ed (what)
4022
"Edit WHAT.
4023
4024
WHAT can be:
4025
A filename (string),
4026
A list (:filename FILENAME &key LINE COLUMN POSITION),
4027
A function name (:function-name STRING)
4028
nil.
4029
4030
This is for use in the implementation of COMMON-LISP:ED."
4031
(when slime-ed-use-dedicated-frame
4032
(unless (and slime-ed-frame (frame-live-p slime-ed-frame))
4033
(setq slime-ed-frame (make-frame)))
4034
(select-frame slime-ed-frame))
4035
(when what
4036
(destructure-case what
4037
((:filename file &key line column position)
4038
(find-file (slime-from-lisp-filename file))
4039
(when line (goto-line line))
4040
(when column (move-to-column column))
4041
(when position (goto-char position)))
4042
((:function-name name)
4043
(slime-edit-definition name)))))
4044
4045
(defun slime-y-or-n-p (thread tag question)
4046
(slime-dispatch-event `(:emacs-return ,thread ,tag ,(y-or-n-p question))))
4047
4048
(defun slime-read-from-minibuffer-for-swank (thread tag prompt initial-value)
4049
(let ((answer (condition-case nil
4050
(slime-read-from-minibuffer prompt initial-value)
4051
(quit nil))))
4052
(slime-dispatch-event `(:emacs-return ,thread ,tag ,answer))))
4053
4054
;;;; Interactive evaluation.
4055
4056
(defun slime-interactive-eval (string)
4057
"Read and evaluate STRING and print value in minibuffer.
4058
4059
Note: If a prefix argument is in effect then the result will be
4060
inserted in the current buffer."
4061
(interactive (list (slime-read-from-minibuffer "Slime Eval: ")))
4062
(cond ((not current-prefix-arg)
4063
(slime-eval-with-transcript `(swank:interactive-eval ,string)))
4064
(t
4065
(slime-eval-print string))))
4066
4067
(defun slime-display-eval-result (value)
4068
(slime-message "%s" value))
4069
4070
(defun slime-eval-print (string)
4071
"Eval STRING in Lisp; insert any output and the result at point."
4072
(slime-eval-async `(swank:eval-and-grab-output ,string)
4073
(lambda (result)
4074
(destructuring-bind (output value) result
4075
(insert output value)))))
4076
4077
(defvar slime-transcript-start-hook nil
4078
"Hook run before start an evalution.")
4079
(defvar slime-transcript-stop-hook nil
4080
"Hook run after finishing a evalution.")
4081
4082
(defun slime-eval-with-transcript (form)
4083
"Eval FROM in Lisp. Display output, if any."
4084
(run-hooks 'slime-transcript-start-hook)
4085
(slime-rex () (form)
4086
((:ok value)
4087
(run-hooks 'slime-transcript-stop-hook)
4088
(slime-display-eval-result value))
4089
((:abort)
4090
(run-hooks 'slime-transcript-stop-hook)
4091
(message "Evaluation aborted."))))
4092
4093
(defun slime-eval-describe (form)
4094
"Evaluate FORM in Lisp and display the result in a new buffer."
4095
(slime-eval-async form (slime-rcurry #'slime-show-description
4096
(slime-current-package))))
4097
4098
(defvar slime-description-autofocus nil
4099
"If non-nil select description windows on display.")
4100
4101
(defun slime-show-description (string package)
4102
;; So we can have one description buffer open per connection. Useful
4103
;; for comparing the output of DISASSEMBLE across implementations.
4104
;; FIXME: could easily be achieved with M-x rename-buffer
4105
(let ((bufname (slime-buffer-name :description)))
4106
(slime-with-popup-buffer (bufname :package package
4107
:connection t
4108
:select slime-description-autofocus)
4109
(princ string)
4110
(goto-char (point-min)))))
4111
4112
(defun slime-last-expression ()
4113
(buffer-substring-no-properties
4114
(save-excursion (backward-sexp) (point))
4115
(point)))
4116
4117
(defun slime-eval-last-expression ()
4118
"Evaluate the expression preceding point."
4119
(interactive)
4120
(slime-interactive-eval (slime-last-expression)))
4121
4122
(defun slime-eval-defun ()
4123
"Evaluate the current toplevel form.
4124
Use `slime-re-evaluate-defvar' if the from starts with '(defvar'"
4125
(interactive)
4126
(let ((form (slime-defun-at-point)))
4127
(cond ((string-match "^(defvar " form)
4128
(slime-re-evaluate-defvar form))
4129
(t
4130
(slime-interactive-eval form)))))
4131
4132
(defun slime-eval-region (start end)
4133
"Evaluate region."
4134
(interactive "r")
4135
(slime-eval-with-transcript
4136
`(swank:interactive-eval-region
4137
,(buffer-substring-no-properties start end))))
4138
4139
(defun slime-eval-buffer ()
4140
"Evaluate the current buffer.
4141
The value is printed in the echo area."
4142
(interactive)
4143
(slime-eval-region (point-min) (point-max)))
4144
4145
(defun slime-re-evaluate-defvar (form)
4146
"Force the re-evaluaton of the defvar form before point.
4147
4148
First make the variable unbound, then evaluate the entire form."
4149
(interactive (list (slime-last-expression)))
4150
(slime-eval-with-transcript `(swank:re-evaluate-defvar ,form)))
4151
4152
(defun slime-pprint-eval-last-expression ()
4153
"Evaluate the form before point; pprint the value in a buffer."
4154
(interactive)
4155
(slime-eval-describe `(swank:pprint-eval ,(slime-last-expression))))
4156
4157
(defun slime-eval-print-last-expression (string)
4158
"Evaluate sexp before point; print value into the current buffer"
4159
(interactive (list (slime-last-expression)))
4160
(insert "\n")
4161
(slime-eval-print string))
4162
4163
;;;; Edit Lisp value
4164
;;;
4165
(defun slime-edit-value (form-string)
4166
"\\<slime-edit-value-mode-map>\
4167
Edit the value of a setf'able form in a new buffer.
4168
The value is inserted into a temporary buffer for editing and then set
4169
in Lisp when committed with \\[slime-edit-value-commit]."
4170
(interactive
4171
(list (slime-read-from-minibuffer "Edit value (evaluated): "
4172
(slime-sexp-at-point))))
4173
(slime-eval-async `(swank:value-for-editing ,form-string)
4174
(lexical-let ((form-string form-string)
4175
(package (slime-current-package)))
4176
(lambda (result)
4177
(slime-edit-value-callback form-string result
4178
package)))))
4179
4180
(make-variable-buffer-local
4181
(defvar slime-edit-form-string nil
4182
"The form being edited by `slime-edit-value'."))
4183
4184
(define-minor-mode slime-edit-value-mode
4185
"Mode for editing a Lisp value."
4186
nil
4187
" Edit-Value"
4188
'(("\C-c\C-c" . slime-edit-value-commit)))
4189
4190
(defun slime-edit-value-callback (form-string current-value package)
4191
(let* ((name (generate-new-buffer-name (format "*Edit %s*" form-string)))
4192
(buffer (slime-with-popup-buffer (name :package package
4193
:connection t
4194
:select t
4195
:mode 'lisp-mode)
4196
(slime-popup-buffer-mode -1) ; don't want binding of 'q'
4197
(slime-mode 1)
4198
(slime-edit-value-mode 1)
4199
(setq slime-edit-form-string form-string)
4200
(insert current-value)
4201
(current-buffer))))
4202
(with-current-buffer buffer
4203
(setq buffer-read-only nil)
4204
(message "Type C-c C-c when done"))))
4205
4206
(defun slime-edit-value-commit ()
4207
"Commit the edited value to the Lisp image.
4208
\\(See `slime-edit-value'.)"
4209
(interactive)
4210
(if (null slime-edit-form-string)
4211
(error "Not editing a value.")
4212
(let ((value (buffer-substring-no-properties (point-min) (point-max))))
4213
(lexical-let ((buffer (current-buffer)))
4214
(slime-eval-async `(swank:commit-edited-value ,slime-edit-form-string
4215
,value)
4216
(lambda (_)
4217
(with-current-buffer buffer
4218
(slime-popup-buffer-quit t))))))))
4219
4220
;;;; Tracing
4221
4222
(defun slime-untrace-all ()
4223
"Untrace all functions."
4224
(interactive)
4225
(slime-eval `(swank:untrace-all)))
4226
4227
(defun slime-toggle-trace-fdefinition (spec)
4228
"Toggle trace."
4229
(interactive (list (slime-read-from-minibuffer
4230
"(Un)trace: " (slime-symbol-at-point))))
4231
(message "%s" (slime-eval `(swank:swank-toggle-trace ,spec))))
4232
4233
4234
4235
(defun slime-disassemble-symbol (symbol-name)
4236
"Display the disassembly for SYMBOL-NAME."
4237
(interactive (list (slime-read-symbol-name "Disassemble: ")))
4238
(slime-eval-describe `(swank:disassemble-form ,(concat "'" symbol-name))))
4239
4240
(defun slime-undefine-function (symbol-name)
4241
"Unbind the function slot of SYMBOL-NAME."
4242
(interactive (list (slime-read-symbol-name "fmakunbound: " t)))
4243
(slime-eval-async `(swank:undefine-function ,symbol-name)
4244
(lambda (result) (message "%s" result))))
4245
4246
(defun slime-load-file (filename)
4247
"Load the Lisp file FILENAME."
4248
(interactive (list
4249
(read-file-name "Load file: " nil nil
4250
nil (if (buffer-file-name)
4251
(file-name-nondirectory
4252
(buffer-file-name))))))
4253
(let ((lisp-filename (slime-to-lisp-filename (expand-file-name filename))))
4254
(slime-eval-with-transcript `(swank:load-file ,lisp-filename))))
4255
4256
(defvar slime-change-directory-hooks nil
4257
"Hook run by `slime-change-directory'.
4258
The functions are called with the new (absolute) directory.")
4259
4260
(defun slime-change-directory (directory)
4261
"Make DIRECTORY become Lisp's current directory.
4262
Return whatever swank:set-default-directory returns."
4263
(let ((dir (expand-file-name directory)))
4264
(prog1 (slime-eval `(swank:set-default-directory
4265
,(slime-to-lisp-filename dir)))
4266
(slime-with-connection-buffer nil (cd-absolute dir))
4267
(run-hook-with-args 'slime-change-directory-hooks dir))))
4268
4269
(defun slime-cd (directory)
4270
"Make DIRECTORY become Lisp's current directory.
4271
Return whatever swank:set-default-directory returns."
4272
(interactive (list (read-directory-name "Directory: " nil nil t)))
4273
(message "default-directory: %s" (slime-change-directory directory)))
4274
4275
(defun slime-pwd ()
4276
"Show Lisp's default directory."
4277
(interactive)
4278
(message "Directory %s" (slime-eval `(swank:default-directory))))
4279
4280
4281
;;;; Profiling
4282
4283
(defun slime-toggle-profile-fdefinition (fname-string)
4284
"Toggle profiling for FNAME-STRING."
4285
(interactive (list (slime-read-from-minibuffer
4286
"(Un)Profile: "
4287
(slime-symbol-at-point))))
4288
(slime-eval-async `(swank:toggle-profile-fdefinition ,fname-string)
4289
(lambda (r) (message "%s" r))))
4290
4291
(defun slime-unprofile-all ()
4292
"Unprofile all functions."
4293
(interactive)
4294
(slime-eval-async '(swank:unprofile-all)
4295
(lambda (r) (message "%s" r))))
4296
4297
(defun slime-profile-report ()
4298
"Print profile report."
4299
(interactive)
4300
(slime-eval-with-transcript '(swank:profile-report)))
4301
4302
(defun slime-profile-reset ()
4303
"Reset profile counters."
4304
(interactive)
4305
(slime-eval-async (slime-eval `(swank:profile-reset))
4306
(lambda (r) (message "%s" r))))
4307
4308
(defun slime-profiled-functions ()
4309
"Return list of names of currently profiled functions."
4310
(interactive)
4311
(slime-eval-async `(swank:profiled-functions)
4312
(lambda (r) (message "%s" r))))
4313
4314
(defun slime-profile-package (package callers methods)
4315
"Profile all functions in PACKAGE.
4316
If CALLER is non-nil names have counts of the most common calling
4317
functions recorded.
4318
If METHODS is non-nil, profile all methods of all generic function
4319
having names in the given package."
4320
(interactive (list (slime-read-package-name "Package: ")
4321
(y-or-n-p "Record the most common callers? ")
4322
(y-or-n-p "Profile methods? ")))
4323
(slime-eval-async `(swank:profile-package ,package ,callers ,methods)
4324
(lambda (r) (message "%s" r))))
4325
4326
(defun slime-profile-by-substring (substring &optional package)
4327
"Profile all functions which names contain SUBSTRING.
4328
If PACKAGE is NIL, then search in all packages."
4329
(interactive (list
4330
(slime-read-from-minibuffer
4331
"Profile by matching substring: "
4332
(slime-symbol-at-point))
4333
(slime-read-package-name "Package (RET for all packages): ")))
4334
(let ((package (unless (equal package "") package)))
4335
(slime-eval-async `(swank:profile-by-substring ,substring ,package)
4336
(lambda (r) (message "%s" r)) )))
4337
4338
;;;; Documentation
4339
4340
(defvar slime-documentation-lookup-function
4341
'slime-hyperspec-lookup)
4342
4343
(defun slime-documentation-lookup ()
4344
"Generalized documentation lookup. Defaults to hyperspec lookup."
4345
(interactive)
4346
(call-interactively slime-documentation-lookup-function))
4347
4348
(defun slime-hyperspec-lookup (symbol-name)
4349
"A wrapper for `hyperspec-lookup'"
4350
(interactive (list (let* ((symbol-at-point (slime-symbol-at-point))
4351
(stripped-symbol
4352
(and symbol-at-point
4353
(downcase
4354
(common-lisp-hyperspec-strip-cl-package
4355
symbol-at-point)))))
4356
(if (and stripped-symbol
4357
(intern-soft stripped-symbol
4358
common-lisp-hyperspec-symbols))
4359
stripped-symbol
4360
(completing-read
4361
"Look up symbol in Common Lisp HyperSpec: "
4362
common-lisp-hyperspec-symbols #'boundp
4363
t stripped-symbol
4364
'common-lisp-hyperspec-history)))))
4365
(hyperspec-lookup symbol-name))
4366
4367
(defun slime-describe-symbol (symbol-name)
4368
"Describe the symbol at point."
4369
(interactive (list (slime-read-symbol-name "Describe symbol: ")))
4370
(when (not symbol-name)
4371
(error "No symbol given"))
4372
(slime-eval-describe `(swank:describe-symbol ,symbol-name)))
4373
4374
(defun slime-documentation (symbol-name)
4375
"Display function- or symbol-documentation for SYMBOL-NAME."
4376
(interactive (list (slime-read-symbol-name "Documentation for symbol: ")))
4377
(when (not symbol-name)
4378
(error "No symbol given"))
4379
(slime-eval-describe
4380
`(swank:documentation-symbol ,symbol-name)))
4381
4382
(defun slime-describe-function (symbol-name)
4383
(interactive (list (slime-read-symbol-name "Describe symbol: ")))
4384
(when (not symbol-name)
4385
(error "No symbol given"))
4386
(slime-eval-describe `(swank:describe-function ,symbol-name)))
4387
4388
(defun slime-apropos-summary (string case-sensitive-p package only-external-p)
4389
"Return a short description for the performed apropos search."
4390
(concat (if case-sensitive-p "Case-sensitive " "")
4391
"Apropos for "
4392
(format "%S" string)
4393
(if package (format " in package %S" package) "")
4394
(if only-external-p " (external symbols only)" "")))
4395
4396
(defun slime-apropos (string &optional only-external-p package
4397
case-sensitive-p)
4398
"Show all bound symbols whose names match STRING. With prefix
4399
arg, you're interactively asked for parameters of the search."
4400
(interactive
4401
(if current-prefix-arg
4402
(list (read-string "SLIME Apropos: ")
4403
(y-or-n-p "External symbols only? ")
4404
(let ((pkg (slime-read-package-name "Package: ")))
4405
(if (string= pkg "") nil pkg))
4406
(y-or-n-p "Case-sensitive? "))
4407
(list (read-string "SLIME Apropos: ") t nil nil)))
4408
(let ((buffer-package (or package (slime-current-package))))
4409
(slime-eval-async
4410
`(swank:apropos-list-for-emacs ,string ,only-external-p
4411
,case-sensitive-p ',package)
4412
(slime-rcurry #'slime-show-apropos string buffer-package
4413
(slime-apropos-summary string case-sensitive-p
4414
package only-external-p)))))
4415
4416
(defun slime-apropos-all ()
4417
"Shortcut for (slime-apropos <string> nil nil)"
4418
(interactive)
4419
(slime-apropos (read-string "SLIME Apropos: ") nil nil))
4420
4421
(defun slime-apropos-package (package &optional internal)
4422
"Show apropos listing for symbols in PACKAGE.
4423
With prefix argument include internal symbols."
4424
(interactive (list (let ((pkg (slime-read-package-name "Package: ")))
4425
(if (string= pkg "") (slime-current-package) pkg))
4426
current-prefix-arg))
4427
(slime-apropos "" (not internal) package))
4428
4429
(defun slime-show-apropos (plists string package summary)
4430
(if (null plists)
4431
(message "No apropos matches for %S" string)
4432
(slime-with-popup-buffer ((slime-buffer-name :apropos)
4433
:package package :connection t
4434
:mode 'apropos-mode)
4435
(if (boundp 'header-line-format)
4436
(setq header-line-format summary)
4437
(insert summary "\n\n"))
4438
(slime-set-truncate-lines)
4439
(slime-print-apropos plists)
4440
(set-syntax-table lisp-mode-syntax-table)
4441
(goto-char (point-min)))))
4442
4443
(defvar slime-apropos-label-properties
4444
(progn
4445
(require 'apropos)
4446
(cond ((and (boundp 'apropos-label-properties)
4447
(symbol-value 'apropos-label-properties)))
4448
((boundp 'apropos-label-face)
4449
(etypecase (symbol-value 'apropos-label-face)
4450
(symbol `(face ,(or (symbol-value 'apropos-label-face)
4451
'italic)
4452
mouse-face highlight))
4453
(list (symbol-value 'apropos-label-face)))))))
4454
4455
(defun slime-print-apropos (plists)
4456
(dolist (plist plists)
4457
(let ((designator (plist-get plist :designator)))
4458
(assert designator)
4459
(slime-insert-propertized `(face ,apropos-symbol-face) designator))
4460
(terpri)
4461
(let ((apropos-label-properties slime-apropos-label-properties))
4462
(loop for (prop namespace)
4463
in '((:variable "Variable")
4464
(:function "Function")
4465
(:generic-function "Generic Function")
4466
(:macro "Macro")
4467
(:special-operator "Special Operator")
4468
(:setf "Setf")
4469
(:type "Type")
4470
(:class "Class")
4471
(:alien-type "Alien type")
4472
(:alien-struct "Alien struct")
4473
(:alien-union "Alien type")
4474
(:alien-enum "Alien enum"))
4475
;; Properties not listed here will not show up in the buffer
4476
do
4477
(let ((value (plist-get plist prop))
4478
(start (point)))
4479
(when value
4480
(princ " ")
4481
(slime-insert-propertized apropos-label-properties namespace)
4482
(princ ": ")
4483
(princ (etypecase value
4484
(string value)
4485
((member :not-documented) "(not documented)")))
4486
(add-text-properties
4487
start (point)
4488
(list 'type prop 'action 'slime-call-describer
4489
'button t 'apropos-label namespace
4490
'item (plist-get plist :designator)))
4491
(terpri)))))))
4492
4493
(defun slime-call-describer (arg)
4494
(let* ((pos (if (markerp arg) arg (point)))
4495
(type (get-text-property pos 'type))
4496
(item (get-text-property pos 'item)))
4497
(slime-eval-describe `(swank:describe-definition-for-emacs ,item ,type))))
4498
4499
(defun slime-info ()
4500
"Open Slime manual"
4501
(interactive)
4502
(let ((file (expand-file-name "doc/slime.info" slime-path)))
4503
(if (file-exists-p file)
4504
(info file)
4505
(message "No slime.info, run `make slime.info' in %s"
4506
(expand-file-name "doc/" slime-path)))))
4507
4508
4509
;;;; XREF: cross-referencing
4510
4511
(defvar slime-xref-mode-map)
4512
4513
(define-derived-mode slime-xref-mode lisp-mode "Xref"
4514
"slime-xref-mode: Major mode for cross-referencing.
4515
\\<slime-xref-mode-map>\
4516
The most important commands:
4517
\\[slime-xref-quit] - Dismiss buffer.
4518
\\[slime-show-xref] - Display referenced source and keep xref window.
4519
\\[slime-goto-xref] - Jump to referenced source and dismiss xref window.
4520
4521
\\{slime-xref-mode-map}
4522
\\{slime-popup-buffer-mode-map}
4523
"
4524
(setq font-lock-defaults nil)
4525
(setq delayed-mode-hooks nil)
4526
(slime-mode -1))
4527
4528
(slime-define-keys slime-xref-mode-map
4529
((kbd "RET") 'slime-goto-xref)
4530
((kbd "SPC") 'slime-goto-xref)
4531
("v" 'slime-show-xref)
4532
("n" (lambda () (interactive) (next-line)))
4533
("p" (lambda () (interactive) (previous-line)))
4534
("\C-c\C-c" 'slime-recompile-xref)
4535
("\C-c\C-k" 'slime-recompile-all-xrefs)
4536
("\M-," 'slime-xref-retract)
4537
([remap next-line] 'slime-xref-next-line)
4538
([remap previous-line] 'slime-xref-prev-line)
4539
;; for XEmacs:
4540
([down] 'slime-xref-next-line)
4541
([up] 'slime-xref-prev-line))
4542
4543
(defun slime-next-line/not-add-newlines ()
4544
(interactive)
4545
(let ((next-line-add-newlines nil))
4546
(next-line 1)))
4547
4548
4549
;;;;; XREF results buffer and window management
4550
4551
(defmacro* slime-with-xref-buffer ((xref-type symbol &optional package)
4552
&body body)
4553
"Execute BODY in a xref buffer, then show that buffer."
4554
`(let ((xref-buffer-name% (slime-buffer-name :xref)))
4555
(slime-with-popup-buffer (xref-buffer-name%
4556
:package ,package
4557
:connection t
4558
:select t
4559
:mode 'slime-xref-mode)
4560
(slime-set-truncate-lines)
4561
,@body)))
4562
4563
(put 'slime-with-xref-buffer 'lisp-indent-function 1)
4564
4565
(defun slime-insert-xrefs (xref-alist)
4566
"Insert XREF-ALIST in the current-buffer.
4567
XREF-ALIST is of the form ((GROUP . ((LABEL LOCATION) ...)) ...).
4568
GROUP and LABEL are for decoration purposes. LOCATION is a
4569
source-location."
4570
(loop for (group . refs) in xref-alist do
4571
(slime-insert-propertized '(face bold) group "\n")
4572
(loop for (label location) in refs do
4573
(slime-insert-propertized
4574
(list 'slime-location location 'face 'font-lock-keyword-face)
4575
" " (slime-one-line-ify label) "\n")))
4576
;; Remove the final newline to prevent accidental window-scrolling
4577
(backward-delete-char 1))
4578
4579
(defun slime-xref-next-line ()
4580
(interactive)
4581
(slime-xref-show-location (slime-search-property 'slime-location)))
4582
4583
(defun slime-xref-prev-line ()
4584
(interactive)
4585
(slime-xref-show-location (slime-search-property 'slime-location t)))
4586
4587
(defun slime-xref-show-location (loc)
4588
(ecase (car loc)
4589
(:location (slime-show-source-location loc t))
4590
(:error (message "%s" (cadr loc)))
4591
((nil))))
4592
4593
(defvar slime-next-location-function nil
4594
"Function to call for going to the next location.")
4595
4596
(defvar slime-previous-location-function nil
4597
"Function to call for going to the previous location.")
4598
4599
(defvar slime-xref-last-buffer nil
4600
"The most recent XREF results buffer.
4601
This is used by `slime-goto-next-xref'")
4602
4603
(defun slime-show-xref-buffer (xrefs type symbol package)
4604
(slime-with-xref-buffer (type symbol package)
4605
(slime-insert-xrefs xrefs)
4606
(setq slime-next-location-function 'slime-goto-next-xref)
4607
(setq slime-previous-location-function 'slime-goto-previous-xref)
4608
(setq slime-xref-last-buffer (current-buffer))
4609
(goto-char (point-min))))
4610
4611
(defun slime-show-xrefs (xrefs type symbol package)
4612
"Show the results of an XREF query."
4613
(if (null xrefs)
4614
(message "No references found for %s." symbol)
4615
(slime-show-xref-buffer xrefs type symbol package)))
4616
4617
4618
;;;;; XREF commands
4619
4620
(defun slime-who-calls (symbol)
4621
"Show all known callers of the function SYMBOL."
4622
(interactive (list (slime-read-symbol-name "Who calls: " t)))
4623
(slime-xref :calls symbol))
4624
4625
(defun slime-calls-who (symbol)
4626
"Show all known functions called by the function SYMBOL."
4627
(interactive (list (slime-read-symbol-name "Who calls: " t)))
4628
(slime-xref :calls-who symbol))
4629
4630
(defun slime-who-references (symbol)
4631
"Show all known referrers of the global variable SYMBOL."
4632
(interactive (list (slime-read-symbol-name "Who references: " t)))
4633
(slime-xref :references symbol))
4634
4635
(defun slime-who-binds (symbol)
4636
"Show all known binders of the global variable SYMBOL."
4637
(interactive (list (slime-read-symbol-name "Who binds: " t)))
4638
(slime-xref :binds symbol))
4639
4640
(defun slime-who-sets (symbol)
4641
"Show all known setters of the global variable SYMBOL."
4642
(interactive (list (slime-read-symbol-name "Who sets: " t)))
4643
(slime-xref :sets symbol))
4644
4645
(defun slime-who-macroexpands (symbol)
4646
"Show all known expanders of the macro SYMBOL."
4647
(interactive (list (slime-read-symbol-name "Who macroexpands: " t)))
4648
(slime-xref :macroexpands symbol))
4649
4650
(defun slime-who-specializes (symbol)
4651
"Show all known methods specialized on class SYMBOL."
4652
(interactive (list (slime-read-symbol-name "Who specializes: " t)))
4653
(slime-xref :specializes symbol))
4654
4655
(defun slime-list-callers (symbol-name)
4656
"List the callers of SYMBOL-NAME in a xref window."
4657
(interactive (list (slime-read-symbol-name "List callers: ")))
4658
(slime-xref :callers symbol-name))
4659
4660
(defun slime-list-callees (symbol-name)
4661
"List the callees of SYMBOL-NAME in a xref window."
4662
(interactive (list (slime-read-symbol-name "List callees: ")))
4663
(slime-xref :callees symbol-name))
4664
4665
(defun slime-xref (type symbol &optional continuation)
4666
"Make an XREF request to Lisp."
4667
(slime-eval-async
4668
`(swank:xref ',type ',symbol)
4669
(slime-rcurry (lambda (result type symbol package cont)
4670
(slime-check-xref-implemented type result)
4671
(let* ((xrefs (slime-postprocess-xrefs result))
4672
(file-alist (cadr (slime-analyze-xrefs result))))
4673
(funcall (or cont 'slime-show-xrefs)
4674
file-alist type symbol package)))
4675
type
4676
symbol
4677
(slime-current-package)
4678
continuation)))
4679
4680
(defun slime-check-xref-implemented (type xrefs)
4681
(when (eq xrefs :not-implemented)
4682
(error "%s is not implemented yet on %s."
4683
(slime-xref-type type)
4684
(slime-lisp-implementation-name))))
4685
4686
(defun slime-xref-type (type)
4687
(format "who-%s" (slime-cl-symbol-name type)))
4688
4689
(defun slime-xrefs (types symbol &optional continuation)
4690
"Make multiple XREF requests at once."
4691
(slime-eval-async
4692
`(swank:xrefs ',types ',symbol)
4693
(slime-rcurry (lambda (result types symbol package cont)
4694
(funcall (or cont 'slime-show-xrefs)
4695
(slime-map-alist #'slime-xref-type
4696
#'identity
4697
result)
4698
types symbol package))
4699
types
4700
symbol
4701
(slime-current-package)
4702
continuation)))
4703
4704
4705
;;;;; XREF navigation
4706
4707
(defun slime-xref-location-at-point ()
4708
(save-excursion
4709
;; When the end of the last line is at (point-max) we can't find
4710
;; the text property there. Going to bol avoids this problem.
4711
(beginning-of-line 1)
4712
(or (get-text-property (point) 'slime-location)
4713
(error "No reference at point."))))
4714
4715
(defun slime-xref-dspec-at-point ()
4716
(save-excursion
4717
(beginning-of-line 1)
4718
(with-syntax-table lisp-mode-syntax-table
4719
(forward-sexp) ; skip initial whitespaces
4720
(backward-sexp)
4721
(slime-sexp-at-point))))
4722
4723
(defun slime-all-xrefs ()
4724
(let ((xrefs nil))
4725
(save-excursion
4726
(goto-char (point-min))
4727
(while (ignore-errors (slime-next-line/not-add-newlines) t)
4728
(when-let (loc (get-text-property (point) 'slime-location))
4729
(let* ((dspec (slime-xref-dspec-at-point))
4730
(xref (make-slime-xref :dspec dspec :location loc)))
4731
(push xref xrefs)))))
4732
(nreverse xrefs)))
4733
4734
(defun slime-goto-xref ()
4735
"Goto the cross-referenced location at point."
4736
(interactive)
4737
(slime-show-xref)
4738
(slime-popup-buffer-quit))
4739
4740
(defun slime-show-xref ()
4741
"Display the xref at point in the other window."
4742
(interactive)
4743
(let ((location (slime-xref-location-at-point)))
4744
(slime-show-source-location location)))
4745
4746
(defun slime-goto-next-xref (&optional backward)
4747
"Goto the next cross-reference location."
4748
(if (not (buffer-live-p slime-xref-last-buffer))
4749
(error "No XREF buffer alive.")
4750
(multiple-value-bind (location pos)
4751
(with-current-buffer slime-xref-last-buffer
4752
(values (slime-search-property 'slime-location backward)
4753
(point)))
4754
(cond ((slime-location-p location)
4755
(slime-pop-to-location location)
4756
;; We do this here because changing the location can take
4757
;; a while when Emacs needs to read a file from disk.
4758
(with-current-buffer slime-xref-last-buffer
4759
(slime-show-buffer-position pos)
4760
(slime-highlight-line 0.35)))
4761
((null location)
4762
(message (if backward "No previous xref" "No next xref.")))
4763
(t ; error location
4764
(slime-goto-next-xref backward))))))
4765
4766
(defun slime-goto-previous-xref ()
4767
"Goto the previous cross-reference location."
4768
(slime-goto-next-xref t))
4769
4770
(defun slime-search-property (prop &optional backward prop-value-fn)
4771
"Search the next text range where PROP is non-nil.
4772
Return the value of PROP.
4773
If BACKWARD is non-nil, search backward.
4774
If PROP-VALUE-FN is non-nil use it to extract PROP's value."
4775
(let ((next-candidate (if backward
4776
#'previous-single-char-property-change
4777
#'next-single-char-property-change))
4778
(prop-value-fn (or prop-value-fn
4779
(lambda ()
4780
(get-text-property (point) prop))))
4781
(start (point))
4782
(prop-value))
4783
(while (progn
4784
(goto-char (funcall next-candidate (point) prop))
4785
(not (or (setq prop-value (funcall prop-value-fn))
4786
(eobp)
4787
(bobp)))))
4788
(cond (prop-value)
4789
(t (goto-char start) nil))))
4790
4791
(defun slime-next-location ()
4792
"Go to the next location, depending on context.
4793
When displaying XREF information, this goes to the next reference."
4794
(interactive)
4795
(when (null slime-next-location-function)
4796
(error "No context for finding locations."))
4797
(funcall slime-next-location-function))
4798
4799
(defun slime-previous-location ()
4800
"Go to the previous location, depending on context.
4801
When displaying XREF information, this goes to the previous reference."
4802
(interactive)
4803
(when (null slime-previous-location-function)
4804
(error "No context for finding locations."))
4805
(funcall slime-previous-location-function))
4806
4807
(defun slime-recompile-xref (&optional raw-prefix-arg)
4808
(interactive "P")
4809
(let ((slime-compilation-policy (slime-compute-policy raw-prefix-arg)))
4810
(let ((location (slime-xref-location-at-point))
4811
(dspec (slime-xref-dspec-at-point)))
4812
(slime-recompile-locations
4813
(list location)
4814
(slime-rcurry #'slime-xref-recompilation-cont
4815
(list dspec) (current-buffer))))))
4816
4817
(defun slime-recompile-all-xrefs (&optional raw-prefix-arg)
4818
(interactive "P")
4819
(let ((slime-compilation-policy (slime-compute-policy raw-prefix-arg)))
4820
(let ((dspecs) (locations))
4821
(dolist (xref (slime-all-xrefs))
4822
(when (slime-xref-has-location-p xref)
4823
(push (slime-xref.dspec xref) dspecs)
4824
(push (slime-xref.location xref) locations)))
4825
(slime-recompile-locations
4826
locations
4827
(slime-rcurry #'slime-xref-recompilation-cont
4828
dspecs (current-buffer))))))
4829
4830
(defun slime-xref-recompilation-cont (results dspecs buffer)
4831
;; Extreme long-windedness to insert status of recompilation;
4832
;; sometimes Elisp resembles more of an Ewwlisp.
4833
4834
;; FIXME: Should probably throw out the whole recompilation cruft
4835
;; anyway. -- helmut
4836
(with-current-buffer buffer
4837
(slime-compilation-finished (slime-aggregate-compilation-results results))
4838
(save-excursion
4839
(slime-xref-insert-recompilation-flags
4840
dspecs (loop for r in results collect
4841
(or (slime-compilation-result.successp r)
4842
(and (slime-compilation-result.notes r)
4843
:complained)))))))
4844
4845
(defun slime-aggregate-compilation-results (results)
4846
`(:compilation-result
4847
,(reduce #'append (mapcar #'slime-compilation-result.notes results))
4848
,(every #'slime-compilation-result.successp results)
4849
,(reduce #'+ (mapcar #'slime-compilation-result.duration results))))
4850
4851
(defun slime-xref-insert-recompilation-flags (dspecs compilation-results)
4852
(let* ((buffer-read-only nil)
4853
(max-column (slime-column-max)))
4854
(goto-char (point-min))
4855
(loop for dspec in dspecs
4856
for result in compilation-results
4857
do (save-excursion
4858
(loop for dspec-at-point = (progn (search-forward dspec)
4859
(slime-xref-dspec-at-point))
4860
until (equal dspec-at-point dspec))
4861
(end-of-line) ; skip old status information.
4862
(dotimes (i (- max-column (current-column)))
4863
(insert " "))
4864
(insert " ")
4865
(insert (format "[%s]"
4866
(case result
4867
((t) :success)
4868
((nil) :failure)
4869
(t result))))))))
4870
4871
4872
;;;; Macroexpansion
4873
4874
(define-minor-mode slime-macroexpansion-minor-mode
4875
"SLIME mode for macroexpansion"
4876
nil
4877
" Macroexpand"
4878
'(("g" . slime-macroexpand-again)))
4879
4880
(flet ((remap (from to)
4881
(dolist (mapping (where-is-internal from slime-mode-map))
4882
(define-key slime-macroexpansion-minor-mode-map mapping to))))
4883
(remap 'slime-macroexpand-1 'slime-macroexpand-1-inplace)
4884
(remap 'slime-macroexpand-all 'slime-macroexpand-all-inplace)
4885
(remap 'slime-compiler-macroexpand-1 'slime-compiler-macroexpand-1-inplace)
4886
(remap 'slime-compiler-macroexpand 'slime-compiler-macroexpand-inplace)
4887
(remap 'advertised-undo 'slime-macroexpand-undo)
4888
(remap 'undo 'slime-macroexpand-undo))
4889
4890
(defun slime-macroexpand-undo (&optional arg)
4891
(interactive)
4892
(flet ((undo-only (arg)
4893
;; Emacs 22.x introduced `undo-only' which works by binding
4894
;; `undo-no-redo' to t. We do it this way so we don't break
4895
;; prior Emacs versions.
4896
(let ((undo-no-redo t)) (undo arg))))
4897
(let ((inhibit-read-only t))
4898
(when (fboundp 'slime-remove-edits)
4899
(slime-remove-edits (point-min) (point-max)))
4900
(undo-only arg))))
4901
4902
(defun slime-sexp-at-point-for-macroexpansion ()
4903
"`slime-sexp-at-point' with special cases for LOOP."
4904
(let ((string (slime-sexp-at-point-or-error))
4905
(bounds (bounds-of-thing-at-point 'sexp))
4906
(char-at-point (substring-no-properties (thing-at-point 'char))))
4907
;; SLIME-SEXP-AT-POINT(-OR-ERROR) uses (THING-AT-POINT 'SEXP)
4908
;; which is quite a bit botched: it returns "'(FOO BAR BAZ)" even
4909
;; when point is placed _at the opening parenthesis_, and hence
4910
;; "(FOO BAR BAZ)" wouldn't get expanded. Likewise for ",(...)",
4911
;; ",@(...)" (would return "@(...)"!!), and "\"(...)".
4912
;; So we better fix this up here:
4913
(when (string= char-at-point "(")
4914
(let ((char0 (elt string 0)))
4915
(when (member char0 '(?\' ?\, ?\" ?\@))
4916
(setf string (substring string 1))
4917
(incf (car bounds)))))
4918
(list string (cons (set-marker (make-marker) (car bounds))
4919
(set-marker (make-marker) (cdr bounds))))))
4920
4921
(defvar slime-eval-macroexpand-expression nil
4922
"Specifies the last macroexpansion preformed.
4923
This variable specifies both what was expanded and how.")
4924
4925
(defun slime-eval-macroexpand (expander &optional string)
4926
(let ((string (or string
4927
(car (slime-sexp-at-point-for-macroexpansion)))))
4928
(setq slime-eval-macroexpand-expression `(,expander ,string))
4929
(slime-eval-async slime-eval-macroexpand-expression
4930
#'slime-initialize-macroexpansion-buffer)))
4931
4932
(defun slime-macroexpand-again ()
4933
"Reperform the last macroexpansion."
4934
(interactive)
4935
(slime-eval-async slime-eval-macroexpand-expression
4936
(slime-rcurry #'slime-initialize-macroexpansion-buffer
4937
(current-buffer))))
4938
4939
(defun slime-initialize-macroexpansion-buffer (expansion &optional buffer)
4940
(pop-to-buffer (or buffer (slime-create-macroexpansion-buffer)))
4941
(setq buffer-undo-list nil) ; Get rid of undo information from
4942
; previous expansions.
4943
(let ((inhibit-read-only t)
4944
(buffer-undo-list t)) ; Make the initial insertion not be undoable.
4945
(erase-buffer)
4946
(insert expansion)
4947
(goto-char (point-min))
4948
(indent-sexp)
4949
(font-lock-fontify-buffer)))
4950
4951
(defun slime-create-macroexpansion-buffer ()
4952
(let ((name (slime-buffer-name :macroexpansion)))
4953
(slime-with-popup-buffer (name :package t :connection t
4954
:mode 'lisp-mode)
4955
(slime-mode 1)
4956
(slime-macroexpansion-minor-mode 1)
4957
(setq font-lock-keywords-case-fold-search t)
4958
(current-buffer))))
4959
4960
(defun slime-eval-macroexpand-inplace (expander)
4961
"Substitute the sexp at point with its macroexpansion.
4962
4963
NB: Does not affect slime-eval-macroexpand-expression"
4964
(interactive)
4965
(destructuring-bind (string bounds)
4966
(slime-sexp-at-point-for-macroexpansion)
4967
(lexical-let* ((start (car bounds))
4968
(end (cdr bounds))
4969
(point (point))
4970
(package (slime-current-package))
4971
(buffer (current-buffer)))
4972
(slime-eval-async
4973
`(,expander ,string)
4974
(lambda (expansion)
4975
(with-current-buffer buffer
4976
(let ((buffer-read-only nil))
4977
(when (fboundp 'slime-remove-edits)
4978
(slime-remove-edits (point-min) (point-max)))
4979
(goto-char start)
4980
(delete-region start end)
4981
(insert expansion)
4982
(goto-char start)
4983
(indent-sexp)
4984
(goto-char point))))))))
4985
4986
(defun slime-macroexpand-1 (&optional repeatedly)
4987
"Display the macro expansion of the form at point.
4988
The form is expanded with CL:MACROEXPAND-1 or, if a prefix
4989
argument is given, with CL:MACROEXPAND."
4990
(interactive "P")
4991
(slime-eval-macroexpand
4992
(if repeatedly 'swank:swank-macroexpand 'swank:swank-macroexpand-1)))
4993
4994
(defun slime-macroexpand-1-inplace (&optional repeatedly)
4995
(interactive "P")
4996
(slime-eval-macroexpand-inplace
4997
(if repeatedly 'swank:swank-macroexpand 'swank:swank-macroexpand-1)))
4998
4999
(defun slime-macroexpand-all ()
5000
"Display the recursively macro expanded sexp at point."
5001
(interactive)
5002
(slime-eval-macroexpand 'swank:swank-macroexpand-all))
5003
5004
(defun slime-macroexpand-all-inplace ()
5005
"Display the recursively macro expanded sexp at point."
5006
(interactive)
5007
(slime-eval-macroexpand-inplace 'swank:swank-macroexpand-all))
5008
5009
(defun slime-compiler-macroexpand ()
5010
"Display the compiler-macro expansion of sexp at point."
5011
(interactive)
5012
(slime-eval-macroexpand 'swank:swank-compiler-macroexpand))
5013
5014
(defun slime-compiler-macroexpand-inplace ()
5015
"Display the compiler-macro expansion of sexp at point."
5016
(interactive)
5017
(slime-eval-macroexpand-inplace 'swank:swank-compiler-macroexpand))
5018
5019
(defun slime-compiler-macroexpand-1 ()
5020
"Display the compiler-macro expansion of sexp at point."
5021
(interactive)
5022
(slime-eval-macroexpand 'swank:swank-compiler-macroexpand-1))
5023
5024
(defun slime-compiler-macroexpand-1-inplace ()
5025
"Display the compiler-macro expansion of sexp at point."
5026
(interactive)
5027
(slime-eval-macroexpand-inplace 'swank:swank-compiler-macroexpand-1))
5028
5029
(defun slime-format-string-expand ()
5030
"Expand the format-string at point and display it."
5031
(interactive)
5032
(slime-eval-macroexpand 'swank:swank-format-string-expand
5033
(slime-string-at-point-or-error)))
5034
5035
5036
;;;; Subprocess control
5037
5038
(defun slime-interrupt ()
5039
"Interrupt Lisp."
5040
(interactive)
5041
(cond ((slime-use-sigint-for-interrupt) (slime-send-sigint))
5042
(t (slime-dispatch-event `(:emacs-interrupt ,slime-current-thread)))))
5043
5044
(defun slime-quit ()
5045
(error "Not implemented properly. Use `slime-interrupt' instead."))
5046
5047
(defun slime-quit-lisp (&optional kill)
5048
"Quit lisp, kill the inferior process and associated buffers."
5049
(interactive "P")
5050
(slime-quit-lisp-internal (slime-connection) 'slime-quit-sentinel kill))
5051
5052
(defun slime-quit-lisp-internal (connection sentinel kill)
5053
(let ((slime-dispatching-connection connection))
5054
(slime-eval-async '(swank:quit-lisp))
5055
(let* ((process (slime-inferior-process connection)))
5056
(set-process-filter connection nil)
5057
(set-process-sentinel connection sentinel)
5058
(when (and kill process)
5059
(sleep-for 0.2)
5060
(unless (memq (process-status process) '(exit signal))
5061
(kill-process process))))))
5062
5063
(defun slime-quit-sentinel (process message)
5064
(assert (process-status process) 'closed)
5065
(let* ((inferior (slime-inferior-process process))
5066
(inferior-buffer (if inferior (process-buffer inferior))))
5067
(when inferior (delete-process inferior))
5068
(when inferior-buffer (kill-buffer inferior-buffer))
5069
(slime-net-close process)
5070
(message "Connection closed.")))
5071
5072
5073
;;;; Debugger (SLDB)
5074
5075
(defvar sldb-hook nil
5076
"Hook run on entry to the debugger.")
5077
5078
(defcustom sldb-initial-restart-limit 6
5079
"Maximum number of restarts to display initially."
5080
:group 'slime-debugger
5081
:type 'integer)
5082
5083
5084
;;;;; Local variables in the debugger buffer
5085
5086
;; Small helper.
5087
(defun slime-make-variables-buffer-local (&rest variables)
5088
(mapcar #'make-variable-buffer-local variables))
5089
5090
(slime-make-variables-buffer-local
5091
(defvar sldb-condition nil
5092
"A list (DESCRIPTION TYPE) describing the condition being debugged.")
5093
5094
(defvar sldb-restarts nil
5095
"List of (NAME DESCRIPTION) for each available restart.")
5096
5097
(defvar sldb-level nil
5098
"Current debug level (recursion depth) displayed in buffer.")
5099
5100
(defvar sldb-backtrace-start-marker nil
5101
"Marker placed at the first frame of the backtrace.")
5102
5103
(defvar sldb-restart-list-start-marker nil
5104
"Marker placed at the first restart in the restart list.")
5105
5106
(defvar sldb-continuations nil
5107
"List of ids for pending continuation."))
5108
5109
;;;;; SLDB macros
5110
5111
;; some macros that we need to define before the first use
5112
5113
;; FIXME: rename
5114
(defmacro in-sldb-face (name string)
5115
"Return STRING propertised with face sldb-NAME-face."
5116
(let ((facename (intern (format "sldb-%s-face" (symbol-name name))))
5117
(var (gensym "string")))
5118
`(let ((,var ,string))
5119
(slime-add-face ',facename ,var)
5120
,var)))
5121
5122
(put 'in-sldb-face 'lisp-indent-function 1)
5123
5124
5125
;;;;; sldb-mode
5126
5127
(defvar sldb-mode-syntax-table
5128
(let ((table (copy-syntax-table lisp-mode-syntax-table)))
5129
;; We give < and > parenthesis syntax, so that #< ... > is treated
5130
;; as a balanced expression. This enables autodoc-mode to match
5131
;; #<unreadable> actual arguments in the backtraces with formal
5132
;; arguments of the function. (For Lisp mode, this is not
5133
;; desirable, since we do not wish to get a mismatched paren
5134
;; highlighted everytime we type < or >.)
5135
(modify-syntax-entry ?< "(" table)
5136
(modify-syntax-entry ?> ")" table)
5137
table)
5138
"Syntax table for SLDB mode.")
5139
5140
(define-derived-mode sldb-mode fundamental-mode "sldb"
5141
"Superior lisp debugger mode.
5142
In addition to ordinary SLIME commands, the following are
5143
available:\\<sldb-mode-map>
5144
5145
Commands to examine the selected frame:
5146
\\[sldb-toggle-details] - toggle details (local bindings, CATCH tags)
5147
\\[sldb-show-source] - view source for the frame
5148
\\[sldb-eval-in-frame] - eval in frame
5149
\\[sldb-pprint-eval-in-frame] - eval in frame, pretty-print result
5150
\\[sldb-disassemble] - disassemble
5151
\\[sldb-inspect-in-frame] - inspect
5152
5153
Commands to invoke restarts:
5154
\\[sldb-quit] - quit
5155
\\[sldb-abort] - abort
5156
\\[sldb-continue] - continue
5157
\\[sldb-invoke-restart-0]-\\[sldb-invoke-restart-9] - restart shortcuts
5158
\\[sldb-invoke-restart-by-name] - invoke restart by name
5159
5160
Commands to navigate frames:
5161
\\[sldb-down] - down
5162
\\[sldb-up] - up
5163
\\[sldb-details-down] - down, with details
5164
\\[sldb-details-up] - up, with details
5165
\\[sldb-cycle] - cycle between restarts & backtrace
5166
\\[sldb-beginning-of-backtrace] - beginning of backtrace
5167
\\[sldb-end-of-backtrace] - end of backtrace
5168
5169
Miscellaneous commands:
5170
\\[sldb-restart-frame] - restart frame
5171
\\[sldb-return-from-frame] - return from frame
5172
\\[sldb-step] - step
5173
\\[sldb-break-with-default-debugger] - switch to native debugger
5174
\\[sldb-break-with-system-debugger] - switch to system debugger (gdb)
5175
\\[slime-interactive-eval] - eval
5176
\\[sldb-inspect-condition] - inspect signalled condition
5177
5178
Full list of commands:
5179
5180
\\{sldb-mode-map}"
5181
(erase-buffer)
5182
(set-syntax-table sldb-mode-syntax-table)
5183
(slime-set-truncate-lines)
5184
;; Make original slime-connection "sticky" for SLDB commands in this buffer
5185
(setq slime-buffer-connection (slime-connection)))
5186
5187
(set-keymap-parent sldb-mode-map slime-parent-map)
5188
5189
(slime-define-keys sldb-mode-map
5190
5191
((kbd "RET") 'sldb-default-action)
5192
("\C-m" 'sldb-default-action)
5193
([return] 'sldb-default-action)
5194
([mouse-2] 'sldb-default-action/mouse)
5195
([follow-link] 'mouse-face)
5196
("\C-i" 'sldb-cycle)
5197
("h" 'describe-mode)
5198
("v" 'sldb-show-source)
5199
("e" 'sldb-eval-in-frame)
5200
("d" 'sldb-pprint-eval-in-frame)
5201
("D" 'sldb-disassemble)
5202
("i" 'sldb-inspect-in-frame)
5203
("n" 'sldb-down)
5204
("p" 'sldb-up)
5205
("\M-n" 'sldb-details-down)
5206
("\M-p" 'sldb-details-up)
5207
("<" 'sldb-beginning-of-backtrace)
5208
(">" 'sldb-end-of-backtrace)
5209
("t" 'sldb-toggle-details)
5210
("r" 'sldb-restart-frame)
5211
("I" 'sldb-invoke-restart-by-name)
5212
("R" 'sldb-return-from-frame)
5213
("c" 'sldb-continue)
5214
("s" 'sldb-step)
5215
("x" 'sldb-next)
5216
("o" 'sldb-out)
5217
("b" 'sldb-break-on-return)
5218
("a" 'sldb-abort)
5219
("q" 'sldb-quit)
5220
("A" 'sldb-break-with-system-debugger)
5221
("B" 'sldb-break-with-default-debugger)
5222
("P" 'sldb-print-condition)
5223
("C" 'sldb-inspect-condition)
5224
(":" 'slime-interactive-eval)
5225
("\C-c\C-c" 'sldb-recompile-frame-source))
5226
5227
;; Keys 0-9 are shortcuts to invoke particular restarts.
5228
(dotimes (number 10)
5229
(let ((fname (intern (format "sldb-invoke-restart-%S" number)))
5230
(docstring (format "Invoke restart numbered %S." number)))
5231
(eval `(defun ,fname ()
5232
,docstring
5233
(interactive)
5234
(sldb-invoke-restart ,number)))
5235
(define-key sldb-mode-map (number-to-string number) fname)))
5236
5237
5238
;;;;; SLDB buffer creation & update
5239
5240
(defun sldb-buffers (&optional connection)
5241
"Return a list of all sldb buffers (belonging to CONNECTION.)"
5242
(if connection
5243
(slime-filter-buffers (lambda ()
5244
(and (eq slime-buffer-connection connection)
5245
(eq major-mode 'sldb-mode))))
5246
(slime-filter-buffers (lambda () (eq major-mode 'sldb-mode)))))
5247
5248
(defun sldb-find-buffer (thread &optional connection)
5249
(let ((connection (or connection (slime-connection))))
5250
(find-if (lambda (buffer)
5251
(with-current-buffer buffer
5252
(and (eq slime-buffer-connection connection)
5253
(eq slime-current-thread thread))))
5254
(sldb-buffers))))
5255
5256
(defun sldb-get-default-buffer ()
5257
"Get a sldb buffer.
5258
The buffer is chosen more or less randomly."
5259
(car (sldb-buffers)))
5260
5261
(defun sldb-get-buffer (thread &optional connection)
5262
"Find or create a sldb-buffer for THREAD."
5263
(let ((connection (or connection (slime-connection))))
5264
(or (sldb-find-buffer thread connection)
5265
(let ((name (format "*sldb %s/%s*" (slime-connection-name) thread)))
5266
(with-current-buffer (generate-new-buffer name)
5267
(setq slime-buffer-connection connection
5268
slime-current-thread thread)
5269
(current-buffer))))))
5270
5271
(defun sldb-debugged-continuations (connection)
5272
"Return the debugged continuations for CONNECTION."
5273
(lexical-let ((accu '()))
5274
(dolist (b (sldb-buffers))
5275
(with-current-buffer b
5276
(when (eq slime-buffer-connection connection)
5277
(setq accu (append sldb-continuations accu)))))
5278
accu))
5279
5280
(defun sldb-setup (thread level condition restarts frames conts)
5281
"Setup a new SLDB buffer.
5282
CONDITION is a string describing the condition to debug.
5283
RESTARTS is a list of strings (NAME DESCRIPTION) for each available restart.
5284
FRAMES is a list (NUMBER DESCRIPTION &optional PLIST) describing the initial
5285
portion of the backtrace. Frames are numbered from 0.
5286
CONTS is a list of pending Emacs continuations."
5287
(with-current-buffer (sldb-get-buffer thread)
5288
(unless (equal sldb-level level)
5289
(setq buffer-read-only nil)
5290
(slime-save-local-variables (slime-popup-restore-data)
5291
(sldb-mode))
5292
(setq slime-current-thread thread)
5293
(setq sldb-level level)
5294
(setq mode-name (format "sldb[%d]" sldb-level))
5295
(setq sldb-condition condition)
5296
(setq sldb-restarts restarts)
5297
(setq sldb-continuations conts)
5298
(sldb-insert-condition condition)
5299
(insert "\n\n" (in-sldb-face section "Restarts:") "\n")
5300
(setq sldb-restart-list-start-marker (point-marker))
5301
(sldb-insert-restarts restarts 0 sldb-initial-restart-limit)
5302
(insert "\n" (in-sldb-face section "Backtrace:") "\n")
5303
(setq sldb-backtrace-start-marker (point-marker))
5304
(save-excursion
5305
(if frames
5306
(sldb-insert-frames (sldb-prune-initial-frames frames) t)
5307
(insert "[No backtrace]")))
5308
(run-hooks 'sldb-hook)
5309
(set-syntax-table lisp-mode-syntax-table))
5310
(slime-display-popup-buffer t)
5311
(sldb-recenter-region (point-min) (point))
5312
(setq buffer-read-only t)
5313
(when (and slime-stack-eval-tags
5314
;; (y-or-n-p "Enter recursive edit? ")
5315
)
5316
(message "Entering recursive edit..")
5317
(recursive-edit))))
5318
5319
(defun sldb-activate (thread level select)
5320
"Display the debugger buffer for THREAD.
5321
If LEVEL isn't the same as in the buffer reinitialize the buffer."
5322
(or (let ((buffer (sldb-find-buffer thread)))
5323
(when buffer
5324
(with-current-buffer buffer
5325
(when (equal sldb-level level)
5326
(when select (pop-to-buffer (current-buffer)))
5327
t))))
5328
(sldb-reinitialize thread level)))
5329
5330
(defun sldb-reinitialize (thread level)
5331
(slime-rex (thread level)
5332
('(swank:debugger-info-for-emacs 0 10)
5333
nil thread)
5334
((:ok result)
5335
(apply #'sldb-setup thread level result))))
5336
5337
(defun sldb-exit (thread level &optional stepping)
5338
"Exit from the debug level LEVEL."
5339
(when-let (sldb (sldb-find-buffer thread))
5340
(with-current-buffer sldb
5341
(cond (stepping
5342
(setq sldb-level nil)
5343
(run-with-timer 0.4 nil 'sldb-close-step-buffer sldb))
5344
(t
5345
(slime-popup-buffer-quit t))))))
5346
5347
(defun sldb-close-step-buffer (buffer)
5348
(when (buffer-live-p buffer)
5349
(with-current-buffer buffer
5350
(when (not sldb-level)
5351
(slime-popup-buffer-quit t)))))
5352
5353
5354
;;;;;; SLDB buffer insertion
5355
5356
(defun sldb-insert-condition (condition)
5357
"Insert the text for CONDITION.
5358
CONDITION should be a list (MESSAGE TYPE EXTRAS).
5359
EXTRAS is currently used for the stepper."
5360
(destructuring-bind (message type extras) condition
5361
(slime-insert-propertized '(sldb-default-action sldb-inspect-condition)
5362
(in-sldb-face topline message)
5363
"\n"
5364
(in-sldb-face condition type))
5365
(sldb-dispatch-extras extras)))
5366
5367
(defvar sldb-extras-hooks)
5368
5369
(defun sldb-dispatch-extras (extras)
5370
;; this is (mis-)used for the stepper
5371
(dolist (extra extras)
5372
(destructure-case extra
5373
((:show-frame-source n)
5374
(sldb-show-frame-source n))
5375
(t
5376
(or (run-hook-with-args-until-success 'sldb-extras-hooks extra)
5377
;;(error "Unhandled extra element:" extra)
5378
)))))
5379
5380
(defun sldb-insert-restarts (restarts start count)
5381
"Insert RESTARTS and add the needed text props
5382
RESTARTS should be a list ((NAME DESCRIPTION) ...)."
5383
(let* ((len (length restarts))
5384
(end (if count (min (+ start count) len) len)))
5385
(loop for (name string) in (subseq restarts start end)
5386
for number from start
5387
do (slime-insert-propertized
5388
`(,@nil restart ,number
5389
sldb-default-action sldb-invoke-restart
5390
mouse-face highlight)
5391
" " (in-sldb-face restart-number (number-to-string number))
5392
": [" (in-sldb-face restart-type name) "] "
5393
(in-sldb-face restart string))
5394
(insert "\n"))
5395
(when (< end len)
5396
(let ((pos (point)))
5397
(slime-insert-propertized
5398
(list 'sldb-default-action
5399
(slime-rcurry #'sldb-insert-more-restarts restarts pos end))
5400
" --more--\n")))))
5401
5402
(defun sldb-insert-more-restarts (restarts position start)
5403
(goto-char position)
5404
(let ((inhibit-read-only t))
5405
(delete-region position (1+ (line-end-position)))
5406
(sldb-insert-restarts restarts start nil)))
5407
5408
(defun sldb-frame.string (frame)
5409
(destructuring-bind (_ str &optional _) frame str))
5410
5411
(defun sldb-frame.number (frame)
5412
(destructuring-bind (n _ &optional _) frame n))
5413
5414
(defun sldb-frame.plist (frame)
5415
(destructuring-bind (_ _ &optional plist) frame plist))
5416
5417
(defun sldb-frame-restartable-p (frame)
5418
(and (plist-get (sldb-frame.plist frame) :restartable) t))
5419
5420
(defun sldb-prune-initial-frames (frames)
5421
"Return the prefix of FRAMES to initially present to the user.
5422
Regexp heuristics are used to avoid showing SWANK-internal frames."
5423
(let* ((case-fold-search t)
5424
(rx "^\\([() ]\\|lambda\\)*swank\\>"))
5425
(or (loop for frame in frames
5426
until (string-match rx (sldb-frame.string frame))
5427
collect frame)
5428
frames)))
5429
5430
(defun sldb-insert-frames (frames more)
5431
"Insert FRAMES into buffer.
5432
If MORE is non-nil, more frames are on the Lisp stack."
5433
(mapc #'sldb-insert-frame frames)
5434
(when more
5435
(slime-insert-propertized
5436
`(,@nil sldb-default-action sldb-fetch-more-frames
5437
sldb-previous-frame-number
5438
,(sldb-frame.number (first (last frames)))
5439
point-entered sldb-fetch-more-frames
5440
start-open t
5441
face sldb-section-face
5442
mouse-face highlight)
5443
" --more--")
5444
(insert "\n")))
5445
5446
(defun sldb-compute-frame-face (frame)
5447
(if (sldb-frame-restartable-p frame)
5448
'sldb-restartable-frame-line-face
5449
'sldb-frame-line-face))
5450
5451
(defun sldb-insert-frame (frame &optional face)
5452
"Insert FRAME with FACE at point.
5453
If FACE is nil, `sldb-compute-frame-face' is used to determine the face."
5454
(setq face (or face (sldb-compute-frame-face frame)))
5455
(let ((number (sldb-frame.number frame))
5456
(string (sldb-frame.string frame))
5457
(props `(frame ,frame sldb-default-action sldb-toggle-details)))
5458
(slime-propertize-region props
5459
(slime-propertize-region '(mouse-face highlight)
5460
(insert " " (in-sldb-face frame-label (format "%2d:" number)) " ")
5461
(slime-insert-indented
5462
(slime-add-face face string)))
5463
(insert "\n"))))
5464
5465
(defun sldb-fetch-more-frames (&rest ignore)
5466
"Fetch more backtrace frames.
5467
Called on the `point-entered' text-property hook."
5468
(let ((inhibit-point-motion-hooks t)
5469
(inhibit-read-only t)
5470
(prev (get-text-property (point) 'sldb-previous-frame-number)))
5471
;; we may be called twice, PREV is nil the second time
5472
(when prev
5473
(let* ((count 40)
5474
(from (1+ prev))
5475
(to (+ from count))
5476
(frames (slime-eval `(swank:backtrace ,from ,to)))
5477
(more (slime-length= frames count))
5478
(pos (point)))
5479
(delete-region (line-beginning-position) (point-max))
5480
(sldb-insert-frames frames more)
5481
(goto-char pos)))))
5482
5483
5484
;;;;;; SLDB examining text props
5485
5486
(defun sldb-restart-at-point ()
5487
(or (get-text-property (point) 'restart)
5488
(error "No restart at point")))
5489
5490
(defun sldb-frame-number-at-point ()
5491
(let ((frame (get-text-property (point) 'frame)))
5492
(cond (frame (car frame))
5493
(t (error "No frame at point")))))
5494
5495
(defun sldb-var-number-at-point ()
5496
(let ((var (get-text-property (point) 'var)))
5497
(cond (var var)
5498
(t (error "No variable at point")))))
5499
5500
(defun sldb-previous-frame-number ()
5501
(save-excursion
5502
(sldb-backward-frame)
5503
(sldb-frame-number-at-point)))
5504
5505
(defun sldb-frame-details-visible-p ()
5506
(and (get-text-property (point) 'frame)
5507
(get-text-property (point) 'details-visible-p)))
5508
5509
(defun sldb-frame-region ()
5510
(slime-property-bounds 'frame))
5511
5512
(defun sldb-forward-frame ()
5513
(goto-char (next-single-char-property-change (point) 'frame)))
5514
5515
(defun sldb-backward-frame ()
5516
(when (> (point) sldb-backtrace-start-marker)
5517
(goto-char (previous-single-char-property-change
5518
(if (get-text-property (point) 'frame)
5519
(car (sldb-frame-region))
5520
(point))
5521
'frame
5522
nil sldb-backtrace-start-marker))))
5523
5524
(defun sldb-goto-last-frame ()
5525
(goto-char (point-max))
5526
(while (not (get-text-property (point) 'frame))
5527
(goto-char (previous-single-property-change (point) 'frame))
5528
;; Recenter to bottom of the window; -2 to account for the
5529
;; empty last line displayed in sldb buffers.
5530
(recenter -2)))
5531
5532
(defun sldb-beginning-of-backtrace ()
5533
"Goto the first frame."
5534
(interactive)
5535
(goto-char sldb-backtrace-start-marker))
5536
5537
5538
;;;;;; SLDB recenter & redisplay
5539
5540
;; FIXME: these functions need factorization
5541
5542
(defun slime-show-buffer-position (position &optional recenter)
5543
"Ensure sure that the POSITION in the current buffer is visible."
5544
(let ((window (display-buffer (current-buffer) t)))
5545
(save-selected-window
5546
(select-window window)
5547
(goto-char position)
5548
(ecase recenter
5549
(top (recenter 0))
5550
(center (recenter))
5551
((nil)
5552
(unless (pos-visible-in-window-p)
5553
(cond ((= (current-column) 0) (recenter 1))
5554
(t (recenter)))))))))
5555
5556
(defun sldb-recenter-region (start end &optional center)
5557
"Make the region from START to END visible.
5558
Avoid point motions, if possible.
5559
Minimize scrolling, if CENTER is nil.
5560
If CENTER is true, scroll enough to center the region in the window."
5561
(let ((pos (point)) (lines (count-screen-lines start end t)))
5562
(assert (and (<= start pos) (<= pos end)))
5563
;;(sit-for 0)
5564
(cond ((and (pos-visible-in-window-p start)
5565
(pos-visible-in-window-p end)))
5566
((< lines (window-height))
5567
(cond (center (recenter (+ (/ (- (window-height) 1 lines)
5568
2)
5569
(slime-count-lines start pos))))
5570
(t (recenter (+ (- (window-height) 1 lines)
5571
(slime-count-lines start pos))))))
5572
(t
5573
(goto-char start)
5574
(recenter 0)
5575
(cond ((pos-visible-in-window-p pos)
5576
(goto-char pos))
5577
(t
5578
(goto-char start)
5579
(unless noninteractive ; for running the test suite
5580
(forward-line (- (window-height) 2)))))))))
5581
5582
;; not sure yet, whether this is a good idea.
5583
(defmacro slime-save-coordinates (origin &rest body)
5584
"Restore line and column relative to ORIGIN, after executing BODY.
5585
5586
This is useful if BODY deletes and inserts some text but we want to
5587
preserve the current row and column as closely as possible."
5588
(let ((base (make-symbol "base"))
5589
(goal (make-symbol "goal"))
5590
(mark (make-symbol "mark")))
5591
`(let* ((,base ,origin)
5592
(,goal (slime-coordinates ,base))
5593
(,mark (point-marker)))
5594
(set-marker-insertion-type ,mark t)
5595
(prog1 (save-excursion ,@body)
5596
(slime-restore-coordinate ,base ,goal ,mark)))))
5597
5598
(put 'slime-save-coordinates 'lisp-indent-function 1)
5599
5600
(defun slime-coordinates (origin)
5601
;; Return a pair (X . Y) for the column and line distance to ORIGIN.
5602
(let ((y (slime-count-lines origin (point)))
5603
(x (save-excursion
5604
(- (current-column)
5605
(progn (goto-char origin) (current-column))))))
5606
(cons x y)))
5607
5608
(defun slime-restore-coordinate (base goal limit)
5609
;; Move point to GOAL. Coordinates are relative to BASE.
5610
;; Don't move beyond LIMIT.
5611
(save-restriction
5612
(narrow-to-region base limit)
5613
(goto-char (point-min))
5614
(let ((col (current-column)))
5615
(forward-line (cdr goal))
5616
(when (and (eobp) (bolp) (not (bobp)))
5617
(backward-char))
5618
(move-to-column (+ col (car goal))))))
5619
5620
(defun slime-count-lines (start end)
5621
"Return the number of lines between START and END.
5622
This is 0 if START and END at the same line."
5623
(- (count-lines start end)
5624
(if (save-excursion (goto-char end) (bolp)) 0 1)))
5625
5626
5627
;;;;; SLDB commands
5628
5629
(defun sldb-default-action ()
5630
"Invoke the action at point."
5631
(interactive)
5632
(let ((fn (get-text-property (point) 'sldb-default-action)))
5633
(if fn (funcall fn))))
5634
5635
(defun sldb-default-action/mouse (event)
5636
"Invoke the action pointed at by the mouse."
5637
(interactive "e")
5638
(destructuring-bind (mouse-1 (w pos &rest _)) event
5639
(save-excursion
5640
(goto-char pos)
5641
(let ((fn (get-text-property (point) 'sldb-default-action)))
5642
(if fn (funcall fn))))))
5643
5644
(defun sldb-cycle ()
5645
"Cycle between restart list and backtrace."
5646
(interactive)
5647
(let ((pt (point)))
5648
(cond ((< pt sldb-restart-list-start-marker)
5649
(goto-char sldb-restart-list-start-marker))
5650
((< pt sldb-backtrace-start-marker)
5651
(goto-char sldb-backtrace-start-marker))
5652
(t
5653
(goto-char sldb-restart-list-start-marker)))))
5654
5655
(defun sldb-end-of-backtrace ()
5656
"Fetch the entire backtrace and go to the last frame."
5657
(interactive)
5658
(sldb-fetch-all-frames)
5659
(sldb-goto-last-frame))
5660
5661
(defun sldb-fetch-all-frames ()
5662
(let ((inhibit-read-only t)
5663
(inhibit-point-motion-hooks t))
5664
(sldb-goto-last-frame)
5665
(let ((last (sldb-frame-number-at-point)))
5666
(goto-char (next-single-char-property-change (point) 'frame))
5667
(delete-region (point) (point-max))
5668
(save-excursion
5669
(sldb-insert-frames (slime-eval `(swank:backtrace ,(1+ last) nil))
5670
nil)))))
5671
5672
5673
;;;;;; SLDB show source
5674
5675
(defun sldb-show-source ()
5676
"Highlight the frame at point's expression in a source code buffer."
5677
(interactive)
5678
(sldb-show-frame-source (sldb-frame-number-at-point)))
5679
5680
(defun sldb-show-frame-source (frame-number)
5681
(slime-eval-async
5682
`(swank:frame-source-location ,frame-number)
5683
(lambda (source-location)
5684
(destructure-case source-location
5685
((:error message)
5686
(message "%s" message)
5687
(ding))
5688
(t
5689
(slime-show-source-location source-location))))))
5690
5691
(defun slime-show-source-location (source-location &optional no-highlight-p)
5692
(save-selected-window ; show the location, but don't hijack focus.
5693
(slime-goto-source-location source-location)
5694
(unless no-highlight-p (slime-highlight-sexp))
5695
(slime-show-buffer-position (point))))
5696
5697
(defun slime-highlight-sexp (&optional start end)
5698
"Highlight the first sexp after point."
5699
(let ((start (or start (point)))
5700
(end (or end (save-excursion (ignore-errors (forward-sexp)) (point)))))
5701
(slime-flash-region start end)))
5702
5703
(defun slime-highlight-line (&optional timeout)
5704
(slime-flash-region (+ (line-beginning-position) (current-indentation))
5705
(line-end-position)
5706
timeout))
5707
5708
5709
;;;;;; SLDB toggle details
5710
5711
(defun sldb-toggle-details (&optional on)
5712
"Toggle display of details for the current frame.
5713
The details include local variable bindings and CATCH-tags."
5714
(interactive)
5715
(assert (sldb-frame-number-at-point))
5716
(let ((inhibit-read-only t)
5717
(inhibit-point-motion-hooks t))
5718
(if (or on (not (sldb-frame-details-visible-p)))
5719
(sldb-show-frame-details)
5720
(sldb-hide-frame-details))))
5721
5722
(defun sldb-show-frame-details ()
5723
;; fetch and display info about local variables and catch tags
5724
(destructuring-bind (start end frame locals catches) (sldb-frame-details)
5725
(slime-save-coordinates start
5726
(delete-region start end)
5727
(slime-propertize-region `(frame ,frame details-visible-p t)
5728
(sldb-insert-frame frame (if (sldb-frame-restartable-p frame)
5729
'sldb-restartable-frame-line-face
5730
;; FIXME: can we somehow merge the two?
5731
'sldb-detailed-frame-line-face))
5732
(let ((indent1 " ")
5733
(indent2 " "))
5734
(insert indent1 (in-sldb-face section
5735
(if locals "Locals:" "[No Locals]")) "\n")
5736
(sldb-insert-locals locals indent2 frame)
5737
(when catches
5738
(insert indent1 (in-sldb-face section "Catch-tags:") "\n")
5739
(dolist (tag catches)
5740
(slime-propertize-region `(catch-tag ,tag)
5741
(insert indent2 (in-sldb-face catch-tag (format "%s" tag))
5742
"\n"))))
5743
(setq end (point)))))
5744
(sldb-recenter-region start end)))
5745
5746
(defun sldb-frame-details ()
5747
;; Return a list (START END FRAME LOCALS CATCHES) for frame at point.
5748
(let* ((frame (get-text-property (point) 'frame))
5749
(num (car frame)))
5750
(destructuring-bind (start end) (sldb-frame-region)
5751
(list* start end frame
5752
(slime-eval `(swank:frame-locals-and-catch-tags ,num))))))
5753
5754
(defvar sldb-insert-frame-variable-value-function
5755
'sldb-insert-frame-variable-value)
5756
5757
(defun sldb-insert-locals (vars prefix frame)
5758
"Insert VARS and add PREFIX at the beginning of each inserted line.
5759
VAR should be a plist with the keys :name, :id, and :value."
5760
(loop for i from 0
5761
for var in vars do
5762
(destructuring-bind (&key name id value) var
5763
(slime-propertize-region (list 'sldb-default-action 'sldb-inspect-var
5764
'var i)
5765
(insert prefix
5766
(in-sldb-face local-name
5767
(concat name (if (zerop id) "" (format "#%d" id))))
5768
" = ")
5769
(funcall sldb-insert-frame-variable-value-function value frame i)
5770
(insert "\n")))))
5771
5772
(defun sldb-insert-frame-variable-value (value frame index)
5773
(insert (in-sldb-face local-value value)))
5774
5775
(defun sldb-hide-frame-details ()
5776
;; delete locals and catch tags, but keep the function name and args.
5777
(destructuring-bind (start end) (sldb-frame-region)
5778
(let ((frame (get-text-property (point) 'frame)))
5779
(slime-save-coordinates start
5780
(delete-region start end)
5781
(slime-propertize-region '(details-visible-p nil)
5782
(sldb-insert-frame frame))))))
5783
5784
(defun sldb-disassemble ()
5785
"Disassemble the code for the current frame."
5786
(interactive)
5787
(let ((frame (sldb-frame-number-at-point)))
5788
(slime-eval-async `(swank:sldb-disassemble ,frame)
5789
(lambda (result)
5790
(slime-show-description result nil)))))
5791
5792
5793
;;;;;; SLDB eval and inspect
5794
5795
(defun sldb-eval-in-frame (string)
5796
"Prompt for an expression and evaluate it in the selected frame."
5797
(interactive (list (slime-read-from-minibuffer "Eval in frame: ")))
5798
(let* ((number (sldb-frame-number-at-point)))
5799
(slime-eval-async `(swank:eval-string-in-frame ,string ,number)
5800
(if current-prefix-arg
5801
'slime-write-string
5802
'slime-display-eval-result))))
5803
5804
(defun sldb-pprint-eval-in-frame (string)
5805
"Prompt for an expression, evaluate in selected frame, pretty-print result."
5806
(interactive (list (slime-read-from-minibuffer "Eval in frame: ")))
5807
(let* ((number (sldb-frame-number-at-point)))
5808
(slime-eval-async `(swank:pprint-eval-string-in-frame ,string ,number)
5809
(lambda (result)
5810
(slime-show-description result nil)))))
5811
5812
(defun sldb-inspect-in-frame (string)
5813
"Prompt for an expression and inspect it in the selected frame."
5814
(interactive (list (slime-read-from-minibuffer
5815
"Inspect in frame (evaluated): "
5816
(slime-sexp-at-point))))
5817
(let ((number (sldb-frame-number-at-point)))
5818
(slime-eval-async `(swank:inspect-in-frame ,string ,number)
5819
'slime-open-inspector)))
5820
5821
(defun sldb-inspect-var ()
5822
(let ((frame (sldb-frame-number-at-point))
5823
(var (sldb-var-number-at-point)))
5824
(slime-eval-async `(swank:inspect-frame-var ,frame ,var)
5825
'slime-open-inspector)))
5826
5827
(defun sldb-inspect-condition ()
5828
"Inspect the current debugger condition."
5829
(interactive)
5830
(slime-eval-async '(swank:inspect-current-condition)
5831
'slime-open-inspector))
5832
5833
5834
;;;;;; SLDB movement
5835
5836
(defun sldb-down ()
5837
"Select next frame."
5838
(interactive)
5839
(sldb-forward-frame))
5840
5841
(defun sldb-up ()
5842
"Select previous frame."
5843
(interactive)
5844
(sldb-backward-frame)
5845
(when (= (point) sldb-backtrace-start-marker)
5846
(recenter (1+ (count-lines (point-min) (point))))))
5847
5848
(defun sldb-sugar-move (move-fn)
5849
(let ((inhibit-read-only t))
5850
(when (sldb-frame-details-visible-p) (sldb-hide-frame-details))
5851
(funcall move-fn)
5852
(sldb-show-source)
5853
(sldb-toggle-details t)))
5854
5855
(defun sldb-details-up ()
5856
"Select previous frame and show details."
5857
(interactive)
5858
(sldb-sugar-move 'sldb-up))
5859
5860
(defun sldb-details-down ()
5861
"Select next frame and show details."
5862
(interactive)
5863
(sldb-sugar-move 'sldb-down))
5864
5865
5866
;;;;;; SLDB restarts
5867
5868
(defun sldb-quit ()
5869
"Quit to toplevel."
5870
(interactive)
5871
(assert sldb-restarts () "sldb-quit called outside of sldb buffer")
5872
(slime-rex () ('(swank:throw-to-toplevel))
5873
((:ok x) (error "sldb-quit returned [%s]" x))
5874
((:abort))))
5875
5876
(defun sldb-continue ()
5877
"Invoke the \"continue\" restart."
5878
(interactive)
5879
(assert sldb-restarts () "sldb-continue called outside of sldb buffer")
5880
(slime-rex ()
5881
('(swank:sldb-continue))
5882
((:ok _)
5883
(message "No restart named continue")
5884
(ding))
5885
((:abort))))
5886
5887
(defun sldb-abort ()
5888
"Invoke the \"abort\" restart."
5889
(interactive)
5890
(slime-eval-async '(swank:sldb-abort)
5891
(lambda (v) (message "Restart returned: %S" v))))
5892
5893
(defun sldb-invoke-restart (&optional number)
5894
"Invoke a restart.
5895
Optional NUMBER (index into `sldb-restarts') specifies the
5896
restart to invoke, otherwise use the restart at point."
5897
(interactive)
5898
(let ((restart (or number (sldb-restart-at-point))))
5899
(slime-rex ()
5900
((list 'swank:invoke-nth-restart-for-emacs sldb-level restart))
5901
((:ok value) (message "Restart returned: %s" value))
5902
((:abort)))))
5903
5904
(defun sldb-invoke-restart-by-name (restart-name)
5905
(interactive (list (let ((completion-ignore-case t))
5906
(completing-read "Restart: " sldb-restarts nil t
5907
""
5908
'sldb-invoke-restart-by-name))))
5909
(sldb-invoke-restart (position restart-name sldb-restarts
5910
:test 'string= :key 'first)))
5911
5912
(defun sldb-break-with-default-debugger (&optional dont-unwind)
5913
"Enter default debugger."
5914
(interactive "P")
5915
(slime-rex ()
5916
((list 'swank:sldb-break-with-default-debugger
5917
(not (not dont-unwind)))
5918
nil slime-current-thread)
5919
((:abort))))
5920
5921
(defun sldb-break-with-system-debugger (&optional lightweight)
5922
"Enter system debugger (gdb)."
5923
(interactive "P")
5924
(slime-attach-gdb slime-buffer-connection lightweight))
5925
5926
(defun slime-attach-gdb (connection &optional lightweight)
5927
"Run `gud-gdb'on the connection with PID `pid'.
5928
5929
If `lightweight' is given, do not send any request to the
5930
inferior Lisp (e.g. to obtain default gdb config) but only
5931
operate from the Emacs side; intended for cases where the Lisp is
5932
truly screwed up."
5933
(interactive
5934
(list (slime-read-connection "Attach gdb to: " (slime-connection)) "P"))
5935
(let ((pid (slime-pid connection))
5936
(file (slime-lisp-implementation-program connection))
5937
(commands (unless lightweight
5938
(let ((slime-dispatching-connection connection))
5939
(slime-eval `(swank:gdb-initial-commands))))))
5940
(gud-gdb (format "gdb -p %d %s" pid (or file "")))
5941
(with-current-buffer gud-comint-buffer
5942
(dolist (cmd commands)
5943
;; First wait until gdb was initialized, then wait until current
5944
;; command was processed.
5945
(while (not (looking-back comint-prompt-regexp))
5946
(sit-for 0.01))
5947
;; We do not use `gud-call' because we want the initial commands
5948
;; to be displayed by the user so he knows what he's got.
5949
(insert cmd)
5950
(comint-send-input)))))
5951
5952
(defun slime-read-connection (prompt &optional initial-value)
5953
"Read a connection from the minibuffer. Returns the net
5954
process, or nil."
5955
(assert (memq initial-value slime-net-processes))
5956
(flet ((connection-identifier (p)
5957
(format "%s (pid %d)" (slime-connection-name p) (slime-pid p))))
5958
(let ((candidates (mapcar (lambda (p)
5959
(cons (connection-identifier p) p))
5960
slime-net-processes)))
5961
(cdr (assoc (completing-read prompt candidates
5962
nil t (connection-identifier initial-value))
5963
candidates)))))
5964
5965
(defun sldb-step ()
5966
"Step to next basic-block boundary."
5967
(interactive)
5968
(let ((frame (sldb-frame-number-at-point)))
5969
(slime-eval-async `(swank:sldb-step ,frame))))
5970
5971
(defun sldb-next ()
5972
"Step over call."
5973
(interactive)
5974
(let ((frame (sldb-frame-number-at-point)))
5975
(slime-eval-async `(swank:sldb-next ,frame))))
5976
5977
(defun sldb-out ()
5978
"Resume stepping after returning from this function."
5979
(interactive)
5980
(let ((frame (sldb-frame-number-at-point)))
5981
(slime-eval-async `(swank:sldb-out ,frame))))
5982
5983
(defun sldb-break-on-return ()
5984
"Set a breakpoint at the current frame.
5985
The debugger is entered when the frame exits."
5986
(interactive)
5987
(let ((frame (sldb-frame-number-at-point)))
5988
(slime-eval-async `(swank:sldb-break-on-return ,frame)
5989
(lambda (msg) (message "%s" msg)))))
5990
5991
(defun sldb-break (name)
5992
"Set a breakpoint at the start of the function NAME."
5993
(interactive (list (slime-read-symbol-name "Function: " t)))
5994
(slime-eval-async `(swank:sldb-break ,name)
5995
(lambda (msg) (message "%s" msg))))
5996
5997
(defun sldb-return-from-frame (string)
5998
"Reads an expression in the minibuffer and causes the function to
5999
return that value, evaluated in the context of the frame."
6000
(interactive (list (slime-read-from-minibuffer "Return from frame: ")))
6001
(let* ((number (sldb-frame-number-at-point)))
6002
(slime-rex ()
6003
((list 'swank:sldb-return-from-frame number string))
6004
((:ok value) (message "%s" value))
6005
((:abort)))))
6006
6007
(defun sldb-restart-frame ()
6008
"Causes the frame to restart execution with the same arguments as it
6009
was called originally."
6010
(interactive)
6011
(let* ((number (sldb-frame-number-at-point)))
6012
(slime-rex ()
6013
((list 'swank:restart-frame number))
6014
((:ok value) (message "%s" value))
6015
((:abort)))))
6016
6017
6018
;;;;;; SLDB recompilation commands
6019
6020
(defun sldb-recompile-frame-source (&optional raw-prefix-arg)
6021
(interactive "P")
6022
(slime-eval-async
6023
`(swank:frame-source-location ,(sldb-frame-number-at-point))
6024
(lexical-let ((policy (slime-compute-policy raw-prefix-arg)))
6025
(lambda (source-location)
6026
(destructure-case source-location
6027
((:error message)
6028
(message "%s" message)
6029
(ding))
6030
(t
6031
(let ((slime-compilation-policy policy))
6032
(slime-recompile-location source-location))))))))
6033
6034
6035
;;;; Thread control panel
6036
6037
(defvar slime-threads-buffer-name (slime-buffer-name :threads))
6038
(defvar slime-threads-buffer-timer nil)
6039
6040
(defcustom slime-threads-update-interval nil
6041
"Interval at which the list of threads will be updated."
6042
:type '(choice
6043
(number :value 0.5)
6044
(const nil))
6045
:group 'slime-ui)
6046
6047
(defun slime-list-threads ()
6048
"Display a list of threads."
6049
(interactive)
6050
(let ((name slime-threads-buffer-name))
6051
(slime-with-popup-buffer (name :connection t
6052
:mode 'slime-thread-control-mode)
6053
(slime-update-threads-buffer)
6054
(goto-char (point-min))
6055
(when slime-threads-update-interval
6056
(when slime-threads-buffer-timer
6057
(cancel-timer slime-threads-buffer-timer))
6058
(setq slime-threads-buffer-timer
6059
(run-with-timer
6060
slime-threads-update-interval
6061
slime-threads-update-interval
6062
'slime-update-threads-buffer)))
6063
(setq slime-popup-buffer-quit-function 'slime-quit-threads-buffer))))
6064
6065
(defun slime-longest-lines (list-of-lines)
6066
(let ((lengths (make-list (length (car list-of-lines)) 0)))
6067
(flet ((process-line (line)
6068
(loop for element in line
6069
for length on lengths
6070
do (setf (car length)
6071
(max (length (prin1-to-string element t))
6072
(car length))))))
6073
(mapc 'process-line list-of-lines)
6074
lengths)))
6075
6076
(defvar slime-thread-index-to-id nil)
6077
6078
(defun slime-quit-threads-buffer (&optional _)
6079
(when slime-threads-buffer-timer
6080
(cancel-timer slime-threads-buffer-timer)
6081
(setq slime-threads-buffer-timer nil))
6082
(slime-popup-buffer-quit t)
6083
(setq slime-thread-index-to-id nil)
6084
(slime-eval-async `(swank:quit-thread-browser)))
6085
6086
(defun slime-update-threads-buffer ()
6087
(interactive)
6088
(with-current-buffer slime-threads-buffer-name
6089
(slime-eval-async '(swank:list-threads)
6090
'slime-display-threads)))
6091
6092
(defun slime-move-point (position)
6093
"Move point in the current buffer and in the window the buffer is displayed."
6094
(let ((window (get-buffer-window (current-buffer) t)))
6095
(goto-char position)
6096
(when window
6097
(set-window-point window position))))
6098
6099
;;; FIXME: the region selection is jumping
6100
(defun slime-display-threads (threads)
6101
(with-current-buffer slime-threads-buffer-name
6102
(let* ((inhibit-read-only t)
6103
(index (get-text-property (point) 'thread-id))
6104
(old-thread-id (and (numberp index)
6105
(elt slime-thread-index-to-id index)))
6106
(old-line (line-number-at-pos))
6107
(old-column (current-column)))
6108
(setq slime-thread-index-to-id (mapcar 'car (cdr threads)))
6109
(erase-buffer)
6110
(slime-insert-threads threads)
6111
(let ((new-position (position old-thread-id threads :key 'car)))
6112
(goto-char (point-min))
6113
(forward-line (1- (or new-position old-line)))
6114
(move-to-column old-column)
6115
(slime-move-point (point))))))
6116
6117
(defvar *slime-threads-table-properties*
6118
'(nil (face bold)))
6119
6120
(defun slime-format-threads-labels (threads)
6121
(let ((labels (mapcar (lambda (x)
6122
(capitalize (substring (symbol-name x) 1)))
6123
(car threads))))
6124
(cons labels (cdr threads))))
6125
6126
(defun slime-insert-thread (thread longest-lines)
6127
(unless (bolp) (insert "\n"))
6128
(loop for i from 0
6129
for align in longest-lines
6130
for element in thread
6131
for string = (prin1-to-string element t)
6132
for property = (nth i *slime-threads-table-properties*)
6133
do
6134
(if property
6135
(slime-insert-propertized property string)
6136
(insert string))
6137
(insert-char ?\ (- align (length string) -3))))
6138
6139
(defun slime-insert-threads (threads)
6140
(let* ((threads (slime-format-threads-labels threads))
6141
(longest-lines (slime-longest-lines threads))
6142
(labels (let (*slime-threads-table-properties*)
6143
(with-temp-buffer
6144
(slime-insert-thread (car threads) longest-lines)
6145
(buffer-string)))))
6146
(if (boundp 'header-line-format)
6147
(setq header-line-format
6148
(concat (propertize " " 'display '((space :align-to 0)))
6149
labels))
6150
(insert labels))
6151
(loop for index from 0
6152
for thread in (cdr threads)
6153
do
6154
(slime-propertize-region `(thread-id ,index)
6155
(slime-insert-thread thread longest-lines)))))
6156
6157
6158
;;;;; Major mode
6159
6160
(define-derived-mode slime-thread-control-mode fundamental-mode
6161
"Threads"
6162
"SLIME Thread Control Panel Mode.
6163
6164
\\{slime-thread-control-mode-map}
6165
\\{slime-popup-buffer-mode-map}"
6166
(when slime-truncate-lines
6167
(set (make-local-variable 'truncate-lines) t))
6168
(setq buffer-undo-list t))
6169
6170
(slime-define-keys slime-thread-control-mode-map
6171
("a" 'slime-thread-attach)
6172
("d" 'slime-thread-debug)
6173
("g" 'slime-update-threads-buffer)
6174
("k" 'slime-thread-kill))
6175
6176
(defun slime-thread-kill ()
6177
(interactive)
6178
(slime-eval `(cl:mapc 'swank:kill-nth-thread
6179
',(slime-get-properties 'thread-id)))
6180
(call-interactively 'slime-update-threads-buffer))
6181
6182
(defun slime-get-region-properties (prop start end)
6183
(loop for position = (if (get-text-property start prop)
6184
start
6185
(next-single-property-change start prop))
6186
then (next-single-property-change position prop)
6187
while (<= position end)
6188
collect (get-text-property position prop)))
6189
6190
(defun slime-get-properties (prop)
6191
(if (use-region-p)
6192
(slime-get-region-properties prop
6193
(region-beginning)
6194
(region-end))
6195
(let ((value (get-text-property (point) prop)))
6196
(when value
6197
(list value)))))
6198
6199
(defun slime-thread-attach ()
6200
(interactive)
6201
(let ((id (get-text-property (point) 'thread-id))
6202
(file (slime-swank-port-file)))
6203
(slime-eval-async `(swank:start-swank-server-in-thread ,id ,file)))
6204
(slime-read-port-and-connect nil nil))
6205
6206
(defun slime-thread-debug ()
6207
(interactive)
6208
(let ((id (get-text-property (point) 'thread-id)))
6209
(slime-eval-async `(swank:debug-nth-thread ,id))))
6210
6211
6212
;;;;; Connection listing
6213
6214
(define-derived-mode slime-connection-list-mode fundamental-mode
6215
"Slime-Connections"
6216
"SLIME Connection List Mode.
6217
6218
\\{slime-connection-list-mode-map}
6219
\\{slime-popup-buffer-mode-map}"
6220
(when slime-truncate-lines
6221
(set (make-local-variable 'truncate-lines) t)))
6222
6223
(slime-define-keys slime-connection-list-mode-map
6224
("d" 'slime-connection-list-make-default)
6225
("g" 'slime-update-connection-list)
6226
((kbd "C-k") 'slime-quit-connection-at-point)
6227
("R" 'slime-restart-connection-at-point))
6228
6229
(defun slime-connection-at-point ()
6230
(or (get-text-property (point) 'slime-connection)
6231
(error "No connection at point")))
6232
6233
(defun slime-quit-connection-at-point (connection)
6234
(interactive (list (slime-connection-at-point)))
6235
(let ((slime-dispatching-connection connection)
6236
(end (time-add (current-time) (seconds-to-time 3))))
6237
(slime-quit-lisp t)
6238
(while (memq connection slime-net-processes)
6239
(when (time-less-p end (current-time))
6240
(message "Quit timeout expired. Disconnecting.")
6241
(delete-process connection))
6242
(sit-for 0 100)))
6243
(slime-update-connection-list))
6244
6245
(defun slime-restart-connection-at-point (connection)
6246
(interactive (list (slime-connection-at-point)))
6247
(let ((slime-dispatching-connection connection))
6248
(slime-restart-inferior-lisp)))
6249
6250
(defun slime-connection-list-make-default ()
6251
"Make the connection at point the default connection."
6252
(interactive)
6253
(slime-select-connection (slime-connection-at-point))
6254
(slime-update-connection-list))
6255
6256
(defvar slime-connections-buffer-name (slime-buffer-name :connections))
6257
6258
(defun slime-list-connections ()
6259
"Display a list of all connections."
6260
(interactive)
6261
(slime-with-popup-buffer (slime-connections-buffer-name
6262
:mode 'slime-connection-list-mode)
6263
(slime-draw-connection-list)))
6264
6265
(defun slime-update-connection-list ()
6266
"Display a list of all connections."
6267
(interactive)
6268
(let ((pos (point))
6269
(inhibit-read-only t))
6270
(erase-buffer)
6271
(slime-draw-connection-list)
6272
(goto-char pos)))
6273
6274
(defun slime-draw-connection-list ()
6275
(let ((default-pos nil)
6276
(default slime-default-connection)
6277
(fstring "%s%2s %-10s %-17s %-7s %-s\n"))
6278
(insert (format fstring " " "Nr" "Name" "Port" "Pid" "Type")
6279
(format fstring " " "--" "----" "----" "---" "----"))
6280
(dolist (p (reverse slime-net-processes))
6281
(when (eq default p) (setf default-pos (point)))
6282
(slime-insert-propertized
6283
(list 'slime-connection p)
6284
(format fstring
6285
(if (eq default p) "*" " ")
6286
(slime-connection-number p)
6287
(slime-connection-name p)
6288
(or (process-id p) (process-contact p))
6289
(slime-pid p)
6290
(slime-lisp-implementation-type p))))
6291
(when default
6292
(goto-char default-pos))))
6293
6294
6295
;;;; Inspector
6296
6297
(defgroup slime-inspector nil
6298
"Inspector faces."
6299
:prefix "slime-inspector-"
6300
:group 'slime)
6301
6302
(defface slime-inspector-topline-face
6303
'((t ()))
6304
"Face for top line describing object."
6305
:group 'slime-inspector)
6306
6307
(defface slime-inspector-label-face
6308
'((t (:inherit font-lock-constant-face)))
6309
"Face for labels in the inspector."
6310
:group 'slime-inspector)
6311
6312
(defface slime-inspector-value-face
6313
(if (slime-face-inheritance-possible-p)
6314
'((t (:inherit font-lock-builtin-face)))
6315
'((((background light)) (:foreground "MediumBlue" :bold t))
6316
(((background dark)) (:foreground "LightGray" :bold t))))
6317
"Face for things which can themselves be inspected."
6318
:group 'slime-inspector)
6319
6320
(defface slime-inspector-action-face
6321
(if (slime-face-inheritance-possible-p)
6322
'((t (:inherit font-lock-warning-face)))
6323
'((t (:foreground "OrangeRed"))))
6324
"Face for labels of inspector actions."
6325
:group 'slime-inspector)
6326
6327
(defface slime-inspector-type-face
6328
'((t (:inherit font-lock-type-face)))
6329
"Face for type description in inspector."
6330
:group 'slime-inspector)
6331
6332
(defvar slime-inspector-mark-stack '())
6333
(defvar slime-saved-window-config)
6334
6335
(defun slime-inspect (string)
6336
"Eval an expression and inspect the result."
6337
(interactive
6338
(list (slime-read-from-minibuffer "Inspect value (evaluated): "
6339
(slime-sexp-at-point))))
6340
(slime-eval-async `(swank:init-inspector ,string) 'slime-open-inspector))
6341
6342
(define-derived-mode slime-inspector-mode fundamental-mode
6343
"Slime-Inspector"
6344
"
6345
\\{slime-inspector-mode-map}
6346
\\{slime-popup-buffer-mode-map}"
6347
(set-syntax-table lisp-mode-syntax-table)
6348
(slime-set-truncate-lines)
6349
(setq buffer-read-only t))
6350
6351
(defun slime-inspector-buffer ()
6352
(or (get-buffer (slime-buffer-name :inspector))
6353
(slime-with-popup-buffer ((slime-buffer-name :inspector)
6354
:mode 'slime-inspector-mode)
6355
(setq slime-inspector-mark-stack '())
6356
(buffer-disable-undo)
6357
(make-local-variable 'slime-saved-window-config)
6358
(setq slime-popup-buffer-quit-function 'slime-inspector-quit)
6359
(setq slime-saved-window-config (current-window-configuration))
6360
(current-buffer))))
6361
6362
(defmacro slime-inspector-fontify (face string)
6363
`(slime-add-face ',(intern (format "slime-inspector-%s-face" face)) ,string))
6364
6365
(defvar slime-inspector-insert-ispec-function 'slime-inspector-insert-ispec)
6366
6367
(defun slime-open-inspector (inspected-parts &optional point hook)
6368
"Display INSPECTED-PARTS in a new inspector window.
6369
Optionally set point to POINT. If HOOK is provided, it is added to local
6370
KILL-BUFFER hooks for the inspector buffer."
6371
(with-current-buffer (slime-inspector-buffer)
6372
(when hook
6373
(add-hook 'kill-buffer-hook hook t t))
6374
(setq slime-buffer-connection (slime-current-connection))
6375
(let ((inhibit-read-only t))
6376
(erase-buffer)
6377
(destructuring-bind (&key id title content) inspected-parts
6378
(macrolet ((fontify (face string)
6379
`(slime-inspector-fontify ,face ,string)))
6380
(slime-propertize-region
6381
(list 'slime-part-number id
6382
'mouse-face 'highlight
6383
'face 'slime-inspector-value-face)
6384
(insert title))
6385
(while (eq (char-before) ?\n)
6386
(backward-delete-char 1))
6387
(insert "\n" (fontify label "--------------------") "\n")
6388
(save-excursion
6389
(slime-inspector-insert-content content))
6390
(pop-to-buffer (current-buffer))
6391
(when point
6392
(check-type point cons)
6393
(ignore-errors
6394
(goto-char (point-min))
6395
(forward-line (1- (car point)))
6396
(move-to-column (cdr point)))))))))
6397
6398
(defvar slime-inspector-limit 500)
6399
6400
(defun slime-inspector-insert-content (content)
6401
(slime-inspector-fetch-chunk
6402
content nil
6403
(lambda (chunk)
6404
(let ((inhibit-read-only t))
6405
(slime-inspector-insert-chunk chunk t t)))))
6406
6407
(defun slime-inspector-insert-chunk (chunk prev next)
6408
"Insert CHUNK at point.
6409
If PREV resp. NEXT are true insert more-buttons as needed."
6410
(destructuring-bind (ispecs len start end) chunk
6411
(when (and prev (> start 0))
6412
(slime-inspector-insert-more-button start t))
6413
(mapc #'slime-inspector-insert-ispec ispecs)
6414
(when (and next (< end len))
6415
(slime-inspector-insert-more-button end nil))))
6416
6417
(defun slime-inspector-insert-ispec (ispec)
6418
(if (stringp ispec)
6419
(insert ispec)
6420
(destructure-case ispec
6421
((:value string id)
6422
(slime-propertize-region
6423
(list 'slime-part-number id
6424
'mouse-face 'highlight
6425
'face 'slime-inspector-value-face)
6426
(insert string)))
6427
((:action string id)
6428
(slime-insert-propertized (list 'slime-action-number id
6429
'mouse-face 'highlight
6430
'face 'slime-inspector-action-face)
6431
string)))))
6432
6433
(defun slime-inspector-position ()
6434
"Return a pair (Y-POSITION X-POSITION) representing the
6435
position of point in the current buffer."
6436
;; We make sure we return absolute coordinates even if the user has
6437
;; narrowed the buffer.
6438
;; FIXME: why would somebody narrow the buffer?
6439
(save-restriction
6440
(widen)
6441
(cons (line-number-at-pos)
6442
(current-column))))
6443
6444
(defun slime-inspector-operate-on-point ()
6445
"Invoke the command for the text at point.
6446
1. If point is on a value then recursivly call the inspector on
6447
that value.
6448
2. If point is on an action then call that action.
6449
3. If point is on a range-button fetch and insert the range."
6450
(interactive)
6451
(let ((part-number (get-text-property (point) 'slime-part-number))
6452
(range-button (get-text-property (point) 'slime-range-button))
6453
(action-number (get-text-property (point) 'slime-action-number))
6454
(opener (lexical-let ((point (slime-inspector-position)))
6455
(lambda (parts)
6456
(when parts
6457
(slime-open-inspector parts point))))))
6458
(cond (part-number
6459
(slime-eval-async `(swank:inspect-nth-part ,part-number)
6460
opener)
6461
(push (slime-inspector-position) slime-inspector-mark-stack))
6462
(range-button
6463
(slime-inspector-fetch-more range-button))
6464
(action-number
6465
(slime-eval-async `(swank::inspector-call-nth-action ,action-number)
6466
opener))
6467
(t (error "No object at point")))))
6468
6469
(defun slime-inspector-operate-on-click (event)
6470
"Move to events' position and operate the part."
6471
(interactive "@e")
6472
(let ((point (posn-point (event-end event))))
6473
(cond ((and point
6474
(or (get-text-property point 'slime-part-number)
6475
(get-text-property point 'slime-range-button)
6476
(get-text-property point 'slime-action-number)))
6477
(goto-char point)
6478
(slime-inspector-operate-on-point))
6479
(t
6480
(error "No clickable part here")))))
6481
6482
(defun slime-inspector-pop ()
6483
"Reinspect the previous object."
6484
(interactive)
6485
(slime-eval-async
6486
`(swank:inspector-pop)
6487
(lambda (result)
6488
(cond (result
6489
(slime-open-inspector result (pop slime-inspector-mark-stack)))
6490
(t
6491
(message "No previous object")
6492
(ding))))))
6493
6494
(defun slime-inspector-next ()
6495
"Inspect the next object in the history."
6496
(interactive)
6497
(let ((result (slime-eval `(swank:inspector-next))))
6498
(cond (result
6499
(push (slime-inspector-position) slime-inspector-mark-stack)
6500
(slime-open-inspector result))
6501
(t (message "No next object")
6502
(ding)))))
6503
6504
(defun slime-inspector-quit (&optional kill-buffer)
6505
"Quit the inspector and kill the buffer."
6506
(interactive)
6507
(slime-eval-async `(swank:quit-inspector))
6508
(set-window-configuration slime-saved-window-config)
6509
(slime-popup-buffer-quit t))
6510
6511
;; FIXME: first return value is just point.
6512
;; FIXME: could probably use slime-search-property.
6513
(defun slime-find-inspectable-object (direction limit)
6514
"Find the next/previous inspectable object.
6515
DIRECTION can be either 'next or 'prev.
6516
LIMIT is the maximum or minimum position in the current buffer.
6517
6518
Return a list of two values: If an object could be found, the
6519
starting position of the found object and T is returned;
6520
otherwise LIMIT and NIL is returned."
6521
(let ((finder (ecase direction
6522
(next 'next-single-property-change)
6523
(prev 'previous-single-property-change))))
6524
(let ((prop nil) (curpos (point)))
6525
(while (and (not prop) (not (= curpos limit)))
6526
(let ((newpos (funcall finder curpos 'slime-part-number nil limit)))
6527
(setq prop (get-text-property newpos 'slime-part-number))
6528
(setq curpos newpos)))
6529
(list curpos (and prop t)))))
6530
6531
(defun slime-inspector-next-inspectable-object (arg)
6532
"Move point to the next inspectable object.
6533
With optional ARG, move across that many objects.
6534
If ARG is negative, move backwards."
6535
(interactive "p")
6536
(let ((maxpos (point-max)) (minpos (point-min))
6537
(previously-wrapped-p nil))
6538
;; Forward.
6539
(while (> arg 0)
6540
(destructuring-bind (pos foundp)
6541
(slime-find-inspectable-object 'next maxpos)
6542
(if foundp
6543
(progn (goto-char pos) (setq arg (1- arg))
6544
(setq previously-wrapped-p nil))
6545
(if (not previously-wrapped-p) ; cycle detection
6546
(progn (goto-char minpos) (setq previously-wrapped-p t))
6547
(error "No inspectable objects")))))
6548
;; Backward.
6549
(while (< arg 0)
6550
(destructuring-bind (pos foundp)
6551
(slime-find-inspectable-object 'prev minpos)
6552
;; SLIME-OPEN-INSPECTOR inserts the title of an inspector page
6553
;; as a presentation at the beginning of the buffer; skip
6554
;; that. (Notice how this problem can not arise in ``Forward.'')
6555
(if (and foundp (/= pos minpos))
6556
(progn (goto-char pos) (setq arg (1+ arg))
6557
(setq previously-wrapped-p nil))
6558
(if (not previously-wrapped-p) ; cycle detection
6559
(progn (goto-char maxpos) (setq previously-wrapped-p t))
6560
(error "No inspectable objects")))))))
6561
6562
(defun slime-inspector-previous-inspectable-object (arg)
6563
"Move point to the previous inspectable object.
6564
With optional ARG, move across that many objects.
6565
If ARG is negative, move forwards."
6566
(interactive "p")
6567
(slime-inspector-next-inspectable-object (- arg)))
6568
6569
(defun slime-inspector-describe ()
6570
(interactive)
6571
(slime-eval-describe `(swank:describe-inspectee)))
6572
6573
(defun slime-inspector-pprint (part)
6574
(interactive (list (or (get-text-property (point) 'slime-part-number)
6575
(error "No part at point"))))
6576
(slime-eval-describe `(swank:pprint-inspector-part ,part)))
6577
6578
(defun slime-inspector-eval (string)
6579
"Eval an expression in the context of the inspected object."
6580
(interactive (list (slime-read-from-minibuffer "Inspector eval: ")))
6581
(slime-eval-with-transcript `(swank:inspector-eval ,string)))
6582
6583
(defun slime-inspector-history ()
6584
"Show the previously inspected objects."
6585
(interactive)
6586
(slime-eval-describe `(swank:inspector-history)))
6587
6588
(defun slime-inspector-show-source (part)
6589
(interactive (list (or (get-text-property (point) 'slime-part-number)
6590
(error "No part at point"))))
6591
(slime-eval-async
6592
`(swank:find-source-location-for-emacs '(:inspector ,part))
6593
#'slime-show-source-location))
6594
6595
(defun slime-inspector-reinspect ()
6596
(interactive)
6597
(slime-eval-async `(swank:inspector-reinspect)
6598
(lexical-let ((point (slime-inspector-position)))
6599
(lambda (parts)
6600
(slime-open-inspector parts point)))))
6601
6602
(defun slime-inspector-toggle-verbose ()
6603
(interactive)
6604
(slime-eval-async `(swank:inspector-toggle-verbose)
6605
(lexical-let ((point (slime-inspector-position)))
6606
(lambda (parts)
6607
(slime-open-inspector parts point)))))
6608
6609
(defun slime-inspector-insert-more-button (index previous)
6610
(slime-insert-propertized
6611
(list 'slime-range-button (list index previous)
6612
'mouse-face 'highlight
6613
'face 'slime-inspector-action-face)
6614
(if previous " [--more--]\n" " [--more--]")))
6615
6616
(defun slime-inspector-fetch-all ()
6617
"Fetch all inspector contents and go to the end."
6618
(interactive)
6619
(goto-char (1- (point-max)))
6620
(let ((button (get-text-property (point) 'slime-range-button)))
6621
(when button
6622
(let (slime-inspector-limit)
6623
(slime-inspector-fetch-more button)))))
6624
6625
(defun slime-inspector-fetch-more (button)
6626
(destructuring-bind (index prev) button
6627
(slime-inspector-fetch-chunk
6628
(list '() (1+ index) index index) prev
6629
(slime-rcurry
6630
(lambda (chunk prev)
6631
(let ((inhibit-read-only t))
6632
(apply #'delete-region (slime-property-bounds 'slime-range-button))
6633
(slime-inspector-insert-chunk chunk prev (not prev))))
6634
prev))))
6635
6636
(defun slime-inspector-fetch-chunk (chunk prev cont)
6637
(slime-inspector-fetch chunk slime-inspector-limit prev cont))
6638
6639
(defun slime-inspector-fetch (chunk limit prev cont)
6640
(destructuring-bind (from to) (slime-inspector-next-range chunk limit prev)
6641
(cond ((and from to)
6642
(slime-eval-async
6643
`(swank:inspector-range ,from ,to)
6644
(slime-rcurry (lambda (chunk2 chunk1 limit prev cont)
6645
(slime-inspector-fetch
6646
(slime-inspector-join-chunks chunk1 chunk2)
6647
limit prev cont))
6648
chunk limit prev cont)))
6649
(t (funcall cont chunk)))))
6650
6651
(defun slime-inspector-next-range (chunk limit prev)
6652
(destructuring-bind (_ len start end) chunk
6653
(let ((count (- end start)))
6654
(cond ((and prev (< 0 start) (or (not limit) (< count limit)))
6655
(list (if limit (max (- end limit) 0) 0) start))
6656
((and (not prev) (< end len) (or (not limit) (< count limit)))
6657
(list end (if limit (+ start limit) most-positive-fixnum)))
6658
(t '(nil nil))))))
6659
6660
(defun slime-inspector-join-chunks (chunk1 chunk2)
6661
(destructuring-bind (i1 l1 s1 e1) chunk1
6662
(destructuring-bind (i2 l2 s2 e2) chunk2
6663
(cond ((= e1 s2)
6664
(list (append i1 i2) l2 s1 e2))
6665
((= e2 s1)
6666
(list (append i2 i1) l2 s2 e1))
6667
(t (error "Invalid chunks"))))))
6668
6669
(set-keymap-parent slime-inspector-mode-map slime-parent-map)
6670
6671
(slime-define-keys slime-inspector-mode-map
6672
([return] 'slime-inspector-operate-on-point)
6673
("\C-m" 'slime-inspector-operate-on-point)
6674
([mouse-2] 'slime-inspector-operate-on-click)
6675
("l" 'slime-inspector-pop)
6676
("n" 'slime-inspector-next)
6677
(" " 'slime-inspector-next)
6678
("d" 'slime-inspector-describe)
6679
("p" 'slime-inspector-pprint)
6680
("e" 'slime-inspector-eval)
6681
("h" 'slime-inspector-history)
6682
("g" 'slime-inspector-reinspect)
6683
("v" 'slime-inspector-toggle-verbose)
6684
("\C-i" 'slime-inspector-next-inspectable-object)
6685
([(shift tab)] 'slime-inspector-previous-inspectable-object) ; Emacs translates S-TAB
6686
([backtab] 'slime-inspector-previous-inspectable-object) ; to BACKTAB on X.
6687
("." 'slime-inspector-show-source)
6688
(">" 'slime-inspector-fetch-all))
6689
6690
6691
;;;; Buffer selector
6692
6693
(defvar slime-selector-methods nil
6694
"List of buffer-selection methods for the `slime-select' command.
6695
Each element is a list (KEY DESCRIPTION FUNCTION).
6696
DESCRIPTION is a one-line description of what the key selects.")
6697
6698
(defvar slime-selector-other-window nil
6699
"If non-nil use switch-to-buffer-other-window.")
6700
6701
(defun slime-selector (&optional other-window)
6702
"Select a new buffer by type, indicated by a single character.
6703
The user is prompted for a single character indicating the method by
6704
which to choose a new buffer. The `?' character describes the
6705
available methods.
6706
6707
See `def-slime-selector-method' for defining new methods."
6708
(interactive)
6709
(message "Select [%s]: "
6710
(apply #'string (mapcar #'car slime-selector-methods)))
6711
(let* ((slime-selector-other-window other-window)
6712
(ch (save-window-excursion
6713
(select-window (minibuffer-window))
6714
(read-char)))
6715
(method (find ch slime-selector-methods :key #'car)))
6716
(cond (method
6717
(funcall (third method)))
6718
(t
6719
(message "No method for character: ?\\%c" ch)
6720
(ding)
6721
(sleep-for 1)
6722
(discard-input)
6723
(slime-selector)))))
6724
6725
(defmacro def-slime-selector-method (key description &rest body)
6726
"Define a new `slime-select' buffer selection method.
6727
6728
KEY is the key the user will enter to choose this method.
6729
6730
DESCRIPTION is a one-line sentence describing how the method
6731
selects a buffer.
6732
6733
BODY is a series of forms which are evaluated when the selector
6734
is chosen. The returned buffer is selected with
6735
switch-to-buffer."
6736
(let ((method `(lambda ()
6737
(let ((buffer (progn ,@body)))
6738
(cond ((not (get-buffer buffer))
6739
(message "No such buffer: %S" buffer)
6740
(ding))
6741
((get-buffer-window buffer)
6742
(select-window (get-buffer-window buffer)))
6743
(slime-selector-other-window
6744
(switch-to-buffer-other-window buffer))
6745
(t
6746
(switch-to-buffer buffer)))))))
6747
`(setq slime-selector-methods
6748
(sort* (cons (list ,key ,description ,method)
6749
(remove* ,key slime-selector-methods :key #'car))
6750
#'< :key #'car))))
6751
6752
(def-slime-selector-method ?? "Selector help buffer."
6753
(ignore-errors (kill-buffer "*Select Help*"))
6754
(with-current-buffer (get-buffer-create "*Select Help*")
6755
(insert "Select Methods:\n\n")
6756
(loop for (key line function) in slime-selector-methods
6757
do (insert (format "%c:\t%s\n" key line)))
6758
(goto-char (point-min))
6759
(help-mode)
6760
(display-buffer (current-buffer) t))
6761
(slime-selector)
6762
(current-buffer))
6763
6764
(pushnew (list ?4 "Select in other window" (lambda () (slime-selector t)))
6765
slime-selector-methods :key #'car)
6766
6767
(def-slime-selector-method ?q "Abort."
6768
(top-level))
6769
6770
(def-slime-selector-method ?i
6771
"*inferior-lisp* buffer."
6772
(cond ((and (slime-connected-p) (slime-process))
6773
(process-buffer (slime-process)))
6774
(t
6775
"*inferior-lisp*")))
6776
6777
(def-slime-selector-method ?v
6778
"*slime-events* buffer."
6779
slime-event-buffer-name)
6780
6781
(def-slime-selector-method ?l
6782
"most recently visited lisp-mode buffer."
6783
(slime-recently-visited-buffer 'lisp-mode))
6784
6785
(def-slime-selector-method ?d
6786
"*sldb* buffer for the current connection."
6787
(or (sldb-get-default-buffer)
6788
(error "No debugger buffer")))
6789
6790
(def-slime-selector-method ?e
6791
"most recently visited emacs-lisp-mode buffer."
6792
(slime-recently-visited-buffer 'emacs-lisp-mode))
6793
6794
(def-slime-selector-method ?c
6795
"SLIME connections buffer."
6796
(slime-list-connections)
6797
slime-connections-buffer-name)
6798
6799
(def-slime-selector-method ?n
6800
"Cycle to the next Lisp connection."
6801
(slime-cycle-connections)
6802
(concat "*slime-repl "
6803
(slime-connection-name (slime-current-connection))
6804
"*"))
6805
6806
(def-slime-selector-method ?t
6807
"SLIME threads buffer."
6808
(slime-list-threads)
6809
slime-threads-buffer-name)
6810
6811
(defun slime-recently-visited-buffer (mode)
6812
"Return the most recently visited buffer whose major-mode is MODE.
6813
Only considers buffers that are not already visible."
6814
(loop for buffer in (buffer-list)
6815
when (and (with-current-buffer buffer (eq major-mode mode))
6816
(not (string-match "^ " (buffer-name buffer)))
6817
(null (get-buffer-window buffer 'visible)))
6818
return buffer
6819
finally (error "Can't find unshown buffer in %S" mode)))
6820
6821
6822
;;;; Indentation
6823
6824
(defun slime-update-indentation ()
6825
"Update indentation for all macros defined in the Lisp system."
6826
(interactive)
6827
(slime-eval-async '(swank:update-indentation-information)))
6828
6829
(defvar slime-indentation-update-hooks)
6830
6831
(defun slime-handle-indentation-update (alist)
6832
"Update Lisp indent information.
6833
6834
ALIST is a list of (SYMBOL-NAME . INDENT-SPEC) of proposed indentation
6835
settings for `common-lisp-indent-function'. The appropriate property
6836
is setup, unless the user already set one explicitly."
6837
(dolist (info alist)
6838
(let ((symbol (intern (car info)))
6839
(indent (cdr info)))
6840
;; Does the symbol have an indentation value that we set?
6841
(when (equal (get symbol 'common-lisp-indent-function)
6842
(get symbol 'slime-indent))
6843
(put symbol 'common-lisp-indent-function indent)
6844
(put symbol 'slime-indent indent))
6845
(run-hook-with-args 'slime-indentation-update-hooks symbol indent))))
6846
6847
6848
;;;; Contrib modules
6849
6850
(defvar slime-required-modules '())
6851
6852
(defun slime-require (module)
6853
(pushnew module slime-required-modules)
6854
(when (slime-connected-p)
6855
(slime-load-contribs)))
6856
6857
(defun slime-load-contribs ()
6858
(let ((needed (remove-if (lambda (s)
6859
(member (subseq (symbol-name s) 1)
6860
(mapcar #'downcase (slime-lisp-modules))))
6861
slime-required-modules)))
6862
(when needed
6863
;; No asynchronous request because with :SPAWN that could result
6864
;; in the attempt to load modules concurrently which may not be
6865
;; supported by the host Lisp.
6866
(setf (slime-lisp-modules)
6867
(slime-eval `(swank:swank-require ',needed))))))
6868
6869
(defmacro define-slime-contrib (name docstring &rest clauses)
6870
(destructuring-bind (&key slime-dependencies
6871
swank-dependencies
6872
on-load
6873
on-unload
6874
gnu-emacs-only
6875
authors
6876
license)
6877
(loop for (key . value) in clauses append `(,key ,value))
6878
`(progn
6879
,(when gnu-emacs-only
6880
`(eval-and-compile
6881
(assert (not (featurep 'xemacs)) ()
6882
,(concat (symbol-name name)
6883
" does not work with XEmacs."))))
6884
,@(mapcar (lambda (d) `(require ',d)) slime-dependencies)
6885
(defun ,(intern (concat (symbol-name name) "-init")) ()
6886
,@(mapcar (lambda (d) `(slime-require ',d)) swank-dependencies)
6887
,@on-load)
6888
(defun ,(intern (concat (symbol-name name) "-unload")) ()
6889
,@on-unload))))
6890
6891
(put 'define-slime-contrib 'lisp-indent-function 1)
6892
(put 'slime-indulge-pretty-colors 'define-slime-contrib t)
6893
6894
6895
;;;;; Pull-down menu
6896
6897
(defvar slime-easy-menu
6898
(let ((C '(slime-connected-p)))
6899
`("SLIME"
6900
[ "Edit Definition..." slime-edit-definition ,C ]
6901
[ "Return From Definition" slime-pop-find-definition-stack ,C ]
6902
[ "Complete Symbol" slime-complete-symbol ,C ]
6903
"--"
6904
("Evaluation"
6905
[ "Eval Defun" slime-eval-defun ,C ]
6906
[ "Eval Last Expression" slime-eval-last-expression ,C ]
6907
[ "Eval And Pretty-Print" slime-pprint-eval-last-expression ,C ]
6908
[ "Eval Region" slime-eval-region ,C ]
6909
[ "Interactive Eval..." slime-interactive-eval ,C ]
6910
[ "Edit Lisp Value..." slime-edit-value ,C ]
6911
[ "Call Defun" slime-call-defun ,C ])
6912
("Debugging"
6913
[ "Macroexpand Once..." slime-macroexpand-1 ,C ]
6914
[ "Macroexpand All..." slime-macroexpand-all ,C ]
6915
[ "Create Trace Buffer" slime-redirect-trace-output ,C ]
6916
[ "Toggle Trace..." slime-toggle-trace-fdefinition ,C ]
6917
[ "Untrace All" slime-untrace-all ,C]
6918
[ "Disassemble..." slime-disassemble-symbol ,C ]
6919
[ "Inspect..." slime-inspect ,C ])
6920
("Compilation"
6921
[ "Compile Defun" slime-compile-defun ,C ]
6922
[ "Compile/Load File" slime-compile-and-load-file ,C ]
6923
[ "Compile File" slime-compile-file ,C ]
6924
[ "Compile Region" slime-compile-region ,C ]
6925
"--"
6926
[ "Next Note" slime-next-note t ]
6927
[ "Previous Note" slime-previous-note t ]
6928
[ "Remove Notes" slime-remove-notes t ]
6929
[ "List Notes" slime-list-compiler-notes ,C ])
6930
("Cross Reference"
6931
[ "Who Calls..." slime-who-calls ,C ]
6932
[ "Who References... " slime-who-references ,C ]
6933
[ "Who Sets..." slime-who-sets ,C ]
6934
[ "Who Binds..." slime-who-binds ,C ]
6935
[ "Who Macroexpands..." slime-who-macroexpands ,C ]
6936
[ "Who Specializes..." slime-who-specializes ,C ]
6937
[ "List Callers..." slime-list-callers ,C ]
6938
[ "List Callees..." slime-list-callees ,C ]
6939
[ "Next Location" slime-next-location t ])
6940
("Editing"
6941
[ "Check Parens" check-parens t]
6942
[ "Update Indentation" slime-update-indentation ,C]
6943
[ "Select Buffer" slime-selector t])
6944
("Profiling"
6945
[ "Toggle Profiling..." slime-toggle-profile-fdefinition ,C ]
6946
[ "Profile Package" slime-profile-package ,C]
6947
[ "Profile by Substring" slime-profile-by-substring ,C ]
6948
[ "Unprofile All" slime-unprofile-all ,C ]
6949
[ "Show Profiled" slime-profiled-functions ,C ]
6950
"--"
6951
[ "Report" slime-profile-report ,C ]
6952
[ "Reset Counters" slime-profile-reset ,C ])
6953
("Documentation"
6954
[ "Describe Symbol..." slime-describe-symbol ,C ]
6955
[ "Lookup Documentation..." slime-documentation-lookup t ]
6956
[ "Apropos..." slime-apropos ,C ]
6957
[ "Apropos all..." slime-apropos-all ,C ]
6958
[ "Apropos Package..." slime-apropos-package ,C ]
6959
[ "Hyperspec..." slime-hyperspec-lookup t ])
6960
"--"
6961
[ "Interrupt Command" slime-interrupt ,C ]
6962
[ "Abort Async. Command" slime-quit ,C ]
6963
[ "Sync Package & Directory" slime-sync-package-and-default-directory ,C]
6964
)))
6965
6966
(defvar slime-sldb-easy-menu
6967
(let ((C '(slime-connected-p)))
6968
`("SLDB"
6969
[ "Next Frame" sldb-down t ]
6970
[ "Previous Frame" sldb-up t ]
6971
[ "Toggle Frame Details" sldb-toggle-details t ]
6972
[ "Next Frame (Details)" sldb-details-down t ]
6973
[ "Previous Frame (Details)" sldb-details-up t ]
6974
"--"
6975
[ "Eval Expression..." slime-interactive-eval ,C ]
6976
[ "Eval in Frame..." sldb-eval-in-frame ,C ]
6977
[ "Eval in Frame (pretty print)..." sldb-pprint-eval-in-frame ,C ]
6978
[ "Inspect In Frame..." sldb-inspect-in-frame ,C ]
6979
[ "Inspect Condition Object" sldb-inspect-condition ,C ]
6980
"--"
6981
[ "Restart Frame" sldb-restart-frame ,C ]
6982
[ "Return from Frame..." sldb-return-from-frame ,C ]
6983
("Invoke Restart"
6984
[ "Continue" sldb-continue ,C ]
6985
[ "Abort" sldb-abort ,C ]
6986
[ "Step" sldb-step ,C ]
6987
[ "Step next" sldb-next ,C ]
6988
[ "Step out" sldb-out ,C ]
6989
)
6990
"--"
6991
[ "Quit (throw)" sldb-quit ,C ]
6992
[ "Break With Default Debugger" sldb-break-with-default-debugger ,C ])))
6993
6994
(easy-menu-define menubar-slime slime-mode-map "SLIME" slime-easy-menu)
6995
6996
(defun slime-add-easy-menu ()
6997
(easy-menu-add slime-easy-menu 'slime-mode-map))
6998
6999
(add-hook 'slime-mode-hook 'slime-add-easy-menu)
7000
7001
(defun slime-sldb-add-easy-menu ()
7002
(easy-menu-define menubar-slime-sldb
7003
sldb-mode-map "SLDB" slime-sldb-easy-menu)
7004
(easy-menu-add slime-sldb-easy-menu 'sldb-mode-map))
7005
7006
(add-hook 'sldb-mode-hook 'slime-sldb-add-easy-menu)
7007
7008
7009
;;;; Cheat Sheet
7010
7011
(defvar slime-cheat-sheet-table
7012
'((:title "Editing lisp code"
7013
:map slime-mode-map
7014
:bindings ((slime-eval-defun "Evaluate current top level form")
7015
(slime-compile-defun "Compile current top level form")
7016
(slime-interactive-eval "Prompt for form and eval it")
7017
(slime-compile-and-load-file "Compile and load current file")
7018
(slime-sync-package-and-default-directory "Synch default package and directory with current buffer")
7019
(slime-next-note "Next compiler note")
7020
(slime-previous-note "Previous compiler note")
7021
(slime-remove-notes "Remove notes")
7022
slime-documentation-lookup))
7023
(:title "Completion"
7024
:map slime-mode-map
7025
:bindings (slime-indent-and-complete-symbol
7026
slime-fuzzy-complete-symbol))
7027
(:title "Within SLDB buffers"
7028
:map sldb-mode-map
7029
:bindings ((sldb-default-action "Do 'whatever' with thing at point")
7030
(sldb-toggle-details "Toggle frame details visualization")
7031
(sldb-quit "Quit to REPL")
7032
(sldb-abort "Invoke ABORT restart")
7033
(sldb-continue "Invoke CONTINUE restart (if available)")
7034
(sldb-show-source "Jump to frame's source code")
7035
(sldb-eval-in-frame "Evaluate in frame at point")
7036
(sldb-inspect-in-frame "Evaluate in frame at point and inspect result")))
7037
(:title "Within the Inspector"
7038
:map slime-inspector-mode-map
7039
:bindings ((slime-inspector-next-inspectable-object "Jump to next inspectable object")
7040
(slime-inspector-operate-on-point "Inspect object or execute action at point")
7041
(slime-inspector-reinspect "Reinspect current object")
7042
(slime-inspector-pop "Return to previous object")
7043
;;(slime-inspector-copy-down "Send object at point to REPL")
7044
(slime-inspector-toggle-verbose "Toggle verbose mode")
7045
(slime-inspector-quit "Quit")))
7046
(:title "Finding Definitions"
7047
:map slime-mode-map
7048
:bindings (slime-edit-definition
7049
slime-pop-find-definition-stack))))
7050
7051
(defun slime-cheat-sheet ()
7052
(interactive)
7053
(switch-to-buffer-other-frame (get-buffer-create (slime-buffer-name :cheat-sheet)))
7054
(setq buffer-read-only nil)
7055
(delete-region (point-min) (point-max))
7056
(goto-char (point-min))
7057
(insert "SLIME: The Superior Lisp Interaction Mode for Emacs (minor-mode).\n\n")
7058
(dolist (mode slime-cheat-sheet-table)
7059
(let ((title (getf mode :title))
7060
(mode-map (getf mode :map))
7061
(mode-keys (getf mode :bindings)))
7062
(insert title)
7063
(insert ":\n")
7064
(insert (make-string (1+ (length title)) ?-))
7065
(insert "\n")
7066
(let ((keys '())
7067
(descriptions '()))
7068
(dolist (func mode-keys)
7069
;; func is eithor the function name or a list (NAME DESCRIPTION)
7070
(push (if (symbolp func)
7071
(prin1-to-string func)
7072
(second func))
7073
descriptions)
7074
(let ((all-bindings (where-is-internal (if (symbolp func)
7075
func
7076
(first func))
7077
(symbol-value mode-map)))
7078
(key-bindings '()))
7079
(dolist (binding all-bindings)
7080
(when (and (vectorp binding)
7081
(integerp (aref binding 0)))
7082
(push binding key-bindings)))
7083
(push (mapconcat 'key-description key-bindings " or ") keys)))
7084
(loop
7085
with key-length = (apply 'max (mapcar 'length keys))
7086
with desc-length = (apply 'max (mapcar 'length descriptions))
7087
for key in (nreverse keys)
7088
for desc in (nreverse descriptions)
7089
do (insert desc)
7090
do (insert (make-string (- desc-length (length desc)) ? ))
7091
do (insert " => ")
7092
do (insert (if (string= "" key)
7093
"<not on any key>"
7094
key))
7095
do (insert "\n")
7096
finally do (insert "\n")))))
7097
(setq buffer-read-only t)
7098
(goto-char (point-min)))
7099
7100
7101
;;;; Test suite
7102
7103
(defstruct (slime-test (:conc-name slime-test.))
7104
name fname args doc inputs fails-for style)
7105
7106
(defvar slime-tests '()
7107
"Names of test functions.")
7108
7109
(defvar slime-test-debug-on-error nil
7110
"*When non-nil debug errors in test cases.")
7111
7112
(defvar slime-total-tests nil
7113
"Total number of tests executed during a test run.")
7114
7115
(defvar slime-failed-tests nil
7116
"Total number of failed tests during a test run.")
7117
7118
(defvar slime-skipped-tests nil
7119
"Total number of skipped tests during a test run.")
7120
7121
(defvar slime-expected-failures nil
7122
"Total number of expected failures during a test run")
7123
7124
(defvar slime-test-buffer-name "*Tests*"
7125
"The name of the buffer used to display test results.")
7126
7127
(defvar slime-lisp-under-test nil
7128
"The name of Lisp currently executing the tests.")
7129
7130
(defvar slime-randomize-test-order t
7131
"*If t execute tests in random order.
7132
If nil, execute them in definition order.")
7133
7134
;; dynamically bound during a single test
7135
(defvar slime-current-test)
7136
(defvar slime-unexpected-failures)
7137
7138
7139
;;;;; Execution engine
7140
7141
(defun slime-run-tests ()
7142
"Run the test suite.
7143
The results are presented in an outline-mode buffer, with the tests
7144
that succeeded initially folded away."
7145
(interactive)
7146
(assert (slime-at-top-level-p) () "Pending RPCs or open debuggers.")
7147
(slime-create-test-results-buffer)
7148
(unwind-protect
7149
(let ((slime-repl-history-file
7150
(expand-file-name "slime-repl-history" (slime-temp-directory)))
7151
(slime-tests (if slime-randomize-test-order
7152
(slime-shuffle-list slime-tests)
7153
slime-tests)))
7154
(slime-execute-tests))
7155
(pop-to-buffer slime-test-buffer-name)
7156
(goto-char (point-min))
7157
(hide-body)
7158
;; Expose failed tests
7159
(dolist (o (overlays-in (point-min) (point-max)))
7160
(when (overlay-get o 'slime-failed-test)
7161
(goto-char (overlay-start o))
7162
(show-subtree)))))
7163
7164
(defun slime-run-one-test (name)
7165
"Ask for the name of a test and then execute the test."
7166
(interactive (list (slime-read-test-name)))
7167
(let ((test (find name slime-tests :key #'slime-test.name)))
7168
(assert test)
7169
(let ((slime-tests (list test)))
7170
(slime-run-tests))))
7171
7172
(defun slime-read-test-name ()
7173
(let ((alist (mapcar (lambda (test)
7174
(list (symbol-name (slime-test.name test))))
7175
slime-tests)))
7176
(read (completing-read "Test: " alist nil t))))
7177
7178
(defun slime-test-should-fail-p ()
7179
(member slime-lisp-under-test (slime-test.fails-for slime-current-test)))
7180
7181
(defun slime-shuffle-list (list)
7182
(let* ((len (length list))
7183
(taken (make-vector len nil))
7184
(result (make-vector len nil)))
7185
(dolist (e list)
7186
(while (let ((i (random len)))
7187
(cond ((aref taken i))
7188
(t (aset taken i t)
7189
(aset result i e)
7190
nil)))))
7191
(append result '())))
7192
7193
(defun slime-execute-tests ()
7194
"Execute each test case with each input.
7195
Return the number of failed tests."
7196
(save-window-excursion
7197
(let ((slime-total-tests 0)
7198
(slime-skipped-tests 0)
7199
(slime-expected-passes 0)
7200
(slime-unexpected-failures 0)
7201
(slime-expected-failures 0)
7202
(slime-lisp-under-test (slime-lisp-implementation-name)))
7203
(dolist (slime-current-test slime-tests)
7204
(with-struct (slime-test. name (function fname) inputs style)
7205
slime-current-test
7206
(if (and style (not (memq (slime-communication-style) style)))
7207
(incf slime-skipped-tests)
7208
(slime-test-heading 1 "%s" name)
7209
(dolist (input inputs)
7210
(incf slime-total-tests)
7211
(message "%s: %s" name input)
7212
(slime-test-heading 2 "input: %s" input)
7213
(if slime-test-debug-on-error
7214
(let ((debug-on-error t)
7215
(debug-on-quit t))
7216
(catch 'skip
7217
(apply function input)))
7218
(condition-case err
7219
(apply function input)
7220
(error
7221
(cond ((slime-test-should-fail-p)
7222
(incf slime-expected-failures)
7223
(slime-test-failure "ERROR (expected)"
7224
(format "%S" err)))
7225
(t
7226
(incf slime-unexpected-failures)
7227
(slime-print-check-error err))))))))))
7228
(let ((summary
7229
(concat (if (and (zerop slime-expected-failures)
7230
(zerop slime-unexpected-failures))
7231
(format "All %d tests completed successfully."
7232
slime-total-tests)
7233
(format "Failed on %d (%d expected) of %d tests."
7234
(+ slime-expected-failures
7235
slime-unexpected-failures)
7236
slime-expected-failures
7237
slime-total-tests))
7238
(if (zerop slime-skipped-tests)
7239
""
7240
(format " Skipped %d tests." slime-skipped-tests)))))
7241
(save-excursion
7242
(with-current-buffer slime-test-buffer-name
7243
(goto-char (point-min))
7244
(insert summary "\n\n")))
7245
(message "%s" summary)
7246
slime-unexpected-failures))))
7247
7248
(defun slime-batch-test (results-file &optional test-name randomize)
7249
"Run the test suite in batch-mode.
7250
Exits Emacs when finished. The exit code is the number of failed tests."
7251
(let ((slime-test-debug-on-error nil))
7252
(slime)
7253
;; Block until we are up and running.
7254
(let* ((timeout 30)
7255
(cell (cons nil nil))
7256
(timer (run-with-timer timeout nil (lambda (cell)
7257
(setcar cell t))
7258
cell)))
7259
(while (not (slime-connected-p))
7260
(sit-for 1)
7261
(when (car cell)
7262
(with-temp-file results-file
7263
(insert (format "TIMEOUT: Failed to connect within %s seconds."
7264
timeout)))
7265
(kill-emacs 252))))
7266
(slime-sync-to-top-level 5)
7267
(switch-to-buffer "*scratch*")
7268
(let* ((slime-randomize-test-order (when randomize (random t) t))
7269
(failed-tests (cond (test-name (slime-run-one-test test-name))
7270
(t (slime-run-tests)))))
7271
(with-current-buffer slime-test-buffer-name
7272
(slime-delete-hidden-outline-text)
7273
(goto-char (point-min))
7274
(insert "-*- outline -*-\n\n")
7275
(write-file results-file))
7276
(kill-emacs failed-tests))))
7277
7278
7279
;;;;; Results buffer creation and output
7280
7281
(defun slime-create-test-results-buffer ()
7282
"Create and initialize the buffer for test suite results."
7283
(ignore-errors (kill-buffer slime-test-buffer-name))
7284
(with-current-buffer (get-buffer-create slime-test-buffer-name)
7285
(erase-buffer)
7286
(outline-mode)
7287
(set (make-local-variable 'outline-regexp) "\\*+")
7288
(slime-set-truncate-lines)))
7289
7290
(defun slime-delete-hidden-outline-text ()
7291
"Delete the hidden parts of an outline-mode buffer."
7292
(loop do (when (eq (get-char-property (point) 'invisible) 'outline)
7293
(delete-region (point)
7294
(next-single-char-property-change (point)
7295
'invisible)))
7296
until (eobp)
7297
do (goto-char (next-single-char-property-change (point) 'invisible))))
7298
7299
(defun slime-test-heading (level format &rest args)
7300
"Output a test suite heading.
7301
LEVEL gives the depth of nesting: 1 for top-level, 2 for a subheading, etc."
7302
(with-current-buffer slime-test-buffer-name
7303
(goto-char (point-max))
7304
(insert (make-string level ?*)
7305
" "
7306
(apply 'format format args)
7307
"\n")))
7308
7309
(defun slime-test-failure (keyword string)
7310
"Output a failure message from the test suite.
7311
KEYWORD names the type of failure and STRING describes the reason."
7312
(with-current-buffer slime-test-buffer-name
7313
(goto-char (point-max))
7314
(let ((start (point)))
7315
(insert keyword ": ")
7316
(let ((overlay (make-overlay start (point))))
7317
(overlay-put overlay 'slime-failed-test t)
7318
(overlay-put overlay 'face 'bold)))
7319
(insert string "\n")))
7320
7321
(defun slime-test-message (string)
7322
"Output a message from the test suite."
7323
(with-current-buffer slime-test-buffer-name
7324
(goto-char (point-max))
7325
(insert string "\n")))
7326
7327
7328
;;;;; Macros for defining test cases
7329
7330
(defmacro def-slime-test (name args doc inputs &rest body)
7331
"Define a test case.
7332
NAME ::= SYMBOL | (SYMBOL OPTION*) is a symbol naming the test.
7333
OPTION ::= (:fails-for IMPLEMENTATION*) | (:style COMMUNICATION-STYLE*)
7334
ARGS is a lambda-list.
7335
DOC is a docstring.
7336
INPUTS is a list of argument lists, each tested separately.
7337
BODY is the test case. The body can use `slime-check' to test
7338
conditions (assertions)."
7339
(multiple-value-bind (name fails-for style)
7340
(etypecase name
7341
(symbol (values name nil nil))
7342
(cons (let* ((opts (rest name))
7343
(name (first name))
7344
(fails-for (cdr (assq :fails-for opts)))
7345
(style (cdr (assq :style opts))))
7346
;; :style and :fails-for only options, given no more than one time?
7347
(assert (null (remove* :style (remove* :fails-for opts :key #'car)
7348
:key #'car)))
7349
(values name fails-for style))))
7350
(let ((fname (intern (format "slime-test-%s" name))))
7351
`(progn
7352
(defun ,fname ,args
7353
,doc
7354
(slime-sync-to-top-level 0.3)
7355
,@body
7356
(slime-sync-to-top-level 0.3))
7357
(setq slime-tests
7358
(append (remove* ',name slime-tests :key 'slime-test.name)
7359
(list (make-slime-test :name ',name :fname ',fname
7360
:fails-for ',fails-for
7361
:style ',style
7362
:inputs ,inputs))))))))
7363
7364
(put 'def-slime-test 'lisp-indent-function 4)
7365
7366
(defmacro slime-check (test-name &rest body)
7367
"Check a condition (assertion.)
7368
TEST-NAME can be a symbol, a string, or a (FORMAT-STRING . ARGS) list.
7369
BODY returns true if the check succeeds."
7370
(let ((check-name (gensym "check-name-")))
7371
`(let ((,check-name ,(typecase test-name
7372
(symbol (symbol-name test-name))
7373
(string test-name)
7374
(cons `(format ,@test-name)))))
7375
(if (progn ,@body)
7376
(slime-print-check-ok ,check-name)
7377
(cond ((slime-test-should-fail-p)
7378
(incf slime-expected-failures)
7379
(slime-test-failure "FAIL (expected)" ,check-name))
7380
(t
7381
(incf slime-unexpected-failures)
7382
(slime-print-check-failed ,check-name)))
7383
(when slime-test-debug-on-error
7384
(debug (format "Check failed: %S" ,check-name)))))))
7385
7386
(defun slime-print-check-ok (test-name)
7387
(slime-test-message (concat "OK: " test-name)))
7388
7389
(defun slime-print-check-failed (test-name)
7390
(slime-test-failure "FAILED" test-name))
7391
7392
(defun slime-print-check-error (reason)
7393
(slime-test-failure "ERROR" (format "%S" reason)))
7394
7395
(put 'slime-check 'lisp-indent-function 1)
7396
7397
7398
;;;;; Test case definitions
7399
7400
;; Clear out old tests.
7401
(setq slime-tests nil)
7402
7403
(defun slime-check-top-level (&optional test-name)
7404
(slime-accept-process-output nil 0.001)
7405
(slime-check "At the top level (no debugging or pending RPCs)"
7406
(slime-at-top-level-p)))
7407
7408
(defun slime-at-top-level-p ()
7409
(and (not (sldb-get-default-buffer))
7410
(null (slime-rex-continuations))))
7411
7412
(defun slime-wait-condition (name predicate timeout)
7413
(let ((end (time-add (current-time) (seconds-to-time timeout))))
7414
(while (not (funcall predicate))
7415
(let ((now (current-time)))
7416
(message "waiting for condition: %s [%s.%06d]" name
7417
(format-time-string "%H:%M:%S" now) (third now)))
7418
(cond ((time-less-p end (current-time))
7419
(error "Timeout waiting for condition: %S" name))
7420
(t
7421
;; XXX if a process-filter enters a recursive-edit, we
7422
;; hang forever
7423
(slime-accept-process-output nil 0.1))))))
7424
7425
(defun slime-sync-to-top-level (timeout)
7426
(slime-wait-condition "top-level" #'slime-at-top-level-p timeout))
7427
7428
;; XXX: unused function
7429
(defun slime-check-sldb-level (expected)
7430
(let ((sldb-level (when-let (sldb (sldb-get-default-buffer))
7431
(with-current-buffer sldb
7432
sldb-level))))
7433
(slime-check ("SLDB level (%S) is %S" expected sldb-level)
7434
(equal expected sldb-level))))
7435
7436
(defun slime-test-expect (name expected actual &optional test)
7437
(when (stringp expected) (setq expected (substring-no-properties expected)))
7438
(when (stringp actual) (setq actual (substring-no-properties actual)))
7439
(slime-check ("%s:\nexpected: [%S]\n actual: [%S]" name expected actual)
7440
(funcall (or test #'equal) expected actual)))
7441
7442
(defun sldb-level ()
7443
(when-let (sldb (sldb-get-default-buffer))
7444
(with-current-buffer sldb
7445
sldb-level)))
7446
7447
(defun slime-sldb-level= (level)
7448
(equal level (sldb-level)))
7449
7450
(defvar slime-test-symbols
7451
'(("foobar") ("foo@bar") ("@foobar") ("foobar@") ("\\@foobar")
7452
("|asdf||foo||bar|")
7453
("\\#<Foo@Bar>")
7454
("\\(setf\\ car\\)")))
7455
7456
(defun slime-check-symbol-at-point (prefix symbol suffix)
7457
;; We test that `slime-symbol-at-point' works at every
7458
;; character of the symbol name.
7459
(with-temp-buffer
7460
(lisp-mode)
7461
(insert prefix)
7462
(let ((start (point)))
7463
(insert symbol suffix)
7464
(dotimes (i (length symbol))
7465
(goto-char (+ start i))
7466
(slime-test-expect (format "Check `%s' (at %d)..."
7467
(buffer-string) (point))
7468
symbol
7469
(slime-symbol-at-point)
7470
#'equal)))))
7471
7472
(def-slime-test symbol-at-point.1 (sym)
7473
"Check that we can cope with idiosyncratic symbol names."
7474
slime-test-symbols
7475
(slime-check-symbol-at-point "" sym ""))
7476
7477
(def-slime-test symbol-at-point.2 (sym)
7478
"fancy symbol-name _not_ at BOB/EOB"
7479
slime-test-symbols
7480
(slime-check-symbol-at-point "(foo " sym " bar)"))
7481
7482
(def-slime-test symbol-at-point.3 (sym)
7483
"fancy symbol-name with leading ,"
7484
(remove-if (lambda (s) (eq (aref (car s) 0) ?@)) slime-test-symbols)
7485
(slime-check-symbol-at-point "," sym ""))
7486
7487
(def-slime-test symbol-at-point.4 (sym)
7488
"fancy symbol-name with leading ,@"
7489
slime-test-symbols
7490
(slime-check-symbol-at-point ",@" sym ""))
7491
7492
(def-slime-test symbol-at-point.5 (sym)
7493
"fancy symbol-name with leading `"
7494
slime-test-symbols
7495
(slime-check-symbol-at-point "`" sym ""))
7496
7497
(def-slime-test symbol-at-point.6 (sym)
7498
"fancy symbol-name wrapped in ()"
7499
slime-test-symbols
7500
(slime-check-symbol-at-point "(" sym ")"))
7501
7502
(def-slime-test symbol-at-point.7 (sym)
7503
"fancy symbol-name wrapped in #< {DEADBEEF}>"
7504
slime-test-symbols
7505
(slime-check-symbol-at-point "#<" sym " {DEADBEEF}>"))
7506
7507
;;(def-slime-test symbol-at-point.8 (sym)
7508
;; "fancy symbol-name wrapped in #<>"
7509
;; slime-test-symbols
7510
;; (slime-check-symbol-at-point "#<" sym ">"))
7511
7512
(def-slime-test symbol-at-point.9 (sym)
7513
"fancy symbol-name wrapped in #| ... |#"
7514
slime-test-symbols
7515
(slime-check-symbol-at-point "#|\n" sym "\n|#"))
7516
7517
(def-slime-test symbol-at-point.10 (sym)
7518
"fancy symbol-name after #| )))(( |# (1)"
7519
slime-test-symbols
7520
(slime-check-symbol-at-point "#| )))(( #|\n" sym ""))
7521
7522
(def-slime-test symbol-at-point.11 (sym)
7523
"fancy symbol-name after #| )))(( |# (2)"
7524
slime-test-symbols
7525
(slime-check-symbol-at-point "#| )))(( #|" sym ""))
7526
7527
(def-slime-test symbol-at-point.12 (sym)
7528
"fancy symbol-name wrapped in \"...\""
7529
slime-test-symbols
7530
(slime-check-symbol-at-point "\"\n" sym "\"\n"))
7531
7532
(def-slime-test symbol-at-point.13 (sym)
7533
"fancy symbol-name wrapped in \" )))(( \" (1)"
7534
slime-test-symbols
7535
(slime-check-symbol-at-point "\" )))(( \"\n" sym ""))
7536
7537
(def-slime-test symbol-at-point.14 (sym)
7538
"fancy symbol-name wrapped in \" )))(( \" (1)"
7539
slime-test-symbols
7540
(slime-check-symbol-at-point "\" )))(( \"" sym ""))
7541
7542
(def-slime-test symbol-at-point.15 (sym)
7543
"symbol-at-point after #."
7544
slime-test-symbols
7545
(slime-check-symbol-at-point "#." sym ""))
7546
7547
(def-slime-test symbol-at-point.16 (sym)
7548
"symbol-at-point after #+"
7549
slime-test-symbols
7550
(slime-check-symbol-at-point "#+" sym ""))
7551
7552
7553
(def-slime-test sexp-at-point.1 (string)
7554
"symbol-at-point after #'"
7555
'(("foo")
7556
("#:foo")
7557
("#'foo")
7558
("#'(lambda (x) x)"))
7559
(with-temp-buffer
7560
(lisp-mode)
7561
(insert string)
7562
(goto-char (point-min))
7563
(slime-test-expect (format "Check sexp `%s' (at %d)..."
7564
(buffer-string) (point))
7565
string
7566
(slime-sexp-at-point)
7567
#'equal)))
7568
7569
(def-slime-test narrowing ()
7570
"Check that narrowing is properly sustained."
7571
'()
7572
(slime-check-top-level)
7573
(let ((random-buffer-name (symbol-name (gensym)))
7574
(defun-pos) (tmpbuffer))
7575
(with-temp-buffer
7576
(dotimes (i 100) (insert (format ";;; %d. line\n" i)))
7577
(setq tmpbuffer (current-buffer))
7578
(setq defun-pos (point))
7579
(insert (concat "(defun __foo__ (x y)" "\n"
7580
" 'nothing)" "\n"))
7581
(dotimes (i 100) (insert (format ";;; %d. line\n" (+ 100 i))))
7582
(slime-check "Checking that newly created buffer is not narrowed."
7583
(not (slime-buffer-narrowed-p)))
7584
7585
(goto-char defun-pos)
7586
(narrow-to-defun)
7587
(slime-check "Checking that narrowing succeeded."
7588
(slime-buffer-narrowed-p))
7589
7590
(slime-with-popup-buffer (random-buffer-name)
7591
(slime-check ("Checking that we're in Slime's temp buffer `%s'" random-buffer-name)
7592
(equal (buffer-name (current-buffer)) random-buffer-name)))
7593
(with-current-buffer random-buffer-name
7594
;; Notice that we cannot quit the buffer within the the extent
7595
;; of slime-with-output-to-temp-buffer.
7596
(slime-popup-buffer-quit t))
7597
(slime-check ("Checking that we've got back from `%s'" random-buffer-name)
7598
(and (eq (current-buffer) tmpbuffer)
7599
(= (point) defun-pos)))
7600
7601
(slime-check "Checking that narrowing sustained after quitting Slime's temp buffer."
7602
(slime-buffer-narrowed-p))
7603
7604
(let ((slime-buffer-package "SWANK")
7605
(symbol '*buffer-package*))
7606
(slime-edit-definition (symbol-name symbol))
7607
(slime-check ("Checking that we've got M-. into swank.lisp. %S" symbol)
7608
(string= (file-name-nondirectory (buffer-file-name))
7609
"swank.lisp"))
7610
(slime-pop-find-definition-stack)
7611
(slime-check ("Checking that we've got back.")
7612
(and (eq (current-buffer) tmpbuffer)
7613
(= (point) defun-pos)))
7614
7615
(slime-check "Checking that narrowing sustained after M-,"
7616
(slime-buffer-narrowed-p)))
7617
))
7618
(slime-check-top-level))
7619
7620
(def-slime-test find-definition
7621
(name buffer-package snippet)
7622
"Find the definition of a function or macro in swank.lisp."
7623
'(("start-server" "SWANK" "(defun start-server ")
7624
("swank::start-server" "CL-USER" "(defun start-server ")
7625
("swank:start-server" "CL-USER" "(defun start-server "))
7626
(switch-to-buffer "*scratch*") ; not buffer of definition
7627
(slime-check-top-level)
7628
(let ((orig-buffer (current-buffer))
7629
(orig-pos (point))
7630
(enable-local-variables nil) ; don't get stuck on -*- eval: -*-
7631
(slime-buffer-package buffer-package))
7632
(slime-edit-definition name)
7633
;; Postconditions
7634
(slime-check ("Definition of `%S' is in swank.lisp." name)
7635
(string= (file-name-nondirectory (buffer-file-name)) "swank.lisp"))
7636
(slime-check "Definition now at point." (looking-at snippet))
7637
(slime-pop-find-definition-stack)
7638
(slime-check "Returning from definition restores original buffer/position."
7639
(and (eq orig-buffer (current-buffer))
7640
(= orig-pos (point)))))
7641
(slime-check-top-level))
7642
7643
(def-slime-test (find-definition.2 (:fails-for "allegro" "lispworks"))
7644
(buffer-content buffer-package snippet)
7645
"Check that we're able to find definitions even when
7646
confronted with nasty #.-fu."
7647
'(("#.(prog1 nil (defvar *foobar* 42))
7648
7649
(defun .foo. (x)
7650
(+ x #.*foobar*))
7651
7652
#.(prog1 nil (makunbound '*foobar*))
7653
"
7654
"SWANK"
7655
"[ \t]*(defun .foo. "
7656
))
7657
(let ((slime-buffer-package buffer-package))
7658
(with-temp-buffer
7659
(insert buffer-content)
7660
(slime-check-top-level)
7661
(slime-eval
7662
`(swank:compile-string-for-emacs
7663
,buffer-content
7664
,(buffer-name)
7665
,0
7666
,nil
7667
,nil))
7668
(let ((bufname (buffer-name)))
7669
(slime-edit-definition ".foo.")
7670
(slime-check ("Definition of `.foo.' is in buffer `%s'." bufname)
7671
(string= (buffer-name) bufname))
7672
(slime-check "Definition now at point." (looking-at snippet)))
7673
)))
7674
7675
(def-slime-test complete-symbol
7676
(prefix expected-completions)
7677
"Find the completions of a symbol-name prefix."
7678
'(("cl:compile" (("cl:compile" "cl:compile-file" "cl:compile-file-pathname"
7679
"cl:compiled-function" "cl:compiled-function-p"
7680
"cl:compiler-macro" "cl:compiler-macro-function")
7681
"cl:compile"))
7682
("cl:foobar" (nil ""))
7683
("swank::compile-file" (("swank::compile-file"
7684
"swank::compile-file-for-emacs"
7685
"swank::compile-file-if-needed"
7686
"swank::compile-file-output"
7687
"swank::compile-file-pathname")
7688
"swank::compile-file"))
7689
("cl:m-v-l" (nil "")))
7690
(let ((completions (slime-simple-completions prefix)))
7691
(slime-test-expect "Completion set" expected-completions completions)))
7692
7693
(def-slime-test arglist
7694
;; N.B. Allegro apparently doesn't return the default values of
7695
;; optional parameters. Thus the regexp in the start-server
7696
;; expected value. In a perfect world we'd find a way to smooth
7697
;; over this difference between implementations--perhaps by
7698
;; convincing Franz to provide a function that does what we want.
7699
(function-name expected-arglist)
7700
"Lookup the argument list for FUNCTION-NAME.
7701
Confirm that EXPECTED-ARGLIST is displayed."
7702
'(("swank::operator-arglist" "(swank::operator-arglist name package)")
7703
("swank::create-socket" "(swank::create-socket host port)")
7704
("swank::emacs-connected" "(swank::emacs-connected)")
7705
("swank::compile-string-for-emacs"
7706
"(swank::compile-string-for-emacs string buffer position filename policy)")
7707
("swank::connection.socket-io"
7708
"(swank::connection.socket-io \\(struct\\(ure\\)?\\|object\\|instance\\|x\\))")
7709
("cl:lisp-implementation-type" "(cl:lisp-implementation-type)")
7710
("cl:class-name"
7711
"(cl:class-name \\(class\\|object\\|instance\\|structure\\))"))
7712
(let ((arglist (slime-eval `(swank:operator-arglist ,function-name
7713
"swank"))))
7714
(slime-test-expect "Argument list is as expected"
7715
expected-arglist (and arglist (downcase arglist))
7716
(lambda (pattern arglist)
7717
(and arglist (string-match pattern arglist))))))
7718
7719
(def-slime-test (compile-defun (:fails-for "allegro" "lispworks" "clisp" "ccl"))
7720
(program subform)
7721
"Compile PROGRAM containing errors.
7722
Confirm that SUBFORM is correctly located."
7723
'(("(defun cl-user::foo () (cl-user::bar))" (cl-user::bar))
7724
("(defun cl-user::foo ()
7725
#\\space
7726
;;Sdf
7727
(cl-user::bar))"
7728
(cl-user::bar))
7729
("(defun cl-user::foo ()
7730
#+(or)skipped
7731
#| #||#
7732
#||# |#
7733
(cl-user::bar))"
7734
(cl-user::bar))
7735
("(defun cl-user::foo ()
7736
(list `(1 ,(random 10) 2 ,@(random 10) 3 ,(cl-user::bar))))"
7737
(cl-user::bar))
7738
("(defun cl-user::foo ()
7739
\"\\\" bla bla \\\"\"
7740
(cl-user::bar))"
7741
(cl-user::bar))
7742
("(defun cl-user::foo ()
7743
#.*log-events*
7744
(cl-user::bar))"
7745
(cl-user::bar))
7746
("#.'(defun x () (/ 1 0))
7747
(defun foo ()
7748
(cl-user::bar))
7749
7750
"
7751
(cl-user::bar))
7752
("(defun foo ()
7753
#+#.'(:and) (/ 1 0))"
7754
(/ 1 0))
7755
)
7756
(slime-check-top-level)
7757
(with-temp-buffer
7758
(lisp-mode)
7759
(insert program)
7760
(let ((font-lock-verbose nil))
7761
(setq slime-buffer-package ":swank")
7762
(slime-compile-string (buffer-string) 1)
7763
(setq slime-buffer-package ":cl-user")
7764
(slime-sync-to-top-level 5)
7765
(goto-char (point-max))
7766
(slime-previous-note)
7767
(slime-check error-location-correct
7768
(equal (read (current-buffer)) subform))))
7769
(slime-check-top-level))
7770
7771
(def-slime-test (compile-file (:fails-for "allegro" "lispworks" "clisp"))
7772
(string)
7773
"Insert STRING in a file, and compile it."
7774
`((,(pp-to-string '(defun foo () nil))))
7775
(let ((filename "/tmp/slime-tmp-file.lisp"))
7776
(with-temp-file filename
7777
(insert string))
7778
(let ((cell (cons nil nil)))
7779
(slime-eval-async
7780
`(swank:compile-file-for-emacs ,filename nil)
7781
(slime-rcurry (lambda (result cell)
7782
(setcar cell t)
7783
(setcdr cell result))
7784
cell))
7785
(slime-wait-condition "Compilation finished" (lambda () (car cell))
7786
0.5)
7787
(let ((result (cdr cell)))
7788
(slime-check "Compilation successfull"
7789
(eq (slime-compilation-result.successp result) t))))))
7790
7791
(def-slime-test async-eval-debugging (depth)
7792
"Test recursive debugging of asynchronous evaluation requests."
7793
'((1) (2) (3))
7794
(lexical-let ((depth depth)
7795
(debug-hook-max-depth 0))
7796
(let ((debug-hook
7797
(lambda ()
7798
(with-current-buffer (sldb-get-default-buffer)
7799
(when (> sldb-level debug-hook-max-depth)
7800
(setq debug-hook-max-depth sldb-level)
7801
(if (= sldb-level depth)
7802
;; We're at maximum recursion - time to unwind
7803
(sldb-quit)
7804
;; Going down - enter another recursive debug
7805
;; Recursively debug.
7806
(slime-eval-async '(error))))))))
7807
(let ((sldb-hook (cons debug-hook sldb-hook)))
7808
(slime-eval-async '(error))
7809
(slime-sync-to-top-level 5)
7810
(slime-check ("Maximum depth reached (%S) is %S."
7811
debug-hook-max-depth depth)
7812
(= debug-hook-max-depth depth))))))
7813
7814
(def-slime-test unwind-to-previous-sldb-level (level2 level1)
7815
"Test recursive debugging and returning to lower SLDB levels."
7816
'((2 1) (4 2))
7817
(slime-check-top-level)
7818
(lexical-let ((level2 level2)
7819
(level1 level1)
7820
(state 'enter)
7821
(max-depth 0))
7822
(let ((debug-hook
7823
(lambda ()
7824
(with-current-buffer (sldb-get-default-buffer)
7825
(setq max-depth (max sldb-level max-depth))
7826
(ecase state
7827
(enter
7828
(cond ((= sldb-level level2)
7829
(setq state 'leave)
7830
(sldb-invoke-restart (sldb-first-abort-restart)))
7831
(t
7832
(slime-eval-async `(cl:aref cl:nil ,sldb-level)))))
7833
(leave
7834
(cond ((= sldb-level level1)
7835
(setq state 'ok)
7836
(sldb-quit))
7837
(t
7838
(sldb-invoke-restart (sldb-first-abort-restart))
7839
))))))))
7840
(let ((sldb-hook (cons debug-hook sldb-hook)))
7841
(slime-eval-async `(cl:aref cl:nil 0))
7842
(slime-sync-to-top-level 15)
7843
(slime-check-top-level)
7844
(slime-check ("Maximum depth reached (%S) is %S." max-depth level2)
7845
(= max-depth level2))
7846
(slime-check ("Final state reached.")
7847
(eq state 'ok))))))
7848
7849
(defun sldb-first-abort-restart ()
7850
(let ((case-fold-search t))
7851
(position-if (lambda (x) (string-match "abort" (car x))) sldb-restarts)))
7852
7853
(def-slime-test loop-interrupt-quit
7854
()
7855
"Test interrupting a loop."
7856
'(())
7857
(slime-check-top-level)
7858
(slime-eval-async '(cl:loop) (lambda (_) ) "CL-USER")
7859
(slime-accept-process-output nil 1)
7860
(slime-check "In eval state." (slime-busy-p))
7861
(slime-interrupt)
7862
(slime-wait-condition "First interrupt" (lambda () (slime-sldb-level= 1)) 5)
7863
(with-current-buffer (sldb-get-default-buffer)
7864
(sldb-quit))
7865
(slime-sync-to-top-level 5)
7866
(slime-check-top-level))
7867
7868
(def-slime-test loop-interrupt-continue-interrupt-quit
7869
()
7870
"Test interrupting a previously interrupted but continued loop."
7871
'(())
7872
(slime-check-top-level)
7873
(slime-eval-async '(cl:loop) (lambda (_) ) "CL-USER")
7874
(sleep-for 1)
7875
(slime-wait-condition "running" #'slime-busy-p 5)
7876
(slime-interrupt)
7877
(slime-wait-condition "First interrupt" (lambda () (slime-sldb-level= 1)) 5)
7878
(with-current-buffer (sldb-get-default-buffer)
7879
(sldb-continue))
7880
(slime-wait-condition "running" (lambda ()
7881
(and (slime-busy-p)
7882
(not (sldb-get-default-buffer)))) 5)
7883
(slime-interrupt)
7884
(slime-wait-condition "Second interrupt" (lambda () (slime-sldb-level= 1)) 5)
7885
(with-current-buffer (sldb-get-default-buffer)
7886
(sldb-quit))
7887
(slime-sync-to-top-level 5)
7888
(slime-check-top-level))
7889
7890
(def-slime-test interactive-eval
7891
()
7892
"Test interactive eval and continuing from the debugger."
7893
'(())
7894
(slime-check-top-level)
7895
(lexical-let ((done nil))
7896
(let ((sldb-hook (lambda () (sldb-continue) (setq done t))))
7897
(slime-interactive-eval
7898
"(progn(cerror \"foo\" \"restart\")(cerror \"bar\" \"restart\")(+ 1 2))")
7899
(while (not done) (slime-accept-process-output))
7900
(slime-sync-to-top-level 5)
7901
(slime-check-top-level)
7902
(unless noninteractive
7903
(let ((message (current-message)))
7904
(slime-check "Minibuffer contains: \"3\""
7905
(equal "=> 3 (2 bits, #x3, #o3, #b11)" message)))))))
7906
7907
(def-slime-test interrupt-bubbling-idiot
7908
()
7909
"Test interrupting a loop that sends a lot of output to Emacs."
7910
'(())
7911
(slime-accept-process-output nil 1)
7912
(slime-check-top-level)
7913
(slime-eval-async '(cl:loop :for i :from 0 :do (cl:progn (cl:print i)
7914
(cl:finish-output)))
7915
(lambda (_) )
7916
"CL-USER")
7917
(sleep-for 1)
7918
(slime-interrupt)
7919
(slime-wait-condition "Debugger visible"
7920
(lambda ()
7921
(and (slime-sldb-level= 1)
7922
(get-buffer-window (sldb-get-default-buffer))))
7923
30)
7924
(with-current-buffer (sldb-get-default-buffer)
7925
(sldb-quit))
7926
(slime-sync-to-top-level 5))
7927
7928
(def-slime-test (interrupt-encode-message (:style :sigio))
7929
()
7930
"Test interrupt processing during swank::encode-message"
7931
'(())
7932
(slime-eval-async '(cl:loop :for i :from 0
7933
:do (swank::background-message "foo ~d" i)))
7934
(sleep-for 1)
7935
(slime-eval-async '(cl:/ 1 0))
7936
(slime-wait-condition "Debugger visible"
7937
(lambda ()
7938
(and (slime-sldb-level= 1)
7939
(get-buffer-window (sldb-get-default-buffer))))
7940
30)
7941
(with-current-buffer (sldb-get-default-buffer)
7942
(sldb-quit))
7943
(slime-sync-to-top-level 5))
7944
7945
(def-slime-test inspector
7946
(exp)
7947
"Test basic inspector workingness."
7948
'(((let ((h (make-hash-table)))
7949
(loop for i below 10 do (setf (gethash i h) i))
7950
h))
7951
((make-array 10))
7952
((make-list 10))
7953
('cons)
7954
(#'cons))
7955
(slime-inspect (prin1-to-string exp))
7956
(assert (not (slime-inspector-visible-p)))
7957
(slime-wait-condition "Inspector visible" #'slime-inspector-visible-p 5)
7958
(with-current-buffer (window-buffer (selected-window))
7959
(slime-inspector-quit))
7960
(slime-wait-condition "Inspector closed"
7961
(lambda () (not (slime-inspector-visible-p)))
7962
5)
7963
(slime-sync-to-top-level 1))
7964
7965
(defun slime-buffer-visible-p (name)
7966
(let ((buffer (window-buffer (selected-window))))
7967
(string-match name (buffer-name buffer))))
7968
7969
(defun slime-inspector-visible-p ()
7970
(slime-buffer-visible-p (slime-buffer-name :inspector)))
7971
7972
(defun slime-execute-as-command (name)
7973
"Execute `name' as if it was done by the user through the
7974
Command Loop. Similiar to `call-interactively' but also pushes on
7975
the buffer's undo-list."
7976
(undo-boundary)
7977
(call-interactively name))
7978
7979
(def-slime-test macroexpand
7980
(macro-defs bufcontent expansion1 search-str expansion2)
7981
"foo"
7982
'((("(defmacro qwertz (&body body) `(list :qwertz ',body))"
7983
"(defmacro yxcv (&body body) `(list :yxcv (qwertz ,@body)))")
7984
"(yxcv :A :B :C)"
7985
"(list :yxcv (qwertz :a :b :c))"
7986
"(qwertz"
7987
"(list :yxcv (list :qwertz '(:a :b :c)))"))
7988
(slime-check-top-level)
7989
(setq slime-buffer-package ":swank")
7990
(with-temp-buffer
7991
(lisp-mode)
7992
(dolist (def macro-defs)
7993
(slime-compile-string def 0)
7994
(slime-sync-to-top-level 5))
7995
(insert bufcontent)
7996
(goto-char (point-min))
7997
(slime-execute-as-command 'slime-macroexpand-1)
7998
(slime-wait-condition "Macroexpansion buffer visible"
7999
(lambda ()
8000
(slime-buffer-visible-p (slime-buffer-name :macroexpansion)))
8001
5)
8002
(with-current-buffer (get-buffer (slime-buffer-name :macroexpansion))
8003
(slime-test-expect "Initial macroexpansion is correct"
8004
expansion1
8005
(downcase (buffer-string)))
8006
(search-forward search-str)
8007
(backward-up-list)
8008
(slime-execute-as-command 'slime-macroexpand-1-inplace)
8009
(slime-sync-to-top-level 3)
8010
(slime-test-expect "In-place macroexpansion is correct"
8011
expansion2
8012
(downcase (buffer-string)))
8013
(slime-execute-as-command 'slime-macroexpand-undo)
8014
(slime-test-expect "Expansion after undo is correct"
8015
expansion1
8016
(downcase (buffer-string)))))
8017
(setq slime-buffer-package ":cl-user"))
8018
8019
(def-slime-test indentation (buffer-content point-markers)
8020
"Check indentation update to work correctly."
8021
'(("
8022
\(in-package :swank)
8023
8024
\(defmacro with-lolipop (&body body)
8025
`(progn ,@body))
8026
8027
\(defmacro lolipop (&body body)
8028
`(progn ,@body))
8029
8030
\(with-lolipop
8031
1
8032
2
8033
42)
8034
8035
\(lolipop
8036
1
8037
2
8038
23)
8039
"
8040
("23" "42")))
8041
(with-temp-buffer
8042
(lisp-mode)
8043
(slime-lisp-mode-hook)
8044
(insert buffer-content)
8045
(slime-compile-region (point-min) (point-max))
8046
(slime-sync-to-top-level 3)
8047
(slime-update-indentation)
8048
(slime-sync-to-top-level 3)
8049
(dolist (marker point-markers)
8050
(search-backward marker)
8051
(beginning-of-defun)
8052
(indent-sexp))
8053
(slime-test-expect "Correct buffer content"
8054
buffer-content
8055
(substring-no-properties (buffer-string)))))
8056
8057
(def-slime-test break
8058
(times exp)
8059
"Test whether BREAK invokes SLDB."
8060
(let ((exp1 '(break)))
8061
`((1 ,exp1) (2 ,exp1) (3 ,exp1)))
8062
(slime-accept-process-output nil 0.2)
8063
(slime-check-top-level)
8064
(slime-eval-async
8065
`(cl:eval (cl:read-from-string
8066
,(prin1-to-string `(dotimes (i ,times)
8067
,exp
8068
(swank::sleep-for 0.2))))))
8069
(dotimes (i times)
8070
(slime-wait-condition "Debugger visible"
8071
(lambda ()
8072
(and (slime-sldb-level= 1)
8073
(get-buffer-window
8074
(sldb-get-default-buffer))))
8075
1)
8076
(with-current-buffer (sldb-get-default-buffer)
8077
(sldb-continue))
8078
(slime-wait-condition "sldb closed"
8079
(lambda () (not (sldb-get-default-buffer)))
8080
0.2))
8081
(slime-sync-to-top-level 1))
8082
8083
(def-slime-test (break2 (:fails-for "cmucl" "allegro" "ccl"))
8084
(times exp)
8085
"Backends should arguably make sure that BREAK does not depend
8086
on *DEBUGGER-HOOK*."
8087
(let ((exp2
8088
'(block outta
8089
(let ((*debugger-hook* (lambda (c h) (return-from outta 42))))
8090
(break)))))
8091
`((1 ,exp2) (2 ,exp2) (3 ,exp2)))
8092
(slime-test-break times exp))
8093
8094
(def-slime-test locally-bound-debugger-hook
8095
()
8096
"Test that binding *DEBUGGER-HOOK* locally works properly."
8097
'(())
8098
(slime-accept-process-output nil 1)
8099
(slime-check-top-level)
8100
(slime-compile-string
8101
(prin1-to-string `(defun cl-user::quux ()
8102
(block outta
8103
(let ((*debugger-hook*
8104
(lambda (c hook)
8105
(declare (ignore c hook))
8106
(return-from outta 42))))
8107
(error "FOO")))))
8108
0)
8109
(slime-sync-to-top-level 2)
8110
(slime-eval-async '(cl-user::quux))
8111
;; FIXME: slime-wait-condition returns immediately if the test returns true
8112
(slime-wait-condition "Checking that Debugger does not popup"
8113
(lambda ()
8114
(not (sldb-get-default-buffer)))
8115
3)
8116
(slime-sync-to-top-level 5))
8117
8118
8119
(def-slime-test interrupt-at-toplevel
8120
()
8121
"Let's see what happens if we send a user interrupt at toplevel."
8122
'(())
8123
(slime-check-top-level)
8124
(unless (and (eq (slime-communication-style) :spawn)
8125
(not (featurep 'slime-repl)))
8126
(slime-interrupt)
8127
(slime-wait-condition
8128
"Debugger visible"
8129
(lambda ()
8130
(and (slime-sldb-level= 1)
8131
(get-buffer-window (sldb-get-default-buffer))))
8132
5)
8133
(with-current-buffer (sldb-get-default-buffer)
8134
(sldb-quit))
8135
(slime-sync-to-top-level 5)))
8136
8137
(def-slime-test interrupt-in-debugger (interrupts continues)
8138
"Let's see what happens if we interrupt the debugger.
8139
INTERRUPTS ... number of nested interrupts
8140
CONTINUES ... how often the continue restart should be invoked"
8141
'((1 0) (2 1) (4 2))
8142
(slime-check "No debugger" (not (sldb-get-default-buffer)))
8143
(when (and (eq (slime-communication-style) :spawn)
8144
(not (featurep 'slime-repl)))
8145
(slime-eval-async '(swank::without-slime-interrupts
8146
(swank::receive)))
8147
(sit-for 0.2))
8148
(dotimes (i interrupts)
8149
(slime-interrupt)
8150
(let ((level (1+ i)))
8151
(slime-wait-condition (format "Debug level %d reachend" level)
8152
(lambda () (equal (sldb-level) level))
8153
2)))
8154
(dotimes (i continues)
8155
(with-current-buffer (sldb-get-default-buffer)
8156
(sldb-continue))
8157
(let ((level (- interrupts (1+ i))))
8158
(slime-wait-condition (format "Return to debug level %d" level)
8159
(lambda () (equal (sldb-level) level))
8160
2)))
8161
(with-current-buffer (sldb-get-default-buffer)
8162
(sldb-quit))
8163
(slime-sync-to-top-level 1))
8164
8165
;;; FIXME: reconnection is broken since the recent io-redirection changes.
8166
(def-slime-test (disconnect-one-connection (:style :spawn)) ()
8167
"`slime-disconnect' should disconnect only the current connection"
8168
'(())
8169
(let ((connection-count (length slime-net-processes))
8170
(old-connection slime-default-connection)
8171
(slime-connected-hook nil))
8172
(unwind-protect
8173
(let ((slime-dispatching-connection
8174
(slime-connect "localhost"
8175
;; Here we assume that the request will
8176
;; be evaluated in its own thread.
8177
(slime-eval `(swank:create-server
8178
:port 0 ; use random port
8179
:style :spawn
8180
:dont-close nil)))))
8181
(slime-sync-to-top-level 3)
8182
(slime-disconnect)
8183
(slime-test-expect "Number of connections must remane the same"
8184
connection-count
8185
(length slime-net-processes)))
8186
(slime-select-connection old-connection))))
8187
8188
(def-slime-test disconnect-and-reconnect
8189
()
8190
"Close the connetion.
8191
Confirm that the subprocess continues gracefully.
8192
Reconnect afterwards."
8193
'(())
8194
(slime-check-top-level)
8195
(let* ((c (slime-connection))
8196
(p (slime-inferior-process c)))
8197
(with-current-buffer (process-buffer p)
8198
(erase-buffer))
8199
(delete-process c)
8200
(assert (equal (process-status c) 'closed) nil "Connection not closed")
8201
(slime-accept-process-output nil 0.1)
8202
(assert (equal (process-status p) 'run) nil "Subprocess not running")
8203
(with-current-buffer (process-buffer p)
8204
(assert (< (buffer-size) 500) nil "Unusual output"))
8205
(slime-inferior-connect p (slime-inferior-lisp-args p))
8206
(lexical-let ((hook nil) (p p))
8207
(setq hook (lambda ()
8208
(slime-test-expect
8209
"We are connected again" p (slime-inferior-process))
8210
(remove-hook 'slime-connected-hook hook)))
8211
(add-hook 'slime-connected-hook hook)
8212
(slime-wait-condition "Lisp restarted"
8213
(lambda ()
8214
(not (member hook slime-connected-hook)))
8215
5))))
8216
8217
8218
;;;; Utilities (no not Paul Graham style)
8219
8220
;;;; List frobbing
8221
8222
;; FIXME: Seems uncommon and less readable than loop.
8223
(defun slime-map-alist (car-fn cdr-fn alist)
8224
"Map over ALIST, calling CAR-FN on the car, and CDR-FN on the
8225
cdr of each entry."
8226
(mapcar (lambda (entry)
8227
(cons (funcall car-fn (car entry))
8228
(funcall cdr-fn (cdr entry))))
8229
alist))
8230
8231
;; XXX: unused function
8232
(defun slime-intersperse (element list)
8233
"Intersperse ELEMENT between each element of LIST."
8234
(if (null list)
8235
'()
8236
(cons (car list)
8237
(mapcan (lambda (x) (list element x)) (cdr list)))))
8238
8239
;;; FIXME: this looks almost slime `slime-alistify', perhaps the two
8240
;;; functions can be merged.
8241
(defun slime-group-similar (similar-p list)
8242
"Return the list of lists of 'similar' adjacent elements of LIST.
8243
The function SIMILAR-P is used to test for similarity.
8244
The order of the input list is preserved."
8245
(if (null list)
8246
nil
8247
(let ((accumulator (list (list (car list)))))
8248
(dolist (x (cdr list))
8249
(if (funcall similar-p x (caar accumulator))
8250
(push x (car accumulator))
8251
(push (list x) accumulator)))
8252
(reverse (mapcar #'reverse accumulator)))))
8253
8254
(defun slime-alistify (list key test)
8255
"Partition the elements of LIST into an alist.
8256
KEY extracts the key from an element and TEST is used to compare
8257
keys."
8258
(declare (type function key))
8259
(let ((alist '()))
8260
(dolist (e list)
8261
(let* ((k (funcall key e))
8262
(probe (assoc* k alist :test test)))
8263
(if probe
8264
(push e (cdr probe))
8265
(push (cons k (list e)) alist))))
8266
;; Put them back in order.
8267
(loop for (key . value) in (reverse alist)
8268
collect (cons key (reverse value)))))
8269
8270
;;;;; Misc.
8271
8272
(defun slime-length= (seq n)
8273
"Return (= (length SEQ) N)."
8274
(etypecase seq
8275
(list
8276
(cond ((zerop n) (null seq))
8277
((let ((tail (nthcdr (1- n) seq)))
8278
(and tail (null (cdr tail)))))))
8279
(sequence
8280
(= (length seq) n))))
8281
8282
(defun slime-length> (seq n)
8283
"Return (> (length SEQ) N)."
8284
(etypecase seq
8285
(list (nthcdr n seq))
8286
(sequence (> (length seq) n))))
8287
8288
(defun slime-trim-whitespace (str)
8289
(save-match-data
8290
(string-match "^\\s-*\\(.*?\\)\\s-*$" str)
8291
(match-string 1 str)))
8292
8293
;;;;; Buffer related
8294
8295
(defun slime-buffer-narrowed-p (&optional buffer)
8296
"Returns T if BUFFER (or the current buffer respectively) is narrowed."
8297
(with-current-buffer (or buffer (current-buffer))
8298
(let ((beg (point-min))
8299
(end (point-max))
8300
(total (buffer-size)))
8301
(or (/= beg 1) (/= end (1+ total))))))
8302
8303
(defun slime-column-max ()
8304
(save-excursion
8305
(goto-char (point-min))
8306
(loop for column = (prog2 (end-of-line) (current-column) (forward-line))
8307
until (= (point) (point-max))
8308
maximizing column)))
8309
8310
;;;;; CL symbols vs. Elisp symbols.
8311
8312
(defun slime-cl-symbol-name (symbol)
8313
(let ((n (if (stringp symbol) symbol (symbol-name symbol))))
8314
(if (string-match ":\\([^:]*\\)$" n)
8315
(let ((symbol-part (match-string 1 n)))
8316
(if (string-match "^|\\(.*\\)|$" symbol-part)
8317
(match-string 1 symbol-part)
8318
symbol-part))
8319
n)))
8320
8321
(defun slime-cl-symbol-package (symbol &optional default)
8322
(let ((n (if (stringp symbol) symbol (symbol-name symbol))))
8323
(if (string-match "^\\([^:]*\\):" n)
8324
(match-string 1 n)
8325
default)))
8326
8327
(defun slime-qualify-cl-symbol-name (symbol-or-name)
8328
"Return a package-qualified string for SYMBOL-OR-NAME.
8329
If SYMBOL-OR-NAME doesn't already have a package prefix the
8330
current package is used."
8331
(let ((s (if (stringp symbol-or-name)
8332
symbol-or-name
8333
(symbol-name symbol-or-name))))
8334
(if (slime-cl-symbol-package s)
8335
s
8336
(format "%s::%s"
8337
(let* ((package (slime-current-package)))
8338
;; package is a string like ":cl-user" or "CL-USER", or "\"CL-USER\"".
8339
(if package
8340
(slime-pretty-package-name package)
8341
"CL-USER"))
8342
(slime-cl-symbol-name s)))))
8343
8344
;;;;; Moving, CL idiosyncracies aware (reader conditionals &c.)
8345
8346
(defmacro slime-point-moves-p (&rest body)
8347
"Execute BODY and return true if the current buffer's point moved."
8348
(let ((pointvar (gensym "point-")))
8349
`(let ((,pointvar (point)))
8350
(save-current-buffer ,@body)
8351
(/= ,pointvar (point)))))
8352
8353
(put 'slime-point-moves-p 'lisp-indent-function 0)
8354
8355
(defun slime-forward-sexp (&optional count)
8356
"Like `forward-sexp', but understands reader-conditionals (#- and #+),
8357
and skips comments."
8358
(dotimes (i (or count 1))
8359
(slime-forward-cruft)
8360
(forward-sexp)))
8361
8362
(defconst slime-reader-conditionals-regexp
8363
;; #!+, #!- are SBCL specific reader-conditional syntax.
8364
;; We need this for the source files of SBCL itself.
8365
(regexp-opt '("#+" "#-" "#!+" "#!-")))
8366
8367
(defun slime-forward-reader-conditional ()
8368
"Move past any reader conditional (#+ or #-) at point."
8369
(when (looking-at slime-reader-conditionals-regexp)
8370
(goto-char (match-end 0))
8371
(let* ((plus-conditional-p (eq (char-before) ?+))
8372
(result (slime-eval-feature-expression
8373
(condition-case e
8374
(read (current-buffer))
8375
(invalid-read-syntax
8376
(signal 'slime-unknown-feature-expression (cdr e)))))))
8377
(unless (if plus-conditional-p result (not result))
8378
;; skip this sexp
8379
(slime-forward-sexp)))))
8380
8381
(defun slime-forward-cruft ()
8382
"Move forward over whitespace, comments, reader conditionals."
8383
(while (slime-point-moves-p (skip-chars-forward "[:space:]")
8384
(forward-comment (buffer-size))
8385
(inline (slime-forward-reader-conditional)))))
8386
8387
(defun slime-keywordify (symbol)
8388
"Make a keyword out of the symbol SYMBOL."
8389
(let ((name (downcase (symbol-name symbol))))
8390
(intern (if (eq ?: (aref name 0))
8391
name
8392
(concat ":" name)))))
8393
8394
(put 'slime-incorrect-feature-expression
8395
'error-conditions '(slime-incorrect-feature-expression error))
8396
8397
(put 'slime-unknown-feature-expression
8398
'error-conditions '(slime-unknown-feature-expression
8399
slime-incorrect-feature-expression
8400
error))
8401
8402
;; FIXME: let it crash
8403
;; FIXME: the length=1 constraint is bogus
8404
(defun slime-eval-feature-expression (e)
8405
"Interpret a reader conditional expression."
8406
(cond ((symbolp e)
8407
(memq (slime-keywordify e) (slime-lisp-features)))
8408
((and (consp e) (symbolp (car e)))
8409
(funcall (let ((head (slime-keywordify (car e))))
8410
(case head
8411
(:and #'every)
8412
(:or #'some)
8413
(:not
8414
(lexical-let ((feature-expression e))
8415
(lambda (f l)
8416
(cond
8417
((slime-length= l 0) t)
8418
((slime-length= l 1) (not (apply f l)))
8419
(t (signal 'slime-incorrect-feature-expression
8420
feature-expression))))))
8421
(t (signal 'slime-unknown-feature-expression head))))
8422
#'slime-eval-feature-expression
8423
(cdr e)))
8424
(t (signal 'slime-incorrect-feature-expression e))))
8425
8426
;;;;; Extracting Lisp forms from the buffer or user
8427
8428
(defun slime-defun-at-point ()
8429
"Return the text of the defun at point."
8430
(apply #'buffer-substring-no-properties
8431
(slime-region-for-defun-at-point)))
8432
8433
(defun slime-region-for-defun-at-point ()
8434
"Return the start and end position of defun at point."
8435
(save-excursion
8436
(save-match-data
8437
(end-of-defun)
8438
(let ((end (point)))
8439
(beginning-of-defun)
8440
(list (point) end)))))
8441
8442
(defun slime-beginning-of-symbol ()
8443
"Move to the beginning of the CL-style symbol at point."
8444
(while (re-search-backward "\\(\\sw\\|\\s_\\|\\s\\.\\|\\s\\\\|[#@|]\\)\\="
8445
(when (> (point) 2000) (- (point) 2000))
8446
t))
8447
(re-search-forward "\\=#[-+.<|]" nil t)
8448
(when (and (looking-at "@") (eq (char-before) ?\,))
8449
(forward-char)))
8450
8451
(defun slime-end-of-symbol ()
8452
"Move to the end of the CL-style symbol at point."
8453
(re-search-forward "\\=\\(\\sw\\|\\s_\\|\\s\\.\\|#:\\|[@|]\\)*"))
8454
8455
(put 'slime-symbol 'end-op 'slime-end-of-symbol)
8456
(put 'slime-symbol 'beginning-op 'slime-beginning-of-symbol)
8457
8458
(defun slime-symbol-start-pos ()
8459
"Return the starting position of the symbol under point.
8460
The result is unspecified if there isn't a symbol under the point."
8461
(save-excursion (slime-beginning-of-symbol) (point)))
8462
8463
(defun slime-symbol-end-pos ()
8464
(save-excursion (slime-end-of-symbol) (point)))
8465
8466
(defun slime-symbol-at-point ()
8467
"Return the name of the symbol at point, otherwise nil."
8468
;; (thing-at-point 'symbol) returns "" in empty buffers
8469
(let ((string (thing-at-point 'slime-symbol)))
8470
(and string
8471
(not (equal string ""))
8472
(substring-no-properties string))))
8473
8474
(defun slime-sexp-at-point ()
8475
"Return the sexp at point as a string, otherwise nil."
8476
(or (slime-symbol-at-point)
8477
(let ((string (thing-at-point 'sexp)))
8478
(if string (substring-no-properties string) nil))))
8479
8480
(defun slime-sexp-at-point-or-error ()
8481
"Return the sexp at point as a string, othwise signal an error."
8482
(or (slime-sexp-at-point) (error "No expression at point.")))
8483
8484
(defun slime-string-at-point ()
8485
"Returns the string at point as a string, otherwise nil."
8486
(let ((sexp (slime-sexp-at-point)))
8487
(if (eql (char-syntax (aref sexp 0)) ?\")
8488
sexp
8489
nil)))
8490
8491
(defun slime-string-at-point-or-error ()
8492
"Return the sexp at point as a string, othwise signal an error."
8493
(or (slime-string-at-point) (error "No string at point.")))
8494
8495
(defun slime-input-complete-p (start end)
8496
"Return t if the region from START to END contains a complete sexp."
8497
(save-excursion
8498
(goto-char start)
8499
(cond ((looking-at "\\s *['`#]?[(\"]")
8500
(ignore-errors
8501
(save-restriction
8502
(narrow-to-region start end)
8503
;; Keep stepping over blanks and sexps until the end of
8504
;; buffer is reached or an error occurs. Tolerate extra
8505
;; close parens.
8506
(loop do (skip-chars-forward " \t\r\n)")
8507
until (eobp)
8508
do (forward-sexp))
8509
t)))
8510
(t t))))
8511
8512
8513
;;;; Portability library
8514
8515
(when (featurep 'xemacs)
8516
(require 'overlay))
8517
8518
(defun slime-emacs-21-p ()
8519
(and (not (featurep 'xemacs))
8520
(= emacs-major-version 21)))
8521
8522
;;; `getf', `get', `symbol-plist' do not work on malformed plists
8523
;;; on Emacs21. On later versions they do.
8524
(when (slime-emacs-21-p)
8525
;; Perhaps we should rather introduce a new `slime-getf' than
8526
;; redefining. But what about (setf getf)? (A redefinition is not
8527
;; necessary, except for consistency.)
8528
(defun getf (plist property &optional default)
8529
(loop for (prop . val) on plist
8530
when (eq prop property) return (car val)
8531
finally (return default))))
8532
8533
(defun slime-split-string (string &optional separators omit-nulls)
8534
"This is like `split-string' in Emacs22, but also works in 21."
8535
(let ((splits (split-string string separators)))
8536
(if omit-nulls
8537
(setq splits (remove "" splits))
8538
;; SPLIT-STRING in Emacs before 22.x automatically removed nulls
8539
;; at beginning and end, so we gotta add them here again.
8540
(when (slime-emacs-21-p)
8541
(when (find (elt string 0) separators)
8542
(push "" splits))
8543
(when (find (elt string (1- (length string))) separators)
8544
(setq splits (append splits (list ""))))))
8545
splits))
8546
8547
(defun slime-delete-and-extract-region (start end)
8548
"Like `delete-and-extract-region' except that it is guaranteed
8549
to return a string. At least Emacs 21.3.50 returned `nil' on
8550
\(delete-and-extract-region (point) (point)), this function
8551
will return \"\"."
8552
(let ((result (delete-and-extract-region start end)))
8553
(if (null result)
8554
""
8555
(assert (stringp result))
8556
result)))
8557
8558
(defmacro slime-defun-if-undefined (name &rest rest)
8559
;; We can't decide at compile time whether NAME is properly
8560
;; bound. So we delay the decision to runtime to ensure some
8561
;; definition
8562
`(unless (fboundp ',name)
8563
(defun ,name ,@rest)))
8564
8565
(put 'slime-defun-if-undefined 'lisp-indent-function 2)
8566
(put 'slime-indulge-pretty-colors 'slime-defun-if-undefined t)
8567
8568
;; FIXME: defining macros here is probably too late for the compiler
8569
(defmacro slime-defmacro-if-undefined (name &rest rest)
8570
`(unless (fboundp ',name)
8571
(defmacro ,name ,@rest)))
8572
8573
(put 'slime-defmacro-if-undefined 'lisp-indent-function 2)
8574
(put 'slime-indulge-pretty-colors 'slime-defmacro-if-undefined t)
8575
8576
(defvar slime-accept-process-output-supports-floats
8577
(ignore-errors (accept-process-output nil 0.0) t))
8578
8579
(defun slime-accept-process-output (&optional process timeout)
8580
"Like `accept-process-output' but the TIMEOUT argument can be a float."
8581
(cond (slime-accept-process-output-supports-floats
8582
(accept-process-output process timeout))
8583
(t
8584
(accept-process-output process
8585
(if timeout (truncate timeout))
8586
;; Emacs 21 uses microsecs; Emacs 22 millisecs
8587
(if timeout (truncate (* timeout 1000000)))))))
8588
8589
(defun slime-pop-to-buffer (buffer &optional other-window)
8590
"Select buffer BUFFER in some window.
8591
This is like `pop-to-buffer' but also sets the input focus
8592
for (somewhat) better multiframe support."
8593
(set-buffer buffer)
8594
(let ((old-frame (selected-frame))
8595
(window (display-buffer buffer other-window)))
8596
(select-window window)
8597
;; select-window doesn't set the input focus
8598
(when (and (not (featurep 'xemacs))
8599
(>= emacs-major-version 22)
8600
(not (eq old-frame (selected-frame))))
8601
(select-frame-set-input-focus (window-frame window))))
8602
buffer)
8603
8604
(defun slime-add-local-hook (hook function &optional append)
8605
(cond ((featurep 'xemacs) (add-local-hook hook function append))
8606
(t (add-hook hook function append t))))
8607
8608
(defun slime-run-mode-hooks (&rest hooks)
8609
(if (fboundp 'run-mode-hooks)
8610
(apply #'run-mode-hooks hooks)
8611
(apply #'run-hooks hooks)))
8612
8613
(if (featurep 'xemacs)
8614
(slime-defun-if-undefined line-number-at-pos (&optional pos)
8615
(line-number pos))
8616
(slime-defun-if-undefined line-number-at-pos (&optional pos)
8617
(save-excursion
8618
(when pos (goto-char pos))
8619
(1+ (count-lines 1 (point-at-bol))))))
8620
8621
(defun slime-local-variable-p (var &optional buffer)
8622
(local-variable-p var (or buffer (current-buffer)))) ; XEmacs
8623
8624
(slime-defun-if-undefined region-active-p ()
8625
(and transient-mark-mode mark-active))
8626
8627
(if (featurep 'xemacs)
8628
(slime-defun-if-undefined use-region-p ()
8629
(region-active-p))
8630
(slime-defun-if-undefined use-region-p ()
8631
(and transient-mark-mode mark-active)))
8632
8633
(slime-defun-if-undefined next-single-char-property-change
8634
(position prop &optional object limit)
8635
(let ((limit (typecase limit
8636
(null nil)
8637
(marker (marker-position limit))
8638
(t limit))))
8639
(if (stringp object)
8640
(or (next-single-property-change position prop object limit)
8641
limit
8642
(length object))
8643
(with-current-buffer (or object (current-buffer))
8644
(let ((initial-value (get-char-property position prop object))
8645
(limit (or limit (point-max))))
8646
(loop for pos = position then
8647
(next-single-property-change pos prop object limit)
8648
if (>= pos limit) return limit
8649
if (not (eq initial-value
8650
(get-char-property pos prop object)))
8651
return pos))))))
8652
8653
(slime-defun-if-undefined previous-single-char-property-change
8654
(position prop &optional object limit)
8655
(let ((limit (typecase limit
8656
(null nil)
8657
(marker (marker-position limit))
8658
(t limit))))
8659
(if (stringp object)
8660
(or (previous-single-property-change position prop object limit)
8661
limit
8662
(length object))
8663
(with-current-buffer (or object (current-buffer))
8664
(let ((limit (or limit (point-min))))
8665
(if (<= position limit)
8666
limit
8667
(let ((initial-value (get-char-property (1- position)
8668
prop object)))
8669
(loop for pos = position then
8670
(previous-single-property-change pos prop object limit)
8671
if (<= pos limit) return limit
8672
if (not (eq initial-value
8673
(get-char-property (1- pos) prop object)))
8674
return pos))))))))
8675
8676
(slime-defun-if-undefined next-char-property-change (position &optional limit)
8677
(let ((tmp (next-overlay-change position)))
8678
(when tmp
8679
(setq tmp (min tmp limit)))
8680
(next-property-change position nil tmp)))
8681
8682
(slime-defun-if-undefined previous-char-property-change
8683
(position &optional limit)
8684
(let ((tmp (previous-overlay-change position)))
8685
(when tmp
8686
(setq tmp (max tmp limit)))
8687
(previous-property-change position nil tmp)))
8688
8689
(slime-defun-if-undefined substring-no-properties (string &optional start end)
8690
(let* ((start (or start 0))
8691
(end (or end (length string)))
8692
(string (substring string start end)))
8693
(set-text-properties 0 (- end start) nil string)
8694
string))
8695
8696
(slime-defun-if-undefined match-string-no-properties (num &optional string)
8697
(if (match-beginning num)
8698
(if string
8699
(substring-no-properties string (match-beginning num)
8700
(match-end num))
8701
(buffer-substring-no-properties (match-beginning num)
8702
(match-end num)))))
8703
8704
(slime-defun-if-undefined set-window-text-height (window height)
8705
(let ((delta (- height (window-text-height window))))
8706
(unless (zerop delta)
8707
(let ((window-min-height 1))
8708
(if (and window (not (eq window (selected-window))))
8709
(save-selected-window
8710
(select-window window)
8711
(enlarge-window delta))
8712
(enlarge-window delta))))))
8713
8714
(slime-defun-if-undefined window-text-height (&optional window)
8715
(1- (window-height window)))
8716
8717
(slime-defun-if-undefined subst-char-in-string (fromchar tochar string
8718
&optional inplace)
8719
"Replace FROMCHAR with TOCHAR in STRING each time it occurs.
8720
Unless optional argument INPLACE is non-nil, return a new string."
8721
(let ((i (length string))
8722
(newstr (if inplace string (copy-sequence string))))
8723
(while (> i 0)
8724
(setq i (1- i))
8725
(if (eq (aref newstr i) fromchar)
8726
(aset newstr i tochar)))
8727
newstr))
8728
8729
(slime-defun-if-undefined count-screen-lines
8730
(&optional beg end count-final-newline window)
8731
(unless beg
8732
(setq beg (point-min)))
8733
(unless end
8734
(setq end (point-max)))
8735
(if (= beg end)
8736
0
8737
(save-excursion
8738
(save-restriction
8739
(widen)
8740
(narrow-to-region (min beg end)
8741
(if (and (not count-final-newline)
8742
(= ?\n (char-before (max beg end))))
8743
(1- (max beg end))
8744
(max beg end)))
8745
(goto-char (point-min))
8746
;; XXX make this xemacs compatible
8747
(1+ (vertical-motion (buffer-size) window))))))
8748
8749
(slime-defun-if-undefined seconds-to-time (seconds)
8750
"Convert SECONDS (a floating point number) to a time value."
8751
(list (floor seconds 65536)
8752
(floor (mod seconds 65536))
8753
(floor (* (- seconds (ffloor seconds)) 1000000))))
8754
8755
(slime-defun-if-undefined time-less-p (t1 t2)
8756
"Say whether time value T1 is less than time value T2."
8757
(or (< (car t1) (car t2))
8758
(and (= (car t1) (car t2))
8759
(< (nth 1 t1) (nth 1 t2)))))
8760
8761
(slime-defun-if-undefined time-add (t1 t2)
8762
"Add two time values. One should represent a time difference."
8763
(let ((high (car t1))
8764
(low (if (consp (cdr t1)) (nth 1 t1) (cdr t1)))
8765
(micro (if (numberp (car-safe (cdr-safe (cdr t1))))
8766
(nth 2 t1)
8767
0))
8768
(high2 (car t2))
8769
(low2 (if (consp (cdr t2)) (nth 1 t2) (cdr t2)))
8770
(micro2 (if (numberp (car-safe (cdr-safe (cdr t2))))
8771
(nth 2 t2)
8772
0)))
8773
;; Add
8774
(setq micro (+ micro micro2))
8775
(setq low (+ low low2))
8776
(setq high (+ high high2))
8777
8778
;; Normalize
8779
;; `/' rounds towards zero while `mod' returns a positive number,
8780
;; so we can't rely on (= a (+ (* 100 (/ a 100)) (mod a 100))).
8781
(setq low (+ low (/ micro 1000000) (if (< micro 0) -1 0)))
8782
(setq micro (mod micro 1000000))
8783
(setq high (+ high (/ low 65536) (if (< low 0) -1 0)))
8784
(setq low (logand low 65535))
8785
8786
(list high low micro)))
8787
8788
(slime-defun-if-undefined line-beginning-position (&optional n)
8789
(save-excursion
8790
(beginning-of-line n)
8791
(point)))
8792
8793
(slime-defun-if-undefined line-end-position (&optional n)
8794
(save-excursion
8795
(end-of-line n)
8796
(point)))
8797
8798
(slime-defun-if-undefined check-parens ()
8799
"Verify that parentheses in the current buffer are balanced.
8800
If they are not, position point at the first syntax error found."
8801
(interactive)
8802
(let ((saved-point (point))
8803
(state (parse-partial-sexp (point-min) (point-max) -1)))
8804
(destructuring-bind (depth innermost-start last-terminated-start
8805
in-string in-comment after-quote
8806
minimum-depth comment-style
8807
comment-or-string-start &rest _) state
8808
(cond ((and (zerop depth)
8809
(not in-string)
8810
(or (not in-comment)
8811
(and (eq comment-style nil)
8812
(eobp)))
8813
(not after-quote))
8814
(goto-char saved-point)
8815
(message "All parentheses appear to be balanced."))
8816
((plusp depth)
8817
(goto-char innermost-start)
8818
(error "Missing )"))
8819
((minusp depth)
8820
(error "Extra )"))
8821
(in-string
8822
(goto-char comment-or-string-start)
8823
(error "String not terminated"))
8824
(in-comment
8825
(goto-char comment-or-string-start)
8826
(error "Comment not terminated"))
8827
(after-quote
8828
(error "After quote"))
8829
(t (error "Shouldn't happen: parsing state: %S" state))))))
8830
8831
(slime-defun-if-undefined read-directory-name (prompt
8832
&optional dir default-dirname
8833
mustmatch initial)
8834
(unless dir
8835
(setq dir default-directory))
8836
(unless default-dirname
8837
(setq default-dirname
8838
(if initial (concat dir initial) default-directory)))
8839
(let ((file (read-file-name prompt dir default-dirname mustmatch initial)))
8840
(setq file (file-name-as-directory (expand-file-name file)))
8841
(cond ((file-directory-p file)
8842
file)
8843
(t
8844
(error "Not a directory: %s" file)))))
8845
8846
(slime-defun-if-undefined check-coding-system (coding-system)
8847
(or (eq coding-system 'binary)
8848
(error "No such coding system: %S" coding-system)))
8849
8850
(slime-defun-if-undefined process-coding-system (process)
8851
'(binary . binary))
8852
8853
(slime-defun-if-undefined set-process-coding-system
8854
(process &optional decoding encoding))
8855
8856
;; For Emacs 21
8857
(slime-defun-if-undefined display-warning
8858
(type message &optional level buffer-name)
8859
(with-output-to-temp-buffer "*Warnings*"
8860
(princ (format "Warning (%s %s): %s" type level message))))
8861
8862
(unless (boundp 'temporary-file-directory)
8863
(defvar temporary-file-directory
8864
(file-name-as-directory
8865
(cond ((memq system-type '(ms-dos windows-nt))
8866
(or (getenv "TEMP") (getenv "TMPDIR") (getenv "TMP") "c:/temp"))
8867
((memq system-type '(vax-vms axp-vms))
8868
(or (getenv "TMPDIR") (getenv "TMP")
8869
(getenv "TEMP") "SYS$SCRATCH:"))
8870
(t
8871
(or (getenv "TMPDIR") (getenv "TMP") (getenv "TEMP") "/tmp"))))
8872
"The directory for writing temporary files."))
8873
8874
(slime-defmacro-if-undefined with-temp-message (message &rest body)
8875
(let ((current-message (make-symbol "current-message"))
8876
(temp-message (make-symbol "with-temp-message")))
8877
`(let ((,temp-message ,message)
8878
(,current-message))
8879
(unwind-protect
8880
(progn
8881
(when ,temp-message
8882
(setq ,current-message (current-message))
8883
(message "%s" ,temp-message))
8884
,@body)
8885
(and ,temp-message ,current-message
8886
(message "%s" ,current-message))))))
8887
8888
(slime-defmacro-if-undefined with-selected-window (window &rest body)
8889
`(save-selected-window
8890
(select-window ,window)
8891
,@body))
8892
8893
8894
(when (featurep 'xemacs)
8895
(add-hook 'sldb-hook 'sldb-xemacs-emulate-point-entered-hook))
8896
8897
(defun sldb-xemacs-emulate-point-entered-hook ()
8898
(add-hook (make-local-variable 'post-command-hook)
8899
'sldb-xemacs-post-command-hook))
8900
8901
(defun sldb-xemacs-post-command-hook ()
8902
(when (get-text-property (point) 'point-entered)
8903
(funcall (get-text-property (point) 'point-entered))))
8904
8905
(when (slime-emacs-21-p)
8906
;; ?\@ is a prefix char from 22 onward, and
8907
;; `slime-symbol-at-point' was written with that assumption.
8908
(modify-syntax-entry ?\@ "' " lisp-mode-syntax-table))
8909
8910
8911
;;;; slime.el in pretty colors
8912
8913
;;; You can use (put 'slime-indulge-pretty-colors 'slime-def-foo t) to
8914
;;; have `slime-def-foo' be fontified like `defun'.
8915
8916
(defun slime-indulge-pretty-colors (def-foo-symbol)
8917
(let ((regexp (format "(\\(%S\\)\\s +\\(\\(\\w\\|\\s_\\)+\\)"
8918
def-foo-symbol)))
8919
(font-lock-add-keywords
8920
'emacs-lisp-mode
8921
`((,regexp (1 font-lock-keyword-face)
8922
(2 font-lock-variable-name-face))))))
8923
8924
(unless (featurep 'xemacs)
8925
(loop for (symbol flag) on (symbol-plist 'slime-indulge-pretty-colors) by 'cddr
8926
when (eq flag 't) do (slime-indulge-pretty-colors symbol)))
8927
8928
;;;; Finishing up
8929
8930
(require 'bytecomp)
8931
(let ((byte-compile-warnings '()))
8932
(mapc #'byte-compile
8933
'(slime-alistify
8934
slime-log-event
8935
slime-events-buffer
8936
;;slime-write-string
8937
;;slime-repl-emit
8938
;;slime-output-buffer
8939
;;slime-connection-output-buffer
8940
;;slime-output-filter
8941
;;slime-repl-show-maximum-output
8942
slime-process-available-input
8943
slime-dispatch-event
8944
slime-net-filter
8945
slime-net-have-input-p
8946
slime-net-decode-length
8947
slime-net-read
8948
slime-print-apropos
8949
slime-insert-propertized
8950
slime-tree-insert
8951
slime-symbol-constituent-at
8952
slime-beginning-of-symbol
8953
slime-end-of-symbol
8954
slime-eval-feature-expression
8955
slime-forward-sexp
8956
slime-forward-cruft
8957
slime-forward-reader-conditional
8958
)))
8959
8960
(provide 'slime)
8961
(run-hooks 'slime-load-hook)
8962
8963
;; Local Variables:
8964
;; outline-regexp: ";;;;+"
8965
;; indent-tabs-mode: nil
8966
;; coding: latin-1-unix
8967
;; compile-command: "emacs -batch -L . -eval '(byte-compile-file \"slime.el\")' ; rm -v slime.elc"
8968
;; End:
8969
;;; slime.el ends here
8970
8971