3 ;;; ll-debug.el --- low level debug tools
5 ;; Copyright (C) 2002-2005 Claus Brunzema <mail@cbrunzema.de>
7 ;; http://www.cbrunzema.de/software.html#ll-debug
10 ;; $Id: ll-debug.el,v 1.22 2004/12/28 22:23:16 chb Exp $
12 ;; This program is free software; you can redistribute it and/or
13 ;; modify it under the terms of the GNU General Public License as
14 ;; published by the Free Software Foundation; either version 2, or
15 ;; (at your option) any later version.
17 ;; It is distributed in the hope that it will be useful, but WITHOUT
18 ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
20 ;; License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with this program; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25 ;; -----------------------------------------------------------------------
30 ;; ll-debug.el provides commands to support a low level debug style.
31 ;; It features quick insertion of various debug output statements and
32 ;; improved functions for commenting and uncommenting chunks of code.
34 ;; I don't use debuggers very much. I know they can be a big help in
35 ;; some situations and I tried some of them, but I find it almost
36 ;; always more direct/convenient/enlightening to put a quick 'printf'
37 ;; into a critical area to see what is happening than to fire up a big
38 ;; clumsy extra program where it takes me ages just to step through to
39 ;; the interesting point. In order to avoid repeated typing of
40 ;; 'printf("I AM HERE\n");' and similar stuff, I created
41 ;; `ll-debug-insert'. It inserts a statement into your
42 ;; sourcecode that will display a debug message. It generates
43 ;; unique messages on each invocation (the message consists of a big
44 ;; fat DEBUG together with a counter and the current filename).
46 ;; See variable `ll-debug-statement-alist' if you want to know which
47 ;; modes are currently supported by ll-debug. You can add new modes
48 ;; with `ll-debug-register-mode'.
50 ;; When I have found the buggy spot, I like to keep a version of the
51 ;; old code in place, just in case I mess things up.
52 ;; `ll-debug-copy-and-comment-region-or-line' helps here, it makes a
53 ;; copy of the current line (or the current region, if active) and
54 ;; comments out the original version.
56 ;; I always missed a keystroke that toggles the 'comment state' of a
57 ;; line (or region) of sourcecode. I need to turn a line or a block of
58 ;; code on and off quickly. `ll-debug-toggle-comment-region-or-line'
61 ;; Finally, if you want to spit out the values of a lot of variables
62 ;; you can use `ll-debug-insert' with a C-u prefix arg. It calls
63 ;; mode-specific skeletons that keep asking for variable names (and
64 ;; sometimes format specifiers) in the minibuffer. If you just press
65 ;; return here the skeleton interaction ends and a statement to print
66 ;; the names and the values of the variables is inserted in the
69 ;; If you want to get rid of the debug messages, use
70 ;; `ll-debug-revert'. It finds and removes the lines with the debug
71 ;; output statements, asking for confirmation before it removes
77 ;; I made the latest version of ll-debug with the following emacs:
79 ;; Please let me know if other versions work.
84 ;; Get the newest version of ll-debug.el via
86 ;; http://www.cbrunzema.de/software.html#ll-debug
88 ;; and put it in your load-path. Add the following form to your init
89 ;; file (~/.emacs or ~/.xemacs/init.el):
91 ;; (require 'll-debug)
93 ;; Now you can bind ll-debug commands to keystrokes yourself or just
94 ;; call `ll-debug-install-suggested-keybindings'. It clobbers C-v,
95 ;; which may not be completely emacs-political-correct, but it happens
96 ;; to be the stuff I use daily, it is only a suggestion, blah, if you
97 ;; don't like it, don't use it blah blah, do it your own way blah bla
98 ;; blah and don't flame me....
99 ;; `ll-debug-install-suggested-keybindings' installs the following
102 ;; C-v C-v ll-debug-toggle-comment-region-or-line
103 ;; C-v v ll-debug-uncomment-region-or-line
104 ;; C-v C-y ll-debug-copy-and-comment-region-or-line
105 ;; C-v C-d ll-debug-insert
110 ;; If you use `ll-debug-install-suggested-keybindings', hitting C-v C-d
111 ;; in a c-mode buffer called 'main.c' produces:
113 ;; printf("DEBUG-1-main.c\n");
115 ;; a second C-v C-d prints
117 ;; printf("DEBUG-2-main.c\n");
119 ;; and so on. The following conversation uses the variable output (the
120 ;; part in '[' and ']' takes place in the minibuffer):
122 ;; C-u C-v C-d [ foo <RET> s <RET> bar <RET> d <RET> baz <RET> f <RET> <RET> ]
126 ;; printf("DEBUG-3-main.c foo:%s bar:%d baz:%f\n", foo, bar, baz);
131 ;; In a lisp-mode buffer called 'tree.lisp' this:
135 ;; C-u C-v C-d [ foo <RET> bar <RET> baz <RET> <RET> ]
137 ;; produces the following lines:
139 ;; (CL:format t "DEBUG-1-tree.lisp~%")
140 ;; (CL:format t "DEBUG-2-tree.lisp~%")
141 ;; (CL:format t "DEBUG-3-tree.lisp foo:~S bar:~S baz:~S~%" foo bar baz)
146 ;; The keybindings installed via
147 ;; `ll-debug-install-suggested-keybindings' will call an alternative
148 ;; versions for variable output if one ore more C-u prefix args are
149 ;; given. An alternative version is currently available in (c)perl-mode
150 ;; only. So, in a (c)perl-mode buffer called 'answer.pl' these keys
152 ;; C-u C-u C-v C-d [ @quux <RET> %thud <RET> $grunt <RET> <RET> ]
156 ;; print "DEBUG-1-answer.pl ", Data::Dumper->Dump([\@quux, \%thud, $grunt], [qw/*quux *thud grunt/]), "\n";
161 ;; You can use a different string for the debug messages by setting the
162 ;; variable `ll-debug-output-prefix'. If you set it e.g. to "# DEBUG-"
163 ;; your debug output won't disturb gnuplot datafiles anymore.
165 ;; If you don't like c++'s streams, you can request the printf style
166 ;; output by putting the following in your init file:
168 ;; (setcdr (assq 'c++-mode ll-debug-statement-alist)
169 ;; (cdr (assq 'c-mode ll-debug-statement-alist)))
172 ;; If you want to have dynamic output not only according to the major
173 ;; mode, you can substitute functions in `ll-debug-statement-alist'.
174 ;; For example, the following snippet uses prefix 'printk' instead of
175 ;; 'printf' if you are editing c-sources in a file on a path
176 ;; containing a 'linux' component:
178 ;; (setf (ll-debug-struct-prefix (cdr (assq 'c-mode
179 ;; ll-debug-statement-alist)))
181 ;; (if (string-match "linux" (buffer-file-name))
187 ;; Please read the documentation for `ll-debug-insert' and
188 ;; `ll-debug-expand' to see what is possible.
191 ;; If you want to teach ll-debug new modes, see
192 ;; `ll-debug-register-mode' and consider sending a patch to
193 ;; <mail@cbrunzema.de>.
197 ;; 2004-12-28 Claus Brunzema
198 ;; * Major rewrite using defstruct.
199 ;; * New ll-debug-insert instead of
200 ;; ll-debug-insert-debug-output and
201 ;; ll-debug-insert-variable-output.
202 ;; * New ll-debug-register-mode.
204 ;; 2003-05-21 Claus Brunzema
205 ;; * Added java support.
206 ;; * Moved prefix calculation stuff into new
207 ;; ll-debug-insert-debug-output-statement.
210 ;; 2003-05-15 Claus Brunzema
211 ;; * Added ll-debug-install-suggested-keybindings.
212 ;; 2003-03-10 Claus Brunzema
213 ;; * Added package/namespace identifiers to common lisp/c++ code
215 ;; 2003-03-10 Claus Brunzema
216 ;; * Put in ll-debug-output-prefix instead of the hardcoded
217 ;; default (thanks to Stefan Kamphausen for the idea with
219 ;; * More documentation.
221 ;; 2003-01-30 Claus Brunzema
222 ;; * added ll-debug-insert-emacs-lisp-variable-output.
223 ;; * ll-debug-insert-perl-variable-output doesn't insert
224 ;; the '$' automatically anymore. That always confused me.
225 ;; * various cleanup and documentation changes.
227 ;; 2003-01-29 Claus Brunzema
228 ;; * added ll-debug-insert-perl-variable-dumper-output.
229 ;; 2003-01-28 Claus Brunzema
230 ;; * after (un)commenting a single line the point is moved
232 ;; 2002-11-20 Claus Brunzema
233 ;; * added ll-debug-insert-scheme-variable-output.
235 ;; 2002-11-11 Claus Brunzema
236 ;; * added ll-debug-create-next-debug-string (thanks to Scott Frazer).
237 ;; * updated skeletons to use ll-debug-create-next-debug-string.
239 ;; 2002-11-09 Claus Brunzema
240 ;; * added DEBUG to skeletons.
241 ;; * added ll-debug-revert (thanks to Scott Frazer for the idea).
242 ;; * removed automatic linebreaks from skeletons, so ll-debug-revert
243 ;; doesn't leave half statemets behind.
244 ;; 2002-10-15 Claus Brunzema
245 ;; * fixed ll-debug-region-or-line-comment-start to look
246 ;; for comment-chars starting a line only (thanks to Stefan
247 ;; Kamphausen for the bug report).
250 ;; 2002-09-04 Claus Brunzema
251 ;; * fixed point position after
252 ;; ll-debug-copy-and-comment-region-or-line
254 ;; 2002-08-17 Claus Brunzema
255 ;; * use (search-forward comment-start ...) instead of
256 ;; (re-search-forward comment-start-skip ...).
257 ;; * use ll-debug-region-or-line-comment-start instead of
258 ;; the optional ignore-current-column argument for
259 ;; ll-debug-region-or-line-start.
260 ;; * ll-debug-copy-and-comment-region-or-line works correctly
261 ;; now if point is in the middle of the line.
263 ;; 2002-08-11 Claus Brunzema
264 ;; * Variable output support for Common Lisp, perl and c.
265 ;; * Various cleanup.
267 ;; 2002-08-08 Claus Brunzema
268 ;; * Uncommenting doesn't check the current column anymore
269 ;; (thanks to Stefan Kamphausen).
272 ;; 2002-08-07 Claus Brunzema
273 ;; * First public version 0.1.0
277 ;; * Check if the strange log calculation in ll-debug-insert is really
278 ;; necessary. I want the number of C-u keypresses to dispatch
279 ;; alternatives on the content slot value of a ll-debug-struct, but
280 ;; every C-u multiplies prefix-numeric-value by 4. Is there a better
282 ;; * Make preferred output stream customizable.
290 ;; Struct------------------------------------------------------------------
291 (defstruct ll-debug-struct
292 "Strings/functions/skeletons to create debug messages for a single mode.
293 See `ll-debug-statement-alist' and `ll-debug-expand', too."
296 (content '() :type list))
299 ;; Variables --------------------------------------------------------------
300 (defvar ll-debug-output-prefix "DEBUG-"
301 "*Prefix string for debug output messages.")
303 (defvar ll-debug-statement-alist ()
304 "Stores mode-specific ll-debug-structs.")
307 ;;; gnuemacs / xemacs compatibility ---------------------------------------
308 (defun ll-debug-region-exists-p ()
309 (if (fboundp 'region-exists-p)
310 (region-exists-p) ;XEmacs
311 (and transient-mark-mode mark-active))) ;GNUEmacs
313 (defun ll-debug-uncomment-region (beg end)
314 (if (fboundp 'uncomment-region)
315 (uncomment-region beg end) ;GNUEmacs
316 (comment-region beg end -1))) ;XEmacs
319 ;;; misc. Functions -------------------------------------------------------
320 (defun ll-debug-region-or-line-start ()
322 (if (ll-debug-region-exists-p)
324 (goto-char (region-beginning))
326 (if (= (current-column) (current-indentation))
330 (defun ll-debug-region-or-line-end ()
332 (if (ll-debug-region-exists-p)
334 (goto-char (region-end))
342 (defun ll-debug-install-suggested-keybindings ()
343 "Install suggested keybindings for ll-debug.
344 This installs the following keybindings (clobbering C-v):
346 C-v C-v ll-debug-toggle-comment-region-or-line
347 C-v v ll-debug-uncomment-region-or-line
348 C-v C-y ll-debug-copy-and-comment-region-or-line
349 C-v C-d ll-debug-insert"
351 (unless (keymapp (global-key-binding '[(control v)]))
352 (global-unset-key '[(control v)]))
354 (define-key global-map '[(control v) (control v)]
355 #'ll-debug-toggle-comment-region-or-line)
356 (define-key global-map '[(control v) v]
357 #'ll-debug-uncomment-region-or-line)
358 (define-key global-map '[(control v) (control y)]
359 #'ll-debug-copy-and-comment-region-or-line)
360 (define-key global-map '[(control v) (control d)]
364 (defun ll-debug-expand (thing)
365 "Expands THING into the current buffer.
366 If THING is a string, it is inserted.
367 If THING is a list, it is treated as a skeleton (see `skeleton-insert')
368 If THING is a function, it is funcalled and `ll-debug-expand' is
369 invoked recursively on the returned value."
375 (skeleton-insert thing))
381 ;; comment in and out -----------------------------------------------------
382 (defun ll-debug-region-or-line-comment-start ()
383 "Find the comment marker at the beginning of the line or region."
385 (when (ll-debug-region-exists-p) (goto-char (region-beginning)))
387 (skip-chars-forward " \t" (point-at-eol))
388 (if (looking-at (regexp-quote comment-start))
392 (defun ll-debug-copy-and-comment-region-or-line ()
393 "Copy the current line/region and comment out the original."
395 (let* ((start (ll-debug-region-or-line-start))
396 (end (ll-debug-region-or-line-end))
397 (src-code (buffer-substring start end)))
399 (comment-region start end)
401 (insert-string src-code))))
403 (defun ll-debug-comment-region-or-line ()
404 "Comment out the current line or all lines of the region."
406 (comment-region (ll-debug-region-or-line-start)
407 (ll-debug-region-or-line-end))
408 (unless (ll-debug-region-exists-p)
411 (defun ll-debug-uncomment-region-or-line ()
412 "Uncomment the current line or all lines of the region."
414 (ll-debug-uncomment-region (ll-debug-region-or-line-comment-start)
415 (ll-debug-region-or-line-end))
416 (unless (ll-debug-region-exists-p)
419 (defun ll-debug-toggle-comment-region-or-line ()
420 "Toggle the current line/region between uncommented and commented state."
422 (if (ll-debug-region-or-line-comment-start)
423 (ll-debug-uncomment-region-or-line)
424 (ll-debug-comment-region-or-line)))
427 ;; debug output statements ------------------------------------------------
428 (defun ll-debug-before-text-p ()
429 "Return t iff point is at bol or in leading whitespace."
431 (skip-chars-backward " \t" (point-at-bol))
434 (defun ll-debug-after-text-p ()
435 "Return t iff point is at eol or in trailing whitespace."
437 (skip-chars-forward " \t" (point-at-eol))
440 (defun ll-debug-open-fresh-line ()
441 "Make room for a debug output statement."
443 ((ll-debug-before-text-p)
445 ((ll-debug-after-text-p)
451 (indent-according-to-mode))
453 (defun ll-debug-register-mode (modes prefix postfix skel1 &rest skels)
454 "Register mode info in `ll-debug-statement-alist'.
455 MODES can be a single symbol denoting a mode or a list of mode
456 symbols. If it is a list, the following info is registered in every
457 listed mode. PREFIX is the prefix thing for debug statements, POSTFIX
458 is the postfix thing. SKEL1 and all following SKELS are the content
459 things. For more information about these, see the documentation of
460 `ll-debug-insert'. If an entry for a given mode already exists in
461 `ll-debug-statement-alist', it will be overwritten."
462 (unless (listp modes)
463 (setq modes (list modes)))
466 (setq ll-debug-statement-alist
467 (cons (cons mode (make-ll-debug-struct :prefix prefix
470 (assq-delete-all mode ll-debug-statement-alist)))))
472 (defun ll-debug-create-next-debug-string ()
473 "Create the next unique debug string."
476 (goto-char (point-min))
477 (while (re-search-forward
478 (concat (regexp-quote ll-debug-output-prefix)
481 (setq max-used (max max-used
482 (string-to-number (match-string 1))))))
484 ll-debug-output-prefix
486 (if (buffer-file-name)
487 (file-name-nondirectory (buffer-file-name))
490 (defun ll-debug-insert (arg)
491 "Insert a line of debug output at point according to mode.
492 Looks up the current mode in `ll-debug-statement-alist'. The prefix
493 thing of the coressponding ll-debug-struct gets inserted by
494 `ll-debug-insert'. The number of times C-u was pressed (prefix arg)
495 determines the entry from the content list of the ll-debug-struct that
496 gets inserted next. Finally the postfix thing from the ll-debug-struct
497 is inserted into the current buffer.
503 (setq arg (floor (/ (log (car arg))
505 (let ((mode-data (cdr (assoc major-mode ll-debug-statement-alist))))
508 (message "%s not supported by ll-debug-insert yet." major-mode))
509 ((>= arg (length (ll-debug-struct-content mode-data)))
510 (message "Only %d flavours of debug output defined for %s."
511 (length (ll-debug-struct-content mode-data))
514 (ll-debug-open-fresh-line)
515 (ll-debug-expand (ll-debug-struct-prefix mode-data))
516 (ll-debug-expand (elt (ll-debug-struct-content mode-data) arg))
517 (ll-debug-expand (ll-debug-struct-postfix mode-data))
518 (indent-according-to-mode)
520 (indent-according-to-mode)))))
522 (defun ll-debug-revert ()
523 "Deletes (with confirmation) lines containing the regexp 'DEBUG-[0-9]+-'.
524 Uses `query-replace-regexp' internally."
527 (goto-char (point-min))
528 (query-replace-regexp (concat "^.*"
529 (regexp-quote ll-debug-output-prefix)
534 ;; register modes ---------------------------------------------------------
535 (ll-debug-register-mode 'scheme-mode
536 "(begin " "(newline))"
538 (ll-debug-create-next-debug-string) "\")")
540 (ll-debug-create-next-debug-string) "\")"
542 "(display \" " str ":\")(display " str ")")))
544 (ll-debug-register-mode 'lisp-mode
546 '(nil "\"" (ll-debug-create-next-debug-string) "~%\"")
547 '(nil "\"" (ll-debug-create-next-debug-string)
550 '(progn (setq v1 (concat v1 " " str)) nil)
555 (ll-debug-register-mode '(emacs-lisp-mode lisp-interaction-mode)
557 '(nil "\"" (ll-debug-create-next-debug-string) "\"")
558 '(nil "\"" (ll-debug-create-next-debug-string)
561 '(progn (setq v1 (concat v1 " " str)) nil))
564 (ll-debug-register-mode '(perl-mode cperl-mode)
566 '(nil "\"" (ll-debug-create-next-debug-string) "\\n\"")
567 '(nil "\"" (ll-debug-create-next-debug-string)
571 '(nil "\"" (ll-debug-create-next-debug-string)
572 " \", Data::Dumper->Dump(["
576 (if (string= "$" (substring str 0 1))
577 (setq v1 (concat v1 " "
587 (substring str 1)))))
590 "], [qw/" v1 "/]), \"\\n\""))
592 (ll-debug-register-mode 'c++-mode
593 "std::cout << " " << std::endl;"
594 '(nil "\"" (ll-debug-create-next-debug-string) "\"")
595 '(nil "\"" (ll-debug-create-next-debug-string) "\""
597 " << \" " str ":\" << " str)))
599 (ll-debug-register-mode 'c-mode
601 '(nil "\"" (ll-debug-create-next-debug-string) "\\n\"")
602 '(nil "\"" (ll-debug-create-next-debug-string)
607 (setq v1 (concat v1 ", " str))
610 (read-string "Format: "))
613 (ll-debug-register-mode '(java-mode jde-mode)
614 "System.out.println(" ");"
615 '(nil "\"" (ll-debug-create-next-debug-string) "\"")
616 '(nil "\"" (ll-debug-create-next-debug-string) "\""
618 "+\" " str ":\"+" str)))
620 (ll-debug-register-mode 'ruby-mode
622 '(nil "\"" (ll-debug-create-next-debug-string) "\""))
624 (ll-debug-register-mode 'sh-mode
626 '(nil (ll-debug-create-next-debug-string)))
628 (ll-debug-register-mode '(octave-mode matlab-mode)
630 '(nil "'" (ll-debug-create-next-debug-string) "'"))
634 ;;; ll-debug.el ends here