Path: blob/master/elisp/slime/slime-1.2/sbcl-pprint-patch.lisp
990 views
;; Pretty printer patch for SBCL, which adds the "annotations" feature1;; required for sending presentations through pretty-printing streams.2;;3;; The section marked "Changed functions" and the DEFSTRUCT4;; PRETTY-STREAM are based on SBCL's pprint.lisp.5;;6;; Public domain.78(in-package "SB!PRETTY")910(defstruct (annotation (:include queued-op))11(handler (constantly nil) :type function)12(record))131415(defstruct (pretty-stream (:include sb!kernel:ansi-stream16(out #'pretty-out)17(sout #'pretty-sout)18(misc #'pretty-misc))19(:constructor make-pretty-stream (target))20(:copier nil))21;; Where the output is going to finally go.22(target (missing-arg) :type stream)23;; Line length we should format to. Cached here so we don't have to keep24;; extracting it from the target stream.25(line-length (or *print-right-margin*26(sb!impl::line-length target)27default-line-length)28:type column)29;; A simple string holding all the text that has been output but not yet30;; printed.31(buffer (make-string initial-buffer-size) :type (simple-array character (*)))32;; The index into BUFFER where more text should be put.33(buffer-fill-pointer 0 :type index)34;; Whenever we output stuff from the buffer, we shift the remaining noise35;; over. This makes it difficult to keep references to locations in36;; the buffer. Therefore, we have to keep track of the total amount of37;; stuff that has been shifted out of the buffer.38(buffer-offset 0 :type posn)39;; The column the first character in the buffer will appear in. Normally40;; zero, but if we end up with a very long line with no breaks in it we41;; might have to output part of it. Then this will no longer be zero.42(buffer-start-column (or (sb!impl::charpos target) 0) :type column)43;; The line number we are currently on. Used for *PRINT-LINES*44;; abbreviations and to tell when sections have been split across45;; multiple lines.46(line-number 0 :type index)47;; the value of *PRINT-LINES* captured at object creation time. We48;; use this, instead of the dynamic *PRINT-LINES*, to avoid49;; weirdness like50;; (let ((*print-lines* 50))51;; (pprint-logical-block ..52;; (dotimes (i 10)53;; (let ((*print-lines* 8))54;; (print (aref possiblybigthings i) prettystream)))))55;; terminating the output of the entire logical blockafter 8 lines.56(print-lines *print-lines* :type (or index null) :read-only t)57;; Stack of logical blocks in effect at the buffer start.58(blocks (list (make-logical-block)) :type list)59;; Buffer holding the per-line prefix active at the buffer start.60;; Indentation is included in this. The length of this is stored61;; in the logical block stack.62(prefix (make-string initial-buffer-size) :type (simple-array character (*)))63;; Buffer holding the total remaining suffix active at the buffer start.64;; The characters are right-justified in the buffer to make it easier65;; to output the buffer. The length is stored in the logical block66;; stack.67(suffix (make-string initial-buffer-size) :type (simple-array character (*)))68;; Queue of pending operations. When empty, HEAD=TAIL=NIL. Otherwise,69;; TAIL holds the first (oldest) cons and HEAD holds the last (newest)70;; cons. Adding things to the queue is basically (setf (cdr head) (list71;; new)) and removing them is basically (pop tail) [except that care must72;; be taken to handle the empty queue case correctly.]73(queue-tail nil :type list)74(queue-head nil :type list)75;; Block-start queue entries in effect at the queue head.76(pending-blocks nil :type list)77;; Queue of annotations to the buffer78(annotations-tail nil :type list)79(annotations-head nil :type list))808182(defmacro enqueue (stream type &rest args)83(let ((constructor (intern (concatenate 'string84"MAKE-"85(symbol-name type))86"SB-PRETTY")))87(once-only ((stream stream)88(entry `(,constructor :posn89(index-posn90(pretty-stream-buffer-fill-pointer91,stream)92,stream)93,@args))94(op `(list ,entry))95(head `(pretty-stream-queue-head ,stream)))96`(progn97(if ,head98(setf (cdr ,head) ,op)99(setf (pretty-stream-queue-tail ,stream) ,op))100(setf (pretty-stream-queue-head ,stream) ,op)101,entry))))102103;;;104;;; New helper functions105;;;106107(defun enqueue-annotation (stream handler record)108(enqueue stream annotation :handler handler109:record record))110111(defun re-enqueue-annotation (stream annotation)112(let* ((annotation-cons (list annotation))113(head (pretty-stream-annotations-head stream)))114(if head115(setf (cdr head) annotation-cons)116(setf (pretty-stream-annotations-tail stream) annotation-cons))117(setf (pretty-stream-annotations-head stream) annotation-cons)118nil))119120(defun re-enqueue-annotations (stream end)121(loop for tail = (pretty-stream-queue-tail stream) then (cdr tail)122while (and tail (not (eql (car tail) end)))123when (annotation-p (car tail))124do (re-enqueue-annotation stream (car tail))))125126(defun dequeue-annotation (stream &key end-posn)127(let ((next-annotation (car (pretty-stream-annotations-tail stream))))128(when next-annotation129(when (or (not end-posn)130(<= (annotation-posn next-annotation) end-posn))131(pop (pretty-stream-annotations-tail stream))132(unless (pretty-stream-annotations-tail stream)133(setf (pretty-stream-annotations-head stream) nil))134next-annotation))))135136(defun invoke-annotation (stream annotation truncatep)137(let ((target (pretty-stream-target stream)))138(funcall (annotation-handler annotation)139(annotation-record annotation)140target141truncatep)))142143(defun output-buffer-with-annotations (stream end)144(let ((target (pretty-stream-target stream))145(buffer (pretty-stream-buffer stream))146(end-posn (index-posn end stream))147(start 0))148(loop149for annotation = (dequeue-annotation stream :end-posn end-posn)150while annotation151do152(let ((annotation-index (posn-index (annotation-posn annotation)153stream)))154(when (> annotation-index start)155(write-string buffer target :start start156:end annotation-index)157(setf start annotation-index))158(invoke-annotation stream annotation nil)))159(when (> end start)160(write-string buffer target :start start :end end))))161162(defun flush-annotations (stream end truncatep)163(let ((end-posn (index-posn end stream)))164(loop165for annotation = (dequeue-annotation stream :end-posn end-posn)166while annotation167do (invoke-annotation stream annotation truncatep))))168169;;;170;;; Changed functions171;;;172173(defun maybe-output (stream force-newlines-p)174(declare (type pretty-stream stream))175(let ((tail (pretty-stream-queue-tail stream))176(output-anything nil))177(loop178(unless tail179(setf (pretty-stream-queue-head stream) nil)180(return))181(let ((next (pop tail)))182(etypecase next183(newline184(when (ecase (newline-kind next)185((:literal :mandatory :linear) t)186(:miser (misering-p stream))187(:fill188(or (misering-p stream)189(> (pretty-stream-line-number stream)190(logical-block-section-start-line191(first (pretty-stream-blocks stream))))192(ecase (fits-on-line-p stream193(newline-section-end next)194force-newlines-p)195((t) nil)196((nil) t)197(:dont-know198(return))))))199(setf output-anything t)200(output-line stream next)))201(indentation202(unless (misering-p stream)203(set-indentation stream204(+ (ecase (indentation-kind next)205(:block206(logical-block-start-column207(car (pretty-stream-blocks stream))))208(:current209(posn-column210(indentation-posn next)211stream)))212(indentation-amount next)))))213(block-start214(ecase (fits-on-line-p stream (block-start-section-end next)215force-newlines-p)216((t)217;; Just nuke the whole logical block and make it look like one218;; nice long literal. (But don't nuke annotations.)219(let ((end (block-start-block-end next)))220(expand-tabs stream end)221(re-enqueue-annotations stream end)222(setf tail (cdr (member end tail)))))223((nil)224(really-start-logical-block225stream226(posn-column (block-start-posn next) stream)227(block-start-prefix next)228(block-start-suffix next)))229(:dont-know230(return))))231(block-end232(really-end-logical-block stream))233(tab234(expand-tabs stream next))235(annotation236(re-enqueue-annotation stream next))))237(setf (pretty-stream-queue-tail stream) tail))238output-anything))239240(defun output-line (stream until)241(declare (type pretty-stream stream)242(type newline until))243(let* ((target (pretty-stream-target stream))244(buffer (pretty-stream-buffer stream))245(kind (newline-kind until))246(literal-p (eq kind :literal))247(amount-to-consume (posn-index (newline-posn until) stream))248(amount-to-print249(if literal-p250amount-to-consume251(let ((last-non-blank252(position #\space buffer :end amount-to-consume253:from-end t :test #'char/=)))254(if last-non-blank255(1+ last-non-blank)2560)))))257(output-buffer-with-annotations stream amount-to-print)258(flush-annotations stream amount-to-consume nil)259(let ((line-number (pretty-stream-line-number stream)))260(incf line-number)261(when (and (not *print-readably*)262(pretty-stream-print-lines stream)263(>= line-number (pretty-stream-print-lines stream)))264(write-string " .." target)265(flush-annotations stream266(pretty-stream-buffer-fill-pointer stream)267t)268(let ((suffix-length (logical-block-suffix-length269(car (pretty-stream-blocks stream)))))270(unless (zerop suffix-length)271(let* ((suffix (pretty-stream-suffix stream))272(len (length suffix)))273(write-string suffix target274:start (- len suffix-length)275:end len))))276(throw 'line-limit-abbreviation-happened t))277(setf (pretty-stream-line-number stream) line-number)278(write-char #\newline target)279(setf (pretty-stream-buffer-start-column stream) 0)280(let* ((fill-ptr (pretty-stream-buffer-fill-pointer stream))281(block (first (pretty-stream-blocks stream)))282(prefix-len283(if literal-p284(logical-block-per-line-prefix-end block)285(logical-block-prefix-length block)))286(shift (- amount-to-consume prefix-len))287(new-fill-ptr (- fill-ptr shift))288(new-buffer buffer)289(buffer-length (length buffer)))290(when (> new-fill-ptr buffer-length)291(setf new-buffer292(make-string (max (* buffer-length 2)293(+ buffer-length294(floor (* (- new-fill-ptr buffer-length)2955)2964)))))297(setf (pretty-stream-buffer stream) new-buffer))298(replace new-buffer buffer299:start1 prefix-len :start2 amount-to-consume :end2 fill-ptr)300(replace new-buffer (pretty-stream-prefix stream)301:end1 prefix-len)302(setf (pretty-stream-buffer-fill-pointer stream) new-fill-ptr)303(incf (pretty-stream-buffer-offset stream) shift)304(unless literal-p305(setf (logical-block-section-column block) prefix-len)306(setf (logical-block-section-start-line block) line-number))))))307308(defun output-partial-line (stream)309(let* ((fill-ptr (pretty-stream-buffer-fill-pointer stream))310(tail (pretty-stream-queue-tail stream))311(count312(if tail313(posn-index (queued-op-posn (car tail)) stream)314fill-ptr))315(new-fill-ptr (- fill-ptr count))316(buffer (pretty-stream-buffer stream)))317(when (zerop count)318(error "Output-partial-line called when nothing can be output."))319(output-buffer-with-annotations stream count)320(incf (pretty-stream-buffer-start-column stream) count)321(replace buffer buffer :end1 new-fill-ptr :start2 count :end2 fill-ptr)322(setf (pretty-stream-buffer-fill-pointer stream) new-fill-ptr)323(incf (pretty-stream-buffer-offset stream) count)))324325(defun force-pretty-output (stream)326(maybe-output stream nil)327(expand-tabs stream nil)328(re-enqueue-annotations stream nil)329(output-buffer-with-annotations stream330(pretty-stream-buffer-fill-pointer stream)))331332333