forked from slime/slime
-
Notifications
You must be signed in to change notification settings - Fork 1
/
slime-clime.el
179 lines (152 loc) · 6.78 KB
/
slime-clime.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
(eval-and-compile
(require 'slime))
(define-slime-contrib slime-clime
"Display CLIM presentations in Emacs."
(:authors "Luke Gorrie <luke@nuddy.co>")
(:license "GPL")
(:slime-dependencies slime-repl)
(:swank-dependencies swank-clime)
(:on-load
(add-hook 'slime-event-hooks 'slime-dispatch-clime-event)))
;;;; Keymap
(defvar slime-clime-image-keymap
(let ((map (make-sparse-keymap)))
;; Currently using a catch-all keybinding because Emacs seems (?)
;; to insist that a specific [mouse-1] binding would be matched as
;; [area-id mouse-1] i.e. that we would need to define a
;; keybinding for each and every presentation. That's a bit much.
;; So for now we just catch all key/mouse events on the image.
;;
;; (If you put the Emacs point/cursor over the image and the
;; keyboard stops working then try clicking somewhere else so that
;; the point/cursor can escape...
;;(define-key map [mouse-1] 'slime-clime-mouse-1)
(define-key map [t] 'slime-clime-input-event)
map))
(defun slime-clime-input-event (&optional event)
(interactive "e")
(when (eq (event-basic-type event) 'mouse-1)
(slime-clime-mouse-1 event)))
(defun slime-clime-mouse-1 (&optional event)
(interactive "e")
(let* ((posn (second event))
(image (posn-image posn))
(area (posn-area posn)))
(when (and area
(slime-clime-input-context)
(eq (slime-connection)
(image-property image 'slime-clime-connection)))
(slime-clime-accept area)
(slime-clime-reset-input-context))))
(defun slime-clime-accept (id)
(let ((index (slime-clime-keyword-to-id id)))
(cl-destructuring-bind (thread tag ctx) (slime-clime-input-context)
(slime-dispatch-event `(:emacs-return ,thread ,tag ,index)))))
(defun slime-dispatch-clime-event (event)
(slime-dcase event
((:write-clime svg-data presentations)
(with-current-buffer (slime-output-buffer)
;; Stolen mostly from slime-media.el, thanks Christophe!
(let ((marker (slime-repl-output-target-marker :repl-result)))
(goto-char marker)
(slime-clime-insert-image svg-data presentations)
(insert "\n")
;; Move the input-start marker after the REPL result.
(set-marker marker (point))))
t)
((:accept-for-clime thread tag input-context)
(slime-clime-set-input-context (slime-connection) thread tag input-context)
t)
(t nil)))
(defun slime-clime-id-to-keyword (id)
(assert (and (integerp id) (>= id 0)))
(intern (format ":%d" id)))
(defun slime-clime-keyword-to-id (id)
(assert (keywordp id))
(cl-parse-integer (substring (symbol-name id) 1))) ;
(defun slime-clime-insert-image (svg-data presentations)
(let* ((map (slime-clime-presentations-map presentations))
(props (list 'slime-clime-presentations presentations
:map map
'slime-clime-connection (slime-connection))))
(insert-image (slime-clime-create-image svg-data map props))
(put-text-property (1- (point)) (point)
'slime-clime-connection (slime-connection))
(put-text-property (1- (point)) (point)
'keymap slime-clime-image-keymap)))
(defun slime-clime-create-image (svg-data map properties)
;; Somehow create-image does not work properly when passed the image
;; data and map attributes directly. This is pure voodoo. Making a
;; fresh copy is the only reliable workaround I have been able to
;; find. -luke
(eval (append (slime-clime-reread `(create-image ,svg-data 'svg t :map ',map))
(mapcar (lambda (prop) (list 'quote prop)) properties))))
(defun slime-clime-reread (form)
"Reread FORM by prining readably and then reading.
Used as an awful workaround for voodoo object identity problems."
(read (let (print-length print-depth) (prin1-to-string form))))
(defun slime-clime-presentations-map (presentations &optional pointer-shape)
"Return an image 'map' property for PRESENTATIONS."
(mapcar (lambda (presentation)
(cl-destructuring-bind (number area tooltip) presentation
(let ((id (slime-clime-id-to-keyword number)))
`(,area ,id (pointer ,(or pointer-shape 'arrow)
help-echo ,tooltip)))))
presentations))
;;;; Input
(slime-def-connection-var slime-clime-input-context nil
"Current CLIM input context for the connection.
List of (THREAD TAG INPUT-CONTEXT)")
(defun slime-clime-set-input-context (slime-connection thread tag input-context)
"Set INPUT-CONTEXT for SLIME-CONNECTION.
The input context is a list of presentation IDs ready for ACCEPT."
;; If an input context already exists then abort it as stale.
(when (slime-clime-input-context)
(cl-destructuring-bind (thread tag ctx) (slime-clime-input-context)
(slime-dispatch-event `(:emacs-return ,thread ,tag :abort)))
(setf (slime-clime-input-context) nil))
;; Save input context
(setf (slime-clime-input-context)
(list thread tag input-context))
;; Update presentations
(slime-clime-map-images
(lambda (image)
(when (eq (get-text-property (1- (point)) 'slime-clime-connection)
(slime-connection))
(slime-clime-filter-presentations image input-context)))
slime-connection))
(defun slime-clime-filter-presentations (image input-context)
"Filter active areas of IMAGE based on INPUT-CONTEXT."
(let* ((all (image-property image 'slime-clime-presentations))
(filtered (cl-remove-if-not (lambda (area)
(member (first area) input-context))
all)))
(setf (image-property image :map) nil)
(setf (image-property image :map)
(slime-clime-reread (slime-clime-presentations-map filtered 'hand)))))
(defun slime-clime-reset-input-context (&optional connection)
"Reset the current input context."
(slime-clime-map-images
(lambda (image)
(setf (image-property image :map)
(slime-clime-reread
(slime-clime-presentations-map
(image-property image 'slime-clime-presentations)))))
(or connection (slime-connection))))
(defun slime-clime-map-images (fn connection)
"Call FN with each CLIME image in all buffers in Emacs."
(dolist (b (buffer-list))
(with-current-buffer b
(save-excursion
(goto-char (point-min))
(while (text-property-search-forward 'slime-clime-connection connection t)
(let ((image (get-text-property (1- (point)) 'display)))
(when image
(funcall fn image))))))))
;;;; Image information
(defslimefun svg-image-size (svg-data)
(let ((image (cons 'image (list :type 'svg :data svg-data))))
(image-size image t)))
(defslimefun window-width-for-margin ()
(window-body-width nil t))
(provide 'slime-clime)