changeset 30640:6f943008c543

(comint-use-prompt-regexp-instead-of-fields): New variable. (comint-prompt-regexp, comint-get-old-input): Document dependence on comint-use-prompt-regexp-instead-of-fields. (comint-send-input): Add `input' field property to stuff we send to the process, if comint-use-prompt-regexp-instead-of-fields is nil. (comint-output-filter): Add `output' field property to process output, if comint-use-prompt-regexp-instead-of-fields is nil. (comint-replace-by-expanded-history) (comint-get-old-input-default, comint-show-output) (comint-backward-matching-input, comint-forward-matching-input) (comint-next-prompt, comint-previous-prompt): Use field properties if comint-use-prompt-regexp-instead-of-fields is nil. (comint-line-beginning-position): New function. (comint-bol): Use comint-line-beginning-position. Make ARG optional. (comint-replace-by-expanded-history-before-point): Use comint-line-beginning-position and line-end-position. (comint-last-output-overlay): New variable. (comint-mode): Make `comint-last-output-overlay' buffer-local.
author Miles Bader <miles@gnu.org>
date Mon, 07 Aug 2000 14:50:09 +0000
parents b9b3d00cd2db
children 297909766bad
files lisp/comint.el
diffstat 1 files changed, 199 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/comint.el	Mon Aug 07 13:12:43 2000 +0000
+++ b/lisp/comint.el	Mon Aug 07 14:50:09 2000 +0000
@@ -141,6 +141,7 @@
 ;;  comint-scroll-to-bottom-on-output	symbol	...
 ;;  comint-scroll-show-maximum-output	boolean	...	
 ;;  comint-accum-marker			maker	  For comint-accumulate
+;;  comint-last-output-overlay          overlay
 ;;
 ;; Comint mode non-buffer local variables:
 ;;  comint-completion-addsuffix		boolean/cons	For file name
@@ -160,11 +161,13 @@
   :prefix "comint-"
   :group 'comint)
 
