;-*- Mode:LISP; Package:ZWEI; Readtable:ZL; Base:10; Lowercase:T -*-

;;; this file provides the interface to the rubout handler.

;>> editing-command not implemented

;; These two are temporary until the changes in sys:window;stream  are installed.

;(defmethod (tv:stream-mixin :displayer) ()
;  (if (variable-boundp tv::displayer) tv::displayer nil))
;(defmethod (tv:stream-mixin :set-displayer) (d)
;  (setq tv::displayer d))
;(defmethod (tv:stream-mixin :case :set :displayer) (d)
;  (setq tv::displayer d))


; tv:selected-window rather than *terminal-io* is the right thing in these
; turds, as consider when debuggin in the cold-load stream.
(defun erh-on (&optional globallyp)
  "GLOBALLYP non-NIL means make the erh the default rubout handler everywhere
-- NIL means just change it for TV:SELECTED-WINDOW"
;  (setq *trace-output* (setq *error-output* (setq *debug-io* si:cold-load-stream)))
  (if (not globallyp)
      (send tv:selected-window :set-stream-rubout-handler 'editor-rubout-handler)
    (setq tv::stream-mixin-default-rubout-handler 'editor-rubout-handler)
    (if (send tv:selected-window :stream-rubout-handler)
	;; make it use the default one
	(send tv:selected-window :set-stream-rubout-handler nil)))
  'editor-rubout-handler)

(defun erh-off (&optional (window (or tv:selected-window *terminal-io*)))
  (setq tv::stream-mixin-default-rubout-handler 'tv::alternate-rubout-handler)
  (when window (send window :send-if-handles :set-stream-rubout-handler nil))
  'tv::alternate-rubout-handler)

(tv:add-escape-key #/hyper-help #'(lambda (ignore) (erh-off)) "Attempt to despazz the world")


;;; The :TYI operation on windows calls the function EDITOR-RUBOUT-HANDLER
;;; when this rubout handler is installed.
;;; It finds the rubout handler buffer in the instance variable
;;; TV::RUBOUT-HANDLER-BUFFER (of TV:STREAM-MIXIN)
;;; and binds ZWEI:*RUBOUT-HANDLER-BUFFER*, a special variable,
;;; so that the editor can get at the stuff.
;;; The value of either variable is a string which we update.

;;; The variable RUBOUT-HANDLER-OPTIONS will also be bound
;;; (to the alternating list of options and values supplied to the :RUBOUT-HANDLER operation).

;;; Each window has a single displayer of flavor EDITOR-RUBOUT-DISPLAYER
;;; used to edit for rubout handling on that window.
(defflavor editor-rubout-displayer () (external-stream-displayer)
  (:default-init-plist
    :editor-closure-variables editor-rubout-handler-editor-closure-variables
    :comtab *rubout-handler-comtab*))

(defconst editor-rubout-handler-editor-closure-variables
	  `((*input-history* nil) . ,top-level-editor-closure-variables))

(defvar *input-history* :unbound
  "A history of previous input strings for Control-Meta-Y.
It is a closure var in editor-closures of displayers used for rubout handling.")

(defun window-editor-rubout-handler (window)
  "Return an EDITOR-RUBOUT-DISPLAYER displaying on WINDOW.
Creates one if none exists; from then on, the same one is returned each time."
  (or (send window :displayer)
      (let ((displayer (make-instance 'editor-rubout-displayer :window window)))
; done :after :init
;	(send displayer :set-window window)
	(send window :set-displayer displayer)
	displayer)))


(defmethod (editor-rubout-displayer :reprompt) (&optional char)
  (multiple-value-setq (tv::prompt-starting-x tv::prompt-starting-y)
    (send window :read-cursorpos))
  (let ((prompt (or (assq :reprompt rubout-handler-options)
		    (assq :prompt rubout-handler-options))))
    (and prompt
	 (neq char :null)
	 (tv::rubout-handler-prompt (cadr prompt) window char)))
  (multiple-value-setq (tv::rubout-handler-starting-x tv::rubout-handler-starting-y)
    (send window :read-cursorpos))
  (setq first-line-left tv::rubout-handler-starting-x
	starting-vpos tv::rubout-handler-starting-y))

;;;; Interface with TV::RUBOUT-HANDLER-BUFFER ivar of TV:STREAM-MIXIN

(defvar *rubout-handler-buffer* :unbound
  "The string that the window system expects RH input in.
Bound by ZWEI::EDITOR-RUBOUT-HANDLER.")

(defvar *rubout-handler-activation-character* :unbound
  "Used by ZWEI::EDITOR-RUBOUT-HANDLER.")

;;; First, the names of the slots in it.
;;; These are defined in io;stream, but the structure is in TV.
;;;   It's too tedious to constantly prefix the tv:, so we define these in zwei.

;;; The number of characters in it.
(defsubst rhb-fill-pointer (&optional (buffer *rubout-handler-buffer*))
  (tv::rhb-fill-pointer buffer))
;;; The index at which the caller is reading.
;;; We must set it to 0 to cause him to rescan.
(defsubst rhb-scan-pointer (&optional (buffer *rubout-handler-buffer*))
  (tv::rhb-scan-pointer buffer))
;;; This corresponds to our value of POINT.
(defsubst rhb-typein-pointer (&optional (buffer *rubout-handler-buffer*))
  (tv::rhb-typein-pointer buffer))
;;; T if this doesn't go onto the input ring
(defsubst rhb-dont-save-flag (&optional (buffer *rubout-handler-buffer*))
  (tv::rhb-dont-save-flag buffer))
;;; Not stored into by erh --- we use *input-history* which is an interval history instead
(defsubst rhb-input-history (&optional (buffer *rubout-handler-buffer*))
  (tv::rhb-input-history buffer))
;;; T is this is the first time the rh has been entered - meams we have to initialize
(defsubst rhb-status (&optional (buffer *rubout-handler-buffer*))
  (tv::rhb-status buffer))

;;; The elements of *RUBOUT-HANDLER-BUFFER* are the characters.  It is an ART-Q
;;;  with array-leader slots as above

;;; We do not actually edit the buffer array, of course.  We edit an interval.
;;; When returning to the rubout handler's caller, we must update *RUBOUT-HANDLER-BUFFER*
;;; for the changes that got made in the interval.

;;; We mark the buffer unmodified here.  That way, the buffer is modified only if
;;; it does not match what is in the rh buffer.
(defun update-rh-buffer (buffer interval point
			 &optional (update-start-bp (interval-first-bp interval)))
  "Updates the contents of BUFFER from the INTERVAL.
The RH-TYPEIN-POINTER is set according to the bp POINT.
Assumes that text in INTERVAL up to UPDATE-START-BP
 is already present and correct in BUFFER."
  (send *interval* :not-modified)
  (do ((line (bp-line (interval-first-bp interval)) (line-next line))
       (updating-yet nil)
       line-start-index
       (buffer-index 0)
       (stop-line (bp-line (interval-last-bp interval))))
      (())
    ;; If POINT falls in this line, set BUFFER's typein-pointer.
    (when (eq line (bp-line point))
      (setf (rhb-typein-pointer buffer)
	    (+ buffer-index (bp-index point))))
    ;; Have we arrived yet at the line to start copying at?
    (if (eq line (bp-line update-start-bp))
	(setq updating-yet t
	      line-start-index (bp-index update-start-bp))
      (setq line-start-index 0))
    ;; If copying started, copy this line.
    (when updating-yet
      (unless ( (array-length buffer) (+ buffer-index (length line)))
	(adjust-array-size buffer (* 2 (array-length buffer))))
      (copy-array-portion line line-start-index (length line)
			  buffer (+ buffer-index line-start-index)
			  (+ buffer-index (length line)))
      (setf (aref buffer (+ buffer-index (length line))) #/newline))
    (incf buffer-index (1+ (length line)))
    ;; Exit after processing last line.
    (when (eq line stop-line)
      (setf (rhb-fill-pointer buffer)
	    (1- buffer-index))  ;-1 since final #\newline doesn't belong.
      (return nil))))

(defun old-buffer-contents-match-p (buffer interval)
  "Is BUFFER (an array) an initial segment of INTERVAL's contents?
Value is non-NIL if so; in that case, it is actually
a BP to the first character in INTERVAL not in BUFFER."
  (do ((line (bp-line (interval-first-bp interval))
	     (line-next line))
       (buffer-index 0)
       (stop-line (bp-line (interval-last-bp interval))))
      (())
    (let ((compare-amount (min (length line)
			       (- (length buffer) buffer-index)))
;character lossage
	  kludge)
      (dotimes (i compare-amount)
	(if (fixnump (setq kludge (cli:aref buffer (+ buffer-index i))))
	    (setq kludge (int-char kludge)))
	(unless (eq kludge (char line i))
	  ;; Mismatch within this line.
	  (return nil)))
      (incf buffer-index (length line))
      ;; End of BUFFER reached => all of BUFFER matches.
      (when ( buffer-index (length buffer))
	(return (create-bp line compare-amount))))
    ;; End of INTERVAL means mismatch, since not end of BUFFER.
    (when (eq line stop-line)
      (return nil))
    ;; Mismatch with Return at end of line?
    (unless (char= (char buffer buffer-index) #/newline)
      (return nil))
    (incf buffer-index)
    ;; BUFFER ends with that Return => all matches.
    (when ( buffer-index (length buffer))
      (return (create-bp (line-next line) 0)))))

;;; The :SAVE-RUBOUT-HANDLER-BUFFER window op calls this function
;;; whose purpose is to make sure TV::RUBOUT-HANDLER-BUFFER is up to date to be saved.
(defun editor-rubout-handler-save-buffer (window)	;window is just self
  (declare (:self-flavor tv:stream-mixin))
  (let ((displayer (window-editor-rubout-handler window)))
    (update-rh-buffer tv::rubout-handler-buffer
		      (window-interval displayer)
		      (window-point displayer))
    (setf (rhb-scan-pointer tv::rubout-handler-buffer)
	  (length tv::rubout-handler-buffer))))

;; This property says what function to call to handle
;; a :SAVE-RUBOUT-HANDLER-BUFFER op on the window.
(defprop editor-rubout-handler
	 editor-rubout-handler-save-buffer
	 tv::save-rubout-handler-buffer)


;;; This function gets called whenever a :TYI message is sent to the stream and we are
;;; inside the rubout handler.  If a normal character is typed, it is echoed, put in the
;;; buffer, and returned.  If a rubout or any other editing character is typed, any number
;;; of editing commands are processed by modifying the buffer, then, when the first
;;; non-editing character is typed, a throw is done back to the top level of the read
;;; function and the buffered input is re-scanned.  The character must be typed at the end
;;; of the line in order for the throw to take place.

(defun editor-rubout-handler ()
  (declare (:self-flavor tv:stream-mixin))
  (let* ((*rubout-handler-buffer* tv::rubout-handler-buffer)
	 (*rubout-handler-activation-character* nil)
	 ;; Prevent calls to this function when the editor reads input!
	 (rubout-handler nil)
	 (displayer (window-editor-rubout-handler self)))
    ;; If this is the first entry to us within a given :RUBOUT-HANDLER operation,
    ;; initialize editor buffer from window system buffer.
    (if (memq (rhb-status) '(:initial-entry :restored))
	(editor-rubout-handler-initial-entry (rhb-status) displayer)
      (setf (rhb-status) nil))
    ;; Kludge #4.  After resuming a Break, the stream's cursorpos is wrong.
    ;; In fact, the cursor is at the end of the string in that case.
    (must-redisplay displayer dis-bps)
    ;; In case we had to return to the caller with a EDITING-COMMAND char
    ;; while editing, make things consistent again by causing a rescan now.
    (cond ((and (eq (rhb-status) ':rubout)
		(= (rhb-scan-pointer) most-positive-fixnum))
	   (rh-rescan t))
	  ;; Read characters.
	  ;; A command hook may throw to RETURN-CHARACTER with a character
	  ;; to be returned, if no old characters were changed.
	  ;; Otherwise it will throw to RUBOUT-HANDLER,
	  ;; whose CATCH is in the :RUBOUT-HANDLER method of our caller.
	  ((prog1 *rubout-handler-activation-character*
		  (setq *rubout-handler-activation-character* nil)))
	  (t
	   (catch 'return-character
	     (catch 'abort-out-of-rubout-handler
	       (send displayer :edit))		;Never returns.
	     (signal eh:abort-object))))))

(defun editor-rubout-handler-initial-entry (initial-status displayer)
  (declare (:self-flavor tv:stream-mixin))
  (let* ((initial-input (cdr (assq :initial-input rubout-handler-options)))
	 (length (length initial-input))
	 (initial-input-pointer
	   (if (eq initial-status ':restored)
	       (rhb-typein-pointer)
	       (max (min (or (cdr (assq ':initial-input-pointer rubout-handler-options))
			     length)
			 length)
		    0)))
	 (prompt-option (assq ':prompt rubout-handler-options))
	 (window self))
    (funcall (window-editor-closure displayer)
	     #'(lambda ()
		 (unless *input-history*
		   (setq *input-history*
			 (zwei:make-history (format nil "~A input history" window) 
					    :element-string-function 'identity))
		   ;>>; ?? Is this really useful??
		   ;;Recover anything that was in the rh buffer's input history previously
		   (let ((history (rhb-input-history)))
		     (when (typep history 'history)
		       (dolist (x (history-list history))
			 (setq x (typecase x
				   (node x)
				   (array
				    (let ((tem (make-string (length x))))
				      (copy-array-portion x 0 (length x) tem 0 (length x))
				      tem))))
			 (when x (push-on-history x *input-history*))))))
		 ;;No point in doing this since the histories used by the rh in window;rh
		 ;; doesn't grok nodes (and uses a different yank method in any case)
		 ;; The history from this rh is thus lost if luser switches to a different
		 ;; flavour of rh.
		 ;(setf (rhb-input-history) *input-history*)
		 (setf (rhb-status) nil)
		 (when (eq initial-status ':initial-entry)
		   (unless (bp-= (interval-first-bp (window-interval displayer))
				 (interval-last-bp (window-interval displayer)))
		     (let ((typein-pointer (rhb-typein-pointer)))
		       (when (and (not (rhb-dont-save-flag))
				  typein-pointer
				  ;; only add the contents if different from last time
				  ;; and if it is at least 2 characters long.
				  (> typein-pointer 1)
				  (mismatch *rubout-handler-buffer*
					    (history-latest-element (rhb-input-history))
					    :end1 typein-pointer))
			 (push-on-history (let ((tem (make-string typein-pointer)))
					    (copy-array-portion
					      *rubout-handler-buffer* 0 typein-pointer
					      tem 0 typein-pointer)
					    tem)
					  *input-history*))))
		   (when ( length (array-length *rubout-handler-buffer*))
		     (adjust-array-size *rubout-handler-buffer* (+ length length)))
		   (when initial-input
		     (copy-array-portion initial-input 0 length
					 *rubout-handler-buffer* 0 length)
		     (setf (rhb-status) ':rubout)))
		 (discard-undo-information (window-interval displayer))
		 (send displayer :initialize-contents *rubout-handler-buffer*
		       initial-input-pointer
		       tv::rubout-handler-starting-y
		       tv::rubout-handler-starting-x)
		 (setf (rhb-dont-save-flag)
		       (or (cadr (assq :dont-save rubout-handler-options))
			   (cadr (assq :no-input-save rubout-handler-options))))
		 (when prompt-option
		   ;; this kludgery is to make :reprompt go away, since the :reprompt
		   ;;  operation looks first for :reprompt and then for :prompt
		   (with-stack-list (foo :reprompt nil)
		     (with-stack-list* (rubout-handler-options foo rubout-handler-options)
		       (send window :reprompt))))
		 (must-redisplay displayer dis-text)))))


;;;; Command hooks to make editor interface with its caller the rubout handler.

(defmethod (editor-rubout-displayer :process-command-char) (ch)
  ;; make damn sure our hooks are run first!
  (with-stack-list* (*command-hook* 'rh-pre-command-hook *command-hook*)
    (with-stack-list* (*post-command-hook* 'rh-post-command-hook *post-command-hook*)
      (process-command-char ch))))

;;; Handle the TV::REDISPLAY-RUBOUT-HANDLER blip that indicates a notification
;;; or some such thing was output on the window.
(defmethod (editor-rubout-displayer :process-special-command) (subop &rest args)
  (case subop
    (tv::redisplay-rubout-handler
     (dotimes (p n-plines)
       (setf (pline-tick self p) -1))
     (send window :set-cursorpos left (second args))
     (send self :reprompt)
     (must-redisplay self dis-text))
    (t
     nil)))

;; This function is called before each editing command.

;massive character lossage throughout this entire function

(defun rh-pre-command-hook (char)
  (when t ;;**** have to have some test to check that we're in the right interval ****
    (let ((command-handler (cdr (assq ':command rubout-handler-options)))
	  (do-not-echo (cdr (assq ':do-not-echo rubout-handler-options)))
	  (activation-handler (cdr (assq ':activation rubout-handler-options)))
	  (editing-command (cdr (assq ':editing-command rubout-handler-options)))
	  (pass-through (cdr (assq ':pass-through rubout-handler-options))))
      (cond ((and (or (memq char pass-through)
		      (memq (char-int char) pass-through))
		  (zerop (char-bits char)))
	     ;; Make redisplay finish now, since we will avoid doing so afterward.
	     (redisplay *window* :point nil nil t)
	     (insert-moving (point) char)
	     (rh-continue-or-rescan t))
	    ((and command-handler
		  (apply (car command-handler) char (cdr command-handler)))
	     (move-bp (point) (interval-last-bp *interval*))
	     (update-rh-buffer *rubout-handler-buffer* *interval* (point))
	     (setf (rhb-scan-pointer) 0)
	     (throw 'tv::return-from-rubout-handler
		     (values
		       `(:command ,(char-int char) ,(or *numeric-arg* 1))
		       :command)))
	    ((or (and activation-handler
		      (apply (car activation-handler)
			     char (cdr activation-handler)))
		 (memq char do-not-echo)
		 (memq (char-int char) do-not-echo))
	     (let ((tem (if (or (memq char do-not-echo) (memq (char-int char) do-not-echo))
			    (char-int char)
			  `(:activation ,(char-int char) ,(or *numeric-arg* 1)))))
	       (move-bp (point) (interval-last-bp *interval*))
	       (cond ((eq (rhb-status) ':rubout)
; (format *error-output* "~&>> Throwing to RUBOUT-HANDLER")
		      ;; Why isn't this done in the :RUBOUT-HANDLER method loop?
		      (setq *rubout-handler-activation-character* tem)
		      (rh-rescan t))
		     (t 
; (format *error-output* "~&>> Throwing to RETURN-CHARACTER")
		      (update-rh-buffer *rubout-handler-buffer* *interval* (point))
		      (throw 'return-character tem)))))
;>> this is is utterly bogus
	    ((or (memq char editing-command)
		 (memq (char-int char) editing-command)
		 (si:assq-careful char editing-command)
		 (si:assq-careful (char-int char) editing-command))
	     (if (eq (rhb-status) ':rubout)
		 (setf (rhb-scan-pointer) most-positive-fixnum)))))))

;; Command hook. If at the end of the buffer after the command,
;;  send through the buffered input.
(defun rh-post-command-hook (char)
  (declare (ignore char))
  (cond ;; full-rubout
	((and (cdr (assq :full-rubout rubout-handler-options))
	      (bp-= (interval-first-bp *interval*)
		    (interval-last-bp *interval*)))
	 (rh-rescan nil))
	;; inserting at the end of the buffer
	((and (eq *current-command-type* 'self-insert)
	      (bp-= (point) (interval-last-bp *interval*)))
;>> changed this to inhibit-redisplay to make input faster
	 (rh-continue-or-rescan t))
	;; else just do bookkeeping
	(t
	 (if (send *interval* :modified-p) (setf (rhb-status) ':rubout)))))

;;Call here if we have just inserted a character that should be given now
;;to the parser function.
;;If (rhb-status) is nil, what we have already given to the parser function
;;is still valid, so just give it the additional character.
;;Otherwise, (rhb-status) is :rubout, meaning something previously given
;;was edited.  In that case, the parser function must be restarted at the beginning.
;;>> inhibit-redisplay is always T now...
;;>> (else redisplay when typing quickly at end end of buffer is far too slow)
(defun rh-continue-or-rescan (inhibit-redisplay &aux old-match-p)
  (unless inhibit-redisplay
    (redisplay *window* :point nil nil t))	;Force redisplay to completion.
  (if (not (rhb-status))
      ;; Previously given characters have not been changed.
      (progn
	(setq old-match-p (old-buffer-contents-match-p *rubout-handler-buffer* *interval*))
	(update-rh-buffer *rubout-handler-buffer* *interval* (point)
			  (or old-match-p (interval-first-bp *interval*)))
	(throw 'return-character
	       (prog1 (aref *rubout-handler-buffer* (rhb-scan-pointer))
		      (incf (rhb-scan-pointer)))))
    ;; else rescan
    (rh-rescan t)))

;; Make the parser function scan the buffered input from the beginning.
;; This is necessary if any editing has happened other than insertion at end of buffer.
;; Also, if the user specified the :full-rubout option, he wants this to happen any time
;; the buffer becomes empty.

(defun rh-rescan (inhibit-redisplay)
  (unless inhibit-redisplay				;Force redisplay to completion.
    (redisplay *window* :point nil nil t))
  (update-rh-buffer *rubout-handler-buffer* *interval* (point))
  (setf (rhb-scan-pointer) 0)
  (throw 'rubout-handler t))


;;;; Special editing commands for rubout handling.

(defcom com-kill-all "Kill entire contents of buffer." ()
  (kill-interval (interval-first-bp *interval*) (interval-last-bp *interval*) t t)
  dis-text)

(defcom com-rh-break "Break key while in rubout handling editor." ()
  (let ((*terminal-io* (send *window* :window)))
    (com-break)))

(defcom com-rh-meta-break "Enter the debugger." ()
  (let ((*terminal-io* (send *window* :window)))
    (tv::kbd-intercept-error-break *last-command-char*)
    dis-none))

(defcom com-rh-abort "Abort running program.
This is on the Abort key in the rubout handler editor." ()
  (throw 'abort-out-of-rubout-handler t))

(defcom com-abort-all "Reset this process; abort all the way out." ()
  (tv::kbd-intercept-abort-all *last-command-char*))

(defcom com-rh-yank-input "Reinsert the text of a previous input.
The last entry on the input ring is reinserted.
A numeric arg specifies an input ring entry (1 is most recent)." ()
  (history-yank *input-history*)
  dis-text)

(defcom com-rh-quick-arglist "Print argument list of this function.
Uses the function which POINT is inside a call to." ()
  (quick-arglist *standard-output*)
  dis-none)

(defcom com-rh-quick-documentation "Print long documentation for this function.
Uses the function which POINT is inside a call to." ()
  (let ((name (relevant-function-name (point))))
    (let ((doc (documentation name 'function)))
      (cond ((null doc)
	     (format t "~&Function ~S is not documented" name))
	    (t (if (fdefinedp name)
		   (progn (send *standard-output* :fresh-line)
			  (print-arglist name *standard-output*))
		 (format t "~S:" name))
	       (format t "~%~A" doc)))))
    dis-none)

(defcom com-rh-display-kill-history document-rh-history () 
  (rh-display-history *kill-history* nil))
(defcom com-rh-display-rest-of-kill-history document-rh-history ()
  (rh-display-history *kill-history* t))
(defcom com-rh-display-input-history document-rh-history () 
  (rh-display-history *input-history* nil))
(defcom com-rh-display-rest-of-input-history document-rh-history ()
  (rh-display-history *input-history* t))

(defun rh-display-history (history restp &aux start end)
  (when (eq *last-command-type* history)
    (setq *current-command-type* history))
  (if restp
      (setq start (if *numeric-arg-p* *numeric-arg* *history-menu-length*)
	    end (history-length history))
      (setq start 0
	    end (if *numeric-arg-p* *numeric-arg* *history-menu-length*)))
  (list-history-contents history *standard-output* start end)
  dis-none)

(defun document-rh-history (com ignore op &aux k r)
  (if (eq op ':name)
      (get com 'command-name)
    (case com
      (com-rh-display-input-history)
      (com-rh-display-rest-of-input-history (setq r t))
      (com-rh-display-kill-history (setq k t))
      (com-rh-display-rest-of-kill-history (setq k t r t)))
    (format t "Display summary of the~:[~; rest of the~] ~
		~:[rubout handler input history for this window~;kill history.~]~%" r k)
    (when (eq op ':full)
      (unless r
	(format t "Length of history displayed is controlled by ~S (currently ~D.)~%"
		'zwei:*history-menu-length* zwei:*history-menu-length*)
	(format t "~@[~&Type ~A to see the rest of the history.~]"
		(key-for-command (if k 'com-rh-display-rest-of-kill-history
				   'com-rh-display-rest-of-input-history)
				 *comtab* nil nil
				 (if k #/c-m-status #/m-status)))))))

;;; Comtabs are like standard-comtab but missing
;;; all things that use the minibuffer (for now...)
;;; or hack with fonts or with windows or files or buffers
;;; or do evaluation.

;>> is sparseness the right thing??
(defvar *rubout-handler-comtab* (create-sparse-comtab '*rubout-handler-comtab*))
(defvar *rubout-handler-control-x-comtab* 
	(create-sparse-comtab '*rubout-handler-control-x-comtab*))

(defun init-rubout-handler-comtab ()
  (set-comtab *rubout-handler-comtab*
	      '(#/c-sh-d com-rh-quick-documentation
		#/c-sh-a com-rh-quick-arglist
		#/status com-rh-display-input-history
		#/c-status com-rh-display-kill-history
		#/m-status com-rh-display-rest-of-input-history
		#/c-m-status com-rh-display-rest-of-kill-history
		#/break com-rh-break
		#/meta-break com-rh-meta-break
		#/abort com-rh-abort
		#/m-abort com-abort-all
		#/clear-input com-kill-all
		#/clear-screen com-recenter-window

		#/c-l com-recenter-window
		#/c-m-y com-rh-yank-input
		#/c-c com-rh-yank-input

	        #/c-v :undefined
	        #/m-v :undefined
	        #/c-m-v :undefined
	        #/m-r :undefined
		#/c-m-! :undefined
		#/m- :undefined
		#/c- :undefined
		#/c-h-c :undefined
		#/c-h-e :undefined
		#/m-h-e :undefined
		#/c-m-h-e :undefined
		#/c-m-? :undefined
		#/help :undefined
		#/c-help :undefined
		#/m-x :undefined
		#/c-m-x :undefined
		#/c-m-r :undefined
		#/c-z :undefined
		#/c-j :undefined
		#/m-j :undefined
		#/c-m-j :undefined
		#/m-# :undefined
		#/m-_ :undefined
		#/h-m-p :undefined
		#/mouse-1-1 :undefined
		#/mouse-1-2 :undefined
		#/mouse-2-1 :undefined
		#/mouse-2-2 :undefined
		#/mouse-3-1 :undefined
		))
  (set-comtab-indirection *rubout-handler-comtab* *standard-comtab*)
  (set-comtab *rubout-handler-control-x-comtab*
	      '(#/c-d :undefined
		#/c-q :undefined
		#/c-c :undefined
		#/c-j :undefined
		#/( :undefined
		#/) :undefined
		#/e :undefined
		#/q :undefined
		#/ :undefined
		#/# :undefined
		#/_ :undefined
		))
  (set-comtab-indirection *rubout-handler-control-x-comtab* *standard-control-x-comtab*)
  (set-comtab-control-indirection *rubout-handler-control-x-comtab*)
  (set-comtab *rubout-handler-comtab*
              (list #/c-x (make-extended-command *rubout-handler-control-x-comtab*))))

(unless (comtab-indirect-to *rubout-handler-comtab*)
  (init-rubout-handler-comtab))


(compile-flavor-methods editor-rubout-displayer)
