all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
blob 3809325c4b1d4f47d9fece0541a7f3f376bc70df 11761 bytes (raw)
name: lisp/emacs-lisp/pp.el 	 # note: path name is non-authoritative(*)

  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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
 
;;; pp.el --- pretty printer for Emacs Lisp  -*- lexical-binding: t -*-

;; Copyright (C) 1989, 1993, 2001-2017 Free Software Foundation, Inc.

;; Author: Randal Schwartz <merlyn@stonehenge.com>
;; Keywords: lisp

;; This file is part of GNU Emacs.

;; GNU Emacs 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.

;; GNU Emacs 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 GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;;; Code:

(eval-when-compile (require 'cl-lib))
(require 'ring)

(defvar font-lock-verbose)

(defgroup pp nil
  "Pretty printer for Emacs Lisp."
  :prefix "pp-"
  :group 'lisp)

(defcustom pp-escape-newlines t
  "Value of `print-escape-newlines' used by pp-* functions."
  :type 'boolean
  :group 'pp)

;;;###autoload
(defun pp-to-string (object)
  "Return a string containing the pretty-printed representation of OBJECT.
OBJECT can be any Lisp object.  Quoting characters are used as needed
to make output that `read' can handle, whenever this is possible."
  (with-temp-buffer
    (lisp-mode-variables nil)
    (set-syntax-table emacs-lisp-mode-syntax-table)
    (let ((print-escape-newlines pp-escape-newlines)
          (print-quoted t))
      (prin1 object (current-buffer)))
    (pp-buffer)
    (buffer-string)))

;;;###autoload
(defun pp-buffer ()
  "Prettify the current buffer with printed representation of a Lisp object."
  (goto-char (point-min))
  (while (not (eobp))
    ;; (message "%06d" (- (point-max) (point)))
    (cond
     ((ignore-errors (down-list 1) t)
      (save-excursion
        (backward-char 1)
        (skip-chars-backward "'`#^")
        (when (and (not (bobp)) (memq (char-before) '(?\s ?\t ?\n)))
          (delete-region
           (point)
           (progn (skip-chars-backward " \t\n") (point)))
          (insert "\n"))))
     ((ignore-errors (up-list 1) t)
      (skip-syntax-forward ")")
      (delete-region
       (point)
       (progn (skip-chars-forward " \t\n") (point)))
      (insert ?\n))
     (t (goto-char (point-max)))))
  (goto-char (point-min))
  (indent-sexp))

;;;###autoload
(defun pp (object &optional stream)
  "Output the pretty-printed representation of OBJECT, any Lisp object.
Quoting characters are printed as needed to make output that `read'
can handle, whenever this is possible.
Output stream is STREAM, or value of `standard-output' (which see)."
  (princ (pp-to-string object) (or stream standard-output)))

(defun pp-display-expression (expression out-buffer-name)
  "Prettify and display EXPRESSION in an appropriate way, depending on length.
If a temporary buffer is needed for representation, it will be named
after OUT-BUFFER-NAME."
  (let* ((old-show-function temp-buffer-show-function)
	 ;; Use this function to display the buffer.
	 ;; This function either decides not to display it at all
	 ;; or displays it in the usual way.
	 (temp-buffer-show-function
	  (function
	   (lambda (buf)
	     (with-current-buffer buf
	       (goto-char (point-min))
	       (end-of-line 1)
	       (if (or (< (1+ (point)) (point-max))
		       (>= (- (point) (point-min)) (frame-width)))
		   (let ((temp-buffer-show-function old-show-function)
			 (old-selected (selected-window))
			 (window (display-buffer buf)))
		     (goto-char (point-min)) ; expected by some hooks ...
		     (make-frame-visible (window-frame window))
		     (unwind-protect
			 (progn
			   (select-window window)
			   (run-hooks 'temp-buffer-show-hook))
		       (when (window-live-p old-selected)
			 (select-window old-selected))
		       (message "See buffer %s." out-buffer-name)))
		 (message "%s" (buffer-substring (point-min) (point)))
		 ))))))
    (with-output-to-temp-buffer out-buffer-name
      (pp expression)
      (with-current-buffer standard-output
	(emacs-lisp-mode)
	(setq buffer-read-only nil)
	(set (make-local-variable 'font-lock-verbose) nil)))))


(cl-defstruct (pp-state (:constructor
                         make-pp-state
                         (stream
                          &aux
                          (right-margin fill-column)
                          (left-margin 0)
                          (indent '(0))
                          (scan-depth 0)
                          (print-depth 0)
                          (print-width 0)
                          (scan-width 0)
                          (block-mode (list nil))
                          (fifo (make-ring 30)))))
  stream
  right-margin ; how far we may go.
  left-margin  ; how far printer has gone
  print-width ; total width of tokens printed so far.
  indent ; left-margin, stack per depth.
  scan-width ; total width of tokens scanned so far.
  scan-depth
  print-depth
  block-widths
  block-mode ; `:vertical', `:horizontal', nil (undecided); stack per depth.
  fifo
  )

(defun pp--print (state)
  (cl-symbol-macrolet ((stream (pp-state-stream state))
                       (depth (pp-state-print-depth state))
                       (scan-depth (pp-state-scan-depth state))
                       (fifo (pp-state-fifo state))
                       (left-margin (pp-state-left-margin state))
                       (width (pp-state-print-width state))
                       (indent (pp-state-indent state))
                       (right-margin (pp-state-right-margin state))
                       (block-mode (pp-state-block-mode state)))
    (catch 'rescan
      (while (not (ring-empty-p fifo))
        (pcase (ring-remove fifo)
          ((and `(,len . :open-block) token)
           (if (<= len 0)
               ;; Not ready to print this yet!
               (progn (ring-insert-at-beginning fifo token)
                      (throw 'rescan nil))
             (cl-incf depth)
             (push left-margin indent)
             (push (if (> (+ left-margin len) right-margin)
                       :vertical :horizontal)
                   block-mode)))
          (:close-block (cl-decf depth) (pop indent) (pop block-mode))
          (:blank
           (pcase (car block-mode)
            (:vertical
             (terpri stream)
             (princ (make-string (car indent) ?\s) stream)
             (setf left-margin (car indent)))
            ((or :horizontal 'nil)
             (write-char ?\s stream)
             (cl-incf left-margin))
            (_ (error "oops")))
           (cl-incf width))
          (:eof nil)
          ((and (pred characterp) char)
           (write-char char stream)
           (cl-incf left-margin (char-width char))
           (cl-incf width (char-width char)))
          (string
           (princ string stream)
           (cl-incf left-margin (string-width string))
           (cl-incf width (string-width string))))))))

(defun pp--scan (token state)
  (cl-symbol-macrolet ((stream (pp-state-stream state))
                       (depth (pp-state-scan-depth state))
                       (print-depth (pp-state-print-depth state))
                       (fifo (pp-state-fifo state))
                       (width (pp-state-scan-width state))
                       (right-margin (pp-state-right-margin state))
                       (block-widths (pp-state-block-widths state)))
    (cl-flet ((scanlen (len) (cl-incf width len)))
      (cl-assert (> (ring-size fifo) (ring-length fifo)))
      (ring-insert fifo token)
      (pcase token
        (:open-block
         (cl-incf depth)
         (let ((block-token (cons (- width) (ring-remove fifo 0))))
           (push block-token block-widths)
           (ring-insert fifo block-token)))
        (:close-block
         (cl-incf (caar block-widths) width)
         (when (> (caar block-widths) right-margin)
           (pp--print state))
         (cl-decf depth)
         (pop block-widths))
        (:blank (scanlen 1))
        (:eof (pp--print state))
        ((pred characterp) (scanlen (char-width token)))
        (_ (scanlen (string-width token)))))
    (when block-widths
      (when (> (+ (caar block-widths) width) right-margin)
        (dolist (block-width block-widths)
          (setf (car block-width) (+ right-margin 1))))
      (when (> (caar block-widths) right-margin)
        (pp--print state)))))

(defvar cl-print-readably) ; cl-print.el

(defun pp-prin1 (object &optional stream)
  (let ((cl-print-readably nil)
        (stream (make-pp-state (or stream standard-output))))
    (pp--scan :open-block stream)
    (prog1 (cl-prin1 object (cons :pprint stream))
      (pp--scan :close-block stream)
      (pp--scan :eof stream))))

;; fallback to standard `cl-print-object'.
(cl-defmethod cl-print-object (object (stream (head :pprint)))
  (pp--scan (cl-prin1-to-string object) (cdr stream))
  object)

(cl-defmethod cl-print-object ((list cons) (stream (head :pprint)))
  (let ((state (cdr stream)))
    (pcase list
      (`(,head . ,tail)
       (pp--scan "(" state)
       (pp--scan :open-block state)
       (cl-print-object head stream)
       (while (consp tail)
         (pp--scan :blank state)
         (cl-print-object (pop tail) stream))
       (when tail
         (pp--scan :blank state)
         (pp--scan ?\. state)
         (pp--scan :blank state)
         (cl-print-object tail stream))
       (pp--scan :close-block state)
       (pp--scan ")" state))))
  list)

;;;###autoload
(defun pp-eval-expression (expression)
  "Evaluate EXPRESSION and pretty-print its value.
Also add the value to the front of the list in the variable `values'."
  (interactive
   (list (read--expression "Eval: ")))
  (message "Evaluating...")
  (push (eval expression lexical-binding) values)
  (pp-display-expression (car values) "*Pp Eval Output*"))

;;;###autoload
(defun pp-macroexpand-expression (expression)
  "Macroexpand EXPRESSION and pretty-print its value."
  (interactive
   (list (read--expression "Macroexpand: ")))
  (pp-display-expression (macroexpand-1 expression) "*Pp Macroexpand Output*"))

(defun pp-last-sexp ()
  "Read sexp before point.  Ignores leading comment characters."
  (with-syntax-table emacs-lisp-mode-syntax-table
    (let ((pt (point)))
      (save-excursion
        (forward-sexp -1)
        (read
         ;; If first line is commented, ignore all leading comments:
         (if (save-excursion (beginning-of-line) (looking-at-p "[ \t]*;"))
             (let ((exp (buffer-substring (point) pt))
                   (start nil))
               (while (string-match "\n[ \t]*;+" exp start)
                 (setq start (1+ (match-beginning 0))
                       exp (concat (substring exp 0 start)
                                   (substring exp (match-end 0)))))
               exp)
           (current-buffer)))))))

;;;###autoload
(defun pp-eval-last-sexp (arg)
  "Run `pp-eval-expression' on sexp before point.
With argument, pretty-print output into current buffer.
Ignores leading comment characters."
  (interactive "P")
  (if arg
      (insert (pp-to-string (eval (pp-last-sexp) lexical-binding)))
    (pp-eval-expression (pp-last-sexp))))

;;;###autoload
(defun pp-macroexpand-last-sexp (arg)
  "Run `pp-macroexpand-expression' on sexp before point.
With argument, pretty-print output into current buffer.
Ignores leading comment characters."
  (interactive "P")
  (if arg
      (insert (pp-to-string (macroexpand-1 (pp-last-sexp))))
    (pp-macroexpand-expression (pp-last-sexp))))

(provide 'pp)				; so (require 'pp) works

;;; pp.el ends here

debug log:

solving 3809325c4b ...
found 3809325c4b in https://yhetil.org/emacs/87mvcs8j7w.fsf@users.sourceforge.net/
found 7ef46a48bd in https://git.savannah.gnu.org/cgit/emacs.git
preparing index
index prepared:
100644 7ef46a48bde803173c6b8dcfa0ee7a674bbb4d4a	lisp/emacs-lisp/pp.el

applying [1/1] https://yhetil.org/emacs/87mvcs8j7w.fsf@users.sourceforge.net/
diff --git a/lisp/emacs-lisp/pp.el b/lisp/emacs-lisp/pp.el
index 7ef46a48bd..3809325c4b 100644

Checking patch lisp/emacs-lisp/pp.el...
Applied patch lisp/emacs-lisp/pp.el cleanly.

index at:
100644 3809325c4b1d4f47d9fece0541a7f3f376bc70df	lisp/emacs-lisp/pp.el

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.