Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/slime/contrib/slime-presentations.el
990 views
1
2
(define-slime-contrib slime-presentations
3
"Imitate LispM presentations."
4
(:authors "Alan Ruttenberg <[email protected]>"
5
"Matthias Koeppe <[email protected]>")
6
(:license "GPL")
7
(:slime-dependencies slime-repl)
8
(:swank-dependencies swank-presentations)
9
(:on-load
10
(add-hook 'slime-repl-mode-hook
11
(lambda ()
12
;; Respect the syntax text properties of presentation.
13
(set (make-local-variable 'parse-sexp-lookup-properties) t)
14
(slime-add-local-hook 'after-change-functions
15
'slime-after-change-function)))
16
(add-hook 'slime-event-hooks 'slime-dispatch-presentation-event)
17
(setq slime-write-string-function 'slime-presentation-write)
18
(add-hook 'slime-repl-return-hooks 'slime-presentation-on-return-pressed)
19
(add-hook 'slime-repl-current-input-hooks 'slime-presentation-current-input)
20
(add-hook 'slime-open-stream-hooks 'slime-presentation-on-stream-open)
21
(add-hook 'slime-repl-clear-buffer-hook 'slime-clear-presentations)
22
(add-hook 'slime-edit-definition-hooks 'slime-edit-presentation)
23
(setq slime-inspector-insert-ispec-function 'slime-presentation-inspector-insert-ispec)
24
(setq sldb-insert-frame-variable-value-function
25
'slime-presentation-sldb-insert-frame-variable-value)
26
(slime-presentation-init-keymaps)
27
(slime-presentation-add-easy-menu)))
28
29
(defface slime-repl-output-mouseover-face
30
(if (featurep 'xemacs)
31
'((t (:bold t)))
32
(if (slime-face-inheritance-possible-p)
33
'((t
34
(:box
35
(:line-width 1 :color "black" :style released-button)
36
:inherit
37
slime-repl-inputed-output-face)))
38
'((t (:box (:line-width 1 :color "black"))))))
39
"Face for Lisp output in the SLIME REPL, when the mouse hovers over it"
40
:group 'slime-repl)
41
42
(defface slime-repl-inputed-output-face
43
'((((class color) (background light)) (:foreground "Red"))
44
(((class color) (background dark)) (:foreground "Red"))
45
(t (:slant italic)))
46
"Face for the result of an evaluation in the SLIME REPL."
47
:group 'slime-repl)
48
49
;; FIXME: This conditional is not right - just used because the code
50
;; here does not work in XEmacs.
51
(when (boundp 'text-property-default-nonsticky)
52
(pushnew '(slime-repl-presentation . t) text-property-default-nonsticky
53
:test 'equal)
54
(pushnew '(slime-repl-result-face . t) text-property-default-nonsticky
55
:test 'equal))
56
57
(make-variable-buffer-local
58
(defvar slime-presentation-start-to-point (make-hash-table)))
59
60
(defun slime-mark-presentation-start (id &optional target)
61
"Mark the beginning of a presentation with the given ID.
62
TARGET can be nil (regular process output) or :repl-result."
63
(setf (gethash id slime-presentation-start-to-point)
64
;; We use markers because text can also be inserted before this presentation.
65
;; (Output arrives while we are writing presentations within REPL results.)
66
(copy-marker (slime-output-target-marker target) nil)))
67
68
(defun slime-mark-presentation-start-handler (process string)
69
(if (and string (string-match "<\\([-0-9]+\\)" string))
70
(let* ((match (substring string (match-beginning 1) (match-end 1)))
71
(id (car (read-from-string match))))
72
(slime-mark-presentation-start id))))
73
74
(defun slime-mark-presentation-end (id &optional target)
75
"Mark the end of a presentation with the given ID.
76
TARGET can be nil (regular process output) or :repl-result."
77
(let ((start (gethash id slime-presentation-start-to-point)))
78
(remhash id slime-presentation-start-to-point)
79
(when start
80
(let* ((marker (slime-output-target-marker target))
81
(buffer (and marker (marker-buffer marker))))
82
(with-current-buffer buffer
83
(let ((end (marker-position marker)))
84
(slime-add-presentation-properties start end
85
id nil)))))))
86
87
(defun slime-mark-presentation-end-handler (process string)
88
(if (and string (string-match ">\\([-0-9]+\\)" string))
89
(let* ((match (substring string (match-beginning 1) (match-end 1)))
90
(id (car (read-from-string match))))
91
(slime-mark-presentation-end id))))
92
93
(defstruct slime-presentation text id)
94
95
(defvar slime-presentation-syntax-table
96
(let ((table (copy-syntax-table lisp-mode-syntax-table)))
97
;; We give < and > parenthesis syntax, so that #< ... > is treated
98
;; as a balanced expression. This allows to use C-M-k, C-M-SPC,
99
;; etc. to deal with a whole presentation. (For Lisp mode, this
100
;; is not desirable, since we do not wish to get a mismatched
101
;; paren highlighted everytime we type < or >.)
102
(modify-syntax-entry ?< "(>" table)
103
(modify-syntax-entry ?> ")<" table)
104
table)
105
"Syntax table for presentations.")
106
107
(defun slime-add-presentation-properties (start end id result-p)
108
"Make the text between START and END a presentation with ID.
109
RESULT-P decides whether a face for a return value or output text is used."
110
(let* ((text (buffer-substring-no-properties start end))
111
(presentation (make-slime-presentation :text text :id id)))
112
(let ((inhibit-modification-hooks t))
113
(add-text-properties start end
114
`(modification-hooks (slime-after-change-function)
115
insert-in-front-hooks (slime-after-change-function)
116
insert-behind-hooks (slime-after-change-function)
117
syntax-table ,slime-presentation-syntax-table
118
rear-nonsticky t))
119
;; Use the presentation as the key of a text property
120
(case (- end start)
121
(0)
122
(1
123
(add-text-properties start end
124
`(slime-repl-presentation ,presentation
125
,presentation :start-and-end)))
126
(t
127
(add-text-properties start (1+ start)
128
`(slime-repl-presentation ,presentation
129
,presentation :start))
130
(when (> (- end start) 2)
131
(add-text-properties (1+ start) (1- end)
132
`(,presentation :interior)))
133
(add-text-properties (1- end) end
134
`(slime-repl-presentation ,presentation
135
,presentation :end))))
136
;; Also put an overlay for the face and the mouse-face. This enables
137
;; highlighting of nested presentations. However, overlays get lost
138
;; when we copy a presentation; their removal is also not undoable.
139
;; In these cases the mouse-face text properties need to take over ---
140
;; but they do not give nested highlighting.
141
(slime-ensure-presentation-overlay start end presentation))))
142
143
(defun slime-ensure-presentation-overlay (start end presentation)
144
(unless (find presentation (overlays-at start)
145
:key (lambda (overlay)
146
(overlay-get overlay 'slime-repl-presentation)))
147
(let ((overlay (make-overlay start end (current-buffer) t nil)))
148
(overlay-put overlay 'slime-repl-presentation presentation)
149
(overlay-put overlay 'mouse-face 'slime-repl-output-mouseover-face)
150
(overlay-put overlay 'help-echo
151
(if (eq major-mode 'slime-repl-mode)
152
"mouse-2: copy to input; mouse-3: menu"
153
"mouse-2: inspect; mouse-3: menu"))
154
(overlay-put overlay 'face 'slime-repl-inputed-output-face)
155
(overlay-put overlay 'keymap slime-presentation-map))))
156
157
(defun slime-remove-presentation-properties (from to presentation)
158
(let ((inhibit-read-only t))
159
(remove-text-properties from to
160
`(,presentation t syntax-table t rear-nonsticky t))
161
(when (eq (get-text-property from 'slime-repl-presentation) presentation)
162
(remove-text-properties from (1+ from) `(slime-repl-presentation t)))
163
(when (eq (get-text-property (1- to) 'slime-repl-presentation) presentation)
164
(remove-text-properties (1- to) to `(slime-repl-presentation t)))
165
(dolist (overlay (overlays-at from))
166
(when (eq (overlay-get overlay 'slime-repl-presentation) presentation)
167
(delete-overlay overlay)))))
168
169
(defun slime-insert-presentation (string output-id &optional rectangle)
170
"Insert STRING in current buffer and mark it as a presentation
171
corresponding to OUTPUT-ID. If RECTANGLE is true, indent multi-line
172
strings to line up below the current point."
173
(flet ((insert-it ()
174
(if rectangle
175
(slime-insert-indented string)
176
(insert string))))
177
(let ((start (point)))
178
(insert-it)
179
(slime-add-presentation-properties start (point) output-id t))))
180
181
(defun slime-presentation-whole-p (presentation start end &optional object)
182
(let ((object (or object (current-buffer))))
183
(string= (etypecase object
184
(buffer (with-current-buffer object
185
(buffer-substring-no-properties start end)))
186
(string (substring-no-properties object start end)))
187
(slime-presentation-text presentation))))
188
189
(defun slime-presentations-around-point (point &optional object)
190
(let ((object (or object (current-buffer))))
191
(loop for (key value . rest) on (text-properties-at point object) by 'cddr
192
when (slime-presentation-p key)
193
collect key)))
194
195
(defun slime-presentation-start-p (tag)
196
(memq tag '(:start :start-and-end)))
197
198
(defun slime-presentation-stop-p (tag)
199
(memq tag '(:end :start-and-end)))
200
201
(defun* slime-presentation-start (point presentation
202
&optional (object (current-buffer)))
203
"Find start of `presentation' at `point' in `object'.
204
Return buffer index and whether a start-tag was found."
205
(let* ((this-presentation (get-text-property point presentation object)))
206
(while (not (slime-presentation-start-p this-presentation))
207
(let ((change-point (previous-single-property-change
208
point presentation object)))
209
(unless change-point
210
(return-from slime-presentation-start
211
(values (etypecase object
212
(buffer (with-current-buffer object 1))
213
(string 0))
214
nil)))
215
(setq this-presentation (get-text-property change-point
216
presentation object))
217
(unless this-presentation
218
(return-from slime-presentation-start
219
(values point nil)))
220
(setq point change-point)))
221
(values point t)))
222
223
(defun* slime-presentation-end (point presentation
224
&optional (object (current-buffer)))
225
"Find end of presentation at `point' in `object'. Return buffer
226
index (after last character of the presentation) and whether an
227
end-tag was found."
228
(let* ((this-presentation (get-text-property point presentation object)))
229
(while (not (slime-presentation-stop-p this-presentation))
230
(let ((change-point (next-single-property-change
231
point presentation object)))
232
(unless change-point
233
(return-from slime-presentation-end
234
(values (etypecase object
235
(buffer (with-current-buffer object (point-max)))
236
(string (length object)))
237
nil)))
238
(setq point change-point)
239
(setq this-presentation (get-text-property point
240
presentation object))))
241
(if this-presentation
242
(let ((after-end (next-single-property-change point
243
presentation object)))
244
(if (not after-end)
245
(values (etypecase object
246
(buffer (with-current-buffer object (point-max)))
247
(string (length object)))
248
t)
249
(values after-end t)))
250
(values point nil))))
251
252
(defun* slime-presentation-bounds (point presentation
253
&optional (object (current-buffer)))
254
"Return start index and end index of `presentation' around `point'
255
in `object', and whether the presentation is complete."
256
(multiple-value-bind (start good-start)
257
(slime-presentation-start point presentation object)
258
(multiple-value-bind (end good-end)
259
(slime-presentation-end point presentation object)
260
(values start end
261
(and good-start good-end
262
(slime-presentation-whole-p presentation
263
start end object))))))
264
265
(defun slime-presentation-around-point (point &optional object)
266
"Return presentation, start index, end index, and whether the
267
presentation is complete."
268
(let ((object (or object (current-buffer)))
269
(innermost-presentation nil)
270
(innermost-start 0)
271
(innermost-end most-positive-fixnum))
272
(dolist (presentation (slime-presentations-around-point point object))
273
(multiple-value-bind (start end whole-p)
274
(slime-presentation-bounds point presentation object)
275
(when whole-p
276
(when (< (- end start) (- innermost-end innermost-start))
277
(setq innermost-start start
278
innermost-end end
279
innermost-presentation presentation)))))
280
(values innermost-presentation
281
innermost-start innermost-end)))
282
283
(defun slime-presentation-around-or-before-point (point &optional object)
284
(let ((object (or object (current-buffer))))
285
(multiple-value-bind (presentation start end whole-p)
286
(slime-presentation-around-point point object)
287
(if (or presentation (= point (point-min)))
288
(values presentation start end whole-p)
289
(slime-presentation-around-point (1- point) object)))))
290
291
(defun slime-presentation-around-or-before-point-or-error (point)
292
(multiple-value-bind (presentation start end whole-p)
293
(slime-presentation-around-or-before-point point)
294
(unless presentation
295
(error "No presentation at point"))
296
(values presentation start end whole-p)))
297
298
(defun* slime-for-each-presentation-in-region (from to function &optional (object (current-buffer)))
299
"Call `function' with arguments `presentation', `start', `end',
300
`whole-p' for every presentation in the region `from'--`to' in the
301
string or buffer `object'."
302
(flet ((handle-presentation (presentation point)
303
(multiple-value-bind (start end whole-p)
304
(slime-presentation-bounds point presentation object)
305
(funcall function presentation start end whole-p))))
306
;; Handle presentations active at `from'.
307
(dolist (presentation (slime-presentations-around-point from object))
308
(handle-presentation presentation from))
309
;; Use the `slime-repl-presentation' property to search for new presentations.
310
(let ((point from))
311
(while (< point to)
312
(setq point (next-single-property-change point 'slime-repl-presentation object to))
313
(let* ((presentation (get-text-property point 'slime-repl-presentation object))
314
(status (get-text-property point presentation object)))
315
(when (slime-presentation-start-p status)
316
(handle-presentation presentation point)))))))
317
318
;; XEmacs compatibility hack, from message by Stephen J. Turnbull on
319
;; [email protected] of 18 Mar 2002
320
(unless (boundp 'undo-in-progress)
321
(defvar undo-in-progress nil
322
"Placeholder defvar for XEmacs compatibility from SLIME.")
323
(defadvice undo-more (around slime activate)
324
(let ((undo-in-progress t)) ad-do-it)))
325
326
(defun slime-after-change-function (start end &rest ignore)
327
"Check all presentations within and adjacent to the change.
328
When a presentation has been altered, change it to plain text."
329
(let ((inhibit-modification-hooks t))
330
(let ((real-start (max 1 (1- start)))
331
(real-end (min (1+ (buffer-size)) (1+ end)))
332
(any-change nil))
333
;; positions around the change
334
(slime-for-each-presentation-in-region
335
real-start real-end
336
(lambda (presentation from to whole-p)
337
(cond
338
(whole-p
339
(slime-ensure-presentation-overlay from to presentation))
340
((not undo-in-progress)
341
(slime-remove-presentation-properties from to
342
presentation)
343
(setq any-change t)))))
344
(when any-change
345
(undo-boundary)))))
346
347
(defun slime-presentation-around-click (event)
348
"Return the presentation around the position of the mouse-click EVENT.
349
If there is no presentation, signal an error.
350
Also return the start position, end position, and buffer of the presentation."
351
(when (and (featurep 'xemacs) (not (button-press-event-p event)))
352
(error "Command must be bound to a button-press-event"))
353
(let ((point (if (featurep 'xemacs) (event-point event) (posn-point (event-end event))))
354
(window (if (featurep 'xemacs) (event-window event) (caadr event))))
355
(with-current-buffer (window-buffer window)
356
(multiple-value-bind (presentation start end)
357
(slime-presentation-around-point point)
358
(unless presentation
359
(error "No presentation at click"))
360
(values presentation start end (current-buffer))))))
361
362
(defun slime-check-presentation (from to buffer presentation)
363
(unless (slime-eval `(cl:nth-value 1 (swank:lookup-presented-object
364
',(slime-presentation-id presentation))))
365
(with-current-buffer buffer
366
(slime-remove-presentation-properties from to presentation))))
367
368
(defun slime-copy-or-inspect-presentation-at-mouse (event)
369
(interactive "e") ; no "@" -- we don't want to select the clicked-at window
370
(multiple-value-bind (presentation start end buffer)
371
(slime-presentation-around-click event)
372
(slime-check-presentation start end buffer presentation)
373
(if (with-current-buffer buffer
374
(eq major-mode 'slime-repl-mode))
375
(slime-copy-presentation-at-mouse-to-repl event)
376
(slime-inspect-presentation-at-mouse event))))
377
378
(defun slime-inspect-presentation (presentation start end buffer)
379
(let ((reset-p
380
(with-current-buffer buffer
381
(not (eq major-mode 'slime-inspector-mode)))))
382
(slime-eval-async `(swank:inspect-presentation ',(slime-presentation-id presentation) ,reset-p)
383
'slime-open-inspector)))
384
385
(defun slime-inspect-presentation-at-mouse (event)
386
(interactive "e")
387
(multiple-value-bind (presentation start end buffer)
388
(slime-presentation-around-click event)
389
(slime-inspect-presentation presentation start end buffer)))
390
391
(defun slime-inspect-presentation-at-point (point)
392
(interactive "d")
393
(multiple-value-bind (presentation start end)
394
(slime-presentation-around-or-before-point-or-error point)
395
(slime-inspect-presentation presentation start end (current-buffer))))
396
397
398
(defun slime-M-.-presentation (presentation start end buffer &optional where)
399
(let* ((id (slime-presentation-id presentation))
400
(presentation-string (format "Presentation %s" id))
401
(location (slime-eval `(swank:find-definition-for-thing
402
(swank:lookup-presented-object
403
',(slime-presentation-id presentation))))))
404
(slime-edit-definition-cont
405
(and location (list (make-slime-xref :dspec `(,presentation-string)
406
:location location)))
407
presentation-string
408
where)))
409
410
(defun slime-M-.-presentation-at-mouse (event)
411
(interactive "e")
412
(multiple-value-bind (presentation start end buffer)
413
(slime-presentation-around-click event)
414
(slime-M-.-presentation presentation start end buffer)))
415
416
(defun slime-M-.-presentation-at-point (point)
417
(interactive "d")
418
(multiple-value-bind (presentation start end)
419
(slime-presentation-around-or-before-point-or-error point)
420
(slime-M-.-presentation presentation start end (current-buffer))))
421
422
(defun slime-edit-presentation (name &optional where)
423
(if (or current-prefix-arg (not (equal (slime-symbol-at-point) name)))
424
nil ; NAME came from user explicitly, so decline.
425
(multiple-value-bind (presentation start end whole-p)
426
(slime-presentation-around-or-before-point (point))
427
(when presentation
428
(slime-M-.-presentation presentation start end (current-buffer) where)))))
429
430
431
(defun slime-copy-presentation-to-repl (presentation start end buffer)
432
(let ((presentation-text
433
(with-current-buffer buffer
434
(buffer-substring start end))))
435
(unless (eql major-mode 'slime-repl-mode)
436
(slime-switch-to-output-buffer))
437
(flet ((do-insertion ()
438
(unless (looking-back "\\s-")
439
(insert " "))
440
(insert presentation-text)
441
(unless (or (eolp) (looking-at "\\s-"))
442
(insert " "))))
443
(if (>= (point) slime-repl-prompt-start-mark)
444
(do-insertion)
445
(save-excursion
446
(goto-char (point-max))
447
(do-insertion))))))
448
449
(defun slime-copy-presentation-at-mouse-to-repl (event)
450
(interactive "e")
451
(multiple-value-bind (presentation start end buffer)
452
(slime-presentation-around-click event)
453
(slime-copy-presentation-to-repl presentation start end buffer)))
454
455
(defun slime-copy-presentation-at-point-to-repl (point)
456
(interactive "d")
457
(multiple-value-bind (presentation start end)
458
(slime-presentation-around-or-before-point-or-error point)
459
(slime-copy-presentation-to-repl presentation start end (current-buffer))))
460
461
(defun slime-copy-presentation-at-mouse-to-point (event)
462
(interactive "e")
463
(multiple-value-bind (presentation start end buffer)
464
(slime-presentation-around-click event)
465
(let ((presentation-text
466
(with-current-buffer buffer
467
(buffer-substring start end))))
468
(when (not (string-match "\\s-"
469
(buffer-substring (1- (point)) (point))))
470
(insert " "))
471
(insert presentation-text)
472
(slime-after-change-function (point) (point))
473
(when (and (not (eolp)) (not (looking-at "\\s-")))
474
(insert " ")))))
475
476
(defun slime-copy-presentation-to-kill-ring (presentation start end buffer)
477
(let ((presentation-text
478
(with-current-buffer buffer
479
(buffer-substring start end))))
480
(kill-new presentation-text)
481
(message "Saved presentation \"%s\" to kill ring" presentation-text)))
482
483
(defun slime-copy-presentation-at-mouse-to-kill-ring (event)
484
(interactive "e")
485
(multiple-value-bind (presentation start end buffer)
486
(slime-presentation-around-click event)
487
(slime-copy-presentation-to-kill-ring presentation start end buffer)))
488
489
(defun slime-copy-presentation-at-point-to-kill-ring (point)
490
(interactive "d")
491
(multiple-value-bind (presentation start end)
492
(slime-presentation-around-or-before-point-or-error point)
493
(slime-copy-presentation-to-kill-ring presentation start end (current-buffer))))
494
495
(defun slime-describe-presentation (presentation)
496
(slime-eval-describe
497
`(swank::describe-to-string
498
(swank:lookup-presented-object ',(slime-presentation-id presentation)))))
499
500
(defun slime-describe-presentation-at-mouse (event)
501
(interactive "@e")
502
(multiple-value-bind (presentation) (slime-presentation-around-click event)
503
(slime-describe-presentation presentation)))
504
505
(defun slime-describe-presentation-at-point (point)
506
(interactive "d")
507
(multiple-value-bind (presentation)
508
(slime-presentation-around-or-before-point-or-error point)
509
(slime-describe-presentation presentation)))
510
511
(defun slime-pretty-print-presentation (presentation)
512
(slime-eval-describe
513
`(swank::swank-pprint
514
(cl:list
515
(swank:lookup-presented-object ',(slime-presentation-id presentation))))))
516
517
(defun slime-pretty-print-presentation-at-mouse (event)
518
(interactive "@e")
519
(multiple-value-bind (presentation) (slime-presentation-around-click event)
520
(slime-pretty-print-presentation presentation)))
521
522
(defun slime-pretty-print-presentation-at-point (point)
523
(interactive "d")
524
(multiple-value-bind (presentation)
525
(slime-presentation-around-or-before-point-or-error point)
526
(slime-pretty-print-presentation presentation)))
527
528
(defun slime-mark-presentation (point)
529
(interactive "d")
530
(multiple-value-bind (presentation start end)
531
(slime-presentation-around-or-before-point-or-error point)
532
(goto-char start)
533
(push-mark end nil t)))
534
535
(defun slime-previous-presentation (&optional arg)
536
"Move point to the beginning of the first presentation before point.
537
With ARG, do this that many times.
538
A negative argument means move forward instead."
539
(interactive "p")
540
(unless arg (setq arg 1))
541
(slime-next-presentation (- arg)))
542
543
(defun slime-next-presentation (&optional arg)
544
"Move point to the beginning of the next presentation after point.
545
With ARG, do this that many times.
546
A negative argument means move backward instead."
547
(interactive "p")
548
(unless arg (setq arg 1))
549
(cond
550
((plusp arg)
551
(dotimes (i arg)
552
;; First skip outside the current surrounding presentation (if any)
553
(multiple-value-bind (presentation start end)
554
(slime-presentation-around-point (point))
555
(when presentation
556
(goto-char end)))
557
(let ((p (next-single-property-change (point) 'slime-repl-presentation)))
558
(unless p
559
(error "No next presentation"))
560
(multiple-value-bind (presentation start end)
561
(slime-presentation-around-or-before-point-or-error p)
562
(goto-char start)))))
563
((minusp arg)
564
(dotimes (i (- arg))
565
;; First skip outside the current surrounding presentation (if any)
566
(multiple-value-bind (presentation start end)
567
(slime-presentation-around-point (point))
568
(when presentation
569
(goto-char start)))
570
(let ((p (previous-single-property-change (point) 'slime-repl-presentation)))
571
(unless p
572
(error "No previous presentation"))
573
(multiple-value-bind (presentation start end)
574
(slime-presentation-around-or-before-point-or-error p)
575
(goto-char start)))))))
576
577
(defvar slime-presentation-map (make-sparse-keymap))
578
579
(define-key slime-presentation-map [mouse-2] 'slime-copy-or-inspect-presentation-at-mouse)
580
(define-key slime-presentation-map [mouse-3] 'slime-presentation-menu)
581
582
(when (featurep 'xemacs)
583
(define-key slime-presentation-map [button2] 'slime-copy-or-inspect-presentation-at-mouse)
584
(define-key slime-presentation-map [button3] 'slime-presentation-menu))
585
586
;; protocol for handling up a menu.
587
;; 1. Send lisp message asking for menu choices for this object.
588
;; Get back list of strings.
589
;; 2. Let used choose
590
;; 3. Call back to execute menu choice, passing nth and string of choice
591
592
(defun slime-menu-choices-for-presentation (presentation buffer from to choice-to-lambda)
593
"Return a menu for `presentation' at `from'--`to' in `buffer', suitable for `x-popup-menu'."
594
(let* ((what (slime-presentation-id presentation))
595
(choices (with-current-buffer buffer
596
(slime-eval
597
`(swank::menu-choices-for-presentation-id ',what)))))
598
(flet ((savel (f) ;; IMPORTANT - xemacs can't handle lambdas in x-popup-menu. So give them a name
599
(let ((sym (gensym)))
600
(setf (gethash sym choice-to-lambda) f)
601
sym)))
602
(etypecase choices
603
(list
604
`(,(format "Presentation %s" what)
605
(""
606
("Find Definition" . ,(savel 'slime-M-.-presentation-at-mouse))
607
("Inspect" . ,(savel 'slime-inspect-presentation-at-mouse))
608
("Describe" . ,(savel 'slime-describe-presentation-at-mouse))
609
("Pretty-print" . ,(savel 'slime-pretty-print-presentation-at-mouse))
610
("Copy to REPL" . ,(savel 'slime-copy-presentation-at-mouse-to-repl))
611
("Copy to kill ring" . ,(savel 'slime-copy-presentation-at-mouse-to-kill-ring))
612
,@(unless buffer-read-only
613
`(("Copy to point" . ,(savel 'slime-copy-presentation-at-mouse-to-point))))
614
,@(let ((nchoice 0))
615
(mapcar
616
(lambda (choice)
617
(incf nchoice)
618
(cons choice
619
(savel `(lambda ()
620
(interactive)
621
(slime-eval
622
'(swank::execute-menu-choice-for-presentation-id
623
',what ,nchoice ,(nth (1- nchoice) choices)))))))
624
choices)))))
625
(symbol ; not-present
626
(with-current-buffer buffer
627
(slime-remove-presentation-properties from to presentation))
628
(sit-for 0) ; allow redisplay
629
`("Object no longer recorded"
630
("sorry" . ,(if (featurep 'xemacs) nil '(nil)))))))))
631
632
(defun slime-presentation-menu (event)
633
(interactive "e")
634
(let* ((point (if (featurep 'xemacs) (event-point event)
635
(posn-point (event-end event))))
636
(window (if (featurep 'xemacs) (event-window event) (caadr event)))
637
(buffer (window-buffer window))
638
(choice-to-lambda (make-hash-table)))
639
(multiple-value-bind (presentation from to)
640
(with-current-buffer buffer
641
(slime-presentation-around-point point))
642
(unless presentation
643
(error "No presentation at event position"))
644
(let ((menu (slime-menu-choices-for-presentation
645
presentation buffer from to choice-to-lambda)))
646
(let ((choice (x-popup-menu event menu)))
647
(when choice
648
(call-interactively (gethash choice choice-to-lambda))))))))
649
650
(defun slime-presentation-expression (presentation)
651
"Return a string that contains a CL s-expression accessing
652
the presented object."
653
(let ((id (slime-presentation-id presentation)))
654
(etypecase id
655
(number
656
;; Make sure it works even if *read-base* is not 10.
657
(format "(swank:lookup-presented-object-or-lose %d.)" id))
658
(list
659
;; for frame variables and inspector parts
660
(format "(swank:lookup-presented-object-or-lose '%s)" id)))))
661
662
(defun slime-buffer-substring-with-reified-output (start end)
663
(let ((str-props (buffer-substring start end))
664
(str-no-props (buffer-substring-no-properties start end)))
665
(slime-reify-old-output str-props str-no-props)))
666
667
(defun slime-reify-old-output (str-props str-no-props)
668
(let ((pos (slime-property-position 'slime-repl-presentation str-props)))
669
(if (null pos)
670
str-no-props
671
(multiple-value-bind (presentation start-pos end-pos whole-p)
672
(slime-presentation-around-point pos str-props)
673
(if (not presentation)
674
str-no-props
675
(concat (substring str-no-props 0 pos)
676
;; Eval in the reader so that we play nice with quote.
677
;; -luke (19/May/2005)
678
"#." (slime-presentation-expression presentation)
679
(slime-reify-old-output (substring str-props end-pos)
680
(substring str-no-props end-pos))))))))
681
682
683
684
(defun slime-repl-grab-old-output (replace)
685
"Resend the old REPL output at point.
686
If replace it non-nil the current input is replaced with the old
687
output; otherwise the new input is appended."
688
(multiple-value-bind (presentation beg end)
689
(slime-presentation-around-or-before-point (point))
690
(slime-check-presentation beg end (current-buffer) presentation)
691
(let ((old-output (buffer-substring beg end))) ;;keep properties
692
;; Append the old input or replace the current input
693
(cond (replace (goto-char slime-repl-input-start-mark))
694
(t (goto-char (point-max))
695
(unless (eq (char-before) ?\ )
696
(insert " "))))
697
(delete-region (point) (point-max))
698
(let ((inhibit-read-only t))
699
(insert old-output)))))
700
701
;;; Presentation-related key bindings, non-context menu
702
703
(defvar slime-presentation-command-map nil
704
"Keymap for presentation-related commands. Bound to a prefix key.")
705
706
(defvar slime-presentation-bindings
707
'((?i slime-inspect-presentation-at-point)
708
(?d slime-describe-presentation-at-point)
709
(?w slime-copy-presentation-at-point-to-kill-ring)
710
(?r slime-copy-presentation-at-point-to-repl)
711
(?p slime-previous-presentation)
712
(?n slime-next-presentation)
713
(?\ slime-mark-presentation)))
714
715
(defun slime-presentation-init-keymaps ()
716
(slime-init-keymap 'slime-presentation-command-map nil t
717
slime-presentation-bindings)
718
(define-key slime-presentation-command-map "\M-o" 'slime-clear-presentations)
719
;; C-c C-v is the prefix for the presentation-command map.
720
(define-key slime-prefix-map "\C-v" slime-presentation-command-map))
721
722
(defun slime-presentation-around-or-before-point-p ()
723
(multiple-value-bind (presentation beg end)
724
(slime-presentation-around-or-before-point (point))
725
presentation))
726
727
(defvar slime-presentation-easy-menu
728
(let ((P '(slime-presentation-around-or-before-point-p)))
729
`("Presentations"
730
[ "Find Definition" slime-M-.-presentation-at-point ,P ]
731
[ "Inspect" slime-inspect-presentation-at-point ,P ]
732
[ "Describe" slime-describe-presentation-at-point ,P ]
733
[ "Pretty-print" slime-pretty-print-presentation-at-point ,P ]
734
[ "Copy to REPL" slime-copy-presentation-at-point-to-repl ,P ]
735
[ "Copy to kill ring" slime-copy-presentation-at-point-to-kill-ring ,P ]
736
[ "Mark" slime-mark-presentation ,P ]
737
"--"
738
[ "Previous presentation" slime-previous-presentation ]
739
[ "Next presentation" slime-next-presentation ]
740
"--"
741
[ "Clear all presentations" slime-clear-presentations ])))
742
743
(defun slime-presentation-add-easy-menu ()
744
(easy-menu-define menubar-slime-presentation slime-mode-map "Presentations" slime-presentation-easy-menu)
745
(easy-menu-define menubar-slime-presentation slime-repl-mode-map "Presentations" slime-presentation-easy-menu)
746
(easy-menu-define menubar-slime-presentation sldb-mode-map "Presentations" slime-presentation-easy-menu)
747
(easy-menu-define menubar-slime-presentation slime-inspector-mode-map "Presentations" slime-presentation-easy-menu)
748
(easy-menu-add slime-presentation-easy-menu 'slime-mode-map)
749
(easy-menu-add slime-presentation-easy-menu 'slime-repl-mode-map)
750
(easy-menu-add slime-presentation-easy-menu 'sldb-mode-map)
751
(easy-menu-add slime-presentation-easy-menu 'slime-inspector-mode-map))
752
753
;;; hook functions (hard to isolate stuff)
754
755
(defun slime-dispatch-presentation-event (event)
756
(destructure-case event
757
((:presentation-start id &optional target)
758
(slime-mark-presentation-start id target)
759
t)
760
((:presentation-end id &optional target)
761
(slime-mark-presentation-end id target)
762
t)
763
(t nil)))
764
765
(defun slime-presentation-write-result (string)
766
(with-current-buffer (slime-output-buffer)
767
(let ((marker (slime-output-target-marker :repl-result)))
768
(goto-char marker)
769
(slime-propertize-region `(face slime-repl-result-face
770
rear-nonsticky (face))
771
(insert string))
772
;; Move the input-start marker after the REPL result.
773
(set-marker marker (point)))
774
(slime-repl-show-maximum-output)))
775
776
(defun slime-presentation-write (string &optional target)
777
(case target
778
((nil) ; Regular process output
779
(slime-repl-emit string))
780
(:repl-result
781
(slime-presentation-write-result string))
782
(t (slime-emit-to-target string target))))
783
784
(defun slime-presentation-current-input (&optional until-point-p)
785
"Return the current input as string.
786
The input is the region from after the last prompt to the end of
787
buffer. Presentations of old results are expanded into code."
788
(slime-buffer-substring-with-reified-output slime-repl-input-start-mark
789
(point-max)))
790
791
(defun slime-presentation-on-return-pressed ()
792
(when (and (car (slime-presentation-around-or-before-point (point)))
793
(< (point) slime-repl-input-start-mark))
794
(slime-repl-grab-old-output end-of-input)
795
(slime-repl-recenter-if-needed)
796
t))
797
798
(defun slime-presentation-on-stream-open (stream)
799
(require 'bridge)
800
(defun bridge-insert (process output)
801
(slime-output-filter process (or output "")))
802
(install-bridge)
803
(setq bridge-destination-insert nil)
804
(setq bridge-source-insert nil)
805
(setq bridge-handlers
806
(list* '("<" . slime-mark-presentation-start-handler)
807
'(">" . slime-mark-presentation-end-handler)
808
bridge-handlers)))
809
810
(defun slime-clear-presentations ()
811
"Forget all objects associated to SLIME presentations.
812
This allows the garbage collector to remove these objects
813
even on Common Lisp implementations without weak hash tables."
814
(interactive)
815
(slime-eval-async `(swank:clear-repl-results))
816
(unless (eql major-mode 'slime-repl-mode)
817
(slime-switch-to-output-buffer))
818
(slime-for-each-presentation-in-region 1 (1+ (buffer-size))
819
(lambda (presentation from to whole-p)
820
(slime-remove-presentation-properties from to
821
presentation))))
822
823
(defun slime-presentation-inspector-insert-ispec (ispec)
824
(if (stringp ispec)
825
(insert ispec)
826
(destructure-case ispec
827
((:value string id)
828
(slime-propertize-region
829
(list 'slime-part-number id
830
'mouse-face 'highlight
831
'face 'slime-inspector-value-face)
832
(slime-insert-presentation string `(:inspected-part ,id) t)))
833
((:action string id)
834
(slime-insert-propertized (list 'slime-action-number id
835
'mouse-face 'highlight
836
'face 'slime-inspector-action-face)
837
string)))))
838
839
(defun slime-presentation-sldb-insert-frame-variable-value (value frame index)
840
(slime-insert-presentation
841
(in-sldb-face local-value value)
842
`(:frame-var ,slime-current-thread ,(car frame) ,index) t))
843
844
(provide 'slime-presentations)
845