;; the buffer since we want to save the input to be yanked back. (:CLEAR-INPUT () (COND (RUBOUT-HANDLER (SETF (FILL-POINTER) 0) (SETF (SCAN-POINTER) 0) (SETF (TYPEIN-POINTER) 0))) (SETQ ES-UNTYI-CHAR NIL) (FUNCALL ES-CONSOLE-STREAM ':CLEAR-INPUT)) ;; The first argument for this operation is an alist of options ;; or NIL. The options currently supported are :FULL-RUBOUT, :PROMPT, ;; :REPROMPT and :PASS-THROUGH. (:RUBOUT-HANDLER (OPTIONS READ-FUNCTION &REST ARGS TEMP) ;; Save whatever the previous input was by cycling through the kill ring. ;; If the previous input was the empty string, as in exiting via the :FULL-RUBOUT ;; option, don't bother. (COND ((NOT (ZEROP (FILL-POINTER))) (SETQ ES-KILL-RING (CIRCULAR-LIST-LAST ES-KILL-RING)) (SETQ ES-BUFFER (CAR ES-KILL-RING)))) ;; Empty the rubout handler buffer of the new buffer. (SETF (FILL-POINTER) 0) (SETF (TYPEIN-POINTER) 0) ;; Prompt if desired. (SETQ TEMP (ASSQ ':PROMPT OPTIONS)) (IF TEMP (FUNCALL (CADR TEMP) #'EDITOR-STREAM NIL)) ;; Record the position on the screen at which the handler was entered. (IF (MEMQ ':READ-CURSORPOS ES-WHICH-OPERATIONS) (MULTIPLE-VALUE (ES-X-ORIGIN ES-Y-ORIGIN) (FUNCALL ES-CONSOLE-STREAM ':READ-CURSORPOS))) ;; Absorb and echo the untyi'ed char. We don't need to do this while inside ;; the rubout handler since characters get echoed there. This code runs when ;; READ is called from inside the error handler, for instance. ;; This is kind of a crock since the rubout handler should decide what to do ;; with the character. (COND (ES-UNTYI-CHAR (ARRAY-PUSH ES-BUFFER ES-UNTYI-CHAR) (FUNCALL ES-CONSOLE-STREAM ':TYO (LDB %%KBD-CHAR ES-UNTYI-CHAR)) (SETQ ES-UNTYI-CHAR NIL))) ;; These two specials used for communication up and down the stack. ;; First says we're inside the rubout handler, and the second passes ;; options down to the editor function. These specials could be flushed if this ;; state information were kept in the stream, but it would have to be explicitly ;; turned on and off when a specific stack frame was entered and exited. (DO ((RUBOUT-HANDLER T) (TV-MAKE-STREAM-RUBOUT-HANDLER-OPTIONS OPTIONS)) (NIL) ;; Loop until normal (non-throw) exit, which will pass by the CATCH and DO. ;; Each time we enter this loop (after the first time), we are preparing for ;; a re-scan of the input string, so reset the scan pointer. (SETF (SCAN-POINTER) 0) (*CATCH 'RUBOUT-HANDLER (PROGN (ERRSET ;; APPLY is more efficient than LEXPR-FUNCALL if the read ;; function takes a &rest argument. (MULTIPLE-VALUE-RETURN (APPLY READ-FUNCTION ARGS))) ;; On read error, reprint contents of buffer so user can rub out ok. ;; The ERRSET lets the error message get printed. (IF (MEMQ ':READ-CURSORPOS ES-WHICH-OPERATIONS) (MULTIPLE-VALUE (ES-X-ORIGIN ES-Y-ORIGIN) (FUNCALL ES-CONSOLE-STREAM ':READ-CURSORPOS))) (FUNCALL ES-CONSOLE-STREAM ':STRING-OUT ES-BUFFER) ;; If the user makes an error during read-time, swallow ;; all input until a rubout (or some editing character) ;; is typed. When the edit is complete, we will ;; throw back out of here. This should be changed to stop ;; echoing until the edit is done. (DO () (NIL) (EDITOR-STREAM ':TYI)))) ;; When a rubout or other editing operation is done, throws back to the ;; catch to reread the input. But if the :FULL-RUBOUT option was specified ;; and everything was rubbed out, we return NIL and the specified value. (IF (AND (ZEROP (FILL-POINTER)) (SETQ TEMP (ASSQ ':FULL-RUBOUT OPTIONS))) (RETURN NIL (CADR TEMP))) ))) ;; Returns the "last" element in a circular list, i.e. the cons pointing to the cons we ;; are holding on to. (DEFUN CIRCULAR-LIST-LAST (LIST) (DO ((L (CDR LIST) (CDR L)) (PREV LIST L)) ((EQ LIST L) PREV))) ;; (DEFUN CIRCULAR-LIST-LENGTH (LIST) ;; (IF (NULL LIST) 0 ;; (DO ((L (CDR LIST) (CDR L)) ;; (I 1 (1+ I))) ;; ((EQ LIST L) I)))) ;; Forward all other operations to the underlying stream rather than ;; STREAM-DEFAULT-HANDLER. Again, we really need a handle on SELF. (DEFUN EDITOR-STREAM-DEFAULT-HANDLER (&REST REST) (LEXPR-FUNCALL ES-CONSOLE-STREAM REST))