Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/slime/contrib/slime-repl.el
990 views
1
;;; slime-repl.el ---
2
;;
3
;; Original Author: Helmut Eller
4
;; Contributors: to many to mention
5
;; License: GNU GPL (same license as Emacs)
6
;;
7
;;; Description:
8
;;
9
10
;;
11
;;; Installation:
12
;;
13
;; Call slime-setup and include 'slime-repl as argument:
14
;;
15
;; (slime-setup '(slime-repl [others conribs ...]))
16
;;
17
18
(define-slime-contrib slime-repl
19
"Read-Eval-Print Loop written in Emacs Lisp.
20
21
This contrib implements a Lisp Listener along with some niceties like
22
a persistent history and various \"shortcut\" commands. Nothing here
23
depends on comint.el; I/O is multiplexed over SLIME's socket.
24
25
This used to be the default REPL for SLIME, but it was hard to
26
maintain."
27
(:authors "too many to mention")
28
(:license "GPL")
29
(:on-load
30
(add-hook 'slime-event-hooks 'slime-repl-event-hook-function)
31
(add-hook 'slime-connected-hook 'slime-repl-connected-hook-function)
32
(setq slime-find-buffer-package-function 'slime-repl-find-buffer-package)))
33
34
;;;;; slime-repl
35
36
(defgroup slime-repl nil
37
"The Read-Eval-Print Loop (*slime-repl* buffer)."
38
:prefix "slime-repl-"
39
:group 'slime)
40
41
(defcustom slime-repl-shortcut-dispatch-char ?\,
42
"Character used to distinguish repl commands from lisp forms."
43
:type '(character)
44
:group 'slime-repl)
45
46
(defcustom slime-repl-only-save-lisp-buffers t
47
"When T we only attempt to save lisp-mode file buffers. When
48
NIL slime will attempt to save all buffers (as per
49
save-some-buffers). This applies to all ASDF related repl
50
shortcuts."
51
:type '(boolean)
52
:group 'slime-repl)
53
54
(defface slime-repl-prompt-face
55
(if (slime-face-inheritance-possible-p)
56
'((t (:inherit font-lock-keyword-face)))
57
'((((class color) (background light)) (:foreground "Purple"))
58
(((class color) (background dark)) (:foreground "Cyan"))
59
(t (:weight bold))))
60
"Face for the prompt in the SLIME REPL."
61
:group 'slime-repl)
62
63
(defface slime-repl-output-face
64
(if (slime-face-inheritance-possible-p)
65
'((t (:inherit font-lock-string-face)))
66
'((((class color) (background light)) (:foreground "RosyBrown"))
67
(((class color) (background dark)) (:foreground "LightSalmon"))
68
(t (:slant italic))))
69
"Face for Lisp output in the SLIME REPL."
70
:group 'slime-repl)
71
72
(defface slime-repl-input-face
73
'((t (:bold t)))
74
"Face for previous input in the SLIME REPL."
75
:group 'slime-repl)
76
77
(defface slime-repl-result-face
78
'((t ()))
79
"Face for the result of an evaluation in the SLIME REPL."
80
:group 'slime-repl)
81
82
(defcustom slime-repl-history-file "~/.slime-history.eld"
83
"File to save the persistent REPL history to."
84
:type 'string
85
:group 'slime-repl)
86
87
(defcustom slime-repl-history-size 200
88
"*Maximum number of lines for persistent REPL history."
89
:type 'integer
90
:group 'slime-repl)
91
92
(defcustom slime-repl-history-file-coding-system
93
(cond ((slime-find-coding-system 'utf-8-unix) 'utf-8-unix)
94
(t slime-net-coding-system))
95
"*The coding system for the history file."
96
:type 'symbol
97
:group 'slime-repl)
98
99
100
;; dummy defvar for compiler
101
(defvar slime-repl-read-mode)
102
103
(defun slime-reading-p ()
104
"True if Lisp is currently reading input from the REPL."
105
(with-current-buffer (slime-output-buffer)
106
slime-repl-read-mode))
107
108
109
;;;; Stream output
110
111
(slime-def-connection-var slime-connection-output-buffer nil
112
"The buffer for the REPL. May be nil or a dead buffer.")
113
114
(make-variable-buffer-local
115
(defvar slime-output-start nil
116
"Marker for the start of the output for the evaluation."))
117
118
(make-variable-buffer-local
119
(defvar slime-output-end nil
120
"Marker for end of output. New output is inserted at this mark."))
121
122
;; dummy definitions for the compiler
123
(defvar slime-repl-package-stack)
124
(defvar slime-repl-directory-stack)
125
(defvar slime-repl-input-start-mark)
126
(defvar slime-repl-prompt-start-mark)
127
128
(defun slime-output-buffer (&optional noprompt)
129
"Return the output buffer, create it if necessary."
130
(let ((buffer (slime-connection-output-buffer)))
131
(or (if (buffer-live-p buffer) buffer)
132
(setf (slime-connection-output-buffer)
133
(let ((connection (slime-connection)))
134
(with-current-buffer (slime-repl-buffer t connection)
135
(unless (eq major-mode 'slime-repl-mode)
136
(slime-repl-mode))
137
(setq slime-buffer-connection connection)
138
(setq slime-buffer-package (slime-lisp-package connection))
139
(slime-reset-repl-markers)
140
(unless noprompt
141
(slime-repl-insert-prompt))
142
(current-buffer)))))))
143
144
(defvar slime-repl-banner-function 'slime-repl-insert-banner)
145
146
(defun slime-repl-update-banner ()
147
(funcall slime-repl-banner-function)
148
(slime-move-point (point-max))
149
(slime-mark-output-start)
150
(slime-mark-input-start)
151
(slime-repl-insert-prompt))
152
153
(defun slime-repl-insert-banner ()
154
(when (zerop (buffer-size))
155
(let ((welcome (concat "; SLIME " (or (slime-changelog-date)
156
"- ChangeLog file not found"))))
157
(insert welcome))))
158
159
(defun slime-init-output-buffer (connection)
160
(with-current-buffer (slime-output-buffer t)
161
(setq slime-buffer-connection connection
162
slime-repl-directory-stack '()
163
slime-repl-package-stack '())
164
(slime-repl-update-banner)))
165
166
(defun slime-display-output-buffer ()
167
"Display the output buffer and scroll to bottom."
168
(with-current-buffer (slime-output-buffer)
169
(goto-char (point-max))
170
(unless (get-buffer-window (current-buffer) t)
171
(display-buffer (current-buffer) t))
172
(slime-repl-show-maximum-output)))
173
174
(defun slime-output-filter (process string)
175
(with-current-buffer (process-buffer process)
176
(when (and (plusp (length string))
177
(eq (process-status slime-buffer-connection) 'open))
178
(slime-write-string string))))
179
180
(defvar slime-open-stream-hooks)
181
182
(defun slime-open-stream-to-lisp (port)
183
(let ((stream (open-network-stream "*lisp-output-stream*"
184
(slime-with-connection-buffer ()
185
(current-buffer))
186
slime-lisp-host port)))
187
(slime-set-query-on-exit-flag stream)
188
(set-process-filter stream 'slime-output-filter)
189
(let ((pcs (process-coding-system (slime-current-connection))))
190
(set-process-coding-system stream (car pcs) (cdr pcs)))
191
(when-let (secret (slime-secret))
192
(slime-net-send secret stream))
193
(run-hook-with-args 'slime-open-stream-hooks stream)
194
stream))
195
196
(defun slime-io-speed-test (&optional profile)
197
"A simple minded benchmark for stream performance.
198
If a prefix argument is given, instrument the slime package for
199
profiling before running the benchmark."
200
(interactive "P")
201
(eval-and-compile
202
(require 'elp))
203
(elp-reset-all)
204
(elp-restore-all)
205
(load "slime.el")
206
;;(byte-compile-file "slime-net.el" t)
207
;;(setq slime-log-events nil)
208
(setq slime-enable-evaluate-in-emacs t)
209
;;(setq slime-repl-enable-presentations nil)
210
(when profile
211
(elp-instrument-package "slime-"))
212
(kill-buffer (slime-output-buffer))
213
(switch-to-buffer (slime-output-buffer))
214
(delete-other-windows)
215
(sit-for 0)
216
(slime-repl-send-string "(swank:io-speed-test 4000 1)")
217
(let ((proc (slime-inferior-process)))
218
(when proc
219
(display-buffer (process-buffer proc) t)
220
(goto-char (point-max)))))
221
222
(defvar slime-write-string-function 'slime-repl-write-string)
223
224
(defun slime-write-string (string &optional target)
225
"Insert STRING in the REPL buffer or some other TARGET.
226
If TARGET is nil, insert STRING as regular process
227
output. If TARGET is :repl-result, insert STRING as the result of the
228
evaluation. Other values of TARGET map to an Emacs marker via the
229
hashtable `slime-output-target-to-marker'; output is inserted at this marker."
230
(funcall slime-write-string-function string target))
231
232
(defun slime-repl-write-string (string &optional target)
233
(case target
234
((nil) (slime-repl-emit string))
235
(:repl-result (slime-repl-emit-result string))
236
(t (slime-emit-to-target string target))))
237
238
(defvar slime-repl-popup-on-output nil
239
"Display the output buffer when some output is written.
240
This is set to nil after displaying the buffer.")
241
242
(defmacro slime-save-marker (marker &rest body)
243
(let ((pos (gensym "pos")))
244
`(let ((,pos (marker-position ,marker)))
245
(prog1 (progn . ,body)
246
(set-marker ,marker ,pos)))))
247
248
(put 'slime-save-marker 'lisp-indent-function 1)
249
250
(defun slime-repl-emit (string)
251
;; insert the string STRING in the output buffer
252
(with-current-buffer (slime-output-buffer)
253
(save-excursion
254
(goto-char slime-output-end)
255
(slime-save-marker slime-output-start
256
(slime-propertize-region '(face slime-repl-output-face
257
rear-nonsticky (face))
258
(insert-before-markers string)
259
(when (and (= (point) slime-repl-prompt-start-mark)
260
(not (bolp)))
261
(insert-before-markers "\n")
262
(set-marker slime-output-end (1- (point)))))))
263
(when slime-repl-popup-on-output
264
(setq slime-repl-popup-on-output nil)
265
(display-buffer (current-buffer)))
266
(slime-repl-show-maximum-output)))
267
268
(defun slime-repl-emit-result (string &optional bol)
269
;; insert STRING and mark it as evaluation result
270
(with-current-buffer (slime-output-buffer)
271
(save-excursion
272
(slime-save-marker slime-output-start
273
(slime-save-marker slime-output-end
274
(goto-char slime-repl-input-start-mark)
275
(when (and bol (not (bolp))) (insert-before-markers "\n"))
276
(slime-propertize-region `(face slime-repl-result-face
277
rear-nonsticky (face))
278
(insert-before-markers string)))))
279
(slime-repl-show-maximum-output)))
280
281
(defvar slime-last-output-target-id 0
282
"The last integer we used as a TARGET id.")
283
284
(defvar slime-output-target-to-marker
285
(make-hash-table)
286
"Map from TARGET ids to Emacs markers.
287
The markers indicate where output should be inserted.")
288
289
(defun slime-output-target-marker (target)
290
"Return the marker where output for TARGET should be inserted."
291
(case target
292
((nil)
293
(with-current-buffer (slime-output-buffer)
294
slime-output-end))
295
(:repl-result
296
(with-current-buffer (slime-output-buffer)
297
slime-repl-input-start-mark))
298
(t
299
(gethash target slime-output-target-to-marker))))
300
301
(defun slime-emit-to-target (string target)
302
"Insert STRING at target TARGET.
303
See `slime-output-target-to-marker'."
304
(let* ((marker (slime-output-target-marker target))
305
(buffer (and marker (marker-buffer marker))))
306
(when buffer
307
(with-current-buffer buffer
308
(save-excursion
309
;; Insert STRING at MARKER, then move MARKER behind
310
;; the insertion.
311
(goto-char marker)
312
(insert-before-markers string)
313
(set-marker marker (point)))))))
314
315
(defun slime-switch-to-output-buffer ()
316
"Select the output buffer, when possible in an existing window.
317
318
Hint: You can use `display-buffer-reuse-frames' and
319
`special-display-buffer-names' to customize the frame in which
320
the buffer should appear."
321
(interactive)
322
(slime-pop-to-buffer (slime-output-buffer))
323
(goto-char (point-max)))
324
325
326
;;;; REPL
327
;;
328
;; The REPL uses some markers to separate input from output. The
329
;; usual configuration is as follows:
330
;;
331
;; ... output ... ... result ... prompt> ... input ...
332
;; ^ ^ ^ ^ ^
333
;; output-start output-end prompt-start input-start point-max
334
;;
335
;; input-start is a right inserting marker, because
336
;; we want it to stay behind when the user inserts text.
337
;;
338
;; We maintain the following invariant:
339
;;
340
;; output-start <= output-end <= input-start.
341
;;
342
;; This invariant is important, because we must be prepared for
343
;; asynchronous output and asynchronous reads. ("Asynchronous" means,
344
;; triggered by Lisp and not by Emacs.)
345
;;
346
;; All output is inserted at the output-end marker. Some care must be
347
;; taken when output-end and input-start are at the same position: if
348
;; we insert at that point, we must move the right markers. We should
349
;; also not leave (window-)point in the middle of the new output. The
350
;; idiom we use is a combination to slime-save-marker,
351
;; insert-before-markers, and manually updating window-point
352
;; afterwards.
353
;;
354
;; A "synchronous" evaluation request proceeds as follows: the user
355
;; inserts some text between input-start and point-max and then hits
356
;; return. We send that region to Lisp, move the output and input
357
;; makers to the line after the input and wait. When we receive the
358
;; result, we insert it together with a prompt between the output-end
359
;; and input-start mark. See `slime-repl-insert-prompt'.
360
;;
361
;; It is possible that some output for such an evaluation request
362
;; arrives after the result. This output is inserted before the
363
;; result (and before the prompt).
364
;;
365
;; If we are in "reading" state, e.g., during a call to Y-OR-N-P,
366
;; there is no prompt between output-end and input-start.
367
;;
368
369
;; FIXME: slime-lisp-package should be local in a REPL buffer
370
(slime-def-connection-var slime-lisp-package
371
"COMMON-LISP-USER"
372
"The current package name of the Superior lisp.
373
This is automatically synchronized from Lisp.")
374
375
(slime-def-connection-var slime-lisp-package-prompt-string
376
"CL-USER"
377
"The current package name of the Superior lisp.
378
This is automatically synchronized from Lisp.")
379
380
(slime-make-variables-buffer-local
381
(defvar slime-repl-package-stack nil
382
"The stack of packages visited in this repl.")
383
384
(defvar slime-repl-directory-stack nil
385
"The stack of default directories associated with this repl.")
386
387
(defvar slime-repl-prompt-start-mark)
388
(defvar slime-repl-input-start-mark)
389
(defvar slime-repl-old-input-counter 0
390
"Counter used to generate unique `slime-repl-old-input' properties.
391
This property value must be unique to avoid having adjacent inputs be
392
joined together."))
393
394
(defun slime-reset-repl-markers ()
395
(dolist (markname '(slime-output-start
396
slime-output-end
397
slime-repl-prompt-start-mark
398
slime-repl-input-start-mark))
399
(set markname (make-marker))
400
(set-marker (symbol-value markname) (point))))
401
402
;;;;; REPL mode setup
403
404
(defvar slime-repl-mode-map
405
(let ((map (make-sparse-keymap)))
406
(set-keymap-parent map lisp-mode-map)
407
map))
408
409
(slime-define-keys slime-prefix-map
410
("\C-z" 'slime-switch-to-output-buffer)
411
("\M-p" 'slime-repl-set-package))
412
413
(slime-define-keys slime-mode-map
414
("\C-c~" 'slime-sync-package-and-default-directory)
415
("\C-c\C-y" 'slime-call-defun))
416
417
(slime-define-keys slime-connection-list-mode-map
418
((kbd "RET") 'slime-goto-connection)
419
([return] 'slime-goto-connection))
420
421
(slime-define-keys slime-repl-mode-map
422
("\C-m" 'slime-repl-return)
423
([return] 'slime-repl-return)
424
("\C-j" 'slime-repl-newline-and-indent)
425
("\C-\M-m" 'slime-repl-closing-return)
426
([(control return)] 'slime-repl-closing-return)
427
("\C-a" 'slime-repl-bol)
428
([home] 'slime-repl-bol)
429
("\M-p" 'slime-repl-previous-input)
430
((kbd "C-<up>") 'slime-repl-backward-input)
431
("\M-n" 'slime-repl-next-input)
432
((kbd "C-<down>") 'slime-repl-forward-input)
433
("\M-r" 'slime-repl-previous-matching-input)
434
("\M-s" 'slime-repl-next-matching-input)
435
("\C-c\C-c" 'slime-interrupt)
436
;("\t" 'slime-complete-symbol)
437
("\t" 'slime-indent-and-complete-symbol)
438
("\M-\t" 'slime-complete-symbol)
439
(" " 'slime-space)
440
("\C-c\C-o" 'slime-repl-clear-output)
441
("\C-c\M-o" 'slime-repl-clear-buffer)
442
("\C-c\C-u" 'slime-repl-kill-input)
443
("\C-c\C-n" 'slime-repl-next-prompt)
444
("\C-c\C-p" 'slime-repl-previous-prompt)
445
("\C-c\C-z" 'slime-nop))
446
447
(slime-define-keys slime-inspector-mode-map
448
((kbd "M-RET") 'slime-inspector-copy-down-to-repl))
449
450
(slime-define-keys sldb-mode-map
451
("\C-y" 'sldb-insert-frame-call-to-repl))
452
453
(def-slime-selector-method ?r
454
"SLIME Read-Eval-Print-Loop."
455
(slime-output-buffer))
456
457
(define-minor-mode slime-repl-map-mode
458
"Minor mode which makes slime-repl-mode-map available.
459
\\{slime-repl-mode-map}"
460
nil
461
nil
462
slime-repl-mode-map)
463
464
(defun slime-repl-mode ()
465
"Major mode for interacting with a superior Lisp.
466
\\{slime-repl-mode-map}"
467
(interactive)
468
(kill-all-local-variables)
469
(setq major-mode 'slime-repl-mode)
470
(slime-editing-mode 1)
471
(slime-repl-map-mode 1)
472
(lisp-mode-variables t)
473
(set (make-local-variable 'lisp-indent-function)
474
'common-lisp-indent-function)
475
(setq font-lock-defaults nil)
476
(setq mode-name "REPL")
477
(setq slime-current-thread :repl-thread)
478
(set (make-local-variable 'scroll-conservatively) 20)
479
(set (make-local-variable 'scroll-margin) 0)
480
(when slime-repl-history-file
481
(slime-repl-safe-load-history)
482
(slime-add-local-hook 'kill-buffer-hook
483
'slime-repl-safe-save-merged-history))
484
(add-hook 'kill-emacs-hook 'slime-repl-save-all-histories)
485
(slime-setup-command-hooks)
486
;; At the REPL, we define beginning-of-defun and end-of-defun to be
487
;; the start of the previous prompt or next prompt respectively.
488
;; Notice the interplay with SLIME-REPL-BEGINNING-OF-DEFUN.
489
(set (make-local-variable 'beginning-of-defun-function)
490
'slime-repl-mode-beginning-of-defun)
491
(set (make-local-variable 'end-of-defun-function)
492
'slime-repl-mode-end-of-defun)
493
(slime-run-mode-hooks 'slime-repl-mode-hook))
494
495
(defun slime-repl-buffer (&optional create connection)
496
"Get the REPL buffer for the current connection; optionally create."
497
(funcall (if create #'get-buffer-create #'get-buffer)
498
(format "*slime-repl %s*" (slime-connection-name connection))))
499
500
(defun slime-repl ()
501
(interactive)
502
(slime-switch-to-output-buffer))
503
504
(defun slime-repl-mode-beginning-of-defun ()
505
(slime-repl-previous-prompt)
506
t)
507
508
(defun slime-repl-mode-end-of-defun ()
509
(slime-repl-next-prompt)
510
t)
511
512
(defun slime-repl-send-string (string &optional command-string)
513
(cond (slime-repl-read-mode
514
(slime-repl-return-string string))
515
(t (slime-repl-eval-string string))))
516
517
(defun slime-repl-eval-string (string)
518
(slime-rex ()
519
((list 'swank:listener-eval string) (slime-lisp-package))
520
((:ok result)
521
(slime-repl-insert-result result))
522
((:abort)
523
(slime-repl-show-abort))))
524
525
(defun slime-repl-insert-result (result)
526
(with-current-buffer (slime-output-buffer)
527
(save-excursion
528
(when result
529
(destructure-case result
530
((:values &rest strings)
531
(cond ((null strings)
532
(slime-repl-emit-result "; No value\n" t))
533
(t
534
(dolist (s strings)
535
(slime-repl-emit-result s t)))))))
536
(slime-repl-insert-prompt))
537
(slime-repl-show-maximum-output)))
538
539
(defun slime-repl-show-abort ()
540
(with-current-buffer (slime-output-buffer)
541
(save-excursion
542
(slime-save-marker slime-output-start
543
(slime-save-marker slime-output-end
544
(goto-char slime-output-end)
545
(insert-before-markers "; Evaluation aborted.\n")
546
(slime-repl-insert-prompt))))
547
(slime-repl-show-maximum-output)))
548
549
(defun slime-repl-insert-prompt ()
550
"Insert the prompt (before markers!).
551
Set point after the prompt.
552
Return the position of the prompt beginning."
553
(goto-char slime-repl-input-start-mark)
554
(slime-save-marker slime-output-start
555
(slime-save-marker slime-output-end
556
(unless (bolp) (insert-before-markers "\n"))
557
(let ((prompt-start (point))
558
(prompt (format "%s> " (slime-lisp-package-prompt-string))))
559
(slime-propertize-region
560
'(face slime-repl-prompt-face read-only t intangible t
561
slime-repl-prompt t
562
;; emacs stuff
563
rear-nonsticky (slime-repl-prompt read-only face intangible)
564
;; xemacs stuff
565
start-open t end-open t)
566
(insert-before-markers prompt))
567
(set-marker slime-repl-prompt-start-mark prompt-start)
568
prompt-start))))
569
570
(defun slime-repl-show-maximum-output ()
571
"Put the end of the buffer at the bottom of the window."
572
(when (eobp)
573
(let ((win (if (eq (window-buffer) (current-buffer))
574
(selected-window)
575
(get-buffer-window (current-buffer) t))))
576
(when win
577
(with-selected-window win
578
(set-window-point win (point-max))
579
(recenter -1))))))
580
581
(defvar slime-repl-current-input-hooks)
582
583
(defun slime-repl-current-input (&optional until-point-p)
584
"Return the current input as string.
585
The input is the region from after the last prompt to the end of
586
buffer."
587
(or (run-hook-with-args-until-success 'slime-repl-current-input-hooks
588
until-point-p)
589
(buffer-substring-no-properties slime-repl-input-start-mark
590
(if until-point-p
591
(point)
592
(point-max)))))
593
594
(defun slime-property-position (text-property &optional object)
595
"Return the first position of TEXT-PROPERTY, or nil."
596
(if (get-text-property 0 text-property object)
597
0
598
(next-single-property-change 0 text-property object)))
599
600
(defun slime-mark-input-start ()
601
(set-marker slime-repl-input-start-mark (point) (current-buffer)))
602
603
(defun slime-mark-output-start ()
604
(set-marker slime-output-start (point))
605
(set-marker slime-output-end (point)))
606
607
(defun slime-mark-output-end ()
608
;; Don't put slime-repl-output-face again; it would remove the
609
;; special presentation face, for instance in the SBCL inspector.
610
(add-text-properties slime-output-start slime-output-end
611
'(;;face slime-repl-output-face
612
rear-nonsticky (face))))
613
614
(defun slime-repl-bol ()
615
"Go to the beginning of line or the prompt."
616
(interactive)
617
(cond ((and (>= (point) slime-repl-input-start-mark)
618
(slime-same-line-p (point) slime-repl-input-start-mark))
619
(goto-char slime-repl-input-start-mark))
620
(t (beginning-of-line 1)))
621
(slime-preserve-zmacs-region))
622
623
(defun slime-preserve-zmacs-region ()
624
"In XEmacs, ensure that the zmacs-region stays active after this command."
625
(when (boundp 'zmacs-region-stays)
626
(set 'zmacs-region-stays t)))
627
628
(defun slime-repl-in-input-area-p ()
629
(<= slime-repl-input-start-mark (point)))
630
631
(defun slime-repl-at-prompt-start-p ()
632
;; This will not work on non-current prompts.
633
(= (point) slime-repl-input-start-mark))
634
635
(defun slime-repl-beginning-of-defun ()
636
"Move to beginning of defun."
637
(interactive)
638
;; We call BEGINNING-OF-DEFUN if we're at the start of a prompt
639
;; already, to trigger SLIME-REPL-MODE-BEGINNING-OF-DEFUN by means
640
;; of the locally bound BEGINNING-OF-DEFUN-FUNCTION, in order to
641
;; jump to the start of the previous prompt.
642
(if (and (not (slime-repl-at-prompt-start-p))
643
(slime-repl-in-input-area-p))
644
(goto-char slime-repl-input-start-mark)
645
(beginning-of-defun))
646
t)
647
648
;; FIXME: this looks very strange
649
(defun slime-repl-end-of-defun ()
650
"Move to next of defun."
651
(interactive)
652
;; C.f. SLIME-REPL-BEGINNING-OF-DEFUN.
653
(if (and (not (= (point) (point-max)))
654
(slime-repl-in-input-area-p))
655
(goto-char (point-max))
656
(end-of-defun))
657
t)
658
659
(defun slime-repl-previous-prompt ()
660
"Move backward to the previous prompt."
661
(interactive)
662
(slime-repl-find-prompt t))
663
664
(defun slime-repl-next-prompt ()
665
"Move forward to the next prompt."
666
(interactive)
667
(slime-repl-find-prompt))
668
669
(defun slime-repl-find-prompt (&optional backward)
670
(let ((origin (point))
671
(prop 'slime-repl-prompt))
672
(while (progn
673
(slime-search-property-change prop backward)
674
(not (or (slime-end-of-proprange-p prop) (bobp) (eobp)))))
675
(unless (slime-end-of-proprange-p prop)
676
(goto-char origin))))
677
678
(defun slime-search-property-change (prop &optional backward)
679
(cond (backward
680
(goto-char (previous-single-char-property-change (point) prop)))
681
(t
682
(goto-char (next-single-char-property-change (point) prop)))))
683
684
(defun slime-end-of-proprange-p (property)
685
(and (get-char-property (max 1 (1- (point))) property)
686
(not (get-char-property (point) property))))
687
688
(defvar slime-repl-return-hooks)
689
690
(defun slime-repl-return (&optional end-of-input)
691
"Evaluate the current input string, or insert a newline.
692
Send the current input only if a whole expression has been entered,
693
i.e. the parenthesis are matched.
694
695
With prefix argument send the input even if the parenthesis are not
696
balanced."
697
(interactive "P")
698
(slime-check-connected)
699
(cond (end-of-input
700
(slime-repl-send-input))
701
(slime-repl-read-mode ; bad style?
702
(slime-repl-send-input t))
703
((and (get-text-property (point) 'slime-repl-old-input)
704
(< (point) slime-repl-input-start-mark))
705
(slime-repl-grab-old-input end-of-input)
706
(slime-repl-recenter-if-needed))
707
((run-hook-with-args-until-success 'slime-repl-return-hooks))
708
((slime-input-complete-p slime-repl-input-start-mark (point-max))
709
(slime-repl-send-input t))
710
(t
711
(slime-repl-newline-and-indent)
712
(message "[input not complete]"))))
713
714
(defun slime-repl-recenter-if-needed ()
715
"Make sure that (point) is visible."
716
(unless (pos-visible-in-window-p (point-max))
717
(save-excursion
718
(goto-char (point-max))
719
(recenter -1))))
720
721
(defun slime-repl-send-input (&optional newline)
722
"Goto to the end of the input and send the current input.
723
If NEWLINE is true then add a newline at the end of the input."
724
(unless (slime-repl-in-input-area-p)
725
(error "No input at point."))
726
(goto-char (point-max))
727
(let ((end (point))) ; end of input, without the newline
728
(slime-repl-add-to-input-history
729
(buffer-substring slime-repl-input-start-mark end))
730
(when newline
731
(insert "\n")
732
(slime-repl-show-maximum-output))
733
(let ((inhibit-modification-hooks t))
734
(add-text-properties slime-repl-input-start-mark
735
(point)
736
`(slime-repl-old-input
737
,(incf slime-repl-old-input-counter))))
738
(let ((overlay (make-overlay slime-repl-input-start-mark end)))
739
;; These properties are on an overlay so that they won't be taken
740
;; by kill/yank.
741
(overlay-put overlay 'read-only t)
742
(overlay-put overlay 'face 'slime-repl-input-face)))
743
(let ((input (slime-repl-current-input)))
744
(goto-char (point-max))
745
(slime-mark-input-start)
746
(slime-mark-output-start)
747
(slime-repl-send-string input)))
748
749
(defun slime-repl-grab-old-input (replace)
750
"Resend the old REPL input at point.
751
If replace is non-nil the current input is replaced with the old
752
input; otherwise the new input is appended. The old input has the
753
text property `slime-repl-old-input'."
754
(multiple-value-bind (beg end) (slime-property-bounds 'slime-repl-old-input)
755
(let ((old-input (buffer-substring beg end)) ;;preserve
756
;;properties, they will be removed later
757
(offset (- (point) beg)))
758
;; Append the old input or replace the current input
759
(cond (replace (goto-char slime-repl-input-start-mark))
760
(t (goto-char (point-max))
761
(unless (eq (char-before) ?\ )
762
(insert " "))))
763
(delete-region (point) (point-max))
764
(save-excursion
765
(insert old-input)
766
(when (equal (char-before) ?\n)
767
(delete-char -1)))
768
(forward-char offset))))
769
770
(defun slime-repl-closing-return ()
771
"Evaluate the current input string after closing all open lists."
772
(interactive)
773
(goto-char (point-max))
774
(save-restriction
775
(narrow-to-region slime-repl-input-start-mark (point))
776
(while (ignore-errors (save-excursion (backward-up-list 1)) t)
777
(insert ")")))
778
(slime-repl-return))
779
780
(defun slime-repl-newline-and-indent ()
781
"Insert a newline, then indent the next line.
782
Restrict the buffer from the prompt for indentation, to avoid being
783
confused by strange characters (like unmatched quotes) appearing
784
earlier in the buffer."
785
(interactive)
786
(save-restriction
787
(narrow-to-region slime-repl-prompt-start-mark (point-max))
788
(insert "\n")
789
(lisp-indent-line)))
790
791
(defun slime-repl-delete-current-input ()
792
"Delete all text from the prompt."
793
(interactive)
794
(delete-region slime-repl-input-start-mark (point-max)))
795
796
(defun slime-repl-kill-input ()
797
"Kill all text from the prompt to point."
798
(interactive)
799
(cond ((< (marker-position slime-repl-input-start-mark) (point))
800
(kill-region slime-repl-input-start-mark (point)))
801
((= (point) (marker-position slime-repl-input-start-mark))
802
(slime-repl-delete-current-input))))
803
804
(defun slime-repl-replace-input (string)
805
(slime-repl-delete-current-input)
806
(insert-and-inherit string))
807
808
(defun slime-repl-input-line-beginning-position ()
809
(save-excursion
810
(goto-char slime-repl-input-start-mark)
811
(line-beginning-position)))
812
813
(defvar slime-repl-clear-buffer-hook)
814
815
(defun slime-repl-clear-buffer ()
816
"Delete the output generated by the Lisp process."
817
(interactive)
818
(let ((inhibit-read-only t))
819
(delete-region (point-min) slime-repl-prompt-start-mark)
820
(delete-region slime-output-start slime-output-end)
821
(when (< (point) slime-repl-input-start-mark)
822
(goto-char slime-repl-input-start-mark))
823
(recenter t))
824
(run-hooks 'slime-repl-clear-buffer-hook))
825
826
(defun slime-repl-clear-output ()
827
"Delete the output inserted since the last input."
828
(interactive)
829
(let ((start (save-excursion
830
(slime-repl-previous-prompt)
831
(ignore-errors (forward-sexp))
832
(forward-line)
833
(point)))
834
(end (1- (slime-repl-input-line-beginning-position))))
835
(when (< start end)
836
(let ((inhibit-read-only t))
837
(delete-region start end)
838
(save-excursion
839
(goto-char start)
840
(insert ";;; output flushed"))))))
841
842
(defun slime-repl-set-package (package)
843
"Set the package of the REPL buffer to PACKAGE."
844
(interactive (list (let* ((p (slime-current-package))
845
(p (and p (slime-pretty-package-name p)))
846
(p (and (not (equal p (slime-lisp-package))) p)))
847
(slime-read-package-name "Package: " p))))
848
(with-current-buffer (slime-output-buffer)
849
(let ((previouse-point (- (point) slime-repl-input-start-mark)))
850
(destructuring-bind (name prompt-string)
851
(slime-repl-shortcut-eval `(swank:set-package ,package))
852
(setf (slime-lisp-package) name)
853
(setf (slime-lisp-package-prompt-string) prompt-string)
854
(setf slime-buffer-package name)
855
(slime-repl-insert-prompt)
856
(when (plusp previouse-point)
857
(goto-char (+ previouse-point slime-repl-input-start-mark)))))))
858
859
860
;;;;; History
861
862
(defcustom slime-repl-wrap-history nil
863
"*T to wrap history around when the end is reached."
864
:type 'boolean
865
:group 'slime-repl)
866
867
(defcustom slime-repl-history-remove-duplicates nil
868
"*When T all duplicates are removed except the last one."
869
:type 'boolean
870
:group 'slime-repl)
871
872
(defcustom slime-repl-history-trim-whitespaces nil
873
"*When T strip all whitespaces from the beginning and end."
874
:type 'boolean
875
:group 'slime-repl)
876
877
(make-variable-buffer-local
878
(defvar slime-repl-input-history '()
879
"History list of strings read from the REPL buffer."))
880
881
(defun slime-string-trim (character-bag string)
882
(flet ((find-bound (&optional from-end)
883
(position-if-not (lambda (char) (memq char character-bag))
884
string :from-end from-end)))
885
(let ((start (find-bound))
886
(end (find-bound t)))
887
(if start
888
(subseq string start (1+ end))
889
""))))
890
891
(defun slime-repl-add-to-input-history (string)
892
"Add STRING to the input history.
893
Empty strings and duplicates are ignored."
894
(when slime-repl-history-trim-whitespaces
895
(setq string (slime-string-trim '(?\n ?\ ?\t) string)))
896
(unless (equal string "")
897
(when slime-repl-history-remove-duplicates
898
(setq slime-repl-input-history
899
(remove string slime-repl-input-history)))
900
(unless (equal string (car slime-repl-input-history))
901
(push string slime-repl-input-history))))
902
903
;; These two vars contain the state of the last history search. We
904
;; only use them if `last-command' was 'slime-repl-history-replace,
905
;; otherwise we reinitialize them.
906
907
(defvar slime-repl-input-history-position -1
908
"Newer items have smaller indices.")
909
910
(defvar slime-repl-history-pattern nil
911
"The regexp most recently used for finding input history.")
912
913
(defun slime-repl-history-replace (direction &optional regexp)
914
"Replace the current input with the next line in DIRECTION.
915
DIRECTION is 'forward' or 'backward' (in the history list).
916
If REGEXP is non-nil, only lines matching REGEXP are considered."
917
(setq slime-repl-history-pattern regexp)
918
(let* ((min-pos -1)
919
(max-pos (length slime-repl-input-history))
920
(pos0 (cond ((slime-repl-history-search-in-progress-p)
921
slime-repl-input-history-position)
922
(t min-pos)))
923
(pos (slime-repl-position-in-history pos0 direction (or regexp "")
924
(slime-repl-current-input)))
925
(msg nil))
926
(cond ((and (< min-pos pos) (< pos max-pos))
927
(slime-repl-replace-input (nth pos slime-repl-input-history))
928
(setq msg (format "History item: %d" pos)))
929
((not slime-repl-wrap-history)
930
(setq msg (cond ((= pos min-pos) "End of history")
931
((= pos max-pos) "Beginning of history"))))
932
(slime-repl-wrap-history
933
(setq pos (if (= pos min-pos) max-pos min-pos))
934
(setq msg "Wrapped history")))
935
(when (or (<= pos min-pos) (<= max-pos pos))
936
(when regexp
937
(setq msg (concat msg "; no matching item"))))
938
;;(message "%s [%d %d %s]" msg start-pos pos regexp)
939
(message "%s%s" msg (cond ((not regexp) "")
940
(t (format "; current regexp: %s" regexp))))
941
(setq slime-repl-input-history-position pos)
942
(setq this-command 'slime-repl-history-replace)))
943
944
(defun slime-repl-history-search-in-progress-p ()
945
(eq last-command 'slime-repl-history-replace))
946
947
(defun slime-repl-terminate-history-search ()
948
(setq last-command this-command))
949
950
(defun slime-repl-position-in-history (start-pos direction regexp
951
&optional exclude-string)
952
"Return the position of the history item matching REGEXP.
953
Return -1 resp. the length of the history if no item matches.
954
If EXCLUDE-STRING is specified then it's excluded from the search."
955
;; Loop through the history list looking for a matching line
956
(let* ((step (ecase direction
957
(forward -1)
958
(backward 1)))
959
(history slime-repl-input-history)
960
(len (length history)))
961
(loop for pos = (+ start-pos step) then (+ pos step)
962
if (< pos 0) return -1
963
if (<= len pos) return len
964
for history-item = (nth pos history)
965
if (and (string-match regexp history-item)
966
(not (equal history-item exclude-string)))
967
return pos)))
968
969
(defun slime-repl-previous-input ()
970
"Cycle backwards through input history.
971
If the `last-command' was a history navigation command use the
972
same search pattern for this command.
973
Otherwise use the current input as search pattern."
974
(interactive)
975
(slime-repl-history-replace 'backward (slime-repl-history-pattern t)))
976
977
(defun slime-repl-next-input ()
978
"Cycle forwards through input history.
979
See `slime-repl-previous-input'."
980
(interactive)
981
(slime-repl-history-replace 'forward (slime-repl-history-pattern t)))
982
983
(defun slime-repl-forward-input ()
984
"Cycle forwards through input history."
985
(interactive)
986
(slime-repl-history-replace 'forward (slime-repl-history-pattern)))
987
988
(defun slime-repl-backward-input ()
989
"Cycle backwards through input history."
990
(interactive)
991
(slime-repl-history-replace 'backward (slime-repl-history-pattern)))
992
993
(defun slime-repl-previous-matching-input (regexp)
994
(interactive (list (slime-read-from-minibuffer
995
"Previous element matching (regexp): ")))
996
(slime-repl-terminate-history-search)
997
(slime-repl-history-replace 'backward regexp))
998
999
(defun slime-repl-next-matching-input (regexp)
1000
(interactive (list (slime-read-from-minibuffer
1001
"Next element matching (regexp): ")))
1002
(slime-repl-terminate-history-search)
1003
(slime-repl-history-replace 'forward regexp))
1004
1005
(defun slime-repl-history-pattern (&optional use-current-input)
1006
"Return the regexp for the navigation commands."
1007
(cond ((slime-repl-history-search-in-progress-p)
1008
slime-repl-history-pattern)
1009
(use-current-input
1010
(assert (<= slime-repl-input-start-mark (point)))
1011
(let ((str (slime-repl-current-input t)))
1012
(cond ((string-match "^[ \n]*$" str) nil)
1013
(t (concat "^" (regexp-quote str))))))
1014
(t nil)))
1015
1016
(defun slime-repl-delete-from-input-history (string)
1017
"Delete STRING from the repl input history.
1018
1019
When string is not provided then clear the current repl input and
1020
use it as an input. This is useful to get rid of unwanted repl
1021
history entries while navigating the repl history."
1022
(interactive (list (slime-repl-current-input)))
1023
(let ((merged-history
1024
(slime-repl-merge-histories slime-repl-input-history
1025
(slime-repl-read-history nil t))))
1026
(setq slime-repl-input-history
1027
(delete* string merged-history :test #'string=))
1028
(slime-repl-save-history))
1029
(slime-repl-delete-current-input))
1030
1031
;;;;; Persistent History
1032
1033
(defun slime-repl-merge-histories (old-hist new-hist)
1034
"Merge entries from OLD-HIST and NEW-HIST."
1035
;; Newer items in each list are at the beginning.
1036
(let* ((ht (make-hash-table :test #'equal))
1037
(test (lambda (entry)
1038
(or (gethash entry ht)
1039
(progn (setf (gethash entry ht) t)
1040
nil)))))
1041
(append (remove-if test new-hist)
1042
(remove-if test old-hist))))
1043
1044
(defun slime-repl-load-history (&optional filename)
1045
"Set the current SLIME REPL history.
1046
It can be read either from FILENAME or `slime-repl-history-file' or
1047
from a user defined filename."
1048
(interactive (list (slime-repl-read-history-filename)))
1049
(let ((file (or filename slime-repl-history-file)))
1050
(setq slime-repl-input-history (slime-repl-read-history file t))))
1051
1052
(defun slime-repl-read-history (&optional filename noerrer)
1053
"Read and return the history from FILENAME.
1054
The default value for FILENAME is `slime-repl-history-file'.
1055
If NOERROR is true return and the file doesn't exits return nil."
1056
(let ((file (or filename slime-repl-history-file)))
1057
(cond ((not (file-readable-p file)) '())
1058
(t (with-temp-buffer
1059
(insert-file-contents file)
1060
(read (current-buffer)))))))
1061
1062
(defun slime-repl-read-history-filename ()
1063
(read-file-name "Use SLIME REPL history from file: "
1064
slime-repl-history-file))
1065
1066
(defun slime-repl-save-merged-history (&optional filename)
1067
"Read the history file, merge the current REPL history and save it.
1068
This tries to be smart in merging the history from the file and the
1069
current history in that it tries to detect the unique entries using
1070
`slime-repl-merge-histories'."
1071
(interactive (list (slime-repl-read-history-filename)))
1072
(let ((file (or filename slime-repl-history-file)))
1073
(with-temp-message "saving history..."
1074
(let ((hist (slime-repl-merge-histories (slime-repl-read-history file t)
1075
slime-repl-input-history)))
1076
(slime-repl-save-history file hist)))))
1077
1078
(defun slime-repl-save-history (&optional filename history)
1079
"Simply save the current SLIME REPL history to a file.
1080
When SLIME is setup to always load the old history and one uses only
1081
one instance of slime all the time, there is no need to merge the
1082
files and this function is sufficient.
1083
1084
When the list is longer than `slime-repl-history-size' it will be
1085
truncated. That part is untested, though!"
1086
(interactive (list (slime-repl-read-history-filename)))
1087
(let ((file (or filename slime-repl-history-file))
1088
(hist (or history slime-repl-input-history)))
1089
(unless (file-writable-p file)
1090
(error (format "History file not writable: %s" file)))
1091
(let ((hist (subseq hist 0 (min (length hist) slime-repl-history-size))))
1092
;;(message "saving %s to %s\n" hist file)
1093
(with-temp-file file
1094
(let ((cs slime-repl-history-file-coding-system)
1095
(print-length nil) (print-level nil))
1096
(setq buffer-file-coding-system cs)
1097
(insert (format ";; -*- coding: %s -*-\n" cs))
1098
(insert ";; History for SLIME REPL. Automatically written.\n"
1099
";; Edit only if you know what you're doing\n")
1100
(prin1 (mapcar #'substring-no-properties hist) (current-buffer)))))))
1101
1102
(defun slime-repl-save-all-histories ()
1103
"Save the history in each repl buffer."
1104
(dolist (b (buffer-list))
1105
(with-current-buffer b
1106
(when (eq major-mode 'slime-repl-mode)
1107
(slime-repl-safe-save-merged-history)))))
1108
1109
(defun slime-repl-safe-save-merged-history ()
1110
(slime-repl-call-with-handler
1111
#'slime-repl-save-merged-history
1112
"%S while saving the history. Continue? "))
1113
1114
(defun slime-repl-safe-load-history ()
1115
(slime-repl-call-with-handler
1116
#'slime-repl-load-history
1117
"%S while loading the history. Continue? "))
1118
1119
(defun slime-repl-call-with-handler (fun query)
1120
"Call FUN in the context of an error handler.
1121
The handler will use qeuery to ask the use if the error should be ingored."
1122
(condition-case err
1123
(funcall fun)
1124
(error
1125
(if (y-or-n-p (format query (error-message-string err)))
1126
nil
1127
(signal (car err) (cdr err))))))
1128
1129
1130
;;;;; REPL Read Mode
1131
1132
(define-key slime-repl-mode-map
1133
(string slime-repl-shortcut-dispatch-char) 'slime-handle-repl-shortcut)
1134
1135
(define-minor-mode slime-repl-read-mode
1136
"Mode the read input from Emacs
1137
\\{slime-repl-read-mode-map}"
1138
nil
1139
"[read]"
1140
'(("\C-m" . slime-repl-return)
1141
([return] . slime-repl-return)
1142
("\C-c\C-b" . slime-repl-read-break)
1143
("\C-c\C-c" . slime-repl-read-break)))
1144
1145
(make-variable-buffer-local
1146
(defvar slime-read-string-threads nil))
1147
1148
(make-variable-buffer-local
1149
(defvar slime-read-string-tags nil))
1150
1151
(defun slime-repl-read-string (thread tag)
1152
(slime-switch-to-output-buffer)
1153
(push thread slime-read-string-threads)
1154
(push tag slime-read-string-tags)
1155
(goto-char (point-max))
1156
(slime-mark-output-end)
1157
(slime-mark-input-start)
1158
(slime-repl-read-mode 1))
1159
1160
(defun slime-repl-return-string (string)
1161
(slime-dispatch-event `(:emacs-return-string
1162
,(pop slime-read-string-threads)
1163
,(pop slime-read-string-tags)
1164
,string))
1165
(slime-repl-read-mode -1))
1166
1167
(defun slime-repl-read-break ()
1168
(interactive)
1169
(slime-dispatch-event `(:emacs-interrupt ,(car slime-read-string-threads))))
1170
1171
(defun slime-repl-abort-read (thread tag)
1172
(with-current-buffer (slime-output-buffer)
1173
(pop slime-read-string-threads)
1174
(pop slime-read-string-tags)
1175
(slime-repl-read-mode -1)
1176
(message "Read aborted")))
1177
1178
1179
;;;;; REPL handlers
1180
1181
(defstruct (slime-repl-shortcut (:conc-name slime-repl-shortcut.))
1182
symbol names handler one-liner)
1183
1184
(defvar slime-repl-shortcut-table nil
1185
"A list of slime-repl-shortcuts")
1186
1187
(defvar slime-repl-shortcut-history '()
1188
"History list of shortcut command names.")
1189
1190
(defvar slime-within-repl-shortcut-handler-p nil
1191
"Bound to T if we're in a REPL shortcut handler invoked from the REPL.")
1192
1193
(defun slime-handle-repl-shortcut ()
1194
(interactive)
1195
(if (> (point) slime-repl-input-start-mark)
1196
(insert (string slime-repl-shortcut-dispatch-char))
1197
(let ((shortcut (slime-lookup-shortcut
1198
(completing-read "Command: "
1199
(slime-bogus-completion-alist
1200
(slime-list-all-repl-shortcuts))
1201
nil t nil
1202
'slime-repl-shortcut-history))))
1203
(with-struct (slime-repl-shortcut. handler) shortcut
1204
(let ((slime-within-repl-shortcut-handler-p t))
1205
(call-interactively handler))))))
1206
1207
(defun slime-list-all-repl-shortcuts ()
1208
(loop for shortcut in slime-repl-shortcut-table
1209
append (slime-repl-shortcut.names shortcut)))
1210
1211
(defun slime-lookup-shortcut (name)
1212
(find-if (lambda (s) (member name (slime-repl-shortcut.names s)))
1213
slime-repl-shortcut-table))
1214
1215
(defmacro defslime-repl-shortcut (elisp-name names &rest options)
1216
"Define a new repl shortcut. ELISP-NAME is a symbol specifying
1217
the name of the interactive function to create, or NIL if no
1218
function should be created.
1219
1220
NAMES is a list of \(full-name . aliases\).
1221
1222
OPTIONS is an plist specifying the handler doing the actual work
1223
of the shortcut \(`:handler'\), and a help text \(`:one-liner'\)."
1224
`(progn
1225
,(when elisp-name
1226
`(defun ,elisp-name ()
1227
(interactive)
1228
(call-interactively ,(second (assoc :handler options)))))
1229
(let ((new-shortcut (make-slime-repl-shortcut
1230
:symbol ',elisp-name
1231
:names (list ,@names)
1232
,@(apply #'append options))))
1233
(setq slime-repl-shortcut-table
1234
(remove-if (lambda (s)
1235
(member ',(car names) (slime-repl-shortcut.names s)))
1236
slime-repl-shortcut-table))
1237
(push new-shortcut slime-repl-shortcut-table)
1238
',elisp-name)))
1239
1240
(defun slime-repl-shortcut-eval (sexp &optional package)
1241
"This function should be used by REPL shortcut handlers instead
1242
of `slime-eval' to evaluate their final expansion. (This
1243
expansion will be added to the REPL's history.)"
1244
(when slime-within-repl-shortcut-handler-p ; were we invoked via ,foo?
1245
(slime-repl-add-to-input-history (prin1-to-string sexp)))
1246
(slime-eval sexp package))
1247
1248
(defun slime-repl-shortcut-eval-async (sexp &optional cont package)
1249
"This function should be used by REPL shortcut handlers instead
1250
of `slime-eval-async' to evaluate their final expansion. (This
1251
expansion will be added to the REPL's history.)"
1252
(when slime-within-repl-shortcut-handler-p ; were we invoked via ,foo?
1253
(slime-repl-add-to-input-history (prin1-to-string sexp)))
1254
(slime-eval-async sexp cont package))
1255
1256
(defun slime-list-repl-short-cuts ()
1257
(interactive)
1258
(slime-with-popup-buffer ((slime-buffer-name :repl-help))
1259
(let ((table (sort* (copy-list slime-repl-shortcut-table) #'string<
1260
:key (lambda (x)
1261
(car (slime-repl-shortcut.names x))))))
1262
(save-excursion
1263
(dolist (shortcut table)
1264
(let ((names (slime-repl-shortcut.names shortcut)))
1265
(insert (pop names)) ;; first print the "full" name
1266
(when names
1267
;; we also have aliases
1268
(insert " (aka ")
1269
(while (cdr names)
1270
(insert (pop names) ", "))
1271
(insert (car names) ")"))
1272
(when (slime-repl-shortcut.one-liner shortcut)
1273
(insert "\n " (slime-repl-shortcut.one-liner shortcut)))
1274
(insert "\n")))))))
1275
1276
(defun slime-save-some-lisp-buffers ()
1277
(if slime-repl-only-save-lisp-buffers
1278
(save-some-buffers nil (lambda ()
1279
(and (memq major-mode slime-lisp-modes)
1280
(not (null buffer-file-name)))))
1281
(save-some-buffers)))
1282
1283
1284
(defslime-repl-shortcut slime-repl-shortcut-help ("help" "?")
1285
(:handler 'slime-list-repl-short-cuts)
1286
(:one-liner "Display the help."))
1287
1288
(defslime-repl-shortcut nil ("change-directory" "!d" "cd")
1289
(:handler 'slime-set-default-directory)
1290
(:one-liner "Change the current directory."))
1291
1292
(defslime-repl-shortcut nil ("pwd")
1293
(:handler (lambda ()
1294
(interactive)
1295
(let ((dir (slime-eval `(swank:default-directory))))
1296
(message "Directory %s" dir))))
1297
(:one-liner "Show the current directory."))
1298
1299
(defslime-repl-shortcut slime-repl-push-directory
1300
("push-directory" "+d" "pushd")
1301
(:handler (lambda (directory)
1302
(interactive
1303
(list (read-directory-name
1304
"Push directory: "
1305
(slime-eval '(swank:default-directory))
1306
nil nil "")))
1307
(push (slime-eval '(swank:default-directory))
1308
slime-repl-directory-stack)
1309
(slime-set-default-directory directory)))
1310
(:one-liner "Save the current directory and set it to a new one."))
1311
1312
(defslime-repl-shortcut slime-repl-pop-directory
1313
("pop-directory" "-d" "popd")
1314
(:handler (lambda ()
1315
(interactive)
1316
(if (null slime-repl-directory-stack)
1317
(message "Directory stack is empty.")
1318
(slime-set-default-directory
1319
(pop slime-repl-directory-stack)))))
1320
(:one-liner "Restore the last saved directory."))
1321
1322
(defslime-repl-shortcut nil ("change-package" "!p" "in-package" "in")
1323
(:handler 'slime-repl-set-package)
1324
(:one-liner "Change the current package."))
1325
1326
(defslime-repl-shortcut slime-repl-push-package ("push-package" "+p")
1327
(:handler (lambda (package)
1328
(interactive (list (slime-read-package-name "Package: ")))
1329
(push (slime-lisp-package) slime-repl-package-stack)
1330
(slime-repl-set-package package)))
1331
(:one-liner "Save the current package and set it to a new one."))
1332
1333
(defslime-repl-shortcut slime-repl-pop-package ("pop-package" "-p")
1334
(:handler (lambda ()
1335
(interactive)
1336
(if (null slime-repl-package-stack)
1337
(message "Package stack is empty.")
1338
(slime-repl-set-package
1339
(pop slime-repl-package-stack)))))
1340
(:one-liner "Restore the last saved package."))
1341
1342
(defslime-repl-shortcut slime-repl-resend ("resend-form")
1343
(:handler (lambda ()
1344
(interactive)
1345
(insert (car slime-repl-input-history))
1346
(insert "\n")
1347
(slime-repl-send-input)))
1348
(:one-liner "Resend the last form."))
1349
1350
(defslime-repl-shortcut slime-repl-disconnect ("disconnect")
1351
(:handler 'slime-disconnect)
1352
(:one-liner "Disconnect the current connection."))
1353
1354
(defslime-repl-shortcut slime-repl-disconnect-all ("disconnect-all")
1355
(:handler 'slime-disconnect-all)
1356
(:one-liner "Disconnect all connections."))
1357
1358
(defslime-repl-shortcut slime-repl-sayoonara ("sayoonara")
1359
(:handler (lambda ()
1360
(interactive)
1361
(when (slime-connected-p)
1362
(slime-quit-lisp))
1363
(slime-kill-all-buffers)))
1364
(:one-liner "Quit all Lisps and close all SLIME buffers."))
1365
1366
(defslime-repl-shortcut slime-repl-quit ("quit")
1367
(:handler (lambda ()
1368
(interactive)
1369
;; `slime-quit-lisp' determines the connection to quit
1370
;; on behalf of the REPL's `slime-buffer-connection'.
1371
(let ((repl-buffer (slime-output-buffer)))
1372
(slime-quit-lisp)
1373
(kill-buffer repl-buffer))))
1374
(:one-liner "Quit the current Lisp."))
1375
1376
(defslime-repl-shortcut slime-repl-defparameter ("defparameter" "!")
1377
(:handler (lambda (name value)
1378
(interactive (list (slime-read-symbol-name "Name (symbol): " t)
1379
(slime-read-from-minibuffer "Value: " "*")))
1380
(insert "(cl:defparameter " name " " value
1381
" \"REPL generated global variable.\")")
1382
(slime-repl-send-input t)))
1383
(:one-liner "Define a new global, special, variable."))
1384
1385
(defslime-repl-shortcut slime-repl-compile-and-load ("compile-and-load" "cl")
1386
(:handler (lambda (filename)
1387
(interactive (list (expand-file-name
1388
(read-file-name "File: " nil nil nil nil))))
1389
(slime-save-some-lisp-buffers)
1390
(slime-repl-shortcut-eval-async
1391
`(swank:compile-file-if-needed
1392
,(slime-to-lisp-filename filename) t)
1393
#'slime-compilation-finished)))
1394
(:one-liner "Compile (if neccessary) and load a lisp file."))
1395
1396
(defslime-repl-shortcut nil ("restart-inferior-lisp")
1397
(:handler 'slime-restart-inferior-lisp)
1398
(:one-liner "Restart *inferior-lisp* and reconnect SLIME."))
1399
1400
(defun slime-redirect-inferior-output (&optional noerror)
1401
"Redirect output of the inferior-process to the REPL buffer."
1402
(interactive)
1403
(let ((proc (slime-inferior-process)))
1404
(cond (proc
1405
(let ((filter (slime-rcurry #'slime-inferior-output-filter
1406
(slime-current-connection))))
1407
(set-process-filter proc filter)))
1408
(noerror)
1409
(t (error "No inferior lisp process")))))
1410
1411
(defun slime-inferior-output-filter (proc string conn)
1412
(cond ((eq (process-status conn) 'closed)
1413
(message "Connection closed. Removing inferior output filter.")
1414
(message "Lost output: %S" string)
1415
(set-process-filter proc nil))
1416
(t
1417
(slime-output-filter conn string))))
1418
1419
(defun slime-redirect-trace-output ()
1420
"Redirect the trace output to a separate Emacs buffer."
1421
(interactive)
1422
(let ((buffer (get-buffer-create (slime-buffer-name :trace))))
1423
(with-current-buffer buffer
1424
(let ((marker (copy-marker (buffer-size)))
1425
(target (incf slime-last-output-target-id)))
1426
(puthash target marker slime-output-target-to-marker)
1427
(slime-eval `(swank:redirect-trace-output ,target))))
1428
;; Note: We would like the entries in
1429
;; slime-output-target-to-marker to disappear when the buffers are
1430
;; killed. We cannot just make the hash-table ":weakness 'value"
1431
;; -- there is no reference from the buffers to the markers in the
1432
;; buffer, so entries would disappear even though the buffers are
1433
;; alive. Best solution might be to make buffer-local variables
1434
;; that keep the markers. --mkoeppe
1435
(pop-to-buffer buffer)))
1436
1437
(defun slime-call-defun ()
1438
"Insert a call to the toplevel form defined around point into the REPL."
1439
(interactive)
1440
(flet ((insert-call (symbol &key (function t)
1441
defclass)
1442
(let* ((qualified-symbol-name (slime-qualify-cl-symbol-name symbol))
1443
(symbol-name (slime-cl-symbol-name qualified-symbol-name))
1444
(symbol-package (slime-cl-symbol-package qualified-symbol-name))
1445
(call (if (equalp (slime-lisp-package) symbol-package)
1446
symbol-name
1447
qualified-symbol-name)))
1448
(slime-switch-to-output-buffer)
1449
(goto-char slime-repl-input-start-mark)
1450
(insert (if function
1451
"("
1452
" "))
1453
(if defclass
1454
(insert "make-instance '"))
1455
(insert call)
1456
(when function
1457
(insert " ")
1458
(save-excursion (insert ")")))
1459
(unless function
1460
(goto-char slime-repl-input-start-mark)))))
1461
(let ((toplevel (slime-parse-toplevel-form)))
1462
(if (symbolp toplevel)
1463
(error "Not in a function definition")
1464
(destructure-case toplevel
1465
(((:defun :defgeneric :defmacro :define-compiler-macro) symbol)
1466
(insert-call symbol))
1467
((:defmethod symbol &rest args)
1468
(declare (ignore args))
1469
(insert-call symbol))
1470
(((:defparameter :defvar :defconstant) symbol)
1471
(insert-call symbol :function nil))
1472
(((:defclass) symbol)
1473
(insert-call symbol :defclass t))
1474
(t
1475
(error "Not in a function definition")))))))
1476
1477
(defun slime-inspector-copy-down-to-repl (number)
1478
"Evaluate the inspector slot at point via the REPL (to set `*')."
1479
(interactive (list (or (get-text-property (point) 'slime-part-number)
1480
(error "No part at point"))))
1481
(slime-repl-send-string (format "%s" `(swank:inspector-nth-part ,number)))
1482
(slime-repl))
1483
1484
(defun sldb-insert-frame-call-to-repl ()
1485
"Insert a call to a frame at point."
1486
(interactive)
1487
(let ((call (slime-eval `(swank-backend::frame-call
1488
,(sldb-frame-number-at-point)))))
1489
(slime-switch-to-output-buffer)
1490
(if (>= (point) slime-repl-prompt-start-mark)
1491
(insert call)
1492
(save-excursion
1493
(goto-char (point-max))
1494
(insert call))))
1495
(slime-repl))
1496
1497
(defun slime-set-default-directory (directory)
1498
"Make DIRECTORY become Lisp's current directory."
1499
(interactive (list (read-directory-name "Directory: " nil nil t)))
1500
(let ((dir (expand-file-name directory)))
1501
(message "default-directory: %s"
1502
(slime-from-lisp-filename
1503
(slime-repl-shortcut-eval `(swank:set-default-directory
1504
,(slime-to-lisp-filename dir)))))
1505
(with-current-buffer (slime-output-buffer)
1506
(setq default-directory dir))))
1507
1508
(defun slime-sync-package-and-default-directory ()
1509
"Set Lisp's package and directory to the values in current buffer."
1510
(interactive)
1511
(let* ((package (slime-current-package))
1512
(exists-p (or (null package)
1513
(slime-eval `(cl:packagep (swank::guess-package ,package)))))
1514
(directory default-directory))
1515
(when (and package exists-p)
1516
(slime-repl-set-package package))
1517
(slime-set-default-directory directory)
1518
;; Sync *inferior-lisp* dir
1519
(let* ((proc (slime-process))
1520
(buffer (and proc (process-buffer proc))))
1521
(when buffer
1522
(with-current-buffer buffer
1523
(setq default-directory directory))))
1524
(message "package: %s%s directory: %s"
1525
(with-current-buffer (slime-output-buffer)
1526
(slime-lisp-package))
1527
(if exists-p "" (format " (package %s doesn't exist)" package))
1528
directory)))
1529
1530
(defun slime-goto-connection ()
1531
"Switch to the REPL buffer for the connection at point."
1532
(interactive)
1533
(let ((slime-dispatching-connection (slime-connection-at-point)))
1534
(switch-to-buffer (slime-output-buffer))))
1535
1536
(defun slime-repl-inside-string-or-comment-p ()
1537
(save-restriction
1538
(when (and (boundp 'slime-repl-input-start-mark)
1539
slime-repl-input-start-mark
1540
(>= (point) slime-repl-input-start-mark))
1541
(narrow-to-region slime-repl-input-start-mark (point)))
1542
(slime-inside-string-or-comment-p)))
1543
1544
(defvar slime-repl-easy-menu
1545
(let ((C '(slime-connected-p)))
1546
`("REPL"
1547
[ "Send Input" slime-repl-return ,C ]
1548
[ "Close and Send Input " slime-repl-closing-return ,C ]
1549
[ "Interrupt Lisp process" slime-interrupt ,C ]
1550
"--"
1551
[ "Previous Input" slime-repl-previous-input t ]
1552
[ "Next Input" slime-repl-next-input t ]
1553
[ "Goto Previous Prompt " slime-repl-previous-prompt t ]
1554
[ "Goto Next Prompt " slime-repl-next-prompt t ]
1555
[ "Clear Last Output" slime-repl-clear-output t ]
1556
[ "Clear Buffer " slime-repl-clear-buffer t ]
1557
[ "Kill Current Input" slime-repl-kill-input t ])))
1558
1559
(defun slime-repl-add-easy-menu ()
1560
(easy-menu-define menubar-slime-repl slime-repl-mode-map
1561
"REPL" slime-repl-easy-menu)
1562
(easy-menu-define menubar-slime slime-repl-mode-map
1563
"SLIME" slime-easy-menu)
1564
(easy-menu-add slime-repl-easy-menu 'slime-repl-mode-map))
1565
1566
(add-hook 'slime-repl-mode-hook 'slime-repl-add-easy-menu)
1567
1568
(defun slime-hide-inferior-lisp-buffer ()
1569
"Display the REPL buffer instead of the *inferior-lisp* buffer."
1570
(let* ((buffer (if (slime-process)
1571
(process-buffer (slime-process))))
1572
(window (if buffer (get-buffer-window buffer t)))
1573
(repl-buffer (slime-output-buffer t))
1574
(repl-window (get-buffer-window repl-buffer)))
1575
(when buffer
1576
(bury-buffer buffer))
1577
(cond (repl-window
1578
(when window
1579
(delete-window window)))
1580
(window
1581
(set-window-buffer window repl-buffer))
1582
(t
1583
(pop-to-buffer repl-buffer)
1584
(goto-char (point-max))))))
1585
1586
(defun slime-repl-connected-hook-function ()
1587
(destructuring-bind (package prompt)
1588
(let ((slime-current-thread t))
1589
(slime-eval '(swank:create-repl nil)))
1590
(setf (slime-lisp-package) package)
1591
(setf (slime-lisp-package-prompt-string) prompt))
1592
(slime-hide-inferior-lisp-buffer)
1593
(slime-init-output-buffer (slime-connection)))
1594
1595
(defun slime-repl-event-hook-function (event)
1596
(destructure-case event
1597
((:write-string output &optional target)
1598
(slime-write-string output target)
1599
t)
1600
((:read-string thread tag)
1601
(assert thread)
1602
(slime-repl-read-string thread tag)
1603
t)
1604
((:read-aborted thread tag)
1605
(slime-repl-abort-read thread tag)
1606
t)
1607
((:open-dedicated-output-stream port)
1608
(slime-open-stream-to-lisp port)
1609
t)
1610
((:new-package package prompt-string)
1611
(setf (slime-lisp-package) package)
1612
(setf (slime-lisp-package-prompt-string) prompt-string)
1613
(let ((buffer (slime-connection-output-buffer)))
1614
(when (buffer-live-p buffer)
1615
(with-current-buffer buffer
1616
(setq slime-buffer-package package))))
1617
t)
1618
(t nil)))
1619
1620
(defun slime-repl-find-buffer-package ()
1621
(or (slime-search-buffer-package)
1622
(slime-lisp-package)))
1623
1624
(defun slime-repl-remove-hooks ()
1625
(remove-hook 'slime-event-hooks 'slime-repl-event-hook-function)
1626
(remove-hook 'slime-connected-hook 'slime-repl-connected-hook-function))
1627
1628
(let ((byte-compile-warnings '()))
1629
(mapc #'byte-compile
1630
'(slime-repl-event-hook-function
1631
slime-write-string
1632
slime-repl-write-string
1633
slime-repl-emit
1634
slime-repl-show-maximum-output)))
1635
1636
1637
;;; Tests
1638
1639
(def-slime-test package-updating
1640
(package-name nicknames)
1641
"Test if slime-lisp-package is updated."
1642
'(("COMMON-LISP" ("CL"))
1643
("KEYWORD" ("" "KEYWORD" "||"))
1644
("COMMON-LISP-USER" ("CL-USER")))
1645
(with-current-buffer (slime-output-buffer)
1646
(let ((p (slime-eval
1647
`(swank:listener-eval
1648
,(format
1649
"(cl:setq cl:*print-case* :upcase)
1650
(cl:setq cl:*package* (cl:find-package %S))
1651
(cl:package-name cl:*package*)" package-name))
1652
(slime-lisp-package))))
1653
(slime-check ("slime-lisp-package is %S." package-name)
1654
(equal (slime-lisp-package) package-name))
1655
(slime-check ("slime-lisp-package-prompt-string is in %S." nicknames)
1656
(member (slime-lisp-package-prompt-string) nicknames)))))
1657
1658
(defmacro with-canonicalized-slime-repl-buffer (&rest body)
1659
"Evaluate BODY within a fresh REPL buffer. The REPL prompt is
1660
canonicalized to \"SWANK\"---we do actually switch to that
1661
package, though."
1662
`(let ((%old-prompt% (slime-lisp-package-prompt-string)))
1663
(unwind-protect
1664
(progn (with-current-buffer (slime-output-buffer)
1665
(setf (slime-lisp-package-prompt-string) "SWANK"))
1666
(kill-buffer (slime-output-buffer))
1667
(with-current-buffer (slime-output-buffer)
1668
,@body))
1669
(setf (slime-lisp-package-prompt-string) %old-prompt%))))
1670
1671
(put 'with-canonicalized-slime-repl-buffer 'lisp-indent-function 0)
1672
1673
(def-slime-test repl-test
1674
(input result-contents)
1675
"Test simple commands in the minibuffer."
1676
'(("(+ 1 2)" "SWANK> (+ 1 2)
1677
{}3
1678
SWANK> *[]")
1679
("(princ 10)" "SWANK> (princ 10)
1680
{10
1681
}10
1682
SWANK> *[]")
1683
("(princ 10)(princ 20)" "SWANK> (princ 10)(princ 20)
1684
{1020
1685
}20
1686
SWANK> *[]")
1687
("(dotimes (i 10 77) (princ i) (terpri))"
1688
"SWANK> (dotimes (i 10 77) (princ i) (terpri))
1689
{0
1690
1
1691
2
1692
3
1693
4
1694
5
1695
6
1696
7
1697
8
1698
9
1699
}77
1700
SWANK> *[]")
1701
("(abort)" "SWANK> (abort)
1702
{}; Evaluation aborted.
1703
SWANK> *[]")
1704
("(progn (princ 10) (force-output) (abort))"
1705
"SWANK> (progn (princ 10) (force-output) (abort))
1706
{10}; Evaluation aborted.
1707
SWANK> *[]")
1708
("(progn (princ 10) (abort))"
1709
;; output can be flushed after aborting
1710
"SWANK> (progn (princ 10) (abort))
1711
{10}; Evaluation aborted.
1712
SWANK> *[]")
1713
("(if (fresh-line) 1 0)"
1714
"SWANK> (if (fresh-line) 1 0)
1715
{
1716
}1
1717
SWANK> *[]")
1718
("(values 1 2 3)" "SWANK> (values 1 2 3)
1719
{}1
1720
2
1721
3
1722
SWANK> *[]")
1723
("(with-standard-io-syntax
1724
(write (make-list 15 :initial-element '(1 . 2)) :pretty t) 0)"
1725
"SWANK> (with-standard-io-syntax
1726
(write (make-list 15 :initial-element '(1 . 2)) :pretty t) 0)
1727
{((1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2)
1728
(1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2))
1729
}0
1730
SWANK> *[]")
1731
;; Two times to test the effect of FRESH-LINE.
1732
("(with-standard-io-syntax
1733
(write (make-list 15 :initial-element '(1 . 2)) :pretty t) 0)"
1734
"SWANK> (with-standard-io-syntax
1735
(write (make-list 15 :initial-element '(1 . 2)) :pretty t) 0)
1736
{((1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2)
1737
(1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2))
1738
}0
1739
SWANK> *[]"))
1740
(with-canonicalized-slime-repl-buffer
1741
(insert input)
1742
(slime-check-buffer-contents "Buffer contains input"
1743
(concat "{}SWANK> [" input "*]"))
1744
(call-interactively 'slime-repl-return)
1745
(slime-sync-to-top-level 5)
1746
(slime-check-buffer-contents "Buffer contains result" result-contents)))
1747
1748
(defun slime-check-buffer-contents (msg expected)
1749
(let* ((marks '((point . ?*)
1750
(slime-output-start . ?{) (slime-output-end . ?})
1751
(slime-repl-input-start-mark . ?\[) (point-max . ?\])))
1752
(marks (remove-if-not (lambda (m) (position (cdr m) expected))
1753
marks))
1754
(marks (sort (copy-sequence marks)
1755
(lambda (x y)
1756
(< (position (cdr x) expected)
1757
(position (cdr y) expected)))))
1758
(content (remove-if (lambda (c) (member* c marks :key #'cdr))
1759
expected))
1760
(marks (do ((result '() (acons (caar m) (1+ (position (cdar m) s))
1761
result))
1762
(m marks (cdr m))
1763
(s expected (remove* (cdar m) s)))
1764
((null m) (reverse result))))
1765
(point (point))
1766
(point-max (point-max)))
1767
(slime-test-expect (concat msg " [content]") content (buffer-string))
1768
(macrolet ((test-mark
1769
(mark)
1770
`(when (assoc ',mark marks)
1771
(slime-test-expect (format "%s [%s]" msg ',mark)
1772
(cdr (assoc ',mark marks))
1773
,mark
1774
#'=))))
1775
(test-mark point)
1776
(test-mark slime-output-end)
1777
(test-mark slime-output-start)
1778
(test-mark slime-repl-input-start-mark)
1779
(test-mark point-max))))
1780
1781
(def-slime-test repl-return
1782
(before after result-contents)
1783
"Test if slime-repl-return sends the correct protion to Lisp even
1784
if point is not at the end of the line."
1785
'(("(+ 1 2)" "" "SWANK> (+ 1 2)
1786
3
1787
SWANK> ")
1788
("(+ 1 " "2)" "SWANK> (+ 1 2)
1789
3
1790
SWANK> ")
1791
1792
("(+ 1\n" "2)" "SWANK> (+ 1
1793
2)
1794
3
1795
SWANK> "))
1796
(with-canonicalized-slime-repl-buffer
1797
(insert before)
1798
(save-excursion (insert after))
1799
(slime-test-expect "Buffer contains input"
1800
(concat "SWANK> " before after)
1801
(buffer-string))
1802
(call-interactively 'slime-repl-return)
1803
(slime-sync-to-top-level 5)
1804
(slime-test-expect "Buffer contains result"
1805
result-contents (buffer-string))))
1806
1807
(def-slime-test repl-read
1808
(prompt input result-contents)
1809
"Test simple commands in the minibuffer."
1810
'(("(read-line)" "foo" "SWANK> (values (read-line))
1811
foo
1812
\"foo\"
1813
SWANK> ")
1814
("(read-char)" "1" "SWANK> (values (read-char))
1815
1
1816
#\\1
1817
SWANK> ")
1818
("(read)" "(+ 2 3
1819
4)" "SWANK> (values (read))
1820
\(+ 2 3
1821
4)
1822
\(+ 2 3 4)
1823
SWANK> "))
1824
(with-canonicalized-slime-repl-buffer
1825
(insert (format "(values %s)" prompt))
1826
(call-interactively 'slime-repl-return)
1827
(slime-wait-condition "reading" #'slime-reading-p 5)
1828
(insert input)
1829
(call-interactively 'slime-repl-return)
1830
(slime-sync-to-top-level 5)
1831
(slime-test-expect "Buffer contains result"
1832
result-contents (buffer-string))))
1833
1834
(def-slime-test repl-read-lines
1835
(command inputs final-contents)
1836
"Test reading multiple lines from the repl."
1837
'(("(list (read-line) (read-line) (read-line))"
1838
("a" "b" "c")
1839
"SWANK> (list (read-line) (read-line) (read-line))
1840
a
1841
b
1842
c
1843
\(\"a\" \"b\" \"c\")
1844
SWANK> "))
1845
(with-canonicalized-slime-repl-buffer
1846
(insert command)
1847
(call-interactively 'slime-repl-return)
1848
(dolist (input inputs)
1849
(slime-wait-condition "reading" #'slime-reading-p 5)
1850
(insert input)
1851
(call-interactively 'slime-repl-return))
1852
(slime-sync-to-top-level 5)
1853
(slime-test-expect "Buffer contains result"
1854
final-contents
1855
(buffer-string)
1856
#'equal)))
1857
1858
(def-slime-test repl-type-ahead
1859
(command input final-contents)
1860
"Ensure that user input is preserved correctly.
1861
In particular, input inserted while waiting for a result."
1862
'(("(sleep 0.1)" "foo*" "SWANK> (sleep 0.1)
1863
{}NIL
1864
SWANK> [foo*]")
1865
("(sleep 0.1)" "*foo" "SWANK> (sleep 0.1)
1866
{}NIL
1867
SWANK> [*foo]")
1868
("(progn (sleep 0.1) (abort))" "*foo" "SWANK> (progn (sleep 0.1) (abort))
1869
{}; Evaluation aborted.
1870
SWANK> [*foo]"))
1871
(with-canonicalized-slime-repl-buffer
1872
(insert command)
1873
(call-interactively 'slime-repl-return)
1874
(save-excursion (insert (delete* ?* input)))
1875
(forward-char (position ?* input))
1876
(slime-sync-to-top-level 5)
1877
(slime-check-buffer-contents "Buffer contains result" final-contents)))
1878
1879
1880
(def-slime-test interrupt-in-blocking-read
1881
()
1882
"Let's see what happens if we interrupt a blocking read operation."
1883
'(())
1884
(slime-check-top-level)
1885
(with-canonicalized-slime-repl-buffer
1886
(insert "(read-char)")
1887
(call-interactively 'slime-repl-return)
1888
(slime-wait-condition "reading" #'slime-reading-p 5)
1889
(slime-interrupt)
1890
(slime-wait-condition "Debugger visible"
1891
(lambda ()
1892
(and (slime-sldb-level= 1)
1893
(get-buffer-window (sldb-get-default-buffer))))
1894
5)
1895
(with-current-buffer (sldb-get-default-buffer)
1896
(sldb-continue))
1897
(slime-wait-condition "reading" #'slime-reading-p 5)
1898
(with-current-buffer (slime-output-buffer)
1899
(insert "X")
1900
(call-interactively 'slime-repl-return)
1901
(slime-sync-to-top-level 5)
1902
(slime-test-expect "Buffer contains result"
1903
"SWANK> (read-char)
1904
X
1905
#\\X
1906
SWANK> " (buffer-string)))))
1907
1908
(provide 'slime-repl)
1909
1910