1 ;;; fuel-debug.el -- debugging factor code
3 ;; Copyright (C) 2008, 2009 Jose Antonio Ortega Ruiz
4 ;; See http://factorcode.org/license.txt for BSD license.
6 ;; Author: Jose Antonio Ortega Ruiz <jao@gnu.org>
7 ;; Keywords: languages, fuel, factor
8 ;; Start date: Sun Dec 07, 2008 04:16
12 ;; A mode for displaying the results of run-file and evaluation, with
13 ;; support for restarts.
19 (require 'fuel-font-lock
)
25 (defgroup fuel-debug nil
26 "Major mode for interaction with the Factor debugger."
29 (defcustom fuel-debug-mode-hook nil
30 "Hook run after `fuel-debug-mode' activates."
34 (defcustom fuel-debug-confirm-restarts-p t
35 "Whether to ask for confimation before executing a restart in
40 (defcustom fuel-debug-show-short-help t
41 "Whether to show short help on available keys in debugger."
45 (fuel-font-lock--define-faces
46 fuel-font-lock-debug font-lock fuel-debug
47 ((error warning
"highlighting errors")
48 (line variable-name
"line numbers in errors/warnings")
49 (column variable-name
"column numbers in errors/warnings")
50 (info comment
"information headers")
51 (restart-number warning
"restart numbers")
52 (restart-name function-name
"restart names")
53 (missing-vocab warning
"missing vocabulary names")
54 (unneeded-vocab warning
"unneeded vocabulary names")))
57 ;;; Font lock and other pattern matching:
59 (defconst fuel-debug--compiler-info-alist
60 '((":warnings" . ?w
) (":errors" . ?e
) (":linkage" . ?l
)))
62 (defconst fuel-debug--error-file-regex
"^P\" \\([^\"]+\\)\"")
63 (defconst fuel-debug--error-line-regex
"\\([0-9]+\\):")
64 (defconst fuel-debug--error-cont-regex
"^ +\\(\\^\\)$")
66 (defconst fuel-debug--error-regex
68 fuel-debug--error-file-regex
69 fuel-debug--error-line-regex
))
71 (defconst fuel-debug--compiler-info-regex
73 (regexp-opt (mapcar 'car fuel-debug--compiler-info-alist
))))
75 (defconst fuel-debug--restart-regex
"^:\\([0-9]+\\) \\(.+\\)")
77 (defconst fuel-debug--font-lock-keywords
78 `((,fuel-debug--error-file-regex .
'fuel-font-lock-debug-error
)
79 (,fuel-debug--error-line-regex
1 'fuel-font-lock-debug-line
)
80 (,fuel-debug--error-cont-regex
1 'fuel-font-lock-debug-column
)
81 (,fuel-debug--restart-regex
(1 'fuel-font-lock-debug-restart-number
)
82 (2 'fuel-font-lock-debug-restart-name
))
83 (,fuel-debug--compiler-info-regex
1 'fuel-font-lock-debug-restart-number
)
84 ("^\\(Restarts?\\|Loading\\) .+$" .
'fuel-font-lock-debug-info
)
85 ("^Error: " .
'fuel-font-lock-debug-error
)))
87 (defun fuel-debug--font-lock-setup ()
88 (set (make-local-variable 'font-lock-defaults
)
89 '(fuel-debug--font-lock-keywords t nil nil nil
)))
94 (fuel-popup--define fuel-debug--buffer
95 "*fuel debug*" 'fuel-debug-mode
)
97 (make-variable-buffer-local
98 (defvar fuel-debug--last-ret nil
))
100 (make-variable-buffer-local
101 (defvar fuel-debug--file nil
))
103 (make-variable-buffer-local
104 (defvar fuel-debug--uses nil
))
106 (defun fuel-debug--prepare-compilation (file msg
)
107 (let ((inhibit-read-only t
))
108 (with-current-buffer (fuel-debug--buffer)
111 (setq fuel-debug--file file
))))
113 (defun fuel-debug--display-retort (ret &optional success-msg no-pop
)
114 (let ((err (fuel-eval--retort-error ret
))
115 (inhibit-read-only t
))
116 (with-current-buffer (fuel-debug--buffer)
118 (fuel-debug--display-output ret
)
121 (when (and (not err
) success-msg
)
122 (message "%s" success-msg
)
123 (insert "\n" success-msg
"\n"))
125 (fuel-debug--display-restarts err
)
128 (fuel-debug--display-uses ret
)
129 (let ((hstr (fuel-debug--help-string err fuel-debug--file
)))
130 (if fuel-debug-show-short-help
131 (insert "-----------\n" hstr
"\n")
132 (message "%s" hstr
)))
133 (setq fuel-debug--last-ret ret
)
134 (goto-char (point-max))
135 (font-lock-fontify-buffer)
136 (when (and err
(not no-pop
)) (fuel-popup--display))
139 (defun fuel-debug--uses (ret)
140 (let ((uses (fuel-eval--retort-result ret
)))
141 (and (eq :uses
(car uses
))
144 (defun fuel-debug--insert-vlist (title vlist
)
145 (goto-char (point-max))
146 (insert title
"\n\n ")
147 (let ((i 0) (step 5))
151 (insert (if (zerop (mod i step
)) "\n " " ")))
152 (unless (zerop (mod i step
)) (newline))
155 (defun fuel-debug--highlight-names (names ref face
)
157 (when (not (member n ref
))
158 (put-text-property 0 (length n
) 'font-lock-face face n
))))
160 (defun fuel-debug--insert-uses (uses)
161 (let* ((file (or file fuel-debug--file
))
162 (old (with-current-buffer (find-file-noselect file
)
163 (sort (fuel-syntax--find-usings t
) 'string
<)))
164 (new (sort uses
'string
<)))
165 (when (not (equalp old new
))
166 (fuel-debug--highlight-names old new
'fuel-font-lock-debug-unneeded-vocab
)
168 (fuel-debug--insert-vlist "Correct vocabulary list:" new
)
171 (defun fuel-debug--display-uses (ret)
172 (when (setq fuel-debug--uses
(fuel-debug--uses ret
))
174 (fuel-debug--highlight-names fuel-debug--uses
175 nil
'fuel-font-lock-debug-missing-vocab
)
176 (fuel-debug--insert-vlist "Missing vocabularies:" fuel-debug--uses
)
179 (defun fuel-debug--display-output (ret)
180 (let* ((last (fuel-eval--retort-output fuel-debug--last-ret
))
181 (current (fuel-eval--retort-output ret
))
183 (clen (length current
))
184 (trail (and last
(substring-no-properties last
(/ llen
2))))
185 (err (fuel-eval--retort-error ret
))
187 (when current
(save-excursion (insert current
)))
188 (when (and (> clen llen
) (> llen
0) (search-forward trail nil t
))
189 (delete-region p
(point)))
190 (goto-char (point-max))
192 (insert (format "\nError: %S\n\n" (fuel-eval--error-name err
))))))
194 (defun fuel-debug--display-restarts (err)
195 (let* ((rs (fuel-eval--error-restarts err
))
198 (insert "Restarts:\n\n")
200 (insert (format ":%s %s\n" (1+ n
) (nth n rs
))))
203 (defun fuel-debug--help-string (err &optional file
)
204 (format "Press %s%s%s%sq bury buffer"
205 (if (or file
(fuel-eval--error-file err
)) "g go to file, " "")
206 (let ((rsn (length (fuel-eval--error-restarts err
))))
207 (cond ((zerop rsn
) "")
208 ((= 1 rsn
) "1 invoke restart, ")
209 (t (format "1-%s invoke restarts, " rsn
))))
211 (dolist (ci fuel-debug--compiler-info-alist str
)
213 (goto-char (point-min))
214 (when (search-forward (car ci
) nil t
)
215 (setq str
(format "%c %s, %s" (cdr ci
) (car ci
) str
))))))
216 (if fuel-debug--uses
"u to update USING:, " "")))
218 (defun fuel-debug--buffer-file ()
219 (with-current-buffer (fuel-debug--buffer)
221 (and fuel-debug--last-ret
222 (fuel-eval--error-file
223 (fuel-eval--retort-error fuel-debug--last-ret
))))))
225 (defsubst fuel-debug--buffer-error
()
226 (fuel-eval--retort-error fuel-debug--last-ret
))
228 (defsubst fuel-debug--buffer-restarts
()
229 (fuel-eval--error-restarts (fuel-debug--buffer-error)))
232 ;;; Buffer navigation:
234 (defun fuel-debug-goto-error ()
236 (let* ((err (fuel-debug--buffer-error))
237 (file (or (fuel-debug--buffer-file)
238 (error "No file associated with compilation")))
239 (l/c
(and err
(fuel-eval--error-line/column err
)))
240 (line (or (car l
/c
) 1))
241 (col (or (cdr l
/c
) 0)))
242 (find-file-other-window file
)
245 (when col
(forward-char col
)))))
247 (defun fuel-debug--read-restart-no ()
248 (let ((rs (fuel-debug--buffer-restarts)))
249 (unless rs
(error "No restarts available"))
250 (let* ((rsn (length rs
))
251 (prompt (format "Restart number? (1-%s): " rsn
))
253 (while (or (> (setq no
(read-number prompt
)) rsn
)
257 (defun fuel-debug-exec-restart (&optional n confirm
)
258 (interactive (list (fuel-debug--read-restart-no)))
260 (rs (fuel-debug--buffer-restarts)))
261 (when (zerop (length rs
))
262 (error "No restarts available"))
263 (when (or (< n
1) (> n
(length rs
)))
264 (error "Restart %s not available" n
))
265 (when (or (not confirm
)
266 (y-or-n-p (format "Invoke restart %s? " n
)))
267 (message "Invoking restart %s" n
)
268 (let* ((file (fuel-debug--buffer-file))
269 (buffer (if file
(find-file-noselect file
) (current-buffer))))
270 (with-current-buffer buffer
271 (fuel-debug--display-retort
272 (fuel-eval--send/wait
`(:fuel
((:factor
,(format ":%s" n
)))))
273 (format "Restart %s (%s) successful" n
(nth (1- n
) rs
))))))))
275 (defun fuel-debug-show--compiler-info (info)
277 (goto-char (point-min))
278 (unless (re-search-forward (format "^%s" info
) nil t
)
279 (error "%s information not available" info
))
280 (message "Retrieving %s info ..." info
)
281 (unless (fuel-debug--display-retort
282 (fuel-eval--send/wait
`(:fuel
((:factor
,info
)))) "")
283 (error "Sorry, no %s info available" info
))))
285 (defun fuel-debug--replace-usings (file uses
)
286 (pop-to-buffer (find-file-noselect file
))
287 (goto-char (point-min))
288 (if (re-search-forward "^USING: " nil t
)
289 (let ((begin (point))
290 (end (or (and (re-search-forward ";\\( \\|$\\)") (point))
292 (kill-region begin end
))
293 (re-search-forward "^IN: " nil t
)
297 (let ((start (point)))
298 (insert (mapconcat 'substring-no-properties uses
" ") " ;")
299 (fill-region start
(point) nil
)))
301 (defun fuel-debug-update-usings ()
303 (when (and fuel-debug--file fuel-debug--uses
)
304 (let* ((file fuel-debug--file
)
305 (old (with-current-buffer (find-file-noselect file
)
306 (fuel-syntax--find-usings t
)))
307 (uses (sort (append fuel-debug--uses old
) 'string
<)))
309 (fuel-debug--replace-usings file uses
))))
314 (defvar fuel-debug-mode-map
315 (let ((map (make-keymap)))
316 (suppress-keymap map
)
317 (define-key map
"g" 'fuel-debug-goto-error
)
318 (define-key map
"\C-c\C-c" 'fuel-debug-goto-error
)
319 (define-key map
"n" 'next-line
)
320 (define-key map
"p" 'previous-line
)
321 (define-key map
"u" 'fuel-debug-update-usings
)
323 (define-key map
(vector (+ ?
1 n
))
324 `(lambda () (interactive)
325 (fuel-debug-exec-restart ,(1+ n
) fuel-debug-confirm-restarts-p
))))
326 (dolist (ci fuel-debug--compiler-info-alist
)
327 (define-key map
(vector (cdr ci
))
328 `(lambda () (interactive) (fuel-debug-show--compiler-info ,(car ci
)))))
331 (defun fuel-debug-mode ()
332 "A major mode for displaying Factor's compilation results and
333 invoking restarts as needed.
334 \\{fuel-debug-mode-map}"
336 (kill-all-local-variables)
337 (buffer-disable-undo)
338 (setq major-mode
'fuel-debug-mode
)
339 (setq mode-name
"Fuel Debug")
340 (use-local-map fuel-debug-mode-map
)
341 (fuel-debug--font-lock-setup)
342 (setq fuel-debug--file nil
)
343 (setq fuel-debug--last-ret nil
)
344 (run-hooks 'fuel-debug-mode-hook
))
348 (provide 'fuel-debug
)
349 ;;; fuel-debug.el ends here