anchor.diff:  
   (in-package :mezzanine.editor)
   
  +;;; Lines.
  +
   (defclass line ()
  -  ((%next :initarg :next :accessor next)
  -   (%prev :initarg :prev :accessor prev)
  +  ((%next :initarg :next :accessor next-line)
  +   (%prev :initarg :prev :accessor previous-line)
      (%data :initarg :data :accessor data)
  -   (%version :initarg :version :accessor version))
  +   (%version :initarg :version :accessor line-version)
  +   (%number :initarg :number :accessor line-number)
  +   (%mark-list :initarg :mark-list :accessor line-mark-list)
  +   (%buffer :initarg :buffer :accessor line-buffer))
     (:default-initargs :next nil
                        :prev nil
                        :data (make-array 50 :element-type 'character :adjustable t :fill-pointer 0)
  -                     :version 0))
  +                     :version 0
  +                     :number 0
  +                     :mark-list '()
  +                     :buffer nil))
  +
  +(defgeneric line-character (line charpos))
  +(defgeneric (setf line-character) (value line charpos))
  +(defgeneric line-length (line))
  +
  +(defmethod line-character ((line line) charpos)
  +  (aref (data line) charpos))
  +
  +(defmethod (setf line-character) (value (line line) charpos)
  +  (setf (aref (data line) charpos) value))
  +
  +(defmethod line-length ((line line))
  +  (length (data line)))
  +
  +(defmethod print-object ((object line) stream)
  +  (print-unreadable-object (object stream :type t :identity t)
  +    (format stream "N:~D V:~D" (line-number object) (line-version object))))
  +
  +;;; Marks.
  +
  +(defclass mark ()
  +  ((%line :initarg :line :reader mark-line)
  +   (%charpos :initarg :charpos :reader mark-charpos)
  +   ;; :left, :right or :temporary.
  +   (%kind :initarg :kind :reader mark-kind)))
  +
  +(defgeneric (setf mark-line) (value mark))
  +(defgeneric (setf mark-charpos) (value mark))
  +(defgeneric (setf mark-kind) (value mark))
  +
  +(defmethod print-object ((object mark) stream)
  +  (print-unreadable-object (object stream :type t :identity t)
  +    (format stream "~S:~D ~S" (mark-line object) (mark-charpos object) (mark-kind object))))
  +
  +;;; Buffers.
  +
  +(defclass buffer ()
  +  ((%first-line :accessor first-line)
  +   (%last-line :accessor last-line)
  +   (%point :reader buffer-point)
  +   (%point-column-hint :initarg :point-column-hint :accessor point-column-hint)
  +   (%mark :reader buffer-mark)
  +   (%mark-active :initarg :mark-active :accessor buffer-mark-active)
  +   (%properties))
  +  (:default-initargs :mark-active nil :point-column-hint 0))
  +
  +(defgeneric buffer-property (buffer property-name &optional default))
  +(defgeneric (setf buffer-property) (value buffer property-name &optional default))
  +
  +(defmethod buffer-property ((buffer buffer) property-name &optional default)
  +  (gethash property-name (slot-value buffer '%properties) default))
  +
  +(defmethod (setf buffer-property) (value (buffer buffer) property-name &optional default)
  +  (setf (gethash property-name (slot-value buffer '%properties) default) value))
  +
  +(defmethod initialize-instance :after ((instance buffer) &key &allow-other-keys)
  +  (let ((line (make-instance 'line :buffer instance)))
  +    (setf (first-line instance) line
  +          (last-line instance) line
  +          (slot-value instance '%properties) (make-hash-table)
  +          (slot-value instance '%point) (make-mark line 0 :right)
  +          (slot-value instance '%mark) (make-mark line 0 :left))))
  +
  +;;; The editor itself.
   
   (defclass editor ()
     ((%fifo :initarg :fifo :reader fifo)
      (%pending-event :initarg :pending-event :accessor pending-event)
      (%pending-redisplay :initarg :pending-redisplay :accessor pending-redisplay)
  -   (%font :initarg :font :reader font)
      (%window :initarg :window :accessor window)
      (%frame :initarg :frame :accessor frame)
  -   (%first-line :initarg :first-line :accessor first-line)
  -   (%last-line :initarg :last-line :accessor last-line)
  -   (%point-line :initarg :point-line :accessor point-line)
  -   (%point-offset :initarg :point-offset :accessor point-offset)
  -   (%point-column-hint :initarg :point-column-hint :accessor point-column-hint)
  +   (%buffer :initarg :buffer :accessor current-buffer)
  +   (%font :initarg :font :reader font)
      (%foreground-colour :initarg :foreground-colour :accessor foreground-colour)
      (%background-colour :initarg :background-colour :accessor background-colour)
      ;; Redisplay state.
  -   (%window-top-line :initarg :window-top-line :accessor window-top-line)
  -   (%window-top :initarg :window-top :accessor window-top)
  -   (%current-screen :initarg :current-screen :accessor current-screen)
  -   (%current-point-logical-line :initarg :current-point-screen-line :accessor current-point-logical-line))
  +   (%current-screen :initarg :screen :accessor editor-current-screen)
  +   (%line-cache :initarg :display-line-cache :accessor display-line-cache)
  +   )
     (:default-initargs :pending-event nil
  -                     :pending-redisplay nil
  -                     :csr-x 0 :csr-y 0 :csr-char #\Space
  +                     :pending-redisplay t
                        :foreground-colour #xFFDCDCCC
  -                     :background-colour #xFF3E3E3E))
  +                     :background-colour #xFF3E3E3E
  +                     :screen nil
  +                     :display-line-cache '()))
   
   (defvar *editor*)
   
  
positive.code:                (setf (pending-event *editor*) event)
                 (signal 'pending-input))))))
  
  (defgeneric line-length (line))
  
  (defmethod line-length ((line line))
    (length (data line)))
  
  ;;; Sub-editor. Buffer manipulation.
  
  (defun insert-line ()
    "Insert a new line after the point, splitting the current line if needed.
  The point is positioned at the start of the new line."
    (let* ((current-line (point-line *editor*))
           (new-line (make-instance 'line
                                    :next (next current-line)
                                    :prev current-line
                                    :data (make-array (- (line-length current-line)
                                                         (point-offset *editor*))
                                                      :element-type 'character
                                                      :adjustable t
                                                      :fill-pointer t))))
      (replace (data new-line) (data current-line)
               :start2 (point-offset *editor*))
      (cond ((next current-line)
             (setf (prev (next current-line)) new-line))
            (t (setf (last-line *editor*) new-line)))
      (incf (version current-line))
      (setf (next current-line) new-line
            (fill-pointer (data current-line)) (point-offset *editor*))
      (setf (point-line *editor*) new-line
            (point-offset *editor*) 0
            (point-column-hint *editor*) 0))
    (values))
  
  (defun insert-char (ch)
    "Insert CH after point, then advance point."
    (let ((current-line (point-line *editor*)))
      (cond ((eql (line-length current-line) (point-offset *editor*))
             ;; Inserting at end.
             (vector-push-extend ch (data current-line)))
            (t ;; Inserting in the middle or at the start.
             ;; Make sure the vector is long enough.
             (vector-push-extend ch (data current-line))
             (replace (data current-line) (data current-line)
                      :start1 (1+ (point-offset *editor*))
                      :start2 (point-offset *editor*))
             (setf (aref (data current-line) (point-offset *editor*)) ch)))
      (incf (version current-line)))
    (forward-char))
  
  (defun kill-line ()
    (let ((current-line (point-line *editor*)))
      (cond ((eql (line-length current-line) (point-offset *editor*))
             ;; At end of line. Merge current line with next.
             (when (next current-line)
               (let ((next-line (next current-line))
                     (len (line-length current-line)))
                 (adjust-array (data current-line)
                               (+ len
                                  (line-length next-line))
                               :fill-pointer t)
                 (replace (data current-line) (data next-line)
                          :start1 len)
                 (cond ((next next-line)
                        (setf (prev (next next-line)) current-line))
                       (t (setf (last-line *editor*) current-line)))
                 (setf (next current-line) (next next-line)))))
            (t ;; At start or middle of line. Trim line.
             (setf (fill-pointer (data current-line)) (point-offset *editor*))))
      (incf (version current-line)))
    (values))
  
  (defun delete-one-char-forward ()
    (let ((current-line (point-line *editor*)))
      (cond ((eql (point-offset *editor*) (line-length current-line))
             (kill-line))
            (t
             (replace (data current-line) (data current-line)
                      :start1 (point-offset *editor*)
                      :start2 (1+ (point-offset *editor*)))
             (decf (fill-pointer (data current-line)))
             (incf (version current-line))))))
  
  (defun delete-forward-char (&optional (n 1))
    "Delete the following N characters (previous if N is negative)."
    (cond ((minusp n)
           (setf n (- n))
           (dotimes (i n)
             (when (and (eql (point-line *editor*) (first-line *editor*))
                        (eql (point-offset *editor*) 0))
               (return))
             (backward-char)
             (delete-one-char-forward)))
          (t
           (dotimes (i n)
             (when (and (eql (point-line *editor*) (last-line *editor*))
                        (eql (point-offset *editor*) (line-length (last-line *editor*))))
               (return))
             (delete-one-char-forward))))
    (values))
  
  (defun delete-backward-char (&optional (n 1))
    (delete-forward-char (- n)))
  
  ;;; Point motion.
  
  (defun move-beginning-of-line ()
    (setf (point-offset *editor*) 0
          (point-column-hint *editor*) 0)
    (values))
  
  (defun move-end-of-line ()
    (setf (point-offset *editor*) (line-length (point-line *editor*))
          (point-column-hint *editor*) (line-length (point-line *editor*)))
    (values))
  
  (defun forward-char (&optional (n 1))
    "Move point forward by N characters. N may be negative."
    (cond ((minusp n)
           (setf n (- n))
           (dotimes (i n)
             (let ((current-line (point-line *editor*)))
               (cond ((eql 0 (point-offset *editor*))
                      ;; At start of line.
                      (when (prev current-line)
                        (setf (point-line *editor*) (prev current-line)
                              (point-offset *editor*) (line-length (prev current-line)))))
                     (t ;; Moving within a line.
                      (decf (point-offset *editor*)))))))
          (t
           (dotimes (i n)
             (let ((current-line (point-line *editor*)))
               (cond ((eql (line-length current-line) (point-offset *editor*))
                      ;; At end of line.
                      (when (next current-line)
                        (setf (point-offset *editor*) 0
                              (point-line *editor*) (next current-line))))
                     (t ;; Moving within a line.
                      (incf (point-offset *editor*))))))))
    (setf (point-column-hint *editor*) (point-offset *editor*))
    (values))
  
  (defun backward-char (&optional (n 1))
    (forward-char (- n)))
  
  (defun next-line (&optional (n 1))
    "Move point down by N lines. N may be negative.
  Tries to stay as close to the current column as possible."
    (let ((accessor #'next))
      (when (minusp n)
        (setf n (- n)
              accessor #'prev))
      (dotimes (i n)
        (let* ((current-line (point-line *editor*))
               (new-line (funcall accessor current-line)))
          (cond (new-line
                 (setf (point-offset *editor*) (min (point-column-hint *editor*)
                                                    (line-length new-line))
                       (point-line *editor*) new-line))
                (t (return))))))
    (values))
  
  (defun previous-line (&optional (n 1))
    (next-line (- n)))
  
  ;;; Redisplay.
  
  ;; Lines are currently fixed-height.
  (defun window-rows ()
  
negative.code:     (truncate (- (mezzanine.gui.compositor:height (window *editor*)) top bottom)
                (mezzanine.gui.font:line-height (font *editor*)))))
  
  (defun flush-display-line (mark)
    "Flush the display line containing MARK."
    (setf (display-line-cache *editor*)
          (remove-if (lambda (line)
                       ;; Munch the entire line.
                       (eql (display-line-line line) (mark-line mark)))
                     (display-line-cache *editor*))))
  
  (defun flush-display-lines-in-region (mark-1 mark-2)
    "Flush display lines containing the region specified by MARK-1 and MARK-2."
    (let ((first (min (line-number (mark-line mark-1))
                      (line-number (mark-line mark-2))))
          (last (max (line-number (mark-line mark-1))
                     (line-number (mark-line mark-2)))))
      (setf (display-line-cache *editor*)
            (remove-if (lambda (line)
                         (<= first (line-number (display-line-line line)) last))
                       (display-line-cache *editor*)))))
  
  (defun flush-stale-lines ()
    "Flush any display lines with the wrong version."
    (setf (display-line-cache *editor*)
          (remove-if (lambda (line)
                       (not (eql (display-line-version line)
                                 (line-version (display-line-line line)))))
                     (display-line-cache *editor*))))
  
  (defun editor-width ()
    "Return the width of the display area in pixels."
    (multiple-value-bind (left right top bottom)
        (mezzanine.gui.widgets:frame-size (frame *editor*))
      (- (mezzanine.gui.compositor:width (window *editor*)) left right)))
  
  (defun region-bounds (mark-1 mark-2)
    "Return a bunch of boundary information for the region."
    (cond ((eql (mark-line mark-1) (mark-line mark-2))
           ;; Same line.
           (when (> (mark-charpos mark-1) (mark-charpos mark-2))
             (rotatef mark-1 mark-2))
           (values (mark-line mark-1) (mark-charpos mark-1) nil
                   (mark-line mark-2) (mark-charpos mark-2) nil))
          (t ;; 2 or more lines.
           (when (> (line-number (mark-line mark-1)) (line-number (mark-line mark-2)))
             (rotatef mark-1 mark-2))
           (values (mark-line mark-1) (mark-charpos mark-1) (line-number (mark-line mark-1))
                   (mark-line mark-2) (mark-charpos mark-2) (line-number (mark-line mark-2))))))
  
  (defun render-display-line-2 (line start)
    (multiple-value-bind (line-1 line-1-charpos line-1-number line-2 line-2-charpos line-2-number)
        (region-bounds (buffer-point (current-buffer *editor*)) (buffer-mark (current-buffer *editor*)))
      (loop
         with pen = 0
         with font = (font *editor*)
         with baseline = (mezzanine.gui.font:ascender font)
         with foreground = (foreground-colour *editor*)
         with background = (background-colour *editor*)
         with line-height = (mezzanine.gui.font:line-height font)
         with win-width = (editor-width)
         with point = (buffer-point (current-buffer *editor*))
         with mark-active = (buffer-mark-active (current-buffer *editor*))
         with buffer = (make-array (list line-height win-width)
                                   :element-type '(unsigned-byte 32)
                                   :initial-element background)
         for ch-position from start below (line-length line)
         for glyph = (mezzanine.gui.font:character-to-glyph font (line-character line ch-position))
         for mask = (mezzanine.gui.font:glyph-mask glyph)
         for advance = (mezzanine.gui.font:glyph-advance glyph)
         do
           (when (> (+ pen advance) win-width)
             (return (values buffer ch-position)))
           (let ((at-point (and (eql line (mark-line point))
                                (eql ch-position (mark-charpos point))))
                 (in-region (and mark-active
                                 (or (if line-1-number
                                         (or (< line-1-number (line-number line) line-2-number)
                                             (and (eql line line-1)
                                                  (<= line-1-charpos ch-position))
                                             (and (eql line line-2)
                                                  (< ch-position line-2-charpos)))
                                         (and (eql line line-1)
                                              (<= line-1-charpos ch-position)
                                              (< ch-position line-2-charpos)))))))
             ;; Invert the point.
             (when at-point
               (mezzanine.gui:bitset line-height advance
                                     foreground
                                     buffer 0 pen))
             (mezzanine.gui:bitset-argb-xrgb-mask-8 (array-dimension mask 0) (array-dimension mask 1)
                                                    (if at-point
                                                        background
                                                        foreground)
                                                    mask 0 0
                                                    buffer
                                                    (- baseline (mezzanine.gui.font:glyph-yoff glyph))
                                                    (+ pen (mezzanine.gui.font:glyph-xoff glyph)))
             ;; Underline the region.
             (when in-region
               (mezzanine.gui:bitset-argb-xrgb 1 advance
                                               (if at-point
                                                   background
                                                   foreground)
                                               buffer baseline pen))
             (incf pen advance))
         finally
         ;; Reached end of line, check for the point.
           (when (and (eql line (mark-line point))
                      (eql ch-position (mark-charpos point)))
             ;; Point is here, render it past the last character.
             (let* ((glyph (mezzanine.gui.font:character-to-glyph font #\Space))
                    (advance (mezzanine.gui.font:glyph-advance glyph)))
               (when (<= (+ pen advance) win-width) ; FIXME, how to display point at end of line & display line properly. also fix blit crash bug.
                 (mezzanine.gui:bitset line-height advance
                                       foreground
                                       buffer 0 pen))))
         ;; TODO: Render underline to end of line region spans whole line.
           (return (values buffer ch-position)))))
  
  (defun render-display-line-1 (line start)
    (multiple-value-bind (buffer end)
        (render-display-line-2 line start)
      (let ((display-line (make-instance 'display-line
                                         :line line
                                         :version (line-version line)
                                         :start start
                                         :end end
                                         :representation buffer)))
        (push display-line (display-line-cache *editor*))
        display-line)))
  
  (defun render-display-line (line fn)
    "Render display lines for real line LINE, calling FN with each display line."
    (cond ((zerop (line-length line))
           (funcall fn (or (get-display-line-from-cache line 0)
                           (render-display-line-1 line 0))))
          (t (do ((start 0))
                 ((>= start (line-length line)))
               (let ((display-line (or (get-display-line-from-cache line start)
                                       (render-display-line-1 line start))))
                 (funcall fn display-line)
                 (setf start (display-line-end display-line)))))))
  
  (defun get-display-line-from-cache (line start)
    (dolist (display-line (display-line-cache *editor*))
      (when (and (eql (display-line-line display-line) line)
                 (eql (display-line-start display-line) start))
        ;; MRU cache.
        (setf (display-line-cache *editor*) (remove display-line (display-line-cache *editor*)))
        (push display-line (display-line-cache *editor*))
        (return display-line))))
  
  (defun blit-display-line (line y)
    (multiple-value-bind (left right top bottom)
        (mezzanine.gui.widgets:frame-size (frame *editor*))
      (let* ((fb (mezzanine.gui.compositor:window-buffer (window *editor*)))
             (line-height (mezzanine.gui.font:line-height (font *editor*)))
             (real-y (+ top (* y line-height)))
             (win-width (editor-width)))
        (if line
            ;; Blitting line.
            (mezzanine.gui:bitblt line-height win-width
                                  (display-line-representation line)
                                  0 0
                                  fb
                                  real-y left)
            ;; Line is empty.
            (mezzanine.gui:bitset line-height win-width
                                  (background-colour *editor*)
                                  fb
                                  real-y left))
        (mezzanine.gui.compositor:damage-window (window *editor*)
                                                left real-y
                                                win-width line-height))))
  
  (defun recenter (buffer)
    "Move BUFFER's top line so that the point is displayed."
    (let* ((point (buffer-point buffer))
           (top-line (mark-line point))
           (rendered-lines (make-array (ceiling (window-rows) 2) :fill-pointer 0 :adjustable t))
           (point-display-line nil))
      ;; Move (window-rows)/2 lines up from point.
      (dotimes (i (ceiling (window-rows) 2))
        (when (not (previous-line top-line))
          (return))
        (setf top-line (previous-line top-line)))
      ;; Render display lines until point is reached.
      (do ((line top-line (next-line line)))
          ;; Should always top when the point's line has been reached.
          ()
        (render-display-line line
                             (lambda (display-line)
                               (vector-push-extend display-line rendered-lines)
                               (when (and (eql (mark-line point) (display-line-line display-line))
                                          (<= (display-line-start display-line) (mark-charpos point))
                                          (or (and (eql (display-line-end display-line) (line-length (display-line-line display-line)))
                                                   (eql (display-line-end display-line) (mark-charpos point)))
                                              (< (mark-charpos point) (display-line-end display-line))))
                                 ;; This is point line, stop here.
                                 (setf point-display-line (1- (length rendered-lines)))
                                 (return)))))
      ;; Walk (window-rows)/2 display lines backwards from point. This is the new top-line.
      (let ((new-top-line (aref rendered-lines (max 0 (- point-display-line (truncate (window-rows) 2)))))
            (top-line-mark (buffer-property buffer 'pane-top-line)))
        (setf (mark-line top-line-mark) (display-line-line new-top-line))
              (mark-charpos top-line-mark) (display-line-start new-top-line))))
  
  (defun redisplay ()
    "Perform an incremental redisplay cycle.
  Returns true when the screen is up-to-date, false if the screen is dirty and there is pending input."
    (handler-case
        (progn
          (when (not (eql (length (editor-current-screen *editor*)) (window-rows)))
            (setf (editor-current-screen *editor*) (make-array (window-rows) :initial-element t)))
          (check-pending-input)
          (let* ((buffer (current-buffer *editor*))
                 (current-screen (editor-current-screen *editor*))
                 (new-screen (make-array (window-rows) :fill-pointer 0 :initial-element nil))
                 (point-line nil)
                 (top-line (pane-top-line buffer))
                 (point (buffer-point buffer))
                 (previous-point-position (buffer-property buffer 'pane-previous-point-position))
                 (mark (buffer-mark buffer))
                 (previous-mark-position (buffer-property buffer 'pane-previous-mark-position)))
            (when (not previous-point-position)
              (setf previous-point-position (copy-mark point :right)
                    (buffer-property buffer 'pane-previous-point-position) previous-point-position))
            (when (not previous-mark-position)
              (setf previous-mark-position (copy-mark mark :left)
                    (buffer-property buffer 'pane-previous-mark-position) previous-mark-position))
            ;; If the point has moved, then invalidate the line that contained the point and the line that
            ;; now holds the point.
            #+(or)(format t "Point: ~S  prev point: ~S  equal: ~S~%"
                    point previous-point-position
                    (mark-equal-p point previous-point-position))
            (when (not (mark-equal-p point previous-point-position))
              (flush-display-line previous-point-position)
              (flush-display-line point))
            ;; If the mark changes state, flush lines within the region.
            (when (or (and (not (buffer-mark-active buffer))
                           (buffer-property buffer 'pane-mark-was-active))
                      (and (buffer-mark-active buffer)
                           (not (buffer-property buffer 'pane-mark-was-active))))
              (flush-display-lines-in-region point mark))
            ;; If the mark is active and the point moves, flush lines between the old point position
            ;; and the new position.
            ;; FIXME: This will cause a bunch of lines to be redrawn when the point & mark are exchanged.
            (when (and (buffer-mark-active buffer)
                       (not (mark-equal-p point previous-point-position)))
              (flush-display-lines-in-region point previous-point-position))
            ;; If the mark is or was active and moves, flush lines between the old mark position
            ;; and the new position.
            ;; FIXME: This will cause a bunch of lines to be redrawn when the point & mark are exchanged.
            (when (and (or (buffer-mark-active buffer)
                           (buffer-property buffer 'pane-mark-was-active))
                       (not (mark-equal-p mark previous-mark-position)))
              (format t "Mark moved from ~S to ~S~%" previous-mark-position mark)
              (flush-display-lines-in-region mark previous-mark-position))
            ;; Finally, flush any stale lines.
            (flush-stale-lines)
            ;; Update tracking properties.
            (setf (buffer-property buffer 'pane-mark-was-active) (buffer-mark-active buffer))
            (move-mark-to-mark previous-point-position point)
            (move-mark-to-mark previous-mark-position mark)
            ;; Generate WINDOW-ROWS display lines, starting at TOP-LINE.
            ;; TODO: Don't start from the beginning of the top-line, use the charpos instead.
            (setf (mark-charpos top-line) 0)
            ;(format t "Top line ~S ~S~%" top-line (mark-line top-line))
            (do ((line (mark-line top-line) (next-line line)))
                ;; Stop when there are no more lines or the screen has been filled up.
                ((null line))
              (render-display-line line
                                   (lambda (display-line)
                                     (check-pending-input)
                                     (vector-push display-line new-screen)
                                     (when (and (eql (mark-line point) (display-line-line display-line))
                                                (<= (display-line-start display-line) (mark-charpos point))
                                                (or (and (eql (display-line-end display-line) (line-length (display-line-line display-line)))
                                                         (eql (display-line-end display-line) (mark-charpos point)))
                                                    (< (mark-charpos point) (display-line-end display-line))))
                                       (setf point-line display-line))
                                     (when (eql (fill-pointer new-screen) (window-rows))
                                       (return)))))
            (setf (fill-pointer new-screen) (window-rows))
            ;; If the point is not within the screen bounds, then recenter and retry.
            (when (not point-line)
              (recenter buffer)
              (return-from redisplay nil))
            ;(format t "New screen is ~S~%" new-screen)
            ;; Compare against the current screen, blitting when needed.
            (dotimes (y (window-rows))
              (let ((line (aref new-screen y)))
                (unless (eql (aref current-screen y) line)
                  ;(format t "Update line ~D. Old ~S new ~S.~%" y (aref current-screen y) line)
                  (blit-display-line line y)
                  (setf (aref current-screen y) line)
                  (check-pending-input))))
            ;; Prune the cache.
            (setf (display-line-cache *editor*) (subseq (display-line-cache *editor*) 0 (* (window-rows) 4)))
            #+(or)(format t "Line cache: ~S~%" (display-line-cache *editor*)))
          t)
      (pending-input ()
        nil)))
  
  (defun vomit-buffer (buffer)
    (do ((line (first-line buffer) (next-line line)))
        ((not line))
      (format t "~S ~S~%" line (data line))))
  
  (defun editor-loop ()
    (loop
       (handler-case
           (let ((ch (editor-read-char))
                 (buffer (current-buffer *editor*)))
             (case ch
               ((nil)) ; Happens when there's a redisplay pending.
               (#\Newline (insert-line buffer))
               (#\C-F (forward-char buffer))
               (#\C-B (backward-char buffer))
               (#\C-N (forward-line buffer))
               (#\C-P (backward-line buffer))
               (#\C-A (move-beginning-of-line buffer))
               (#\C-E (move-end-of-line buffer))
               (#\C-K (kill-line buffer))
               (#\C-Q (insert-char buffer (editor-read-char)))
               (#\C-L (recenter buffer))
               (#\M-L (redraw-screen))
               (#\C-Space (set-mark buffer))
               (#\C-X (exchange-point-and-mark buffer))
               (#\Backspace (delete-backward-char buffer))
               (#\C-D (delete-forward-char buffer))
               (#\Delete (delete-forward-char buffer))
               (#\C-V (vomit-buffer buffer))
               (#\C-T (test-fill buffer))
               (t (cond ((find ch "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789!\"£$%^&*()_+-=[]{};'#:@~,./<>?`~|\\"
                               :test #'char=)
                         (insert-char buffer ch)))))
             (format t "Point at ~S  mark at ~S  active:~S~%"
                     (buffer-point buffer) (buffer-mark buffer)
                     (buffer-mark-active buffer))
             (setf (pending-redisplay *editor*) (not (redisplay))))
         (error (c)
           (ignore-errors
  