-
-
Notifications
You must be signed in to change notification settings - Fork 61
/
indium-breakpoint.el
224 lines (185 loc) · 8.4 KB
/
indium-breakpoint.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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
;;; indium-breakpoint.el --- Add/remove breakpoints -*- lexical-binding: t; -*-
;; Copyright (C) 2017-2018 Nicolas Petton
;; Author: Nicolas Petton <[email protected]>
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Add and remove breakpoints to a buffer.
;;
;; Breakpoints can be added to buffer even if Indium is not connected. In such
;; case, Indium will attempt to register them breakpoints in the runtime when a
;; connection is made.
;;; Code:
(require 'indium-client)
(require 'indium-faces)
(require 'indium-structs)
(defvar indium-breakpoint--local-breakpoints (make-hash-table :weakness t)
"Table of all local breakpoints and their buffers.")
(defun indium-breakpoint-add (&optional condition)
"Add a breakpoint at point.
When CONDITION is non-nil, the breakpoint will be hit when
CONDITION is true."
(let* ((brk (indium-breakpoint-create :condition (or condition ""))))
(setf (map-elt indium-breakpoint--local-breakpoints brk nil #'equal) (current-buffer))
(indium-breakpoint--add-overlay brk)
(when (indium-client-process-live-p)
(indium-client-add-breakpoint brk))))
(defun indium-breakpoint-edit-condition ()
"Edit condition of breakpoint at point."
(when-let ((breakpoint (indium-breakpoint-at-point)))
(let* ((old-condition (indium-breakpoint-condition breakpoint))
(new-condition (read-from-minibuffer "Breakpoint condition: "
old-condition nil nil nil old-condition)))
(indium-breakpoint-remove)
(indium-breakpoint-add new-condition))))
(defun indium-breakpoint-remove ()
"Remove all breakpoints from the current line."
(seq-doseq (brk (indium-breakpoint-breakpoints-at-point))
(when (indium-client-process-live-p)
(indium-client-remove-breakpoint brk))
(map-delete indium-breakpoint--local-breakpoints brk)
(indium-breakpoint--remove-overlay)))
(defun indium-breakpoint-remove-breakpoints-from-current-buffer ()
"Remove all breakpoints from the current buffer's file."
(indium-breakpoint--breakpoints-in-buffer-do
(lambda (_ ov)
(save-excursion
(goto-char (overlay-start ov))
(indium-breakpoint-remove)))))
(defun indium-breakpoint-resolve (id line)
"Update the breakpoint with ID for SCRIPT at LINE.
This function should be called upon breakpoint resolution by the
server, or when a breakpoint location gets updated from the
server."
(let* ((brk (indium-breakpoint-breakpoint-with-id id))
(location (indium-breakpoint-location brk)))
(setf (indium-breakpoint-resolved brk) t)
(setf (indium-location-line location) line)
(indium-breakpoint--update-overlay brk location)))
(defun indium-breakpoint-breakpoint-with-id (id)
"Return the breakpoint with ID or nil."
(seq-find (lambda (brk)
(equal id (indium-breakpoint-id brk)))
(map-keys indium-breakpoint--local-breakpoints)))
(defun indium-breakpoint-breakpoints-at-point ()
"Return all breakpoints on the current line.
If there is no breakpoint set on the line, return nil."
(seq-filter (lambda (brk)
(let ((location (indium-breakpoint-location brk)))
(and (equal (indium-location-file location) buffer-file-name)
(equal (indium-location-line location) (line-number-at-pos)))))
(map-keys indium-breakpoint--local-breakpoints)))
(defun indium-breakpoint-at-point ()
"Return the first breakpoint on the current line.
If there is no breakpoint set on the line, return nil."
(car (indium-breakpoint-breakpoints-at-point)))
(defun indium-breakpoint-on-current-line-p ()
"Return non-nil if there is a breakpoint on the current line."
(not (null (indium-breakpoint--overlay-on-current-line))))
(defun indium-breakpoint-remove-overlays-from-current-buffer ()
"Remove all breakpoint markers from the current buffer.
This function does no unset breakpoints."
(remove-overlays (point-min)
(point-max)
'indium-breakpoint-ov
t))
(defun indium-breakpoint--add-overlay (breakpoint)
"Add an overlay for BREAKPOINT on the current line.
An icon is added to the left fringe."
(let ((ov (indium-breakpoint--ensure-overlay)))
(overlay-put ov
'before-string
(indium-breakpoint--fringe-icon breakpoint))
(overlay-put ov
'indium-breakpoint
breakpoint)
(setf (indium-breakpoint-overlay breakpoint) ov)
ov))
(defun indium-breakpoint--remove-overlay ()
"Remove the breakpoint overlay from the current line."
(when-let ((ov (indium-breakpoint--overlay-on-current-line)))
(setf (indium-breakpoint-overlay (overlay-get ov 'indium-breakpoint)) nil)
(remove-overlays (overlay-start ov)
(overlay-end ov)
'indium-breakpoint-ov
t)))
(defun indium-breakpoint--update-overlay (breakpoint location)
"Set the overlay for BREAKPOINT at LOCATION."
(when-let ((buf (indium-breakpoint-buffer breakpoint)))
(with-current-buffer buf
(save-excursion
(goto-char (overlay-start (indium-breakpoint-overlay breakpoint)))
(indium-breakpoint--remove-overlay))))
(when-let ((file (indium-location-file location))
(line (indium-location-line location)))
(with-current-buffer (find-file-noselect file)
(save-excursion
(goto-char (point-min))
(forward-line (1- line))
(indium-breakpoint--add-overlay breakpoint)))))
(defun indium-breakpoint-buffer (breakpoint)
"Return the buffer in which BREAKPOINT is set, or nil."
(when-let ((ov (indium-breakpoint-overlay breakpoint)))
(overlay-buffer ov)))
(defun indium-breakpoint--register-all-breakpoints ()
"Register all local breakpoints."
(map-apply (lambda (brk _)
(indium-client-add-breakpoint brk))
indium-breakpoint--local-breakpoints))
(defun indium-breakpoint--unregister-all-breakpoints ()
"Remove the registration information from all breakpoints."
(map-apply (lambda (brk _)
(setf (indium-breakpoint-resolved brk) nil)
(indium-breakpoint--update-overlay
brk
(indium-breakpoint-location brk)))
indium-breakpoint--local-breakpoints))
(defun indium-breakpoint--fringe-icon (breakpoint)
"Return the fringe icon used for BREAKPOINT."
(propertize "b" 'display
(list 'left-fringe (if (indium-breakpoint-resolved breakpoint)
'indium-breakpoint-resolved
'indium-breakpoint-unresolved)
'indium-breakpoint-face)))
(defun indium-breakpoint--overlay-on-current-line ()
"Return the breakpoint overlay on the current-line.
If no overlay is present, return nil."
(seq-find (lambda (ov)
(overlay-get ov 'indium-breakpoint-ov))
(overlays-in (point-at-bol) (1+ (point-at-eol)))))
(defun indium-breakpoint--ensure-overlay ()
"Return the breakpoint overlay on the current line.
If there is no overlay, make one."
(or (indium-breakpoint--overlay-on-current-line)
(let ((ov (make-overlay (point-at-bol) (point-at-eol) nil t)))
(overlay-put ov 'indium-breakpoint-ov t)
ov)))
;; Handle breakpoint resolution
(add-hook 'indium-client-breakpoint-resolved-hook #'indium-breakpoint-resolve)
;; Update/Restore breakpoints
(add-hook 'indium-client-closed-hook #'indium-breakpoint--unregister-all-breakpoints)
(add-hook 'indium-client-connected-hook #'indium-breakpoint--register-all-breakpoints)
;; Helpers
(defun indium-breakpoint--breakpoints-in-buffer-do (fn)
"Evaluate FN on all breakpoints in the current buffer.
FN takes two arguments, the breakpoint and its associated
overlay."
(let ((overlays (overlays-in (point-min) (point-max))))
(seq-doseq (ov overlays)
(when-let ((brk (overlay-get ov 'indium-breakpoint)))
(funcall fn brk ov)))))
(when (fboundp 'define-fringe-bitmap)
(define-fringe-bitmap 'indium-breakpoint-resolved
"\x3c\x7e\xff\xff\xff\xff\x7e\x3c")
(define-fringe-bitmap 'indium-breakpoint-unresolved
"\x3c\x42\x81\x81\x81\x81\x42\x3c"))
(provide 'indium-breakpoint)
;;; indium-breakpoint.el ends here