(in-package "SB!PRETTY")
(defstruct (annotation (:include queued-op))
(handler (constantly nil) :type function)
(record))
(defstruct (pretty-stream (:include sb!kernel:ansi-stream
(out #'pretty-out)
(sout #'pretty-sout)
(misc #'pretty-misc))
(:constructor make-pretty-stream (target))
(:copier nil))
(target (missing-arg) :type stream)
(line-length (or *print-right-margin*
(sb!impl::line-length target)
default-line-length)
:type column)
(buffer (make-string initial-buffer-size) :type (simple-array character (*)))
(buffer-fill-pointer 0 :type index)
(buffer-offset 0 :type posn)
(buffer-start-column (or (sb!impl::charpos target) 0) :type column)
(line-number 0 :type index)
(print-lines *print-lines* :type (or index null) :read-only t)
(blocks (list (make-logical-block)) :type list)
(prefix (make-string initial-buffer-size) :type (simple-array character (*)))
(suffix (make-string initial-buffer-size) :type (simple-array character (*)))
(queue-tail nil :type list)
(queue-head nil :type list)
(pending-blocks nil :type list)
(annotations-tail nil :type list)
(annotations-head nil :type list))
(defmacro enqueue (stream type &rest args)
(let ((constructor (intern (concatenate 'string
"MAKE-"
(symbol-name type))
"SB-PRETTY")))
(once-only ((stream stream)
(entry `(,constructor :posn
(index-posn
(pretty-stream-buffer-fill-pointer
,stream)
,stream)
,@args))
(op `(list ,entry))
(head `(pretty-stream-queue-head ,stream)))
`(progn
(if ,head
(setf (cdr ,head) ,op)
(setf (pretty-stream-queue-tail ,stream) ,op))
(setf (pretty-stream-queue-head ,stream) ,op)
,entry))))
(defun enqueue-annotation (stream handler record)
(enqueue stream annotation :handler handler
:record record))
(defun re-enqueue-annotation (stream annotation)
(let* ((annotation-cons (list annotation))
(head (pretty-stream-annotations-head stream)))
(if head
(setf (cdr head) annotation-cons)
(setf (pretty-stream-annotations-tail stream) annotation-cons))
(setf (pretty-stream-annotations-head stream) annotation-cons)
nil))
(defun re-enqueue-annotations (stream end)
(loop for tail = (pretty-stream-queue-tail stream) then (cdr tail)
while (and tail (not (eql (car tail) end)))
when (annotation-p (car tail))
do (re-enqueue-annotation stream (car tail))))
(defun dequeue-annotation (stream &key end-posn)
(let ((next-annotation (car (pretty-stream-annotations-tail stream))))
(when next-annotation
(when (or (not end-posn)
(<= (annotation-posn next-annotation) end-posn))
(pop (pretty-stream-annotations-tail stream))
(unless (pretty-stream-annotations-tail stream)
(setf (pretty-stream-annotations-head stream) nil))
next-annotation))))
(defun invoke-annotation (stream annotation truncatep)
(let ((target (pretty-stream-target stream)))
(funcall (annotation-handler annotation)
(annotation-record annotation)
target
truncatep)))
(defun output-buffer-with-annotations (stream end)
(let ((target (pretty-stream-target stream))
(buffer (pretty-stream-buffer stream))
(end-posn (index-posn end stream))
(start 0))
(loop
for annotation = (dequeue-annotation stream :end-posn end-posn)
while annotation
do
(let ((annotation-index (posn-index (annotation-posn annotation)
stream)))
(when (> annotation-index start)
(write-string buffer target :start start
:end annotation-index)
(setf start annotation-index))
(invoke-annotation stream annotation nil)))
(when (> end start)
(write-string buffer target :start start :end end))))
(defun flush-annotations (stream end truncatep)
(let ((end-posn (index-posn end stream)))
(loop
for annotation = (dequeue-annotation stream :end-posn end-posn)
while annotation
do (invoke-annotation stream annotation truncatep))))
(defun maybe-output (stream force-newlines-p)
(declare (type pretty-stream stream))
(let ((tail (pretty-stream-queue-tail stream))
(output-anything nil))
(loop
(unless tail
(setf (pretty-stream-queue-head stream) nil)
(return))
(let ((next (pop tail)))
(etypecase next
(newline
(when (ecase (newline-kind next)
((:literal :mandatory :linear) t)
(:miser (misering-p stream))
(:fill
(or (misering-p stream)
(> (pretty-stream-line-number stream)
(logical-block-section-start-line
(first (pretty-stream-blocks stream))))
(ecase (fits-on-line-p stream
(newline-section-end next)
force-newlines-p)
((t) nil)
((nil) t)
(:dont-know
(return))))))
(setf output-anything t)
(output-line stream next)))
(indentation
(unless (misering-p stream)
(set-indentation stream
(+ (ecase (indentation-kind next)
(:block
(logical-block-start-column
(car (pretty-stream-blocks stream))))
(:current
(posn-column
(indentation-posn next)
stream)))
(indentation-amount next)))))
(block-start
(ecase (fits-on-line-p stream (block-start-section-end next)
force-newlines-p)
((t)
(let ((end (block-start-block-end next)))
(expand-tabs stream end)
(re-enqueue-annotations stream end)
(setf tail (cdr (member end tail)))))
((nil)
(really-start-logical-block
stream
(posn-column (block-start-posn next) stream)
(block-start-prefix next)
(block-start-suffix next)))
(:dont-know
(return))))
(block-end
(really-end-logical-block stream))
(tab
(expand-tabs stream next))
(annotation
(re-enqueue-annotation stream next))))
(setf (pretty-stream-queue-tail stream) tail))
output-anything))
(defun output-line (stream until)
(declare (type pretty-stream stream)
(type newline until))
(let* ((target (pretty-stream-target stream))
(buffer (pretty-stream-buffer stream))
(kind (newline-kind until))
(literal-p (eq kind :literal))
(amount-to-consume (posn-index (newline-posn until) stream))
(amount-to-print
(if literal-p
amount-to-consume
(let ((last-non-blank
(position #\space buffer :end amount-to-consume
:from-end t :test #'char/=)))
(if last-non-blank
(1+ last-non-blank)
0)))))
(output-buffer-with-annotations stream amount-to-print)
(flush-annotations stream amount-to-consume nil)
(let ((line-number (pretty-stream-line-number stream)))
(incf line-number)
(when (and (not *print-readably*)
(pretty-stream-print-lines stream)
(>= line-number (pretty-stream-print-lines stream)))
(write-string " .." target)
(flush-annotations stream
(pretty-stream-buffer-fill-pointer stream)
t)
(let ((suffix-length (logical-block-suffix-length
(car (pretty-stream-blocks stream)))))
(unless (zerop suffix-length)
(let* ((suffix (pretty-stream-suffix stream))
(len (length suffix)))
(write-string suffix target
:start (- len suffix-length)
:end len))))
(throw 'line-limit-abbreviation-happened t))
(setf (pretty-stream-line-number stream) line-number)
(write-char #\newline target)
(setf (pretty-stream-buffer-start-column stream) 0)
(let* ((fill-ptr (pretty-stream-buffer-fill-pointer stream))
(block (first (pretty-stream-blocks stream)))
(prefix-len
(if literal-p
(logical-block-per-line-prefix-end block)
(logical-block-prefix-length block)))
(shift (- amount-to-consume prefix-len))
(new-fill-ptr (- fill-ptr shift))
(new-buffer buffer)
(buffer-length (length buffer)))
(when (> new-fill-ptr buffer-length)
(setf new-buffer
(make-string (max (* buffer-length 2)
(+ buffer-length
(floor (* (- new-fill-ptr buffer-length)
5)
4)))))
(setf (pretty-stream-buffer stream) new-buffer))
(replace new-buffer buffer
:start1 prefix-len :start2 amount-to-consume :end2 fill-ptr)
(replace new-buffer (pretty-stream-prefix stream)
:end1 prefix-len)
(setf (pretty-stream-buffer-fill-pointer stream) new-fill-ptr)
(incf (pretty-stream-buffer-offset stream) shift)
(unless literal-p
(setf (logical-block-section-column block) prefix-len)
(setf (logical-block-section-start-line block) line-number))))))
(defun output-partial-line (stream)
(let* ((fill-ptr (pretty-stream-buffer-fill-pointer stream))
(tail (pretty-stream-queue-tail stream))
(count
(if tail
(posn-index (queued-op-posn (car tail)) stream)
fill-ptr))
(new-fill-ptr (- fill-ptr count))
(buffer (pretty-stream-buffer stream)))
(when (zerop count)
(error "Output-partial-line called when nothing can be output."))
(output-buffer-with-annotations stream count)
(incf (pretty-stream-buffer-start-column stream) count)
(replace buffer buffer :end1 new-fill-ptr :start2 count :end2 fill-ptr)
(setf (pretty-stream-buffer-fill-pointer stream) new-fill-ptr)
(incf (pretty-stream-buffer-offset stream) count)))
(defun force-pretty-output (stream)
(maybe-output stream nil)
(expand-tabs stream nil)
(re-enqueue-annotations stream nil)
(output-buffer-with-annotations stream
(pretty-stream-buffer-fill-pointer stream)))