-
 (defvar comint-prompt-regexp "^"
   "Regexp to recognise prompts in the inferior process.
 Defaults to \"^\", the null string at BOL.
 
+This variable is only used if the variable
+`comint-use-prompt-regexp-instead-of-fields' is non-nil.
+
 Good choices:
   Canonical Lisp: \"^[^> \\n]*>+:? *\" (Lucid, franz, kcl, T, cscheme, oaklisp)
   Lucid Common Lisp: \"^\\\\(>\\\\|\\\\(->\\\\)+\\\\) *\"
@@ -309,10 +312,12 @@
 ;; Here are the per-interpreter hooks.
 (defvar comint-get-old-input (function comint-get-old-input-default)
   "Function that returns old text in comint mode.
-This function is called when return is typed while the point is in old text.
-It returns the text to be submitted as process input.  The default is
-`comint-get-old-input-default', which grabs the current line, and strips off
-leading text matching `comint-prompt-regexp'.")
+This function is called when return is typed while the point is in old
+text.  It returns the text to be submitted as process input.  The
+default is `comint-get-old-input-default', which either grabs the
+current input field or grabs the current line and strips off leading
+text matching `comint-prompt-regexp', depending on the value of
+`comint-use-prompt-regexp-instead-of-fields'.")
 
 (defvar comint-dynamic-complete-functions
   '(comint-replace-by-expanded-history comint-dynamic-complete-filename)
@@ -359,6 +364,18 @@
   :type 'boolean
   :group 'comint)
 
+;; Note: If it is decided to purge comint-prompt-regexp from the source
+;; entirely, searching for uses of this variable will help to identify
+;; places that need attention.
+(defcustom comint-use-prompt-regexp-instead-of-fields nil
+  "*If non-nil, use `comint-prompt-regexp' to distinguish prompts from user-input.
+If nil, then program output and user-input are given different `field'
+properties, which emacs commands can use to distinguish them (in
+particular, common movement commands such as begining-of-line respect
+field boundaries in a natural way)."
+  :type 'boolean
+  :group 'comint)
+
 (defcustom comint-mode-hook '()
   "Called upon entry into comint-mode
 This is run before the process is cranked up."
@@ -462,6 +479,7 @@
   (set-marker comint-last-input-end (point-min))
   (make-local-variable 'comint-last-output-start)
   (setq comint-last-output-start (make-marker))
+  (make-local-variable 'comint-last-output-overlay)
   (make-local-variable 'comint-prompt-regexp)        ; Don't set; default
   (make-local-variable 'comint-input-ring-size)      ; ...to global val.
   (make-local-variable 'comint-input-ring)
@@ -1070,8 +1088,13 @@
   (interactive)
   (if (and comint-input-autoexpand
 	   (string-match "!\\|^\\^" (funcall comint-get-old-input))
-	   (save-excursion (beginning-of-line)
-			   (looking-at comint-prompt-regexp)))
+	   (if comint-use-prompt-regexp-instead-of-fields
+	       ;; Use comint-prompt-regexp
+	       (save-excursion (beginning-of-line)
+			       (looking-at comint-prompt-regexp))
+	     ;; Use input fields.  User input that hasn't been entered
+	     ;; yet, at the end of the buffer, has a nil `field' property.
+	     (null (get-char-property (point) 'field))))
       ;; Looks like there might be history references in the command.
       (let ((previous-modified-tick (buffer-modified-tick)))
 	(comint-replace-by-expanded-history-before-point silent start)
@@ -1086,15 +1109,11 @@
 start of the text to scan for history references, rather
 than the logical beginning of line."
   (save-excursion
-    (let ((toend (- (save-excursion (end-of-line nil) (point)) (point)))
-	  (start (or start (progn (comint-bol nil) (point)))))
+    (let ((toend (- (line-end-position) (point)))
+	  (start (comint-line-beginning-position)))
       (while (progn
-	       (skip-chars-forward "^!^"
-				   (save-excursion
-				     (end-of-line nil) (- (point) toend)))
-	       (< (point)
-		  (save-excursion
-		    (end-of-line nil) (- (point) toend))))
+	       (skip-chars-forward "^!^" (- (line-end-position) toend))
+	       (< (point) (- (line-end-position) toend)))
 	;; This seems a bit complex.  We look for references such as !!, !-num,
 	;; !foo, !?foo, !{bar}, !?{bar}, ^oh, ^my^, ^god^it, ^never^ends^.
 	;; If that wasn't enough, the plings can be suffixed with argument
@@ -1317,8 +1336,10 @@
 in the buffer.  E.g.,
 
 If the interpreter is the csh,
-    comint-get-old-input is the default: take the current line, discard any
-        initial string matching regexp comint-prompt-regexp.
+    comint-get-old-input is the default: either return the current
+        field, or take the current line and discard any
+        initial string matching regexp `comint-prompt-regexp', depending
+	on the value of `comint-use-prompt-regexp-instead-of-fields'.
     comint-input-filter-functions monitors input for \"cd\", \"pushd\", and
         \"popd\" commands. When it sees one, it cd's the buffer.
     comint-input-filter is the default: returns t if the input isn't all white
@@ -1360,9 +1381,11 @@
 			    (insert input)
 			    (delete-region pmark start)
 			    copy))))
+
           (if comint-process-echoes
               (delete-region pmark (point))
 	    (insert-before-markers ?\n))
+
 	  (if (and (funcall comint-input-filter history)
 		   (or (null comint-input-ignoredups)
 		       (not (ring-p comint-input-ring))
@@ -1370,17 +1393,33 @@
 		       (not (string-equal (ring-ref comint-input-ring 0)
 					  history))))
 	      (ring-insert comint-input-ring history))
+
 	  (run-hook-with-args 'comint-input-filter-functions
 			      (concat input "\n"))
+
 	  (let ((beg (marker-position pmark))
 		(end (1- (point))))
-	    (when (and comint-highlight-input
-		       ;; handle a special case
-		       (not (> beg end)))
+	    (when (not (> beg end))	; handle a special case
+	      ;; Make an overlay for the input field
 	      (let ((over (make-overlay beg end)))
-		(overlay-put over 'face comint-highlight-face)
-		(overlay-put over 'mouse-face 'highlight)		
+		(unless comint-use-prompt-regexp-instead-of-fields
+		  ;; Give old user input a field property of `input', to
+		  ;; distinguish it from both process output and unsent
+		  ;; input.  The terminating newline is put into a special
+		  ;; `boundary' field to make cursor movement between input
+		  ;; and output fields smoother.
+		  (overlay-put over 'field 'input)
+		  (overlay-put over 'front-sticky t))
+		(when comint-highlight-input
+		  (overlay-put over 'face comint-highlight-face)
+		  (overlay-put over 'mouse-face 'highlight)
+		  (overlay-put over 'evaporate t))))
+	    (unless comint-use-prompt-regexp-instead-of-fields
+	      ;; Make an overlay for the terminating newline
+	      (let ((over (make-overlay end (1+ end))))
+		(overlay-put over 'field 'boundary)
 		(overlay-put over 'evaporate t))))
+
 	  (setq comint-save-input-ring-index comint-input-ring-index)
 	  (setq comint-input-ring-index nil)
 	  ;; Update the markers before we send the input
@@ -1409,6 +1448,11 @@
 
 This variable is permanent-local.")
 
+;; When non-nil, this is the last overlay used for output.
+;; It is kept around so that we can extend it instead of creating
+;; multiple contiguous overlays for multiple contiguous output chunks.
+(defvar comint-last-output-overlay nil)
+
 ;; The purpose of using this filter for comint processes
 ;; is to keep comint-last-input-end from moving forward
 ;; when output is inserted.
@@ -1439,6 +1483,23 @@
 	    (if (<= (point) oend)
 		(setq oend (+ oend nchars)))
 	    (insert-before-markers string)
+
+	    (unless comint-use-prompt-regexp-instead-of-fields
+	      ;; We check to see if the last overlay used for output has
+	      ;; already been extended to include STRING (because it was
+	      ;; inserted with insert-before-markers?), and only make
+	      ;; a new overlay if it hasn't.
+	      (unless (and comint-last-output-overlay
+			   (= (overlay-end comint-last-output-overlay)
+			      (point)))
+		;; Create a new overlay
+		(let ((over (make-overlay ostart (point))))
+		  (overlay-put over 'field 'output)
+		  (overlay-put over 'rear-nonsticky t)
+		  (overlay-put over 'inhibit-line-move-field-capture t)
+		  (overlay-put over 'evaporate t)
+		  (setq comint-last-output-overlay over))))
+
 	    ;; Don't insert initial prompt outside the top of the window.
 	    (if (= (window-start (selected-window)) (point))
 		(set-window-start (selected-window) (- (point) (length string))))
@@ -1552,14 +1613,19 @@
 
 (defun comint-get-old-input-default ()
   "Default for `comint-get-old-input'.
-Take the current line, and discard any initial text matching
-`comint-prompt-regexp'."
-  (save-excursion
-    (beginning-of-line)
-    (comint-skip-prompt)
-    (let ((beg (point)))
-      (end-of-line)
-      (buffer-substring beg (point)))))
+Returns either the current field, or the current line with any initial
+text matching `comint-prompt-regexp' stripped off, depending on the
+value of `comint-use-prompt-regexp-instead-of-fields'."
+  (if comint-use-prompt-regexp-instead-of-fields
+      (save-excursion
+	(beginning-of-line)
+	(comint-skip-prompt)
+	(let ((beg (point)))
+	  (end-of-line)
+	  (buffer-substring beg (point))))
+    ;; Return the contents of the field at the current point.
+    (field-string)))
+
 
 (defun comint-copy-old-input ()
   "Insert after prompt old input at point as new input to be edited.
@@ -1592,15 +1658,38 @@
   (comint-send-string proc string)
   (comint-send-string proc "\n"))
 
-(defun comint-bol (arg)
+(defun comint-line-beginning-position ()
+  "Returns the buffer position of the beginning of the line, after any prompt.
+If `comint-use-prompt-regexp-instead-of-fields' is non-nil, then the
+prompt skip is done by skipping text matching the regular expression
+`comint-prompt-regexp', a buffer local variable."
+  (if comint-use-prompt-regexp-instead-of-fields
+      ;; Use comint-prompt-regexp
+      (save-excursion
+	(beginning-of-line)
+	(comint-skip-prompt)
+	(point))
+    ;; Use input fields.  Note that, unlike the behavior of
+    ;; `line-beginning-position' inside a field, this function will
+    ;; return the position of the end of a prompt, even if the point is
+    ;; already inside the prompt.  In order to do this, it assumes that
+    ;; if there are two fields on a line, then the first one is the
+    ;; prompt, and the second one is an input field, and is front-sticky
+    ;; (as input fields should be).
+    (constrain-to-field (line-beginning-position) (line-end-position))))
+
+(defun comint-bol (&optional arg)
   "Goes to the beginning of line, then skips past the prompt, if any.
-If prefix argument is given (\\[universal-argument]) the prompt is not skipped. 
-
-The prompt skip is done by skipping text matching the regular expression
+If prefix argument is given (\\[universal-argument]) the prompt is not skipped.
+If `comint-use-prompt-regexp-instead-of-fields' is non-nil, then the
+prompt skip is done by skipping text matching the regular expression
 `comint-prompt-regexp', a buffer local variable."
   (interactive "P")
-  (beginning-of-line)
-  (if (null arg) (comint-skip-prompt)))
+  (if arg
+      ;; Disregard prompt
+      (let ((inhibit-field-text-motion t))
+	(beginning-of-line))
+    (goto-char (comint-line-beginning-position))))
 
 ;; These three functions are for entering text you don't want echoed or
 ;; saved -- typically passwords to ftp, telnet, or somesuch.
@@ -1721,11 +1810,16 @@
 Sets mark to the value of point when this command is run."
   (interactive)
   (push-mark)
-  (let ((pos (point)))
-    (goto-char (or (marker-position comint-last-input-end) (point-max)))
-    (beginning-of-line 0)
-    (set-window-start (selected-window) (point))
-    (comint-skip-prompt)))
+  (let ((pos (or (marker-position comint-last-input-end) (point-max))))
+    (cond (comint-use-prompt-regexp-instead-of-fields
+	   (goto-char pos)
+	   (beginning-of-line 0)
+	   (set-window-start (selected-window) (point))
+	   (comint-skip-prompt))
+	  (t
+	   (goto-char (field-beginning pos))
+	   (set-window-start (selected-window) (point))))))
+
 
 (defun comint-interrupt-subjob ()
   "Interrupt the current subjob.
@@ -1793,24 +1887,42 @@
 
 
 (defun comint-backward-matching-input (regexp arg)
-  "Search backward through buffer for match for REGEXP.
-Matches are searched for on lines that match `comint-prompt-regexp'.
+  "Search backward through buffer for input fields that match REGEXP.
+If `comint-use-prompt-regexp-instead-of-fields' is non-nil, then input
+fields are identified by lines that match `comint-prompt-regexp'.
+
 With prefix argument N, search for Nth previous match.
 If N is negative, find the next or Nth next match."
   (interactive (comint-regexp-arg "Backward input matching (regexp): "))
-  (let* ((re (concat comint-prompt-regexp ".*" regexp))
-	 (pos (save-excursion (end-of-line (if (> arg 0) 0 1))
-			      (if (re-search-backward re nil t arg)
-				  (point)))))
-    (if (null pos)
-	(progn (message "Not found")
-	       (ding))
-      (goto-char pos)
-      (comint-bol nil))))
+  (if comint-use-prompt-regexp-instead-of-fields
+      ;; Use comint-prompt-regexp
+      (let* ((re (concat comint-prompt-regexp ".*" regexp))
+	     (pos (save-excursion (end-of-line (if (> arg 0) 0 1))
+				  (if (re-search-backward re nil t arg)
+				      (point)))))
+	(if (null pos)
+	    (progn (message "Not found")
+		   (ding))
+	  (goto-char pos)
+	  (comint-bol nil)))
+    ;; Use input fields
+    (let* ((dir (if (< arg 0) -1 1))
+	   (pos
+	    (save-excursion
+	      (while (/= arg 0)
+		(unless (re-search-backward regexp nil t dir)
+		  (error "Not found"))
+		(when (eq (get-char-property (point) 'field) 'input)
+		  (setq arg (- arg dir))))
+	      (field-beginning))))
+      (goto-char pos))))
+
 
 (defun comint-forward-matching-input (regexp arg)
-  "Search forward through buffer for match for REGEXP.
-Matches are searched for on lines that match `comint-prompt-regexp'.
+  "Search forward through buffer for input fields that match REGEXP.
+If `comint-use-prompt-regexp-instead-of-fields' is non-nil, then input
+fields are identified by lines that match `comint-prompt-regexp'.
+
 With prefix argument N, search for Nth following match.
 If N is negative, find the previous or Nth previous match."
   (interactive (comint-regexp-arg "Forward input matching (regexp): "))
@@ -1819,16 +1931,39 @@
 
 (defun comint-next-prompt (n)
   "Move to end of Nth next prompt in the buffer.
-See `comint-prompt-regexp'."
+If `comint-use-prompt-regexp-instead-of-fields' is nil, then this means
+the beginning of the Nth next `input' field, otherwise, it means the Nth
+occurance of text matching `comint-prompt-regexp'."
   (interactive "p")
-  (let ((paragraph-start comint-prompt-regexp))
-    (end-of-line (if (> n 0) 1 0))
-    (forward-paragraph n)
-    (comint-skip-prompt)))
+  (if comint-use-prompt-regexp-instead-of-fields
+      ;; Use comint-prompt-regexp
+      (let ((paragraph-start comint-prompt-regexp))
+	(end-of-line (if (> n 0) 1 0))
+	(forward-paragraph n)
+	(comint-skip-prompt))
+    ;; Use input fields
+    (let ((pos (point))
+	  (input-pos nil))
+      (while (/= n 0)
+	(setq pos
+	      (if (> n 0)
+		  (next-single-char-property-change pos 'field)
+		(previous-single-char-property-change pos 'field)))
+	(cond ((null pos)
+	       ;; Ran off the end of the buffer.
+	       (setq n 0))
+	      ((eq (get-char-property pos 'field) 'input)
+	       (setq n (if (< n 0) (1+ n) (1- n)))
+	       (setq input-pos pos))))
+      (when input-pos
+	(goto-char input-pos)))))
+
 
 (defun comint-previous-prompt (n)
   "Move to end of Nth previous prompt in the buffer.
-See `comint-prompt-regexp'."
+If `comint-use-prompt-regexp-instead-of-fields' is nil, then this means
+the beginning of the Nth previous `input' field, otherwise, it means the Nth
+occurance of text matching `comint-prompt-regexp'."
   (interactive "p")
   (comint-next-prompt (- n)))
 
@@ -2468,6 +2603,10 @@
 ;; a prompt in the comint buffer; however, it is still important to ensure that
 ;; this prompt is set correctly.
 ;;
+;; XXX: This doesn't work so well unless comint-prompt-regexp is set;
+;; perhaps it should prompt for a terminating string (with an
+;; appropriate magic default by examining what we think is the prompt)?
+;;
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Variables