;;; -*- Mode:LISP; Package:ZWEI; Readtable:ZL; Base:8; Patch-File:T -*- ;; This adds wraparound to incremental search -- see l.zwei;coms.lisp ;; To patchify this it would be necessary to add the following after the ;; revised defresource: (SETF (GET 'ISEARCH-VECTORS 'DEFRESOURCE) NIL) (SETF (GET 'ISEARCH-VECTORS 'DEFRESOURCE) NIL) (defresource isearch-vectors () :constructor (vector ;; *is-string* (make-string 128. :fill-pointer 0) ;; All of the arrays below constitute a push-down stack. ;; *is-bp* (make-array 128. :fill-pointer 0) ;; *is-status* ;; STATUS is NIL for a failing search, T for a successful one, ;; and :GO for one that is still looking. (make-array 128. :fill-pointer 0) ;; *is-reverse-p* ;; T if the search is reverse at this level. (make-array 128. :fill-pointer 0) ;; *is-pointer* ;; This points to the end of the part of *IS-STRING* active at this level. (make-array 128. :fill-pointer 0) ;; *is-operation* ;; This is what sort of thing the char at this level is: ;; :NORMAL, :REVERSE or :REPEAT. (make-array 128. :fill-pointer 0) ;; *is-wrapped-p* ;; Boolean -- whether search has wrapped around. (make-array 128. :fill-pointer 0)) :initializer (dotimes (x (length object)) (setf (fill-pointer (svref object x)) 0))) (DEFVAR *IS-STRING*) (DEFVAR *IS-BP*) (DEFVAR *IS-STATUS*) (DEFVAR *IS-REVERSE-P*) (DEFVAR *IS-wrapped-P*) (DEFVAR *IS-POINTER*) (DEFVAR *IS-OPERATION*) (DEFUN INITIALIZE-INCREMENTAL-SEARCH-GLOBALS () (USING-RESOURCE (TEM ISEARCH-VECTORS) TEM NIL)) (DEFMACRO PUSH-ISEARCH-STATUS () '(PUSH-ISEARCH-STATUS-1 (SETQ P (1+ P)))) (DEFUN PUSH-ISEARCH-STATUS-1 (P) (WHEN (= P (ARRAY-LENGTH *IS-REVERSE-P*)) (ADJUST-ARRAY-SIZE *IS-REVERSE-P* (+ P 64.)) (ADJUST-ARRAY-SIZE *IS-wrapped-P* (+ P 64.)) (ADJUST-ARRAY-SIZE *IS-STATUS* (+ P 64.)) (ADJUST-ARRAY-SIZE *IS-OPERATION* (+ P 64.)) (ADJUST-ARRAY-SIZE *IS-BP* (+ P 64.)) (ADJUST-ARRAY-SIZE *IS-POINTER* (+ P 64.))) (SETF (AREF *IS-REVERSE-P* P) (AREF *IS-REVERSE-P* (1- P))) (SETF (AREF *IS-wrapped-P* P) (AREF *IS-wrapped-P* (1- P))) (SETF (AREF *IS-POINTER* P) (AREF *IS-POINTER* (1- P))) (SETF (AREF *IS-STATUS* P) ':GO)) ;;; This is how incremental search manages to allow both type-ahead and rubout-ahead: ;;; What to do is kept in five stacks, arrays in the *IS-...* variables. ;;; Input of normal characters pushes onto the end using index P, ;;; and rubout pops off at the same index. *IS-REVERSE-P* remembers the ;;; search direction at each level, *IS-OPERATION* remembers the type of search ;;; (:NORMAL for a normal character, :REVERSE for a Control-R or Control-S that reverses, ;;; or :REPEAT for a Control-R or Control-S that repeats), *IS-POINTER* is the length of ;;; the search string at that level. ;;; In parallel, with lower priority, the entries thus pushed are processed ;;; by searching according to them. P1 is the index of the entry or "level" ;;; which is currently being worked on. P1 advances only when the level is ;;; determined to be successful or failing. Advancing involves examining the three ;;; *IS-...* entries of the next level to see what to do. If P1 meets P, then there is no ;;; work to do for the moment. The state of this process is kept in *IS-STATUS* ;;; and *IS-BP*. *IS-BP* is the bp of the place found at a given level or the ;;; place at which searching is going on. *IS-STATUS* is T for a successful search, ;;; NIL for a failing one, and :GO if it isn't known yet. New levels are pushed ;;; (via P) in the :GO state. ;;; Rubbing out decrements P1 if necessary to keep it no greater than P. ;;; The searching process is not confused because it keeps all its state ;;; in *IS-STATUS* and *IS-BP* and all that is needed is to change P1. ;;; Updating the echo area is under input in priority, but above actual searching. ;;; Thus, as soon as there is no type-ahead everything will be correct. ;;; This is because the echo area is presumed to be fast to update. ;;; Buffer redisplay is lower than searching, of course. ; character lossage rampant (DEFUN INCREMENTAL-SEARCH (REVERSE-P &AUX (ORIG-PT (COPY-BP (POINT))) ISEARCH-VECTORS) (SELECT-WINDOW *WINDOW*) (SEND *QUERY-IO* :FRESH-LINE) ;Necessary if in the mini-buffer (UNWIND-PROTECT (TYPEIN-LINE-ACTIVATE (PROG* (CHAR ; The current command. XCHAR ; Upcase version of character MUST-REDIS ; T => The echo-area must be completely redisplayed. (P 0) ; The stack pointer into *IS-BP*, etc. for input and rubout (P1 0) ; The pointer for which search we are doing. ; Can never exceed P. SUPPRESSED-REDISPLAY ; T if the last input char was read before ; redisplay had a chance to finish. ; A Control-G read that way acts like ; a failing search quit. BP1 ; Aux BP used for actual searching. NEW-BP TIME-OUT ; Set by SEARCH when it times out so we can check input. INPUT-DONE ; An altmode or control char has been seen. ; Do not look for input any more; just search, then exit. (*is-string*) (*is-bp*) (*is-status*) (*is-reverse-p*) (*is-wrapped-p*) (*is-pointer*) (*is-operation*) ; (TV:KBD-INTERCEPTED-CHARACTERS ; (COPY-LIST TV:KBD-INTERCEPTED-CHARACTERS)) ) ; (SETQ TV:KBD-INTERCEPTED-CHARACTERS ; (DELQ (ASSQ #/ABORT TV:KBD-INTERCEPTED-CHARACTERS) ; TV:KBD-INTERCEPTED-CHARACTERS)) (SETQ ISEARCH-VECTORS (ALLOCATE-RESOURCE 'ISEARCH-VECTORS) *IS-STRING* (SVREF ISEARCH-VECTORS 0) *IS-BP* (SVREF ISEARCH-VECTORS 1) *IS-STATUS* (SVREF ISEARCH-VECTORS 2) *IS-REVERSE-P* (SVREF ISEARCH-VECTORS 3) *IS-POINTER* (SVREF ISEARCH-VECTORS 4) *IS-OPERATION* (SVREF ISEARCH-VECTORS 5) *IS-wrapped-P* (SVREF ISEARCH-VECTORS 6)) (SETF (AREF *IS-STATUS* 0) T) ; Initialize the stacks. (SETF (AREF *IS-REVERSE-P* 0) REVERSE-P) (SETF (AREF *IS-OPERATION* 0) ':NORMAL) (SETF (AREF *IS-POINTER* 0) 0) (SETF (AREF *IS-BP* 0) (COPY-BP (POINT))) (SETQ MUST-REDIS T) ; Initially we must redisplay. (GO CHECK-FOR-INPUT) ;; Come here if there is input, or nothing to do until there is input. INPUT (SETQ SUPPRESSED-REDISPLAY NIL) (AND (WINDOW-READY-P *WINDOW*) ;In case of minibuffer (REDISPLAY *WINDOW* :POINT)) ; Redisplay point position while waiting. (IF (= (WINDOW-REDISPLAY-DEGREE *WINDOW*) DIS-NONE) (REDISPLAY-MODE-LINE) ;Update indication of more above or below. (SETQ SUPPRESSED-REDISPLAY T)) (IF SUPPRESSED-REDISPLAY (SETQ CHAR (TYI-WITH-SCROLLING NIL T)) ;; If must wait for input, make the window's blinker blink ;; even though not selected. (IF (OPERATION-HANDLED-P *WINDOW* :POINT-BLINKER) (UNWIND-PROTECT (PROGN (SEND (WINDOW-POINT-BLINKER *WINDOW*) :SET-VISIBILITY :BLINK) (SEND (WINDOW-POINT-BLINKER *WINDOW*) :SET-DESELECTED-VISIBILITY :BLINK) (SETQ CHAR (TYI-WITH-SCROLLING NIL T))) (SEND (WINDOW-POINT-BLINKER *WINDOW*) :SET-VISIBILITY ;tv lossage (IF (EQ *WINDOW* TV:SELECTED-WINDOW) :BLINK (TV:SHEET-EXPOSED-P *WINDOW*))) (SEND (WINDOW-POINT-BLINKER *WINDOW*) :SET-DESELECTED-VISIBILITY :ON)) (SETQ CHAR (TYI-WITH-SCROLLING NIL T)))) (COND ((CONSP CHAR) (SEND *STANDARD-INPUT* :UNTYI CHAR) (SETQ INPUT-DONE T) (GO CHECK-FOR-INPUT)) ; character lossage ((FIXNUMP CHAR) (SETQ CHAR (INT-CHAR CHAR)))) (SETQ XCHAR (CHAR-UPCASE CHAR)) (COND ((NOT (OR ( (CHAR-BITS CHAR) 0) (TV:CHAR-MOUSE-P CHAR) (MEMQ CHAR '(#/ #/END #/RUBOUT #/HELP #/ABORT #/CLEAR-INPUT)))) (GO NORMAL)) ((MEMQ XCHAR '(#/C-S #/C-R)) (PUSH-ISEARCH-STATUS) (SETF (AREF *IS-OPERATION* P) ':REPEAT) (LET ((NEW-REVERSE-P (EQL XCHAR #/C-R))) (COND ;; In reverse mode, just go to forward. ((NEQ (AREF *IS-REVERSE-P* P) NEW-REVERSE-P) (SETF (AREF *IS-REVERSE-P* P) NEW-REVERSE-P) (SETQ MUST-REDIS T) (SETF (AREF *IS-OPERATION* P) ':REVERSE)) ((ZEROP (AREF *IS-POINTER* P)) (LET ((STRING (STRING (OR (CAAR *SEARCH-RING*) (BARF))))) (COPY-ARRAY-CONTENTS STRING *IS-STRING*) (SETF (AREF *IS-POINTER* P) (LENGTH STRING))) (SETQ MUST-REDIS T)))) (GO CHECK-FOR-INPUT)) ((eql char #/C-W) ;snarf word at cursor - smh 24jun88 (let* ((saved-point (point)) (pointer (if (aref *is-reverse-p* p) (forward-char saved-point (aref *is-pointer* p)) (copy-bp saved-point))) (other-end (or (forward-word pointer 1) (progn (beep) ;; at end of file, just flash and wait for more (go check-for-input))))) (PUSH-ISEARCH-STATUS) (loop while (bp-< pointer other-end) as char = (int-char (bp-char pointer)) do (OR MUST-REDIS (FORMAT *QUERY-IO* "~C" CHAR)) (LET ((IDX (AREF *IS-POINTER* P))) (AND ( IDX (ARRAY-LENGTH *IS-STRING*)) (ADJUST-ARRAY-SIZE *IS-STRING* (+ IDX 64.))) (SETF (CHAR *IS-STRING* IDX) CHAR) (SETF (AREF *IS-POINTER* P) (1+ IDX))) (SETF (AREF *IS-OPERATION* P) ':NORMAL) (setq pointer (forward-char pointer 1))) (move-bp (point) saved-point) (go check-for-input))) ((EQL XCHAR #/C-Q) (LET ((NEW-CH (READ-CHAR *STANDARD-INPUT*))) (SETQ CHAR (IF (CHAR-BIT NEW-CH :CONTROL) ;; ascii version of control characters (INT-CHAR (LOGAND #o37 (CHAR-CODE NEW-CH))) (INT-CHAR (CHAR-CODE NEW-CH))))) (GO NORMAL)) ((EQL CHAR #/HELP) (PRINT-DOC :FULL *CURRENT-COMMAND*) (SEND *STANDARD-INPUT* :UNTYI (SEND *STANDARD-INPUT* :ANY-TYI)) (GO INPUT)) ((OR (EQL XCHAR #/C-G) (EQL XCHAR #/ABORT)) (BEEP) (COND ((AND (OR SUPPRESSED-REDISPLAY (NEQ (AREF *IS-STATUS* P) T)) (PLUSP P)) ;; Control-G in other than a successful search ;; rubs out until it becomes successful. (SETQ P (DO ((P (1- P) (1- P))) ((EQ (AREF *IS-STATUS* P) T) P))) (SETQ P1 (MIN P P1) MUST-REDIS T) (GO CHECK-FOR-INPUT)) (T (MOVE-BP (POINT) (AREF *IS-BP* 0)) (RETURN)))) ((OR (EQL CHAR #/) (EQL CHAR #/END)) (AND (ZEROP P) ;; Call string search, and make self-doc print the right thing there. (LET ((*CURRENT-COMMAND* 'COM-STRING-SEARCH-INTERNAL)) (RETURN (COM-STRING-SEARCH-INTERNAL REVERSE-P NIL NIL NIL)))) (SETQ INPUT-DONE T) (GO CHECK-FOR-INPUT)) ((OR (EQL CHAR #/RUBOUT) (EQL CHAR #/CLEAR-INPUT)) ;; Clear-input rubs out all the way. Set P to 1 and let it be decremented. (IF (EQL CHAR #/CLEAR-INPUT) (SETQ P 1)) (COND (( P 0) ; If he over-rubbed out, (BEEP) ; that is an error. (GO CHECK-FOR-INPUT)) (T ;; Rubout pops all of these PDLs. (SETQ P (1- P)) (SETQ P1 (MIN P P1)) (SETQ MUST-REDIS T) (GO CHECK-FOR-INPUT)))) (T ;character lossage (SEND *STANDARD-INPUT* :UNTYI (CHAR-INT CHAR)) (SETQ INPUT-DONE T) (GO CHECK-FOR-INPUT))) (FERROR "A clause fell through.") ;; Normal chars to be searched for come here. NORMAL (OR MUST-REDIS (FORMAT *QUERY-IO* "~C" CHAR)) (PUSH-ISEARCH-STATUS) (LET ((IDX (AREF *IS-POINTER* P))) (AND ( IDX (ARRAY-LENGTH *IS-STRING*)) (ADJUST-ARRAY-SIZE *IS-STRING* (+ IDX 64.))) (SETF (CHAR *IS-STRING* IDX) CHAR) (SETF (AREF *IS-POINTER* P) (1+ IDX))) (SETF (AREF *IS-OPERATION* P) ':NORMAL) ;; Come here after possibly processing input to update the search tables ;; to search for a while. First, if necessary and not suppressed ;; update the search string displayed in the echo area. CHECK-FOR-INPUT ;; If there is input available, go read it. ;; Otherwise, do work if there is work to be done. (AND (NOT INPUT-DONE) (SEND *STANDARD-INPUT* :LISTEN) (GO INPUT)) ;; Now do some work for a while, then go back to CHECK-FOR-INPUT. (WHEN MUST-REDIS (SETQ MUST-REDIS NIL) (FORMAT *QUERY-IO* "~&~:|") (OR (AREF *IS-STATUS* P1) (FORMAT *QUERY-IO* "Failing ")) (AND (AREF *IS-REVERSE-P* P) (FORMAT *QUERY-IO* "Reverse ")) (AND (AREF *IS-wrapped-P* P) (FORMAT *QUERY-IO* "Wrapped ")) (FORMAT *QUERY-IO* "I-Search: ") (SETF (FILL-POINTER *IS-STRING*) (AREF *IS-POINTER* P)) (FORMAT *QUERY-IO* "~A" *IS-STRING*)) ;; Now see what sort of state the actual search is in, and what work there is to do. ;; P1 points at the level of the table on which we are actually working. (SETF BP1 (AREF *IS-BP* P1)) ;; Display point at the end of the last search level which has succeeded. (DO ((P0 P1 (1- P0))) ((EQ (AREF *IS-STATUS* P0) T) (MOVE-BP (POINT) (AREF *IS-BP* P0)))) (MUST-REDISPLAY *WINDOW* DIS-BPS) (COND ((EQ (AREF *IS-STATUS* P1) ':GO) (SETF (FILL-POINTER *IS-STRING*) (AREF *IS-POINTER* P1)) ;; If the level we were working on is still not finished, ;; search at most 64. more lines. If we find it or the end of the buffer ;; before then, this level is determined and we can work on the next. ;; Otherwise, we remain in the :GO state and do 64. more lines next time. (MULTIPLE-VALUE (NEW-BP TIME-OUT) (ZWEI-SEARCH BP1 *IS-STRING* (AREF *IS-REVERSE-P* P1) NIL 64.)) ;; What happened? (COND (TIME-OUT ;; Nothing determined. NEW-BP has where we stopped. (MOVE-BP BP1 NEW-BP) (DBP BP1)) ;Avoids missing occurrences, if string starts with CR ((NULL NEW-BP) ;; This search was determined to be a failure. (OR (SEND-IF-HANDLES *STANDARD-INPUT* :MACRO-ERROR) (BEEP)) (SETF (AREF *IS-STATUS* P1) NIL) (MOVE-BP BP1 (AREF *IS-BP* (1- P1))) (MOVE-BP (POINT) BP1) (SETQ MUST-REDIS T)) (T ;; This search level has succeeded. (SETF (AREF *IS-STATUS* P1) T) (MOVE-BP (POINT) NEW-BP) (MOVE-BP BP1 NEW-BP)))) (( P P1) ;; This level is finished, but there are more pending levels typed ahead. (INCF P1) (SETF (AREF *IS-BP* P1) (SETQ BP1 (COPY-BP BP1))) (SETF (FILL-POINTER *IS-STRING*) (AREF *IS-POINTER* P1)) (COND ((NULL (AREF *IS-STATUS* (1- P1))) ;search is failing (COND ((EQ (AREF *IS-OPERATION* P1) ':REVERSE) ;; If we reverse direction, change prompt line. (SETQ MUST-REDIS T)) ((eq (aref *is-operation* p1) ':repeat) ;smh 30jun88 ;; Wrap around to top (or bottom). (setq must-redis t) (setf (aref *is-wrapped-p* p1) t) (move-bp bp1 (if (aref *is-reverse-p* p1) (interval-last-bp *interval*) (interval-first-bp *interval*)))) (T ;; A failing search remains so unless we reverse direction. (SETF (AREF *IS-STATUS* P1) NIL)))) ((EQ (AREF *IS-OPERATION* P1) ':NORMAL) ;; Normal char to be searched for comes next. ;; We must adjust the bp at which we start to search ;; so as to allow the user to extend the string already found. (MOVE-BP BP1 (FORWARD-CHAR BP1 (IF (AREF *IS-REVERSE-P* P1) (IF (= (LENGTH *IS-STRING*) 1) 0 (LENGTH *IS-STRING*)) (- 1 (LENGTH *IS-STRING*))) T))))) ;; If there is nothing left to do, and terminator seen, exit. (INPUT-DONE (SEARCH-RING-PUSH ;; Entries on the search ring should have a leader (STRING-NCONC (MAKE-STRING (LENGTH *IS-STRING*) :FILL-POINTER 0) *IS-STRING*) 'ZWEI-SEARCH) (FORMAT *QUERY-IO* "") (MAYBE-PUSH-POINT ORIG-PT) ;(SELECT-WINDOW *WINDOW*) (RETURN)) ;; Nothing to do and no terminator, wait for input. (T (GO INPUT))) (GO CHECK-FOR-INPUT) ) (SETQ ORIG-PT NIL)) ;; unwind-protect cleanup (IF ISEARCH-VECTORS (DEALLOCATE-RESOURCE 'ISEARCH-VECTORS ISEARCH-VECTORS)) (IF ORIG-PT (MOVE-BP (POINT) ORIG-PT)) (SEND-IF-HANDLES *QUERY-IO* :MAKE-COMPLETE) (MUST-REDISPLAY *WINDOW* DIS-BPS) (SEND *MODE-LINE-WINDOW* :DONE-WITH-MODE-LINE-WINDOW)) DIS-BPS)