aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGemini Lasswell2018-06-15 10:23:58 -0700
committerGemini Lasswell2018-08-03 08:53:01 -0700
commiteba16e5e5829c244d313101a769d4988946387d9 (patch)
tree33b098f6324ce3f5feefa5213403789b29527943
parente65ec81fc3e556719fae8d8b4b42f571c7e9f4fc (diff)
downloademacs-eba16e5e5829c244d313101a769d4988946387d9.tar.gz
emacs-eba16e5e5829c244d313101a769d4988946387d9.zip
Support ellipsis expansion in cl-print
* lisp/emacs-lisp/cl-print.el (cl-print-object-contents): New generic method. (cl-print-object-contents) <cons, vector,cl-structure-object>: New methods. (cl-print-object) <cons>: Use cl-print-insert-ellipsis. (cl-print-object) <vector, cl-structure-object>: Elide whole object if print-level exceeded. Use cl-print-insert-ellipsis. (cl-print-insert-ellipsis, cl-print-propertize-ellipsis) (cl-print-expand-ellipsis): New functions. * test/lisp/emacs-lisp/cl-print-tests.el (cl-print-tests-4): Test printing of objects nested in other objects. (cl-print-tests-strings, cl-print-tests-ellipsis-cons) (cl-print-tests-ellipsis-vector, cl-print-tests-ellipsis-struct) (cl-print-tests-ellipsis-circular): New tests. (cl-print-tests-check-ellipsis-expansion) (cl-print-tests-check-ellipsis-expansion-rx): New functions.
-rw-r--r--lisp/emacs-lisp/cl-print.el155
-rw-r--r--test/lisp/emacs-lisp/cl-print-tests.el89
2 files changed, 220 insertions, 24 deletions
diff --git a/lisp/emacs-lisp/cl-print.el b/lisp/emacs-lisp/cl-print.el
index bf5b1e878d5..e638e58275a 100644
--- a/lisp/emacs-lisp/cl-print.el
+++ b/lisp/emacs-lisp/cl-print.el
@@ -55,10 +55,19 @@ call other entry points instead, such as `cl-prin1'."
55 ;; we should only use it for objects which don't have nesting. 55 ;; we should only use it for objects which don't have nesting.
56 (prin1 object stream)) 56 (prin1 object stream))
57 57
58(cl-defgeneric cl-print-object-contents (_object _start _stream)
59 "Dispatcher to print the contents of OBJECT on STREAM.
60Print the contents starting with the item at START, without
61delimiters."
62 ;; Every cl-print-object method which can print an ellipsis should
63 ;; have a matching cl-print-object-contents method to expand an
64 ;; ellipsis.
65 (error "Missing cl-print-object-contents method"))
66
58(cl-defmethod cl-print-object ((object cons) stream) 67(cl-defmethod cl-print-object ((object cons) stream)
59 (if (and cl-print--depth (natnump print-level) 68 (if (and cl-print--depth (natnump print-level)
60 (> cl-print--depth print-level)) 69 (> cl-print--depth print-level))
61 (princ "..." stream) 70 (cl-print-insert-ellipsis object 0 stream)
62 (let ((car (pop object)) 71 (let ((car (pop object))
63 (count 1)) 72 (count 1))
64 (if (and print-quoted 73 (if (and print-quoted
@@ -84,23 +93,60 @@ call other entry points instead, such as `cl-prin1'."
84 (princ " " stream) 93 (princ " " stream)
85 (if (or (not (natnump print-length)) (> print-length count)) 94 (if (or (not (natnump print-length)) (> print-length count))
86 (cl-print-object (pop object) stream) 95 (cl-print-object (pop object) stream)
87 (princ "..." stream) 96 (cl-print-insert-ellipsis object print-length stream)
88 (setq object nil)) 97 (setq object nil))
89 (cl-incf count)) 98 (cl-incf count))
90 (when object 99 (when object
91 (princ " . " stream) (cl-print-object object stream)) 100 (princ " . " stream) (cl-print-object object stream))
92 (princ ")" stream))))) 101 (princ ")" stream)))))
93 102
103(cl-defmethod cl-print-object-contents ((object cons) _start stream)
104 (let ((count 0))
105 (while (and (consp object)
106 (not (cond
107 (cl-print--number-table
108 (numberp (gethash object cl-print--number-table)))
109 ((memq object cl-print--currently-printing))
110 (t (push object cl-print--currently-printing)
111 nil))))
112 (unless (zerop count)
113 (princ " " stream))
114 (if (or (not (natnump print-length)) (> print-length count))
115 (cl-print-object (pop object) stream)
116 (cl-print-insert-ellipsis object print-length stream)
117 (setq object nil))
118 (cl-incf count))
119 (when object
120 (princ " . " stream) (cl-print-object object stream))))
121
94(cl-defmethod cl-print-object ((object vector) stream) 122(cl-defmethod cl-print-object ((object vector) stream)
95 (princ "[" stream) 123 (if (and cl-print--depth (natnump print-level)
96 (let ((count (length object))) 124 (> cl-print--depth print-level))
97 (dotimes (i (if (natnump print-length) 125 (cl-print-insert-ellipsis object 0 stream)
98 (min print-length count) count)) 126 (princ "[" stream)
99 (unless (zerop i) (princ " " stream)) 127 (let* ((len (length object))
100 (cl-print-object (aref object i) stream)) 128 (limit (if (natnump print-length)
101 (when (and (natnump print-length) (< print-length count)) 129 (min print-length len) len)))
102 (princ " ..." stream))) 130 (dotimes (i limit)
103 (princ "]" stream)) 131 (unless (zerop i) (princ " " stream))
132 (cl-print-object (aref object i) stream))
133 (when (< limit len)
134 (princ " " stream)
135 (cl-print-insert-ellipsis object limit stream)))
136 (princ "]" stream)))
137
138(cl-defmethod cl-print-object-contents ((object vector) start stream)
139 (let* ((len (length object))
140 (limit (if (natnump print-length)
141 (min (+ start print-length) len) len))
142 (i start))
143 (while (< i limit)
144 (unless (= i start) (princ " " stream))
145 (cl-print-object (aref object i) stream)
146 (cl-incf i))
147 (when (< limit len)
148 (princ " " stream)
149 (cl-print-insert-ellipsis object limit stream))))
104 150
105(cl-defmethod cl-print-object ((object hash-table) stream) 151(cl-defmethod cl-print-object ((object hash-table) stream)
106 (princ "#<hash-table " stream) 152 (princ "#<hash-table " stream)
@@ -199,21 +245,46 @@ into a button whose action shows the function's disassembly.")
199 (princ ")" stream))) 245 (princ ")" stream)))
200 246
201(cl-defmethod cl-print-object ((object cl-structure-object) stream) 247(cl-defmethod cl-print-object ((object cl-structure-object) stream)
202 (princ "#s(" stream) 248 (if (and cl-print--depth (natnump print-level)
249 (> cl-print--depth print-level))
250 (cl-print-insert-ellipsis object 0 stream)
251 (princ "#s(" stream)
252 (let* ((class (cl-find-class (type-of object)))
253 (slots (cl--struct-class-slots class))
254 (len (length slots))
255 (limit (if (natnump print-length)
256 (min print-length len) len)))
257 (princ (cl--struct-class-name class) stream)
258 (dotimes (i limit)
259 (let ((slot (aref slots i)))
260 (princ " :" stream)
261 (princ (cl--slot-descriptor-name slot) stream)
262 (princ " " stream)
263 (cl-print-object (aref object (1+ i)) stream)))
264 (when (< limit len)
265 (princ " " stream)
266 (cl-print-insert-ellipsis object limit stream)))
267 (princ ")" stream)))
268
269(cl-defmethod cl-print-object-contents ((object cl-structure-object) start stream)
203 (let* ((class (cl-find-class (type-of object))) 270 (let* ((class (cl-find-class (type-of object)))
204 (slots (cl--struct-class-slots class)) 271 (slots (cl--struct-class-slots class))
205 (count (length slots))) 272 (len (length slots))
206 (princ (cl--struct-class-name class) stream) 273 (limit (if (natnump print-length)
207 (dotimes (i (if (natnump print-length) 274 (min (+ start print-length) len) len))
208 (min print-length count) count)) 275 (i start))
276 (while (< i limit)
209 (let ((slot (aref slots i))) 277 (let ((slot (aref slots i)))
210 (princ " :" stream) 278 (unless (= i start) (princ " " stream))
279 (princ ":" stream)
211 (princ (cl--slot-descriptor-name slot) stream) 280 (princ (cl--slot-descriptor-name slot) stream)
212 (princ " " stream) 281 (princ " " stream)
213 (cl-print-object (aref object (1+ i)) stream))) 282 (cl-print-object (aref object (1+ i)) stream))
214 (when (and (natnump print-length) (< print-length count)) 283 (cl-incf i))
215 (princ " ..." stream))) 284 (when (< limit len)
216 (princ ")" stream)) 285 (princ " " stream)
286 (cl-print-insert-ellipsis object limit stream))))
287
217 288
218;;; Circularity and sharing. 289;;; Circularity and sharing.
219 290
@@ -291,6 +362,48 @@ into a button whose action shows the function's disassembly.")
291 (cl-print--find-sharing object print-number-table))) 362 (cl-print--find-sharing object print-number-table)))
292 print-number-table)) 363 print-number-table))
293 364
365(defun cl-print-insert-ellipsis (object start stream)
366 "Print \"...\" to STREAM with the `cl-print-ellipsis' text property.
367Save state in the text property in order to print the elided part
368of OBJECT later. START should be 0 if the whole OBJECT is being
369elided, otherwise it should be an index or other pointer into the
370internals of OBJECT which can be passed to
371`cl-print-object-contents' at a future time."
372 (unless stream (setq stream standard-output))
373 (let ((ellipsis-start (and (bufferp stream)
374 (with-current-buffer stream (point)))))
375 (princ "..." stream)
376 (when ellipsis-start
377 (with-current-buffer stream
378 (cl-print-propertize-ellipsis object start ellipsis-start (point)
379 stream)))))
380
381(defun cl-print-propertize-ellipsis (object start beg end stream)
382 "Add the `cl-print-ellipsis' property between BEG and END.
383STREAM should be a buffer. OBJECT and START are as described in
384`cl-print-insert-ellipsis'."
385 (let ((value (list object start cl-print--number-table
386 cl-print--currently-printing)))
387 (with-current-buffer stream
388 (put-text-property beg end 'cl-print-ellipsis value stream))))
389
390;;;###autoload
391(defun cl-print-expand-ellipsis (value stream)
392 "Print the expansion of an ellipsis to STREAM.
393VALUE should be the value of the `cl-print-ellipsis' text property
394which was attached to the ellipsis by `cl-prin1'."
395 (let ((cl-print--depth 1)
396 (object (nth 0 value))
397 (start (nth 1 value))
398 (cl-print--number-table (nth 2 value))
399 (print-number-table (nth 2 value))
400 (cl-print--currently-printing (nth 3 value)))
401 (when (eq object (car cl-print--currently-printing))
402 (pop cl-print--currently-printing))
403 (if (equal start 0)
404 (cl-print-object object stream)
405 (cl-print-object-contents object start stream))))
406
294;;;###autoload 407;;;###autoload
295(defun cl-prin1 (object &optional stream) 408(defun cl-prin1 (object &optional stream)
296 "Print OBJECT on STREAM according to its type. 409 "Print OBJECT on STREAM according to its type.
diff --git a/test/lisp/emacs-lisp/cl-print-tests.el b/test/lisp/emacs-lisp/cl-print-tests.el
index 404d323d0c1..2b5eb3402bf 100644
--- a/test/lisp/emacs-lisp/cl-print-tests.el
+++ b/test/lisp/emacs-lisp/cl-print-tests.el
@@ -64,11 +64,15 @@
64 64
65(ert-deftest cl-print-tests-4 () 65(ert-deftest cl-print-tests-4 ()
66 "CL printing observes `print-level'." 66 "CL printing observes `print-level'."
67 (let ((deep-list '(a (b (c (d (e)))))) 67 (let* ((deep-list '(a (b (c (d (e))))))
68 (deep-struct (cl-print-tests-con)) 68 (buried-vector '(a (b (c (d [e])))))
69 (print-level 4)) 69 (deep-struct (cl-print-tests-con))
70 (buried-struct `(a (b (c (d ,deep-struct)))))
71 (print-level 4))
70 (setf (cl-print-tests-struct-a deep-struct) deep-list) 72 (setf (cl-print-tests-struct-a deep-struct) deep-list)
71 (should (equal "(a (b (c (d ...))))" (cl-prin1-to-string deep-list))) 73 (should (equal "(a (b (c (d ...))))" (cl-prin1-to-string deep-list)))
74 (should (equal "(a (b (c (d ...))))" (cl-prin1-to-string buried-vector)))
75 (should (equal "(a (b (c (d ...))))" (cl-prin1-to-string buried-struct)))
72 (should (equal "#s(cl-print-tests-struct :a (a (b (c ...))) :b nil :c nil :d nil :e nil)" 76 (should (equal "#s(cl-print-tests-struct :a (a (b (c ...))) :b nil :c nil :d nil :e nil)"
73 (cl-prin1-to-string deep-struct))))) 77 (cl-prin1-to-string deep-struct)))))
74 78
@@ -82,6 +86,85 @@
82 (should (equal "((quote a) (function b) (\\` ((\\, c) (\\,@ d))))" 86 (should (equal "((quote a) (function b) (\\` ((\\, c) (\\,@ d))))"
83 (cl-prin1-to-string quoted-stuff)))))) 87 (cl-prin1-to-string quoted-stuff))))))
84 88
89(ert-deftest cl-print-tests-ellipsis-cons ()
90 "Ellipsis expansion works in conses."
91 (let ((print-length 4)
92 (print-level 3))
93 (cl-print-tests-check-ellipsis-expansion
94 '(0 1 2 3 4 5) "(0 1 2 3 ...)" "4 5")
95 (cl-print-tests-check-ellipsis-expansion
96 '(0 1 2 3 4 5 6 7 8 9) "(0 1 2 3 ...)" "4 5 6 7 ...")
97 (cl-print-tests-check-ellipsis-expansion
98 '(a (b (c (d (e))))) "(a (b (c ...)))" "(d (e))")
99 (cl-print-tests-check-ellipsis-expansion
100 (let ((x (make-list 6 'b)))
101 (setf (nthcdr 6 x) 'c)
102 x)
103 "(b b b b ...)" "b b . c")))
104
105(ert-deftest cl-print-tests-ellipsis-vector ()
106 "Ellipsis expansion works in vectors."
107 (let ((print-length 4)
108 (print-level 3))
109 (cl-print-tests-check-ellipsis-expansion
110 [0 1 2 3 4 5] "[0 1 2 3 ...]" "4 5")
111 (cl-print-tests-check-ellipsis-expansion
112 [0 1 2 3 4 5 6 7 8 9] "[0 1 2 3 ...]" "4 5 6 7 ...")
113 (cl-print-tests-check-ellipsis-expansion
114 [a [b [c [d [e]]]]] "[a [b [c ...]]]" "[d [e]]")))
115
116(ert-deftest cl-print-tests-ellipsis-struct ()
117 "Ellipsis expansion works in structures."
118 (let ((print-length 4)
119 (print-level 3)
120 (struct (cl-print-tests-con)))
121 (cl-print-tests-check-ellipsis-expansion
122 struct "#s(cl-print-tests-struct :a nil :b nil :c nil :d nil ...)" ":e nil")
123 (let ((print-length 2))
124 (cl-print-tests-check-ellipsis-expansion
125 struct "#s(cl-print-tests-struct :a nil :b nil ...)" ":c nil :d nil ..."))
126 (cl-print-tests-check-ellipsis-expansion
127 `(a (b (c ,struct)))
128 "(a (b (c ...)))"
129 "#s(cl-print-tests-struct :a nil :b nil :c nil :d nil ...)")))
130
131(ert-deftest cl-print-tests-ellipsis-circular ()
132 "Ellipsis expansion works with circular objects."
133 (let ((wide-obj (list 0 1 2 3 4))
134 (deep-obj `(0 (1 (2 (3 (4))))))
135 (print-length 4)
136 (print-level 3))
137 (setf (nth 4 wide-obj) wide-obj)
138 (setf (car (cadadr (cadadr deep-obj))) deep-obj)
139 (let ((print-circle nil))
140 (cl-print-tests-check-ellipsis-expansion-rx
141 wide-obj (regexp-quote "(0 1 2 3 ...)") "\\`#[0-9]\\'")
142 (cl-print-tests-check-ellipsis-expansion-rx
143 deep-obj (regexp-quote "(0 (1 (2 ...)))") "\\`(3 (#[0-9]))\\'"))
144 (let ((print-circle t))
145 (cl-print-tests-check-ellipsis-expansion
146 wide-obj "#1=(0 1 2 3 ...)" "#1#")
147 (cl-print-tests-check-ellipsis-expansion
148 deep-obj "#1=(0 (1 (2 ...)))" "(3 (#1#))"))))
149
150(defun cl-print-tests-check-ellipsis-expansion (obj expected expanded)
151 (let* ((result (cl-prin1-to-string obj))
152 (pos (next-single-property-change 0 'cl-print-ellipsis result))
153 value)
154 (should pos)
155 (setq value (get-text-property pos 'cl-print-ellipsis result))
156 (should (equal expected result))
157 (should (equal expanded (with-output-to-string (cl-print-expand-ellipsis
158 value nil))))))
159
160(defun cl-print-tests-check-ellipsis-expansion-rx (obj expected expanded)
161 (let* ((result (cl-prin1-to-string obj))
162 (pos (next-single-property-change 0 'cl-print-ellipsis result))
163 (value (get-text-property pos 'cl-print-ellipsis result)))
164 (should (string-match expected result))
165 (should (string-match expanded (with-output-to-string
166 (cl-print-expand-ellipsis value nil))))))
167
85(ert-deftest cl-print-circle () 168(ert-deftest cl-print-circle ()
86 (let ((x '(#1=(a . #1#) #1#))) 169 (let ((x '(#1=(a . #1#) #1#)))
87 (let ((print-circle nil)) 170 (let ((print-circle nil))