Mercurial > hg > xemacs-beta
diff lisp/utils/pretty-print.el @ 12:bcdc7deadc19 r19-15b7
Import from CVS: tag r19-15b7
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:48:16 +0200 |
parents | |
children | ec9a17fef872 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/utils/pretty-print.el Mon Aug 13 08:48:16 2007 +0200 @@ -0,0 +1,572 @@ +;; -*- Syntax: Emacs-Lisp; Mode: emacs-lisp -*- +;; +;; Emacs Lisp pretty printer and macro expander +;; +;; Copyright (C) 1992,1993 Guido Bosch <Guido.Bosch@loria.fr> + +;; This file is written in GNU Emacs Lisp, but is not part of GNU Emacs. + +;; This file is part of XEmacs. + +;; XEmacs 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 2, or (at your option) +;; any later version. + +;; XEmacs 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 XEmacs; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +;; 02111-1307, USA. + +;; Synched up with: Not in FSF. + +;; Please send bugs and comments to the author. +;; +;; <DISCLAIMER> +;; This program is still under development. Neither the author nor +;; CRIN-INRIA accepts responsibility to anyone for the consequences of +;; using it or for whether it serves any particular purpose or works +;; at all. +;; +;; The package has been developed under Lucid Emacs 19, but also runs +;; on Emacs 18, if it is compiled with the version 19 byte compiler +;; (function `compiled-function-p' lacking). +;; + +;; Installation and Usage +;; ---------------------- +;; +;; This package provides an Emacs Lisp sexpression pretty printer and +;; macroexpander. To install it, put the following line in your .emacs, +;; default.el or site-init.el/site-run.el (for Lucid Emacs): +;; (require 'pp) +;; +;; The package can also be made autoloadable, with the following entry +;; points: +;; (autoload 'pp-function "pp" nil t) +;; (autoload 'pp-variable "pp" nil t) +;; (autoload 'pp-plist "pp" nil t) +;; (autoload 'macroexpand-sexp "pp" nil t) +;; (autoload 'macroexpand-all-sexp "pp" nil t) +;; (autoload 'prettyexpand-sexp "pp" nil t) +;; (autoload 'prettyexpand-all-sexp "pp" nil t) +;; +;;(define-key emacs-lisp-mode-map '(control meta m) 'macroexpand-sexp) +;;(define-key emacs-lisp-mode-map '(control meta M) 'macroexpand-all-sexp) +;;(define-key emacs-lisp-mode-map '(control symbol m) 'prettyexpand-sexp) +;;(define-key emacs-lisp-mode-map '(control symbol M) 'prettyexpand-all-sexp) +;; +;;(define-key lisp-interaction-mode-map '(control meta m) 'macroexpand-sexp) +;;(define-key lisp-interaction-mode-map '(control meta M) 'macroexpand-all-sexp) +;;(define-key lisp-interaction-mode-map '(control symbol m) 'prettyexpand-sexp) +;;(define-key lisp-interaction-mode-map '(control symbol M) 'prettyexpand-all-sexp) +;; + +;; Pretty printing of the different cells of a symbol is done with the +;; commands: +;; +;; M-x pp-function +;; M-x pp-variable +;; M-x pp-plist +;; +;; They print a symbol's function definition, variable value and +;; property list, respectively. These commands pop up a separate +;; window in which the pretty printed lisp object is displayed. +;; Completion for function and variable symbols is provided. If a +;; function is byte compiled, `pp-function' proposes to call the Emacs +;; Lisp disassembler (this feature only works for Emacs 19, as it +;; needs the `compiled-function-p' predicate). +;; +;; To use the macro expander, put the cursor at the beginning of the +;; form to be expanded, then type +;; +;; C-M-m (macroexpand-sexp) +;; or C-M-Sh-M (macroexpand-all-sexp) +;; +;; Both commands will pop up a temporary window containing the +;; macroexpanded code. The only difference is that the second command +;; expands recursively all containing macro calls, while the first one +;; does it only for the uppermost sexpression. +;; With a prefix argument, the macro expansion isn't displayed in a +;; separate buffer but replaces the original code in the current +;; buffer. Be aware: Comments will be lost. +;; You can get back the original sexpression using the `undo' +;; command on `C-x u'. +;; +;; There is also a prettyfied version of the macroexpander: +;; +;; C-Sym-m (prettyexpand-sexp) +;; or C-Sym-M (prettyexpand-all-sexp) +;; +;; The only difference with the corresponding macroexpand commands is +;; that calls to macros specified in the variable +;; `pp-shadow-expansion-list' are not expanded, in order to make the +;; code look nicer. This is only useful for Lucid Emacs or code that +;; uses Dave Gillespies cl package, as it inhibits expansion of the +;; following macros: block, eval-when, defun*, defmacro*, function*, +;; setf. + +; Change History +; +; $Log: pretty-print.el,v $ +; Revision 1.2 1997/01/04 21:20:13 steve +; beta6 to beta7 patches +; +; Revision 1.1 1997/01/01 21:45:30 steve +; *** empty log message *** +; +; Revision 1.4 1993/03/25 14:09:52 bosch +; Commands `prettyexpand-sexp' and `prettyexpand-all-sexp' and +; corresponding key bindings added. Commands pp-{function, variable} +; rewritten. `pp-plist' added. Function `pp-internal-loop' (for Dave +; Gillespies CL loop macro) added. +; +; Revision 1.3 1993/03/03 12:24:13 bosch +; Macroexpander rewritten. Function `pp-macroexpand-all' added (snarfed +; from Dave Gillespies cl-extra.el). Pretty printing for top level +; defining forms added (`pp-internal-def'). Key bindings for +; `emacs-lisp-mode-map' and `lisp-interaction-mode-map' added. Built-in +; variable `print-gensym' set for printinng uninterned symbols. Started +; adding support for cl-dg (defun*, defmacro*, ...). Minor bug fixes. +; +; Revision 1.2 1993/02/25 17:35:02 bosch +; Comments about Emacs 18 compatibility added. +; +; Revision 1.1 1993/02/25 16:55:01 bosch +; Initial revision +; +; + + +;; TO DO LIST +;; ---------- +;; Provide full Emacs 18 compatibility. + +;; Popper support +(defvar pp-buffer-name "*Pretty Print*") +(defvar pp-macroexpand-buffer-name "*Macro Expansion*") +(if (featurep 'popper) + (or (eq popper-pop-buffers 't) + (setq popper-pop-buffers + (cons pp-buffer-name + (cons pp-macroexpand-buffer-name + popper-pop-buffers))))) + +;; User level functions +;;;###autoload +(defun pp-function (symbol) + "Pretty print the function definition of SYMBOL in a seperate buffer" + (interactive + (list (pp-read-symbol 'fboundp "Pretty print function definition of: "))) + (if (compiled-function-p (symbol-function symbol)) + (if (y-or-n-p + (format "Function %s is byte compiled. Disassemble? " symbol)) + (disassemble (symbol-function symbol)) + (pp-symbol-cell symbol 'symbol-function)) + (pp-symbol-cell symbol 'symbol-function))) + +;;;###autoload +(defun pp-variable (symbol) + "Pretty print the variable value of SYMBOL in a seperate buffer" + (interactive + (list (pp-read-symbol 'boundp "Pretty print variable value of: "))) + (pp-symbol-cell symbol 'symbol-value)) + +;;;###autoload +(defun pp-plist (symbol) + "Pretty print the property list of SYMBOL in a seperate buffer" + (interactive + (list (pp-read-symbol 'symbol-plist "Pretty print property list of: "))) + (pp-symbol-cell symbol 'symbol-plist)) + +(defun pp-read-symbol (predicate prompt) + "Read a symbol for which PREDICATE is true, promptiong with PROMPT." + (let (symbol) + (while (or (not symbol) (not (funcall predicate symbol))) + (setq symbol + (intern-soft + (completing-read + prompt + obarray + predicate + t + (and symbol (symbol-name symbol)))))) + symbol)) + +(defun pp-symbol-cell (symbol accessor) + "Pretty print the contents of the cell of SYMBOL that can be reached +with the function ACCESSOR." + (with-output-to-temp-buffer pp-buffer-name + (set-buffer pp-buffer-name) + (emacs-lisp-mode) + (erase-buffer) + (pp-internal + (funcall accessor symbol) + (format "%s's %s is:\n" symbol accessor)) + (terpri))) + + + +;; Macro expansion (user level) + +;;;###autoload +(defun macroexpand-sexp (&optional replace) + "Macro expand the sexpression following point. Pretty print expansion in a +temporary buffer. With prefix argument, replace the original +sexpression by its expansion in the current buffer." + (interactive "P") + (pp-macroexpand-internal 'macroexpand replace t)) + +;;;###autoload +(defun macroexpand-all-sexp (&optional replace) + "Macro expand recursively the sexpression following point. Pretty print +expansion in a temporary buffer. With prefix argument, replace the +original sexpression by its expansion in the current buffer." + (interactive "P") + (pp-macroexpand-internal 'pp-macroexpand-all replace t)) + +;;;###autoload +(defun prettyexpand-sexp (&optional replace) + "Macro expand the sexpression following point. Pretty print expansion +in a temporary buffer. With prefix argument, replace the original +sexpression by its expansion in the current buffer. + However, calls to macros specified in the variable +`pp-shadow-expansion-list' are not expanded, in order to make the code +look nicer." + + (interactive "P") + (pp-macroexpand-internal 'macroexpand replace)) + +;;;###autoload +(defun prettyexpand-all-sexp (&optional replace) + "Macro expand recursively the sexpression following point. Pretty print +expansion in a temporary buffer. With prefix argument, replace the +original sexpression by its expansion in the current buffer. + However, calls to macros specified in the variable +`pp-shadow-expansion-list' are not expanded, in order to make the code +look nicer." + (interactive "P") + (pp-macroexpand-internal 'pp-macroexpand-all replace)) + +(define-key emacs-lisp-mode-map '(control meta m) 'macroexpand-sexp) +(define-key emacs-lisp-mode-map '(control meta M) 'macroexpand-all-sexp) +(define-key emacs-lisp-mode-map '(control symbol m) 'prettyexpand-sexp) +(define-key emacs-lisp-mode-map '(control symbol M) 'prettyexpand-all-sexp) + +(define-key lisp-interaction-mode-map '(control meta m) 'macroexpand-sexp) +(define-key lisp-interaction-mode-map '(control meta M) 'macroexpand-all-sexp) +(define-key lisp-interaction-mode-map '(control symbol m) 'prettyexpand-sexp) +(define-key lisp-interaction-mode-map '(control symbol M) 'prettyexpand-all-sexp) + + +;; Macro expansion (internals) + +(defvar pp-shadow-expansion-list + (mapcar 'list '(block eval-when defun* defmacro* function* setf)) + "The value of this variable is given as the optional environment +argument of the macroexpand functions. Forms specified in this list are +not expanded.") + +(defun pp-macroexpand-internal + (macroexpand-function replace &optional dont-shadow) + "Macro expand the sexp that starts at point, using +MACROEXPAND-FUNCTION. If REPLACE is non-nil, replace the original +text by its expansion, otherwise pretty print the expansion in a +temporary buffer. With optional argument DONT-SHADOW non-nil, do not +use the `pp-shadow-expansion-list' to inhibit expansion of some +forms." + + (interactive) + (let ((expansion + (funcall + macroexpand-function + (let ((stab (syntax-table))) + (unwind-protect + (save-excursion + (set-syntax-table emacs-lisp-mode-syntax-table) + ;; (forward-sexp 1) + (read (current-buffer))) + (set-syntax-table stab))) + (if dont-shadow + nil + pp-shadow-expansion-list)))) + (save-excursion + (if replace + (let ((start (point)) + (end (progn (forward-sexp 1) (point)))) + (delete-region start end) + (pp-internal expansion)) + (with-output-to-temp-buffer pp-macroexpand-buffer-name + (set-buffer pp-macroexpand-buffer-name) + (erase-buffer) + (emacs-lisp-mode) + (pp-internal expansion)))))) + +;; Internal pretty print functions + +(defun pp-internal (form &optional title) + "Pretty print FORM in in the current buffer. +Optional string TITEL is inserted before the pretty print." + (let (start) + (if title (princ title)) + (setq start (point)) + ;; print-escape-newlines must be t, otherwise we cannot use + ;; (current-column) to detect good line breaks + (let ((print-escape-newlines t) + (print-gensym t) + ) + (prin1 form (current-buffer)) + (goto-char start) + (pp-internal-sexp)))) + +(defun pp-internal-sexp () + "Pretty print the following sexp. +Point must be on or before the first character." + + (skip-chars-forward " \n\t") + (let* ((char (following-char)) + (ch-class (char-syntax char)) + (start (point))) + + (cond + ;; open paren + ((eq char ?\() + (down-list 1) + (if (memq (char-syntax (following-char)) '(?_ ?w)) + (let ((symbol (read (current-buffer)))) + (cond ((fboundp symbol) + (goto-char start) + (pp-internal-function symbol)) + ((memq symbol '(lambda macro)) + (pp-internal-lambda)) + (t + (goto-char start) + (pp-internal-list)))) + (up-list -1) + (pp-internal-list))) + + ;;symbols & strings + ((memq ch-class '(?_ ; symbol + ?w ; word + ?\" ; string + ?\\ ; escape + ?\' ; quote (for uninterned symbols) + )) (forward-sexp 1)) + + ;; vector + ((eq char ?\[) (pp-internal-list)) + + ;; error otherwise + (t (error "pp-internal-sexp: character class not treated yet: `%c'" + ch-class))))) + +(defun pp-internal-function (func) + "Pretty print a functuion call. +Point must be on the open paren. the function symbol may be passed as an +optional argument." + (let ((start (point)) + (too-large (>= (save-excursion + (forward-sexp 1) + (current-column)) + fill-column)) + (indent-info (get func lisp-indent-function))) + (down-list 1) + ;; skip over function name + (forward-sexp 1) + (cond + ((memq func '(let let*)) (pp-internal-let)) + + ((eq func 'cond) (pp-internal-cond)) + + ((memq func '(if while with-output-to-temp-buffer catch block)) + (pp-internal-sexp) + (pp-internal-body 't)) + + ((eq func 'quote) (pp-internal-quote)) + + ((memq func '(progn + prog1 prog2 + save-window-excursion + save-excursion + save-restriction)) + (pp-internal-body 't)) + + ((memq func '(defun defmacro defsubst defun* defmacro*)) + (pp-internal-def)) + + ((eq func 'loop) (pp-internal-loop)) + + ('t (pp-internal-body too-large))))) + +(defun pp-internal-def () + (forward-sexp 1) ; skip name + (if (looking-at " nil") ; replace nil by () + (replace-match " ()") + (forward-sexp 1)) + (if (looking-at " \"") + ;; comment string. Replace all escaped linefeeds by real ones + (let ((limit (save-excursion (forward-sexp 1) (point-marker)))) + (newline-and-indent) + (while (re-search-forward "\\\\n" limit t) + (replace-match "\n" nil nil)) + (goto-char limit))) + (pp-internal-body 't)) + +(defun pp-internal-list () + "Pretty print a list or a vector. +Point must be on the open paren." + (let ((too-large (>= (save-excursion + (forward-sexp 1) + (current-column)) + fill-column))) + (down-list 1) + (pp-internal-sexp) + (pp-internal-body too-large))) + +(defun pp-internal-body (&optional force-indent) + "Prety print a body of sexp. Stop after reaching a `)'. If argument +FORCE-INDENT is non-nil, break line after each sexpression of the +body." + (skip-chars-forward " \n\t") + (let (ch-class) + ;; while not closing paren + (while (/= (setq ch-class (char-syntax (following-char))) ?\)) + (if force-indent (newline-and-indent)) + (pp-internal-sexp)) + (up-list 1))) + +(defun pp-internal-loop () + "Prety print a loop body. Stop after reaching a `)'. +Line breaks are done before the following keywords: " + (forward-sexp 1) + (skip-chars-forward " \n\t") + (let (ch-class) + ;; while not closing paren + (while (/= (setq ch-class (char-syntax (following-char))) ?\)) + (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")) + (pp-internal-sexp) + (newline-and-indent) + (forward-sexp 1)) + (skip-chars-forward " \n\t")) + (up-list 1))) + +(defun pp-internal-body-list () + (let ((too-large (>= (save-excursion + (forward-sexp 1) + (current-column)) + fill-column)) + ch-class) + (down-list 1) + (pp-internal-sexp) + (while (/= (setq ch-class (char-syntax (following-char))) ?\)) + (if too-large (newline-and-indent)) + (pp-internal-sexp)) + (up-list 1))) + +(defun pp-internal-lambda () + (forward-sexp 1) ; arguments + (pp-internal-body 't)) + +(defun pp-internal-let () + "Pretty print a let-like form. +Cursor is behind funtion symbol." + (down-list 1) + (while (not (= (following-char) ?\))) + (if (= (following-char) ?\() + (pp-internal-body-list) + (forward-sexp 1)) + (if (not (= (following-char) ?\))) + (newline-and-indent))) + (up-list 1) + (pp-internal-body 't)) + +(defun pp-internal-cond () + "Pretty print a cond-like form. +Cursor is behind funtion symbol." + (skip-chars-forward " \n\t") + (while (not (= (following-char) ?\))) + (pp-internal-body-list) + (if (not (= (following-char) ?\))) + (newline-and-indent))) + (up-list 1)) + + +(defun pp-internal-quote () + "Pretty print a quoted list. +Cursor is behind the symbol quote." + (skip-chars-forward " \n\t") + (let ((end (point))) + (backward-sexp 1) + (delete-region (point) end) + (up-list -1) + (setq end (point)) + (forward-sexp 1) + (delete-char -1) + (goto-char end) + (delete-char 1) + (insert "'") + (if (= (char-syntax (following-char)) ?\() + ;; don't print it as sexp, because it could be (let ... ) or + ;; (cond ... ) or whatever. + (pp-internal-list) + (pp-internal-sexp)))) + + +;; Stolen form Dave Gillespies cl-extra.el +(defun pp-macroexpand-all (form &optional env) + "Expand all macro calls through a Lisp FORM. +This also does some trivial optimizations to make the form prettier." + (setq form (macroexpand form env)) + (cond + ((not (consp form)) form) + ((memq (car form) '(let let*)) + (if (null (nth 1 form)) + (pp-macroexpand-all (cons 'progn (cdr (cdr form))) env) + (cons (car form) + (cons (pp-macroexpand-lets (nth 1 form) env) + (pp-macroexpand-body (cdr (cdr form)) env))))) + ((eq (car form) 'cond) + (cons (car form) + (mapcar (function (lambda (x) (pp-macroexpand-body x env))) + (cdr form)))) + ((eq (car form) 'condition-case) + (cons (car form) + (cons (nth 1 form) + (cons (pp-macroexpand-all (nth 2 form) env) + (pp-macroexpand-lets + (cdr (cdr (cdr form))) env))))) + ((memq (car form) '(quote function)) + (if (eq (car-safe (nth 1 form)) 'lambda) + (list (car form) + (cons 'lambda + (cons (car (cdr (car (cdr form)))) + (pp-macroexpand-body + (cdr (cdr (car (cdr form)))) env)))) + form)) + ((memq (car form) '(defun defmacro)) + (cons (car form) + (cons (nth 1 form) + (pp-macroexpand-body (cdr (cdr form)) env)))) + ((and (eq (car form) 'progn) (not (cdr (cdr form)))) + (pp-macroexpand-all (nth 1 form) env)) + (t + (cons (car form) (pp-macroexpand-body (cdr form) env))))) + +(defun pp-macroexpand-body (body &optional env) + (mapcar (function (lambda (x) (pp-macroexpand-all x env))) body)) + +(defun pp-macroexpand-lets (list &optional env) + (mapcar (function + (lambda (x) + (if (consp x) (cons (car x) (pp-macroexpand-body (cdr x) env)) + x))) list)) + +(run-hooks 'pp-load-hook) +(provide 'pp) + +;; end pp.el