Initial Commit
[packages] / xemacs-packages / xemacs-devel / pretty-print.el
1 ;;; pretty-print.el --- Emacs Lisp pretty printer and macro expander
2
3 ;; Copyright (C) 1992,1993 Guido Bosch <Guido.Bosch@loria.fr>
4
5 ;; Author: Guido Bosch
6 ;; Maintainer: None
7 ;; Keywords: lisp, internal
8
9 ;; This file is part of XEmacs.
10
11 ;; XEmacs is free software; you can redistribute it and/or modify it
12 ;; under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; XEmacs is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 ;; General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with XEmacs; see the file COPYING.  If not, write to the Free
23 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24 ;; 02111-1307, USA.
25
26 ;;; Synched up with:  Not in FSF.
27
28 ;;; Commentary:
29
30 ;; Please send bugs and comments to the author.
31 ;;
32 ;; <DISCLAIMER>
33 ;; This program is still under development.  Neither the author nor
34 ;; CRIN-INRIA accepts responsibility to anyone for the consequences of
35 ;; using it or for whether it serves any particular purpose or works
36 ;; at all.
37 ;; 
38 ;; The package has been developed under Lucid Emacs 19, but also runs
39 ;; on Emacs 18, if it is compiled with the version 19 byte compiler
40 ;; (function `compiled-function-p' lacking).
41 ;;
42 \f
43 ;; Installation and Usage
44 ;; ----------------------
45 ;;
46 ;; This package provides an Emacs Lisp sexpression pretty printer and
47 ;; macroexpander.  To install it, put the following line in your .emacs,
48 ;; default.el or site-init.el/site-run.el (for Lucid Emacs): 
49 ;; (require 'pp)
50 ;; 
51 ;; The package can also be made autoloadable, with the following entry 
52 ;; points: 
53 ;; (autoload 'pp-function "pp" nil t)
54 ;; (autoload 'pp-variable "pp" nil t)
55 ;; (autoload 'pp-plist     "pp" nil t)
56 ;; (autoload 'macroexpand-sexp "pp" nil t)
57 ;; (autoload 'macroexpand-all-sexp "pp" nil t)
58 ;; (autoload 'prettyexpand-sexp "pp" nil t)
59 ;; (autoload 'prettyexpand-all-sexp "pp" nil t)
60 ;;
61 ;;(define-key emacs-lisp-mode-map '(control meta m) 'macroexpand-sexp)
62 ;;(define-key emacs-lisp-mode-map '(control meta M) 'macroexpand-all-sexp)
63 ;;(define-key emacs-lisp-mode-map '(control symbol m) 'prettyexpand-sexp)
64 ;;(define-key emacs-lisp-mode-map '(control symbol M) 'prettyexpand-all-sexp)
65 ;;
66 ;;(define-key lisp-interaction-mode-map '(control meta m) 'macroexpand-sexp)
67 ;;(define-key lisp-interaction-mode-map '(control meta M) 'macroexpand-all-sexp)
68 ;;(define-key lisp-interaction-mode-map '(control symbol m) 'prettyexpand-sexp)
69 ;;(define-key lisp-interaction-mode-map '(control symbol M) 'prettyexpand-all-sexp)
70 ;;
71
72 ;; Pretty printing of the different cells of a symbol is done with the
73 ;; commands:
74 ;;
75 ;;              M-x pp-function
76 ;;              M-x pp-variable
77 ;;              M-x pp-plist
78 ;;
79 ;; They print a symbol's function definition, variable value and
80 ;; property list, respectively.  These commands pop up a separate
81 ;; window in which the pretty printed lisp object is displayed.
82 ;; Completion for function and variable symbols is provided. If a
83 ;; function is byte compiled, `pp-function' proposes to call the Emacs
84 ;; Lisp disassembler (this feature only works for Emacs 19, as it
85 ;; needs the `compiled-function-p' predicate).
86 ;;
87 ;; To use the macro expander, put the cursor at the beginning of the
88 ;; form to be expanded, then type
89 ;;
90 ;;              C-M-m           (macroexpand-sexp)
91 ;; or           C-M-Sh-M        (macroexpand-all-sexp)
92 ;; 
93 ;; Both commands will pop up a temporary window containing the
94 ;; macroexpanded code. The only difference is that the second command
95 ;; expands recursively all containing macro calls, while the first one
96 ;; does it only for the uppermost sexpression.  
97 ;;      With a prefix argument, the macro expansion isn't displayed in a
98 ;; separate buffer but replaces the original code in the current
99 ;; buffer. Be aware: Comments will be lost.
100 ;;      You can get back the original sexpression using the `undo'
101 ;;      command on `C-x u'.
102 ;;
103 ;; There is also a prettyfied version of the macroexpander:
104 ;;
105 ;;              C-Sym-m         (prettyexpand-sexp)
106 ;; or           C-Sym-M         (prettyexpand-all-sexp)
107 ;; 
108 ;; The only difference with the corresponding macroexpand commands is 
109 ;; that calls to macros specified in the variable
110 ;; `pp-shadow-expansion-list' are not expanded, in order to make the
111 ;; code look nicer. This is only useful for Lucid Emacs or code that
112 ;; uses Dave Gillespies cl package, as it inhibits expansion of the
113 ;; following macros: block, eval-when, defun*, defmacro*, function*,
114 ;; setf.
115 \f
116 ; Change History
117
118 ; $Log: pretty-print.el,v $
119 ; Revision 1.3  2000-10-06 09:35:05  youngs
120 ; Martin's Monster Mega typo patch
121 ;
122 ; Revision 1.2  1998/02/10 16:23:33  steveb
123 ; pretty-print fixes
124 ;
125 ; Revision 1.4  1993/03/25  14:09:52  bosch
126 ; Commands `prettyexpand-sexp' and `prettyexpand-all-sexp' and
127 ; corresponding key bindings added.  Commands pp-{function, variable}
128 ; rewritten. `pp-plist' added. Function `pp-internal-loop' (for Dave
129 ; Gillespies CL loop macro) added.
130 ;
131 ; Revision 1.3  1993/03/03  12:24:13  bosch
132 ; Macroexpander rewritten. Function `pp-macroexpand-all' added (snarfed
133 ; from Dave Gillespies cl-extra.el). Pretty printing for top level
134 ; defining forms added (`pp-internal-def'). Key bindings for
135 ; `emacs-lisp-mode-map' and `lisp-interaction-mode-map' added.  Built-in
136 ; variable `print-gensym' set for printinng uninterned symbols. Started
137 ; adding support for cl-dg (defun*, defmacro*, ...).  Minor bug fixes.
138 ;
139 ; Revision 1.2  1993/02/25  17:35:02  bosch
140 ; Comments about Emacs 18 compatibility added.
141 ;
142 ; Revision 1.1  1993/02/25  16:55:01  bosch
143 ; Initial revision
144 ;
145 ;
146 ;;; Code:
147 \f
148 ;; TO DO LIST
149 ;; ----------
150 ;; Provide full Emacs 18 compatibility.
151 \f
152 ;; Popper support
153 (defvar pp-buffer-name "*Pretty Print*")
154 (defvar pp-macroexpand-buffer-name "*Macro Expansion*")
155 (if (featurep 'popper)
156     (or (eq popper-pop-buffers 't)
157         (setq popper-pop-buffers 
158               (cons pp-buffer-name 
159                     (cons pp-macroexpand-buffer-name 
160                           popper-pop-buffers)))))
161
162 ;; User level functions
163 ;;;###autoload
164 (defun pp-function (symbol)
165   "Pretty print the function definition of SYMBOL in a separate buffer"
166   (interactive 
167    (list (pp-read-symbol 'fboundp "Pretty print function definition of: ")))
168   (if (compiled-function-p (symbol-function symbol))
169       (if (y-or-n-p 
170            (format "Function %s is byte compiled. Disassemble? " symbol))
171           (disassemble (symbol-function symbol))
172         (pp-symbol-cell symbol 'symbol-function))
173     (pp-symbol-cell symbol 'symbol-function)))
174
175 ;;;###autoload
176 (defun pp-variable (symbol)
177   "Pretty print the variable value of SYMBOL in a separate buffer"
178   (interactive
179    (list (pp-read-symbol 'boundp "Pretty print variable value of: ")))
180   (pp-symbol-cell symbol 'symbol-value))
181
182 ;;;###autoload
183 (defun pp-plist (symbol)
184   "Pretty print the property list of SYMBOL in a separate buffer"
185   (interactive
186    (list (pp-read-symbol 'symbol-plist "Pretty print property list of: ")))
187   (pp-symbol-cell symbol 'symbol-plist))
188
189 (defun pp-read-symbol (predicate prompt)
190   "Read a symbol for which  PREDICATE is true, promptiong with PROMPT."
191   (let (symbol)
192     (while (or (not symbol) (not (funcall predicate symbol)))
193       (setq symbol 
194             (intern-soft 
195              (completing-read
196               prompt
197               obarray
198               predicate
199               t
200               (and symbol (symbol-name symbol))))))
201     symbol))
202
203 (defun pp-symbol-cell (symbol accessor)  
204   "Pretty print the contents of the cell of SYMBOL that can be reached
205 with the function ACCESSOR."
206   (with-output-to-temp-buffer pp-buffer-name
207     (set-buffer pp-buffer-name)
208     (emacs-lisp-mode)
209     (erase-buffer)
210     (pp-internal 
211      (funcall accessor symbol) 
212      (format "%s's %s is:\n" symbol accessor))
213     (terpri)))
214
215 \f
216   
217 ;; Macro expansion (user level)
218
219 ;;;###autoload
220 (defun macroexpand-sexp (&optional replace)
221   "Macro expand the sexpression following point. Pretty print expansion in a
222 temporary buffer. With prefix argument, replace the original
223 sexpression by its expansion in the current buffer."
224   (interactive "P")
225   (pp-macroexpand-internal 'macroexpand replace t))
226
227 ;;;###autoload
228 (defun macroexpand-all-sexp (&optional replace)
229   "Macro expand recursively the sexpression following point. Pretty print
230 expansion in a temporary buffer. With prefix argument, replace the
231 original sexpression by its expansion in the current buffer."
232   (interactive "P")
233   (pp-macroexpand-internal 'pp-macroexpand-all replace t))
234
235 ;;;###autoload
236 (defun prettyexpand-sexp (&optional replace)
237   "Macro expand the sexpression following point. Pretty print expansion
238 in a temporary buffer. With prefix argument, replace the original
239 sexpression by its expansion in the current buffer.  
240         However, calls to macros specified in the variable
241 `pp-shadow-expansion-list' are not expanded, in order to make the code
242 look nicer."
243
244   (interactive "P")
245   (pp-macroexpand-internal 'macroexpand replace))
246
247 ;;;###autoload
248 (defun prettyexpand-all-sexp (&optional replace)
249   "Macro expand recursively the sexpression following point. Pretty print
250 expansion in a temporary buffer. With prefix argument, replace the
251 original sexpression by its expansion in the current buffer.
252         However, calls to macros specified in the variable
253 `pp-shadow-expansion-list' are not expanded, in order to make the code
254 look nicer."
255   (interactive "P")
256   (pp-macroexpand-internal 'pp-macroexpand-all replace))
257
258 ;; XEmacs: don't do this at load time.
259 ;;(define-key emacs-lisp-mode-map '(control meta m) 'macroexpand-sexp)
260 ;;(define-key emacs-lisp-mode-map '(control meta M) 'macroexpand-all-sexp)
261 ;;(define-key emacs-lisp-mode-map '(control symbol m) 'prettyexpand-sexp)
262 ;;(define-key emacs-lisp-mode-map '(control symbol M) 'prettyexpand-all-sexp)
263
264 ;;(define-key lisp-interaction-mode-map '(control meta m) 'macroexpand-sexp)
265 ;;(define-key lisp-interaction-mode-map '(control meta M) 'macroexpand-all-sexp)
266 ;;(define-key lisp-interaction-mode-map '(control symbol m) 'prettyexpand-sexp)
267 ;;(define-key lisp-interaction-mode-map '(control symbol M) 'prettyexpand-all-sexp)
268
269 \f
270 ;; Macro expansion (internals)
271
272 (defvar pp-shadow-expansion-list
273   (mapcar 'list '(block eval-when defun* defmacro* function* setf))
274   "The value of this variable is given as the optional environment
275 argument of the macroexpand functions. Forms specified in this list are
276 not expanded.")
277
278 (defun pp-macroexpand-internal 
279   (macroexpand-function replace &optional dont-shadow)
280   "Macro expand the sexp that starts at point, using
281 MACROEXPAND-FUNCTION.  If REPLACE is non-nil, replace the original
282 text by its expansion, otherwise pretty print the expansion in a
283 temporary buffer. With optional argument DONT-SHADOW non-nil, do not
284 use the `pp-shadow-expansion-list' to inhibit expansion of some
285 forms."
286
287   (interactive)
288   (let ((expansion
289          (funcall 
290           macroexpand-function
291           (let ((stab (syntax-table)))
292             (unwind-protect
293                 (save-excursion
294                   (set-syntax-table emacs-lisp-mode-syntax-table)
295                   ;; (forward-sexp 1)
296                   (read (current-buffer)))
297               (set-syntax-table stab)))
298           (if dont-shadow 
299               nil
300             pp-shadow-expansion-list))))
301     (save-excursion
302       (if replace 
303           (let ((start (point))
304                 (end (progn (forward-sexp 1) (point))))
305             (delete-region start end)
306             (pp-internal expansion))
307         (with-output-to-temp-buffer pp-macroexpand-buffer-name
308           (set-buffer pp-macroexpand-buffer-name)
309           (erase-buffer)
310           (emacs-lisp-mode)
311           (pp-internal expansion))))))
312 \f
313 ;; Internal pretty print functions
314
315 ;;;###autoload
316 (defun pp-internal (form &optional title)
317   "Pretty print FORM in in the current buffer.
318 Optional string TITLE is inserted before the pretty print."
319   (let (start)
320     (if title (princ title))
321     (setq start (point))
322     ;; print-escape-newlines must be t, otherwise we cannot use
323     ;; (current-column) to detect good line breaks
324     (let ((print-escape-newlines t)
325           (print-gensym t)
326           )
327       (prin1 form (current-buffer))
328       (goto-char start)
329       (pp-internal-sexp))))
330
331 (defun pp-internal-sexp ()
332   "Pretty print the following sexp. 
333 Point must be on or before the first character."
334
335   (skip-chars-forward " \n\t")
336   (let* ((char (following-char))
337          (ch-class (char-syntax char))
338          (start (point)))
339
340     (cond
341      ;; open paren
342      ((eq char ?\()
343       (down-list 1)
344       (if (memq  (char-syntax (following-char)) '(?_ ?w))
345           (let ((symbol (read (current-buffer))))
346             (cond ((and (symbolp symbol)
347                         (fboundp symbol))
348                    (goto-char start)
349                    (pp-internal-function symbol))
350                   ((memq symbol '(lambda macro))
351                    (pp-internal-lambda))
352                   (t
353                    (goto-char start)
354                    (pp-internal-list))))
355         (up-list -1)
356         (pp-internal-list)))
357      
358      ;;symbols & strings
359      ((memq  ch-class '(?_              ; symbol
360                         ?w              ; word
361                         ?\"             ; string
362                         ?\\             ; escape
363                         ?\'             ; quote (for uninterned symbols)
364                         )) (forward-sexp 1))
365         
366      ;; vector
367      ((eq char ?\[) (pp-internal-list))
368      
369      ;; error otherwise
370      (t (error "pp-internal-sexp: character class not treated yet: `%c'" 
371                ch-class)))))
372
373 (defun pp-internal-function (func)
374   "Pretty print a functuion call.
375 Point must be on the open paren. the function symbol may be passed as an 
376 optional argument."
377   (let ((start (point))
378         (too-large (>= (save-excursion
379                          (forward-sexp 1)
380                          (current-column))
381                        fill-column))
382         (indent-info (get func lisp-indent-function)))
383     (down-list 1)
384     ;; skip over function name
385     (forward-sexp 1)
386     (cond
387      ((memq func '(let let*)) (pp-internal-let))
388
389      ((eq func 'cond) (pp-internal-cond))
390
391      ((memq func '(if while with-output-to-temp-buffer catch block))
392       (pp-internal-sexp)
393       (pp-internal-body 't))
394
395      ((eq func 'quote) (pp-internal-quote))
396
397      ((memq func '(progn 
398                     prog1 prog2
399                     save-window-excursion 
400                     save-excursion 
401                     save-restriction))
402       (pp-internal-body 't))
403
404      ((memq func '(defun defmacro defsubst defun* defmacro*))
405       (pp-internal-def))
406      
407      ((eq func 'loop) (pp-internal-loop))
408
409      ('t (pp-internal-body too-large)))))
410
411 (defun pp-internal-def ()
412   (forward-sexp 1)                      ; skip name
413   (if (looking-at " nil")               ; replace nil by () 
414       (replace-match " ()")
415     (forward-sexp 1))
416   (if (looking-at " \"")
417       ;; comment string. Replace all escaped linefeeds by real ones
418       (let ((limit (save-excursion (forward-sexp 1) (point-marker))))
419         (newline-and-indent)
420         (while (re-search-forward "\\\\n" limit t)
421           (replace-match "\n" nil nil))
422         (goto-char limit)))
423   (pp-internal-body 't))
424
425 (defun pp-internal-list ()
426   "Pretty print a list  or a vector.
427 Point must be on the open paren."
428   (let ((too-large (>= (save-excursion
429                         (forward-sexp 1)
430                         (current-column))
431                       fill-column)))
432     (down-list 1)
433     (pp-internal-sexp)
434     (pp-internal-body too-large)))
435
436 (defun pp-internal-body (&optional force-indent)
437   "Prety print a body of sexp. Stop after reaching a `)'.  If argument
438 FORCE-INDENT is non-nil, break line after each sexpression of the
439 body."
440   (skip-chars-forward " \n\t")
441   (let (ch-class)
442     ;; while not closing paren
443     (while (/= (setq ch-class (char-syntax (following-char))) ?\)) 
444       (if  force-indent (newline-and-indent))
445       (pp-internal-sexp))
446     (up-list 1)))
447
448 (defun pp-internal-loop ()
449   "Prety print a loop body. Stop after reaching a `)'. 
450 Line breaks are done before the following keywords: "
451   (forward-sexp 1)
452   (skip-chars-forward " \n\t")
453   (let (ch-class)
454     ;; while not closing paren
455     (while (/= (setq ch-class (char-syntax (following-char))) ?\))
456       (if (not (looking-at "for\\|repeat\\|with\\|while\\|until\\|always\\|never\\|thereis\\|collect\\|append\\|nconc\\|sum\\|count\\|maximize\\|minimize\\|if\\|when\\|else\\|unless\\|do\\W\\|initially\\|finally\\|return\\|named"))
457           (pp-internal-sexp)
458         (newline-and-indent)
459         (forward-sexp 1))
460       (skip-chars-forward " \n\t"))
461     (up-list 1)))
462
463 (defun pp-internal-body-list ()
464   (let ((too-large (>= (save-excursion
465                         (forward-sexp 1)
466                         (current-column))
467                       fill-column))
468         ch-class)
469     (down-list 1)
470     (pp-internal-sexp)
471     (while (/= (setq ch-class (char-syntax (following-char))) ?\)) 
472       (if  too-large (newline-and-indent))
473       (pp-internal-sexp))
474     (up-list 1)))
475     
476 (defun pp-internal-lambda ()
477   (forward-sexp 1) ; arguments
478   (pp-internal-body 't))
479
480 (defun pp-internal-let ()
481   "Pretty print a let-like  form.
482 Cursor is behind function symbol."
483   (down-list 1)
484   (while (not (= (following-char) ?\)))
485     (if (= (following-char) ?\()
486         (pp-internal-body-list)
487       (forward-sexp 1))
488     (if (not (= (following-char) ?\)))
489         (newline-and-indent)))
490   (up-list 1)
491   (pp-internal-body 't))
492
493 (defun pp-internal-cond ()
494   "Pretty print a cond-like  form.
495 Cursor is behind function symbol."
496   (skip-chars-forward " \n\t")
497   (while (not (= (following-char) ?\)))
498     (pp-internal-body-list)
499     (if (not (= (following-char) ?\)))
500         (newline-and-indent)))
501   (up-list 1))
502
503       
504 (defun pp-internal-quote ()
505   "Pretty print a quoted list.
506 Cursor is behind the symbol quote."
507   (skip-chars-forward " \n\t")
508   (let ((end (point)))
509     (backward-sexp 1)
510     (delete-region (point) end)
511     (up-list -1)
512     (setq end (point))
513     (forward-sexp 1)
514     (delete-char -1)
515     (goto-char end)
516     (delete-char 1)
517     (insert "'")
518     (if (= (char-syntax (following-char)) ?\()
519         ;; don't print it as sexp, because it could be (let ... ) or
520         ;; (cond ... ) or whatever. 
521         (pp-internal-list)
522       (pp-internal-sexp))))
523
524 \f
525 ;; Stolen form Dave Gillespies cl-extra.el
526 (defun pp-macroexpand-all (form &optional env)
527   "Expand all macro calls through a Lisp FORM.
528 This also does some trivial optimizations to make the form prettier."
529   (setq form (macroexpand form env))
530   (cond 
531    ((not (consp form)) form)
532    ((memq (car form) '(let let*))
533     (if (null (nth 1 form))
534         (pp-macroexpand-all (cons 'progn (cdr (cdr form))) env)
535       (cons (car form) 
536             (cons (pp-macroexpand-lets (nth 1 form) env)
537                   (pp-macroexpand-body (cdr (cdr form)) env)))))
538    ((eq (car form) 'cond)
539     (cons (car form)
540           (mapcar (function (lambda (x) (pp-macroexpand-body x env)))
541                   (cdr form))))
542    ((eq (car form) 'condition-case)
543     (cons (car form)
544           (cons (nth 1 form)
545                 (cons (pp-macroexpand-all (nth 2 form) env)
546                       (pp-macroexpand-lets 
547                        (cdr (cdr (cdr form))) env)))))
548    ((memq (car form) '(quote function))
549     (if (eq (car-safe (nth 1 form)) 'lambda)
550         (list (car form) 
551               (cons 'lambda
552                     (cons (car (cdr (car (cdr form))))
553                           (pp-macroexpand-body 
554                            (cdr (cdr (car (cdr form)))) env))))
555       form))
556    ((memq (car form) '(defun defmacro))
557     (cons (car form)
558           (cons (nth 1 form)
559                 (pp-macroexpand-body (cdr (cdr form)) env))))
560    ((and (eq (car form) 'progn) (not (cdr (cdr form))))
561     (pp-macroexpand-all (nth 1 form) env))
562    (t 
563     (cons (car form) (pp-macroexpand-body (cdr form) env)))))
564
565 (defun pp-macroexpand-body (body &optional env)
566   (mapcar (function (lambda (x) (pp-macroexpand-all x env))) body))
567
568 (defun pp-macroexpand-lets (list &optional env)
569   (mapcar (function
570            (lambda (x)
571              (if (consp x) (cons (car x) (pp-macroexpand-body (cdr x) env))
572                x))) list))
573 \f
574 (run-hooks 'pp-load-hook)
575 (provide 'pp)
576
577 ;;; pretty-print.el ends here