;;; -*- Mode:LISP; Package:TV; Base:8; Lowercase:T; Readtable:ZL -*- ;;; ** (c) Copyright 1980 Massachusetts Institute of Technology ** ;;;This is overridden by loading SYS:WINDOW;RH, which is now standardly loaded. (DEFVAR STREAM-MIXIN-DEFAULT-RUBOUT-HANDLER 'DEFAULT-RUBOUT-HANDLER "Default rubout-handler to use for input from windows") ;;; Io stream stuff (DEFFLAVOR STREAM-MIXIN (;; I/O buffer for this stream (IO-BUFFER NIL) (RUBOUT-HANDLER-BUFFER NIL) ;; Used for :PREEMPTABLE-READ. (OLD-TYPEAHEAD NIL) ;; nil => use stream-mixin-default-rubout-handler (stream-rubout-handler) (displayer nil) stream-spare-1 stream-spare-2 stream-spare-3 stream-spare-4 stream-spare-5 stream-spare-6) () (:REQUIRED-FLAVORS SHEET ESSENTIAL-WINDOW) ;Explicit presence of SHEET ;helps init flavor-unmapped-instance-variables (:SELECT-METHOD-ORDER :TYO :STRING-OUT :LINE-OUT :TYI :TYI-NO-HANG :LISTEN) (:GETTABLE-INSTANCE-VARIABLES IO-BUFFER stream-rubout-handler displayer) (:INITABLE-INSTANCE-VARIABLES IO-BUFFER RUBOUT-HANDLER-BUFFER) (:SETTABLE-INSTANCE-VARIABLES OLD-TYPEAHEAD stream-rubout-handler displayer) (:INIT-KEYWORDS :ASYNCHRONOUS-CHARACTERS) (:DOCUMENTATION :MIXIN "Ordinary tv stream operations. Gives all the meaningful stream operations for a display, such as :TYO, :TYI, :RUBOUT-HANDLER, :STRING-OUT, etc. Include this flavor someplace so that the window can be passed to functions that take streams as arguments, and especially if *TERMINAL-IO* is going to be bound to the window.")) (DEFMETHOD (STREAM-MIXIN :BEFORE :INIT) (INIT-PLIST) (SEND SELF :WHICH-OPERATIONS) ;Pre-create this, certainly going to be used (UNLESS (TYPEP IO-BUFFER 'IO-BUFFER) (LET (SIZE INPUT-FUNCTION OUTPUT-FUNCTION) (IF (NUMBERP IO-BUFFER) (SETQ SIZE IO-BUFFER INPUT-FUNCTION NIL OUTPUT-FUNCTION 'KBD-DEFAULT-OUTPUT-FUNCTION) (SETQ SIZE (OR (FIRST IO-BUFFER) 100) INPUT-FUNCTION (SECOND IO-BUFFER) OUTPUT-FUNCTION (OR (THIRD IO-BUFFER) 'KBD-DEFAULT-OUTPUT-FUNCTION))) (SETQ IO-BUFFER (MAKE-IO-BUFFER SIZE INPUT-FUNCTION OUTPUT-FUNCTION)))) (IF (GETL INIT-PLIST '(:ASYNCHRONOUS-CHARACTERS)) (SETF (GETF (IO-BUFFER-PLIST IO-BUFFER) :ASYNCHRONOUS-CHARACTERS) (GET INIT-PLIST :ASYNCHRONOUS-CHARACTERS))) (UNLESS RUBOUT-HANDLER-BUFFER (SETQ RUBOUT-HANDLER-BUFFER (MAKE-RUBOUT-HANDLER-BUFFER)))) (DEFMETHOD (STREAM-MIXIN :ADD-ASYNCHRONOUS-CHARACTER) (CHARACTER FUNCTION &REST ARGS) ;character lossage (IF (CHARACTERP CHARACTER) (SETQ CHARACTER (CHAR-INT CHARACTER))) (CHECK-TYPE CHARACTER FIXNUM "a character") (CHECK-TYPE FUNCTION (SATISFIES FUNCTIONP) "a function") (LET ((PLIST (LOCF (IO-BUFFER-PLIST IO-BUFFER)))) (PUSH (LIST* CHARACTER FUNCTION (COPYLIST ARGS)) (GET PLIST :ASYNCHRONOUS-CHARACTERS)))) (DEFMETHOD (STREAM-MIXIN :ASYNCHRONOUS-CHARACTER-P) (CHARACTER) ;character lossage (IF (CHARACTERP CHARACTER) (SETQ CHARACTER (CHAR-INT CHARACTER))) (LET* ((PLIST (LOCF (IO-BUFFER-PLIST IO-BUFFER))) (ALIST (GET PLIST :ASYNCHRONOUS-CHARACTERS))) (ASSQ CHARACTER ALIST))) (DEFMETHOD (STREAM-MIXIN :HANDLE-ASYNCHRONOUS-CHARACTER) (CHARACTER) ;character lossage (IF (CHARACTERP CHARACTER) (SETQ CHARACTER (CHAR-INT CHARACTER))) (LET* ((PLIST (LOCF (IO-BUFFER-PLIST IO-BUFFER))) (ALIST (GET PLIST :ASYNCHRONOUS-CHARACTERS)) (TEM (ASSQ CHARACTER ALIST))) (WHEN TEM (APPLY (CADR TEM) (CAR TEM) SELF (CDDR TEM))))) (DEFMETHOD (STREAM-MIXIN :REMOVE-ASYNCHRONOUS-CHARACTER) (CHARACTER) ;character lossage (IF (CHARACTERP CHARACTER) (SETQ CHARACTER (CHAR-INT CHARACTER))) (LET* ((PLIST (LOCF (IO-BUFFER-PLIST IO-BUFFER))) (ALIST (GET PLIST :ASYNCHRONOUS-CHARACTERS))) (SETF (GET PLIST :ASYNCHRONOUS-CHARACTERS) (DELQ (ASSQ CHARACTER ALIST) ALIST)))) (DEFMETHOD (STREAM-MIXIN :DIRECTION) () :BIDIRECTIONAL) (DEFMETHOD (STREAM-MIXIN :BEFORE :SELECT) (&REST IGNORE) (KBD-CLEAR-SELECTED-IO-BUFFER)) (DEFMETHOD (STREAM-MIXIN :BEFORE :DESELECT) (&REST IGNORE) (KBD-CLEAR-SELECTED-IO-BUFFER)) (DEFMETHOD (STREAM-MIXIN :SET-IO-BUFFER) (NEW-BUFFER) (WITHOUT-INTERRUPTS (KBD-CLEAR-SELECTED-IO-BUFFER) (SETQ IO-BUFFER NEW-BUFFER))) (DEFMETHOD (STREAM-MIXIN :PUSH-INPUT) (INPUT) (IF (STRINGP INPUT) (DO ((I (1- (STRING-LENGTH INPUT)) (1- I))) ((MINUSP I)) (IO-BUFFER-PUSH IO-BUFFER (AREF INPUT I))) (IO-BUFFER-PUSH IO-BUFFER INPUT))) (DEFMETHOD (STREAM-MIXIN :UNTYI) (CH) (IF (AND (eq rubout-handler self) ;; RUBOUT-HANDLER added as conjunct 6/1/83 ;; to avoid lossage entering editor rubout handler ;; by typing (= 1 2) then stray ) while inside BREAK. ( 1 (RHB-SCAN-POINTER) (RHB-FILL-POINTER)) (EQ CH (AREF RUBOUT-HANDLER-BUFFER (1- (RHB-SCAN-POINTER))))) (DECF (RHB-SCAN-POINTER)) (IO-BUFFER-UNGET IO-BUFFER CH)) CH) (DEFMETHOD (STREAM-MIXIN :UNREAD-CHAR) (CH) (IF (CHARACTERP CH) (SETQ CH (CHAR-INT CH))) (SEND SELF :UNTYI CH)) (DEFMETHOD (STREAM-MIXIN :LISTEN) () (NOT (AND ( (RHB-FILL-POINTER) (RHB-SCAN-POINTER)) (IO-BUFFER-EMPTY-P IO-BUFFER) (WITHOUT-INTERRUPTS (IF (NEQ IO-BUFFER (KBD-GET-IO-BUFFER)) T (AND (KBD-HARDWARE-CHAR-AVAILABLE) (KBD-PROCESS-MAIN-LOOP-INTERNAL)) (IO-BUFFER-EMPTY-P KBD-IO-BUFFER)))))) (DEFMETHOD (STREAM-MIXIN :WAIT-FOR-INPUT-WITH-TIMEOUT) (TIMEOUT) (KBD-WAIT-FOR-INPUT-WITH-TIMEOUT IO-BUFFER TIMEOUT)) (DEFMETHOD (STREAM-MIXIN :CLEAR-INPUT) () (SETF (RHB-FILL-POINTER) 0) (SETF (RHB-SCAN-POINTER) 0) (IO-BUFFER-CLEAR IO-BUFFER) (AND (EQ IO-BUFFER (KBD-GET-IO-BUFFER)) (KBD-CLEAR-IO-BUFFER))) (DEFMETHOD (STREAM-MIXIN :TYI) (&OPTIONAL IGNORE &AUX CH) (DO-FOREVER (SETQ CH (SEND SELF :ANY-TYI)) (WHEN (NUMBERP CH) (RETURN CH)) (WHEN (AND (CONSP CH) (EQ (CAR CH) :MOUSE-BUTTON) (MEMQ (CADR CH) '(#/MOUSE-3-1 #.(CHAR-INT #/MOUSE-3-1)))) (MOUSE-CALL-SYSTEM-MENU)))) (DEFMETHOD (STREAM-MIXIN :READ-CHAR) (&OPTIONAL IGNORE IGNORE) (INT-CHAR (SEND SELF :TYI))) (DEFMETHOD (STREAM-MIXIN :TYI-NO-HANG) (&OPTIONAL IGNORE &AUX CH) (DO-FOREVER (SETQ CH (SEND SELF :ANY-TYI-NO-HANG)) (WHEN (OR (NULL CH) (NUMBERP CH)) (RETURN CH)) (WHEN (AND (CONSP CH) (EQ (CAR CH) :MOUSE-BUTTON) (MEMQ (CADR CH) '(#/MOUSE-3-1 #.(CHAR-INT #/MOUSE-3-1)))) (MOUSE-CALL-SYSTEM-MENU)))) (DEFMETHOD (STREAM-MIXIN :READ-CHAR-NO-HANG) (&OPTIONAL IGNORE IGNORE) (INT-CHAR (SEND SELF :TYI-NO-HANG))) (DEFMETHOD (STREAM-MIXIN :ANY-TYI) (&OPTIONAL IGNORE &AUX IDX) (COND ((> (RHB-FILL-POINTER) (SETQ IDX (RHB-SCAN-POINTER))) (SETF (RHB-SCAN-POINTER) (1+ IDX)) (OR (AREF RUBOUT-HANDLER-BUFFER IDX) (FERROR "EOF on input from a window."))) ((neq rubout-handler self) ; (SETF (RHB-SCAN-POINTER) (RHB-FILL-POINTER)) (LET ((CHAR (COND ((KBD-IO-BUFFER-GET IO-BUFFER T)) (T (SEND SELF :NOTICE :INPUT-WAIT) (KBD-IO-BUFFER-GET IO-BUFFER))))) (IF (AND (eq rubout-handler-inside self) (EQ OLD-TYPEAHEAD T) (CONSP CHAR) (NEQ (CAR CHAR) 'REDISPLAY-RUBOUT-HANDLER)) ;; If inside the rubout handler in a :PREEMPTABLE-READ ;; and we just got a blip that isn't intended for the rubout handler. (PROGN (MULTIPLE-VALUE-BIND (STRING INDEX) (SEND SELF :SAVE-RUBOUT-HANDLER-BUFFER) (SETQ OLD-TYPEAHEAD (LIST STRING INDEX))) ;; Save the text, rub it all out, and unread the blip. ;; The :FULL-RUBOUT option will cause the RH to return to the caller ;; who will then read the blip. (SEND SELF :UNTYI CHAR) (CHAR-INT #/CLEAR)) CHAR))) (T (OR (FUNCALL (or stream-rubout-handler stream-mixin-default-rubout-handler)) (FERROR "EOF on input from a window."))))) (DEFMETHOD (STREAM-MIXIN :ANY-READ-CHAR) (&OPTIONAL IGNORE IGNORE &AUX CH) (SETQ CH (SEND SELF :TYI-NO-HANG)) (IF (FIXNUMP CH) (INT-CHAR CH) CH)) (DEFMETHOD (STREAM-MIXIN :ANY-TYI-NO-HANG) (&OPTIONAL IGNORE) (if (> (RHB-FILL-POINTER) (RHB-SCAN-POINTER)) (send self :any-tyi)) (if (neq rubout-handler self) (KBD-IO-BUFFER-GET IO-BUFFER T) (FERROR "~S from inside a rubout handler." :ANY-TYI-NO-HANG))) (DEFMETHOD (STREAM-MIXIN :ANY-READ-CHAR-NO-HANG) (&OPTIONAL IGNORE IGNORE &AUX CH) (SETQ CH (SEND SELF :ANY-TYI-NO-HANG)) (IF (FIXNUMP CH) (INT-CHAR CH) CH)) ;;; For things only prepared to deal with fixnums (DEFMETHOD (STREAM-MIXIN :MOUSE-OR-KBD-TYI) (&AUX CH) (DO-FOREVER (SETQ CH (SEND SELF :ANY-TYI)) (WHEN (NUMBERP CH) (RETURN (VALUES CH CH))) (WHEN (AND (CONSP CH) (EQ (CAR CH) :MOUSE-BUTTON)) (RETURN (VALUES (SECOND CH) CH))))) (DEFMETHOD (STREAM-MIXIN :MOUSE-OR-KBD-TYI-NO-HANG) (&AUX CH) (DO-FOREVER (SETQ CH (SEND SELF :ANY-TYI-NO-HANG)) (WHEN (OR (NULL ch) (NUMBERP CH)) (RETURN (VALUES CH CH))) (WHEN (AND (CONSP CH) (EQ (CAR CH) :MOUSE-BUTTON)) (RETURN (VALUES (SECOND CH) CH))))) (DEFMETHOD (STREAM-MIXIN :LIST-TYI) (&AUX CH) "Only return lists" (DO-FOREVER (SETQ CH (SEND SELF :ANY-TYI)) (AND (CONSP CH) (RETURN CH)))) ;;; Return a circular buffer array describing the last however many input characters. ;;; The (array-leader array 1) points at the last slot stored into. (DEFMETHOD (STREAM-MIXIN :PLAYBACK) () (IO-BUFFER-RECORD IO-BUFFER)) (DEFMETHOD (STREAM-MIXIN :FORCE-KBD-INPUT) (CH-OR-STRING) (IF (STRINGP CH-OR-STRING) (DOTIMES (N (LENGTH CH-OR-STRING)) (IO-BUFFER-PUT IO-BUFFER (AREF CH-OR-STRING N))) (IO-BUFFER-PUT IO-BUFFER CH-OR-STRING))) ;all of the old cases. (DEFFLAVOR LIST-TYI-MIXIN () () (:DOCUMENTATION :MIXIN "Makes :TYI discard non-keyboard input.")) (DEFFLAVOR ANY-TYI-MIXIN () () (:DOCUMENTATION :MIXIN "Obsolete flavor")) (DEFFLAVOR PREEMPTABLE-READ-ANY-TYI-MIXIN () ()) (DEFVAR RUBOUT-HANDLER-STARTING-X :UNBOUND "Within rubout handler, X position of beginning of input.") (DEFVAR RUBOUT-HANDLER-STARTING-Y :UNBOUND "Within rubout handler, Y position of beginning of input.") (DEFVAR RUBOUT-HANDLER-RE-ECHO-FLAG :UNBOUND "Within rubout handler, T when there are error messages in the middle of the input. Set to NIL when the input is reprinted and they are gone.") (DEFVAR RUBOUT-HANDLER-INSIDE NIL "Non-NIL while inside the rubout handler.") (DEFVAR RUBOUT-HANDLER-ACTIVATION-CHARACTER NIL "The character or blip that is activating this invocation of :RUBOUT-HANDLER.") (DEFVAR PROMPT-STARTING-X :UNBOUND "Within rubout handler, X position of beginning of printed prompt string.") (DEFVAR PROMPT-STARTING-Y :UNBOUND "Within rubout handler, Y position of beginning of printed prompt string.") (defmethod (stream-mixin :rubout-handler) (options function &rest args) (declare (arglist rubout-handler-options function &rest args)) (if (and (eq rubout-handler self) (not (cdr (assq :nonrecursive options)))) (let ((rubout-handler-options (append options rubout-handler-options))) (apply function args)) (let ((rubout-handler-options options)) (if ( (rhb-fill-pointer) (rhb-scan-pointer)) (setf (rhb-fill-pointer) 0) (copy-array-portion rubout-handler-buffer (rhb-scan-pointer) (rhb-fill-pointer) rubout-handler-buffer 0 (array-length rubout-handler-buffer)) (if (numberp (rhb-typein-pointer)) (decf (rhb-typein-pointer) (rhb-scan-pointer))) (decf (rhb-fill-pointer) (rhb-scan-pointer))) (setf (rhb-scan-pointer) 0 (rhb-status) :initial-entry) (catch 'return-from-rubout-handler (let (prompt-starting-x prompt-starting-y rubout-handler-starting-x rubout-handler-starting-y (rubout-handler self) (rubout-handler-inside self) (rubout-handler-re-echo-flag nil) (rubout-handler-activation-character nil)) (multiple-value (prompt-starting-x prompt-starting-y) (send self :read-cursorpos)) (setq rubout-handler-starting-x prompt-starting-x rubout-handler-starting-y prompt-starting-y) (do-forever (setq rubout-handler-re-echo-flag nil) (catch 'rubout-handler ;Throw here when rubbing out (condition-case (error) (return (multiple-value-prog1 (apply function args) ;Call READ or whatever. (setf (rhb-fill-pointer) (rhb-scan-pointer)) (and (rhb-typein-pointer) (> (rhb-typein-pointer) (rhb-fill-pointer)) (setf (rhb-typein-pointer) (rhb-fill-pointer))))) (sys:parse-error (send self :fresh-line) (princ ">>ERROR: " self) (send error :report self) (send self :fresh-line) (setq rubout-handler-re-echo-flag t) (do-forever (send self :tyi))))) ;If error, force user to rub out ;;Maybe return when user rubs all the way back (and (zerop (rhb-fill-pointer)) (let ((full-rubout-option (assq :full-rubout rubout-handler-options))) (when full-rubout-option ;; Get rid of the prompt, if any. (send self :clear-between-cursorposes prompt-starting-x prompt-starting-y (- cursor-x left-margin-size) (- cursor-y top-margin-size)) (send self :set-cursorpos prompt-starting-x prompt-starting-y) (return (values nil (cadr full-rubout-option)))))))))))) (defmethod (stream-mixin :rescanning-p) () (or (< (rhb-scan-pointer) (rhb-fill-pointer)) rubout-handler-activation-character)) (defmethod (stream-mixin :force-rescan) () (setf (rhb-scan-pointer) 0) (throw 'rubout-handler t)) (defmethod (stream-mixin :read-bp) () (rhb-scan-pointer)) ;;; Foo. (defmethod (stream-mixin :replace-input) (n string &optional (start 0) end) (declare (ignore n string start end)) (ferror "Foo. I haven't written this yet.") ) ;;; Foo. This is hair implemented by Brand S which seems to be a real crock. (defmethod (stream-mixin :start-typeout) (type &optional spacing) type spacing ;(ferror "Foo. I haven't written this yet.") ) (defmethod (stream-mixin :finish-typeout) (&optional spacing erase-p) spacing erase-p ;(ferror "Foo. I haven't written this yet.") ) (DEFMETHOD (STREAM-MIXIN :PREEMPTABLE-READ) (OPTIONS FUN &REST ARGS) (DO ((TYPEAHEAD OLD-TYPEAHEAD NIL) (RESULT) (FLAG)) (()) (SETQ OLD-TYPEAHEAD T) (UNWIND-PROTECT (MULTIPLE-VALUE (RESULT FLAG) (with-stack-list (initial-input :initial-input (if (consp typeahead) (car typeahead))) (with-stack-list (initial-input-pointer :initial-input-pointer (if (consp typeahead) (cadr typeahead))) (with-stack-list* (options initial-input initial-input-pointer '((:full-rubout :full-rubout)) options) (unless (consp typeahead) (setq options (cddr options))) (lexpr-send self :rubout-handler options FUN ARGS))))) (AND (EQ OLD-TYPEAHEAD T) (SETQ OLD-TYPEAHEAD NIL))) (AND (NEQ FLAG :FULL-RUBOUT) (RETURN (VALUES RESULT NIL))) ;; Determine whether a mouse character caused the full-rubout (SETQ RESULT (SEND SELF :ANY-TYI-NO-HANG)) (COND (RESULT (OR (NUMBERP RESULT) (RETURN (VALUES RESULT :MOUSE-CHAR))) (SEND SELF :UNTYI RESULT))) (AND (SETQ FLAG (CADR (ASSQ :FULL-RUBOUT OPTIONS))) (RETURN (VALUES NIL FLAG))) ;; Presumably after this point, the user didn't call us with :FULL-RUBOUT ;; option, so we should retry. We have to fix up the notion of :PROMPT ;; and :REPROMPT first though. (LET ((PROMPT (ASSQ :PROMPT OPTIONS))) (WHEN PROMPT (SETQ OPTIONS (REMQ PROMPT OPTIONS)) ;This next condition may be unnecessary, but just in case. --kmp (UNLESS (NOT (ASSQ :REPROMPT OPTIONS)) ;; make fake reprompt info. our old prompt should still ;; be there --kmp (PUSH `(:REPROMPT . ,(CDR PROMPT)) OPTIONS)))))) ;;; Give a single character, or do rubout processing, throws to RUBOUT-HANDLER on editing. (defun default-rubout-handler () (declare (:self-flavor stream-mixin)) (setf (rhb-typein-pointer) nil) ;Mark that old rubout handler is in use. (when (= (rhb-scan-pointer) most-positive-fixnum) (setf (rhb-scan-pointer) 0) (throw 'rubout-handler t)) (let ((status (rhb-status)) (rubbed-out-some nil) (rubout-handler nil)) (setf (rhb-status) nil) (when (memq status '(:restored :initial-entry)) ;;Prompt if desired (let ((prompt-option (assq :prompt rubout-handler-options))) (when prompt-option (rubout-handler-prompt (cadr prompt-option) self nil))) (multiple-value (rubout-handler-starting-x rubout-handler-starting-y) (send self :read-cursorpos)) ;; Output any "typeahead" (when (plusp (rhb-fill-pointer)) (send self :string-out rubout-handler-buffer)) ;;no point looking for :initial-input-pointer since this rh can't do anything with it (let ((initial-input (cadr (assq :initial-input rubout-handler-options)))) (when initial-input (string-nconc rubout-handler-buffer initial-input)))) (or (prog1 rubout-handler-activation-character (setq rubout-handler-activation-character nil)) (do ((editing-command (cdr (assq :editing-command rubout-handler-options))) (do-not-echo (cdr (assq :do-not-echo rubout-handler-options))) (pass-through (cdr (assq :pass-through rubout-handler-options))) (command-handler (assq :command rubout-handler-options)) (preemptable (assq :preemptable rubout-handler-options)) (activation-handler (assq :activation rubout-handler-options)) ch len) (nil) (setq ch (send self :any-tyi)) (cond ((eq (car-safe ch) 'redisplay-rubout-handler) (send self :set-cursorpos prompt-starting-x prompt-starting-y) (send self :clear-rest-of-line) (and (setq len (or (assq :reprompt rubout-handler-options) (assq :prompt rubout-handler-options))) (rubout-handler-prompt (cadr len) self ch)) (multiple-value (rubout-handler-starting-x rubout-handler-starting-y) (send self :read-cursorpos)) (send self :string-out rubout-handler-buffer)) ((consp ch) (when preemptable (setf (rhb-scan-pointer) 0) (throw 'return-from-rubout-handler (values ch (cadr preemptable))))) ((and command-handler (apply (cadr command-handler) ch (cddr command-handler))) (setf (rhb-scan-pointer) 0) (throw 'return-from-rubout-handler (values `(:command ,ch 1) :command))) ;; Don't touch this character, just return it to caller. ((or (memq ch editing-command) (si:assq-careful ch editing-command)) ;; Cause rubout handler rescan next time the user does :TYI. (if rubbed-out-some (setf (rhb-scan-pointer) most-positive-fixnum)) (return ch)) ;; Is it an editing character? ((and (not (or (memq ch do-not-echo) (and activation-handler (apply (cadr activation-handler) ch (cddr activation-handler))))) (or (ldb-test %%kbd-control-meta ch) (and (memq ch '(#/Rubout #/Clear-input #/Clear-screen #/Delete)) (not (memq ch pass-through))))) (cond ((memq ch '(#/Clear-screen #/Delete)) ;Retype buffered input (send self :tyo ch) ;Echo it (if (= ch #/Clear-screen) (send self :clear-window) (send self :tyo #/Newline)) (multiple-value (prompt-starting-x prompt-starting-y) (send self :read-cursorpos)) (and (setq len (or (assq :reprompt rubout-handler-options) (assq :prompt rubout-handler-options))) (rubout-handler-prompt (cadr len) self ch)) (multiple-value (rubout-handler-starting-x rubout-handler-starting-y) (send self :read-cursorpos)) (send self :string-out rubout-handler-buffer)) ((memq ch '(#/Rubout #/M-rubout #/Clear-input)) ;Delete some characters (cond ((not (zerop (setq len (rhb-fill-pointer)))) (setf (rhb-fill-pointer) (setq len (selectq ch (#/Rubout (1- len)) (#/M-rubout (string-backward-word rubout-handler-buffer len)) (#/Clear-input 0)))) (setf rubbed-out-some t (rhb-status) :rubout) (multiple-value-bind (x y) (send self :compute-motion rubout-handler-buffer 0 len rubout-handler-starting-x rubout-handler-starting-y) (if rubout-handler-re-echo-flag (setq x rubout-handler-starting-x y rubout-handler-starting-y)) (multiple-value-bind (cx cy) (send self :read-cursorpos) (send self :clear-between-cursorposes x y cx cy)) (send self :set-cursorpos x y) (and rubout-handler-re-echo-flag (send self :string-out rubout-handler-buffer)))))) (t (beep))) ;Undefined editing character (cond ((and (zerop (rhb-fill-pointer)) (assq :full-rubout rubout-handler-options)) (setf (rhb-scan-pointer) 0) (throw 'rubout-handler t)))) (t ;; It's a self-inserting character. ;; If this is first character typed in, re-get starting cursorpos since while ;; waiting for input a notification may have been typed out. (and (zerop (rhb-fill-pointer)) (multiple-value (rubout-handler-starting-x rubout-handler-starting-y) (send self :read-cursorpos))) (cond ((memq ch do-not-echo) (setq rubout-handler-activation-character ch)) ((and activation-handler (apply (cadr activation-handler) ch (cddr activation-handler))) (setq ch `(:activation ,ch 1)) (setq rubout-handler-activation-character ch)) (t (send self :tyo ch) (array-push-extend rubout-handler-buffer ch))) (cond (rubbed-out-some (setf (rhb-scan-pointer) 0) (throw 'rubout-handler t)) (t (setf (rhb-scan-pointer) (rhb-fill-pointer)) (setq rubout-handler-activation-character nil) (return ch))))))))) ;;; Use ZWEI's syntax table if ZWEI is around... (DEFUN STRING-BACKWARD-WORD (STRING INDEX &AUX ALPHA-P-FCN) (SETQ ALPHA-P-FCN (IF (BOUNDP 'ZWEI:*WORD-SYNTAX-TABLE*) #'(LAMBDA (X) (EQ (ZWEI:CHAR-SYNTAX X ZWEI:*WORD-SYNTAX-TABLE*) ZWEI:WORD-ALPHABETIC)) 'ALPHA-CHAR-P)) (DO ((I (1- INDEX) (1- I)) (INSIDE-WORD NIL)) ((MINUSP I) 0) (IF (FUNCALL ALPHA-P-FCN (AREF STRING I)) (SETQ INSIDE-WORD T) (AND INSIDE-WORD (RETURN (1+ I)))))) (DEFUN RUBOUT-HANDLER-PROMPT (PROMPT-OPTION STREAM CH) (LET ((RUBOUT-HANDLER NIL)) ;In case of **more** (IF (STRINGP PROMPT-OPTION) (FUNCALL STREAM :STRING-OUT PROMPT-OPTION) (FUNCALL PROMPT-OPTION STREAM CH)))) (defmethod (stream-mixin :save-rubout-handler-buffer) () (when (eq rubout-handler-inside self) ;; Give rubout handler function a chance to put its internal data ;; into RUBOUT-HANDLER-BUFFER where we look for it. ; not patched in 98. (let ((prop (get (or stream-rubout-handler stream-mixin-default-rubout-handler) 'save-rubout-handler-buffer))) (when prop (funcall prop self))) (values (copy-seq rubout-handler-buffer) (rhb-typein-pointer)))) (defmethod (stream-mixin :restore-rubout-handler-buffer) (string &optional pointer) (let ((length (array-active-length string))) (or ( (array-length rubout-handler-buffer) length) (adjust-array-size rubout-handler-buffer length)) (copy-array-contents string rubout-handler-buffer) (setf (rhb-fill-pointer) length)) (setf (rhb-typein-pointer) pointer) (send self :refresh-rubout-handler) (setf (rhb-scan-pointer) 0) ;(setf (rhb-status) :restored) (throw 'rubout-handler t)) (defmethod (stream-mixin :refresh-rubout-handler) (&optional discard-last-character) (if discard-last-character (setf (rhb-fill-pointer) (max 0 (1- (rhb-fill-pointer))))) (if (rhb-typein-pointer) (setf (rhb-typein-pointer) (min (rhb-typein-pointer) (rhb-fill-pointer)))) (send self :fresh-line) (let ((prompt (or (assq :reprompt rubout-handler-options) (assq :prompt rubout-handler-options)))) (when prompt (rubout-handler-prompt (cadr prompt) self #/Delete))) (multiple-value (rubout-handler-starting-x rubout-handler-starting-y) (send self :read-cursorpos)) (send self :string-out rubout-handler-buffer)) ;;; Stream operations which all streams are required to support or ignore ;;; I'm afraid these will appear in the :WHICH-OPERATIONS even though they ;;; aren't "really supported" ;;; These 3 are ignored since we don't have buffered output (DEFMETHOD (STREAM-MIXIN :CLEAR-OUTPUT) () NIL) (DEFMETHOD (STREAM-MIXIN :FORCE-OUTPUT) () NIL) (DEFMETHOD (STREAM-MIXIN :FINISH) () NIL) (DEFMETHOD (STREAM-MIXIN :CLOSE) (&OPTIONAL IGNORE) NIL) (DEFMETHOD (STREAM-MIXIN :LINE-IN) (&OPTIONAL LEADER) (STREAM-DEFAULT-HANDLER SELF :LINE-IN LEADER NIL)) (DEFMETHOD (STREAM-MIXIN :STRING-IN) (EOF &REST REST) (DECLARE (ARGLIST EOF STRING &OPTIONAL START END)) (STREAM-DEFAULT-HANDLER SELF :STRING-IN EOF REST)) (DEFMETHOD (STREAM-MIXIN :STRING-LINE-IN) (EOF &REST REST) (DECLARE (ARGLIST EOF STRING &OPTIONAL START END)) (STREAM-DEFAULT-HANDLER SELF :STRING-LINE-IN EOF REST)) (DEFFLAVOR LINE-TRUNCATING-MIXIN () () (:REQUIRED-FLAVORS STREAM-MIXIN) (:DOCUMENTATION :MIXIN "Causes stream output functions to truncate if the SHEET-TRUNCATE-LINE-OUT-FLAG in the window is set.")) (DEFWRAPPER (LINE-TRUNCATING-MIXIN :TYO) (IGNORE . BODY) `(CATCH 'LINE-OVERFLOW . ,BODY)) (DEFMETHOD (LINE-TRUNCATING-MIXIN :BEFORE :END-OF-LINE-EXCEPTION) () (OR (ZEROP (SHEET-TRUNCATE-LINE-OUT-FLAG)) (THROW 'LINE-OVERFLOW T))) (DEFWHOPPER (LINE-TRUNCATING-MIXIN :STRING-OUT) (STRING &OPTIONAL (START 0) END) (OR END (SETQ END (STRING-LENGTH STRING))) (DO ((I START (1+ CR-IDX)) (CR-IDX)) (( I END)) (SETQ CR-IDX (POSITION #/NEWLINE STRING :START I :END END)) (CATCH 'LINE-OVERFLOW (CONTINUE-WHOPPER STRING I (OR CR-IDX END))) (OR CR-IDX (RETURN NIL)) (SHEET-CRLF SELF))) (DEFFLAVOR TRUNCATING-WINDOW () (LINE-TRUNCATING-MIXIN WINDOW) (:DEFAULT-INIT-PLIST :TRUNCATE-LINE-OUT-FLAG 1) (:DOCUMENTATION :COMBINATION "A window that truncates line of output.")) (DEFFLAVOR AUTOEXPOSING-MORE-MIXIN () () (:REQUIRED-FLAVORS WINDOW) (:DOCUMENTATION :MIXIN "Makes a window expose itself if output on it stops at a **MORE**.")) (DEFMETHOD (AUTOEXPOSING-MORE-MIXIN :BEFORE :MORE-EXCEPTION) () (SEND SELF :EXPOSE))