
;; Returns the position of the first newline appearing before POS-1.
;; Returns -1 if reached the beginning of the buffer.

(DEFUN DE-SEARCH-BACKWARD-NEWLINE (POS)
  (DO ((I (1- POS) (1- I)))
      ((OR (= I -1) (= (AREF ES-BUFFER I) #\RETURN)) I)))

(DEF-DE-COMMAND (DE-BEGINNING-OF-LINE #{{#/}}/A) (STREAM &OPTIONAL IGNORE)
  (DE-SET-POSITION STREAM (1+ (DE-SEARCH-BACKWARD-NEWLINE (TYPEIN-POINTER)))))

(DEF-DE-COMMAND (DE-END-OF-LINE #{{#/}}/E) (STREAM &OPTIONAL IGNORE)
  (DE-SET-POSITION STREAM (DE-SEARCH-FORWARD-NEWLINE (TYPEIN-POINTER))))

(DEF-DE-COMMAND (DE-BEGINNING-OF-BUFFER #{{#/}}/<) (STREAM &OPTIONAL IGNORE)
  (DE-SET-POSITION STREAM 0))

(DEF-DE-COMMAND (DE-END-OF-BUFFER #{{#/}}/>) (STREAM &OPTIONAL IGNORE)
  (DE-SET-POSITION STREAM (FILL-POINTER)))

(DEF-DE-COMMAND (DE-FORWARD-CHAR #{{#/}}/F) (STREAM &OPTIONAL (N 1))
  (DE-SET-POSITION STREAM (+ (TYPEIN-POINTER) N)))

(DEF-DE-COMMAND (DE-BACKWARD-CHAR #{{#/}}/B) (STREAM &OPTIONAL (N 1))
  (DE-SET-POSITION STREAM (- (TYPEIN-POINTER) N)))

(DEF-DE-COMMAND (DE-PREVIOUS-LINE #{{#/}}/P) (STREAM &OPTIONAL (N 1) &AUX LINE-BEGIN INDENT)
  (SETQ LINE-BEGIN (DE-SEARCH-BACKWARD-NEWLINE (TYPEIN-POINTER)))
  (SETQ INDENT (- (TYPEIN-POINTER) LINE-BEGIN))
  (DOTIMES (I N)
    (IF (= LINE-BEGIN -1) (RETURN))
    (SETQ LINE-BEGIN (DE-SEARCH-BACKWARD-NEWLINE LINE-BEGIN)))
  ;; When moving up from a long line to a short line, be sure not to go off the end.
  (DE-SET-POSITION STREAM
		   (+ LINE-BEGIN (MIN INDENT (DE-SEARCH-FORWARD-NEWLINE (1+ LINE-BEGIN))))))

(DEF-DE-COMMAND (DE-NEXT-LINE #{{#/}}/N) (STREAM &OPTIONAL (N 1) &AUX LINE-BEGIN INDENT)
  (SETQ LINE-BEGIN (DE-SEARCH-BACKWARD-NEWLINE (TYPEIN-POINTER)))
  (SETQ INDENT (- (TYPEIN-POINTER) LINE-BEGIN))
  (DOTIMES (I N)
    (COND ((= LINE-BEGIN (FILL-POINTER))
	   (SETQ LINE-BEGIN (DE-SEARCH-BACKWARD-NEWLINE LINE-BEGIN))
	   (RETURN)))
    (SETQ LINE-BEGIN (DE-SEARCH-FORWARD-NEWLINE (1+ LINE-BEGIN))))
  (DE-SET-POSITION STREAM
		   (+ LINE-BEGIN (MIN INDENT (DE-SEARCH-FORWARD-NEWLINE (1+ LINE-BEGIN))))))

;; Deleting Things

;; Deletes a buffer interval as marked by two pointers passed in.  The typein pointer
;; is left at the beginning of the interval.

(DEFUN DE-DELETE-STRING (STREAM BEGIN END &AUX WIDTH TYPEIN-POINTER
				(FILL-POINTER (FILL-POINTER)))
  (SETQ BEGIN (MAX BEGIN 0))
  (SETQ END   (MIN END FILL-POINTER))
  (SETQ WIDTH (- END BEGIN))
  (DE-SET-POSITION STREAM BEGIN)
  ;; Set this after moving the cursor to the beginning of the string.
  (SETQ TYPEIN-POINTER (TYPEIN-POINTER))
  (COND
    ;; Efficiency hack for clearing to the end of line, as in C-K and CLEAR.
    ;; Don't need to add up character widths as needed in :DELETE-STRING.
    ((= END FILL-POINTER)
     (FUNCALL STREAM ':CLEAR-EOL))
    ;; Console can delete characters.  Pass in the string to make variable width fonts win.
    ((MEMQ ':DELETE-STRING ES-WHICH-OPERATIONS)
     (FUNCALL STREAM ':DELETE-STRING ES-BUFFER BEGIN END))
    ;; Console can't delete characters.  Be sure to do a clear-eol to flush those
    ;; characters at the very end.
    (T (FUNCALL STREAM ':CLEAR-EOL)
       (FUNCALL STREAM ':STRING-OUT ES-BUFFER END)
       (DE-CURSOR-MOTION STREAM END)))
  ;; Now actually delete the characters from the buffer.  Do this before decrementing
  ;; the fill pointer so that we don't attempt to reference outside the string.
  (COPY-VECTOR-SEGMENT (- FILL-POINTER END)
		       ES-BUFFER (+ TYPEIN-POINTER WIDTH)
		       ES-BUFFER TYPEIN-POINTER)
  (SETF (FILL-POINTER) (- FILL-POINTER WIDTH))
  ;; If all typein has been deleted and the :FULL-RUBOUT option is
  ;; active, then throw now.  This will throw if the user types rubout
  ;; immediately after entering the read function.
  (IF (AND (ZEROP (FILL-POINTER))
	   (ASSQ ':FULL-RUBOUT TV-MAKE-STREAM-RUBOUT-HANDLER-OPTIONS))
      (*THROW 'RUBOUT-HANDLER T))
  ;; If it turns out that nothing was deleted, then don't bother rescanning input.
  ({{#/}} WIDTH 0))

(DEF-DE-COMMAND (DE-DELETE-CHAR #{{#/}}/D) (STREAM &OPTIONAL (N 1))
  (DE-DELETE-STRING STREAM (TYPEIN-POINTER) (+ (TYPEIN-POINTER) N)))

(DEF-DE-COMMAND (DE-RUBOUT-CHAR #\RUBOUT) (STREAM &OPTIONAL (N 1))
  (DE-DELETE-STRING STREAM (- (TYPEIN-POINTER) N) (TYPEIN-POINTER)))

;; CLEAR flushes all buffered input.  If the full rubout option is in
;; use, then we will throw out of here.  No need to prompt since the prompt still there.
(DEF-DE-COMMAND (DE-CLEAR #\CLEAR) (STREAM &OPTIONAL IGNORE)
  (DE-DELETE-STRING STREAM 0 (FILL-POINTER)))

;; If at the end of the line, change this to kill only the newline.

