X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/11fdef7d0cf3ef1ce30d1cd09ca9ca9a2b099d20..d5c20fe8ca35d0a2b45bfad0a90553be04752d75:/lisp/comint.el diff --git a/lisp/comint.el b/lisp/comint.el index 5548d19ad3..0e614d469d 100644 --- a/lisp/comint.el +++ b/lisp/comint.el @@ -1,6 +1,6 @@ ;;; comint.el --- general command interpreter in a window stuff -*- lexical-binding: t -*- -;; Copyright (C) 1988, 1990, 1992-2011 Free Software Foundation, Inc. +;; Copyright (C) 1988, 1990, 1992-2012 Free Software Foundation, Inc. ;; Author: Olin Shivers ;; Simon Marshall @@ -45,7 +45,7 @@ ;; It is pretty easy to make new derived modes for other processes. ;; For documentation on the functionality provided by Comint mode, and -;; the hooks available for customising it, see the comments below. +;; the hooks available for customizing it, see the comments below. ;; For further information on the standard derived modes (shell, ;; inferior-lisp, inferior-scheme, ...), see the relevant source files. @@ -103,6 +103,8 @@ (eval-when-compile (require 'cl)) (require 'ring) +(require 'ansi-color) +(require 'regexp-opt) ;For regexp-opt-charset. ;; Buffer Local Variables: ;;============================================================================ @@ -347,7 +349,7 @@ This variable is buffer-local." " +\\)" (regexp-opt '("password" "Password" "passphrase" "Passphrase" - "pass phrase" "Pass phrase")) + "pass phrase" "Pass phrase" "Response")) "\\(?:\\(?:, try\\)? *again\\| (empty for no passphrase)\\| (again)\\)?\ \\(?: for [^:]+\\)?:\\s *\\'") "Regexp matching prompts for passwords in the inferior process. @@ -385,7 +387,7 @@ history list. Default is to save anything that isn't all whitespace.") These functions get one argument, a string containing the text to send.") ;;;###autoload -(defvar comint-output-filter-functions '(comint-postoutput-scroll-to-bottom comint-watch-for-password-prompt) +(defvar comint-output-filter-functions '(ansi-color-process-output comint-postoutput-scroll-to-bottom comint-watch-for-password-prompt) "Functions to call after output is inserted into the buffer. One possible function is `comint-postoutput-scroll-to-bottom'. These functions get one argument, a string containing the text as originally @@ -698,16 +700,21 @@ BUFFER can be either a buffer or the name of one." (defun make-comint-in-buffer (name buffer program &optional startfile &rest switches) "Make a Comint process NAME in BUFFER, running PROGRAM. If BUFFER is nil, it defaults to NAME surrounded by `*'s. -PROGRAM should be either a string denoting an executable program to create -via `start-file-process', or a cons pair of the form (HOST . SERVICE) denoting -a TCP connection to be opened via `open-network-stream'. If there is already -a running process in that buffer, it is not restarted. Optional fourth arg -STARTFILE is the name of a file, whose contents are sent to the -process as its initial input. +If there is a running process in BUFFER, it is not restarted. + +PROGRAM should be one of the following: +- a string, denoting an executable program to create via + `start-file-process' +- a cons pair of the form (HOST . SERVICE), denoting a TCP + connection to be opened via `open-network-stream' +- nil, denoting a newly-allocated pty. + +Optional fourth arg STARTFILE is the name of a file, whose +contents are sent to the process as its initial input. If PROGRAM is a string, any more args are arguments to PROGRAM. -Returns the (possibly newly created) process buffer." +Return the (possibly newly created) process buffer." (or (fboundp 'start-file-process) (error "Multi-processing is not supported for this system")) (setq buffer (get-buffer-create (or buffer (concat "*" name "*")))) @@ -751,9 +758,18 @@ See `make-comint' and `comint-exec'." (defun comint-exec (buffer name command startfile switches) "Start up a process named NAME in buffer BUFFER for Comint modes. Runs the given COMMAND with SWITCHES, and initial input from STARTFILE. -Blasts any old process running in the buffer. Doesn't set the buffer mode. -You can use this to cheaply run a series of processes in the same Comint -buffer. The hook `comint-exec-hook' is run after each exec." + +COMMAND should be one of the following: +- a string, denoting an executable program to create via + `start-file-process' +- a cons pair of the form (HOST . SERVICE), denoting a TCP + connection to be opened via `open-network-stream' +- nil, denoting a newly-allocated pty. + +This function blasts any old process running in the buffer, and +does not set the buffer mode. You can use this to cheaply run a +series of processes in the same Comint buffer. The hook +`comint-exec-hook' is run after each exec." (with-current-buffer buffer (let ((proc (get-buffer-process buffer))) ; Blast any old process. (if proc (delete-process proc))) @@ -847,10 +863,10 @@ by the global keymap (usually `mouse-yank-at-click')." ;; If pos is at the very end of a field, the mouse-click was ;; probably outside (to the right) of the field. (and (< pos (field-end pos)) - (setq field (field-at-pos pos)) - (setq input (field-string-no-properties pos)))) - (if (or (null comint-accum-marker) - (not (eq field 'input))) + (< (field-end pos) (point-max)) + (progn (setq field (field-at-pos pos)) + (setq input (field-string-no-properties pos))))) + (if (or (null input) (null comint-accum-marker) field) ;; Fall back to the global definition if (i) the selected ;; buffer is not a comint buffer (which can happen if a ;; non-comint window was selected and we clicked in a comint @@ -921,15 +937,18 @@ See also `comint-input-ignoredups' and `comint-write-input-ring'." (t (let* ((file comint-input-ring-file-name) (count 0) - (size comint-input-ring-size) - (ring (make-ring size))) + ;; Some users set HISTSIZE or `comint-input-ring-size' + ;; to huge numbers. Don't allocate a huge ring right + ;; away; there might not be that much history. + (ring-size (min 1500 comint-input-ring-size)) + (ring (make-ring ring-size))) (with-temp-buffer (insert-file-contents file) ;; Save restriction in case file is already visited... ;; Watch for those date stamps in history files! (goto-char (point-max)) (let (start end history) - (while (and (< count size) + (while (and (< count comint-input-ring-size) (re-search-backward comint-input-ring-separator nil t) (setq end (match-beginning 0))) @@ -940,15 +959,18 @@ See also `comint-input-ignoredups' and `comint-write-input-ring'." (point-min))) (setq history (buffer-substring start end)) (goto-char start) - (if (and (not (string-match comint-input-history-ignore - history)) - (or (null comint-input-ignoredups) - (ring-empty-p ring) - (not (string-equal (ring-ref ring 0) - history)))) - (progn - (ring-insert-at-beginning ring history) - (setq count (1+ count))))))) + (when (and (not (string-match comint-input-history-ignore + history)) + (or (null comint-input-ignoredups) + (ring-empty-p ring) + (not (string-equal (ring-ref ring 0) + history)))) + (when (= count ring-size) + (ring-extend ring (min (- comint-input-ring-size ring-size) + ring-size)) + (setq ring-size (ring-size ring))) + (ring-insert-at-beginning ring history) + (setq count (1+ count)))))) (setq comint-input-ring ring comint-input-ring-index nil))))) @@ -1054,10 +1076,10 @@ See also `comint-read-input-ring'." (defun comint-search-arg (arg) ;; First make sure there is a ring and that we are after the process mark (cond ((not (comint-after-pmark-p)) - (error "Not at command line")) + (user-error "Not at command line")) ((or (null comint-input-ring) (ring-empty-p comint-input-ring)) - (error "Empty input ring")) + (user-error "Empty input ring")) ((zerop arg) ;; arg of zero resets search from beginning, and uses arg of 1 (setq comint-input-ring-index nil) @@ -1124,7 +1146,7 @@ Moves relative to `comint-input-ring-index'." Moves relative to START, or `comint-input-ring-index'." (if (or (not (ring-p comint-input-ring)) (ring-empty-p comint-input-ring)) - (error "No history")) + (user-error "No history")) (let* ((len (ring-length comint-input-ring)) (motion (if (> arg 0) 1 -1)) (n (mod (- (or start (comint-search-start arg)) motion) len)) @@ -1164,7 +1186,7 @@ If N is negative, find the next or Nth next match." (let ((pos (comint-previous-matching-input-string-position regexp n))) ;; Has a match been found? (if (null pos) - (error "Not found") + (user-error "Not found") ;; If leaving the edit line, save partial input (if (null comint-input-ring-index) ;not yet on ring (setq comint-stored-incomplete-input @@ -1350,7 +1372,7 @@ actual side-effect." (goto-char (match-beginning 0)) (if (not (search-forward old pos t)) (or silent - (error "Not found")) + (user-error "Not found")) (replace-match new t t) (message "History item: substituted")))) (t @@ -1419,7 +1441,7 @@ Intended to be added to `isearch-mode-hook' in `comint-mode'." (if comint-history-isearch-message-overlay (delete-overlay comint-history-isearch-message-overlay)) (setq isearch-message-prefix-add nil) - (setq isearch-search-fun-function nil) + (setq isearch-search-fun-function 'isearch-search-fun-default) (setq isearch-message-function nil) (setq isearch-wrap-function nil) (setq isearch-push-state-function nil) @@ -1441,67 +1463,59 @@ Intended to be added to `isearch-mode-hook' in `comint-mode'." (defun comint-history-isearch-search () "Return the proper search function, for Isearch in input history." - (cond - (isearch-word - (if isearch-forward 'word-search-forward 'word-search-backward)) - (t - (lambda (string bound noerror) - (let ((search-fun - ;; Use standard functions to search within comint text - (cond - (isearch-regexp - (if isearch-forward 're-search-forward 're-search-backward)) - (t - (if isearch-forward 'search-forward 'search-backward)))) - found) - ;; Avoid lazy-highlighting matches in the comint prompt and in the - ;; output when searching forward. Lazy-highlight calls this lambda - ;; with the bound arg, so skip the prompt and the output. - (if (and bound isearch-forward (not (comint-after-pmark-p))) - (goto-char (process-mark (get-buffer-process (current-buffer))))) - (or - ;; 1. First try searching in the initial comint text - (funcall search-fun string - (if isearch-forward bound (comint-line-beginning-position)) - noerror) - ;; 2. If the above search fails, start putting next/prev history - ;; elements in the comint successively, and search the string - ;; in them. Do this only when bound is nil (i.e. not while - ;; lazy-highlighting search strings in the current comint text). - (unless bound - (condition-case nil - (progn - (while (not found) - (cond (isearch-forward - ;; Signal an error here explicitly, because - ;; `comint-next-input' doesn't signal an error. - (when (null comint-input-ring-index) - (error "End of history; no next item")) - (comint-next-input 1) - (goto-char (comint-line-beginning-position))) - (t - ;; Signal an error here explicitly, because - ;; `comint-previous-input' doesn't signal an error. - (when (eq comint-input-ring-index - (1- (ring-length comint-input-ring))) - (error "Beginning of history; no preceding item")) - (comint-previous-input 1) - (goto-char (point-max)))) - (setq isearch-barrier (point) isearch-opoint (point)) - ;; After putting the next/prev history element, search - ;; the string in them again, until comint-next-input - ;; or comint-previous-input raises an error at the - ;; beginning/end of history. - (setq found (funcall search-fun string - (unless isearch-forward - ;; For backward search, don't search - ;; in the comint prompt - (comint-line-beginning-position)) - noerror))) - ;; Return point of the new search result - (point)) - ;; Return nil on the error "no next/preceding item" - (error nil))))))))) + (lambda (string bound noerror) + (let ((search-fun + ;; Use standard functions to search within comint text + (isearch-search-fun-default)) + found) + ;; Avoid lazy-highlighting matches in the comint prompt and in the + ;; output when searching forward. Lazy-highlight calls this lambda + ;; with the bound arg, so skip the prompt and the output. + (if (and bound isearch-forward (not (comint-after-pmark-p))) + (goto-char (process-mark (get-buffer-process (current-buffer))))) + (or + ;; 1. First try searching in the initial comint text + (funcall search-fun string + (if isearch-forward bound (comint-line-beginning-position)) + noerror) + ;; 2. If the above search fails, start putting next/prev history + ;; elements in the comint successively, and search the string + ;; in them. Do this only when bound is nil (i.e. not while + ;; lazy-highlighting search strings in the current comint text). + (unless bound + (condition-case nil + (progn + (while (not found) + (cond (isearch-forward + ;; Signal an error here explicitly, because + ;; `comint-next-input' doesn't signal an error. + (when (null comint-input-ring-index) + (error "End of history; no next item")) + (comint-next-input 1) + (goto-char (comint-line-beginning-position))) + (t + ;; Signal an error here explicitly, because + ;; `comint-previous-input' doesn't signal an error. + (when (eq comint-input-ring-index + (1- (ring-length comint-input-ring))) + (error "Beginning of history; no preceding item")) + (comint-previous-input 1) + (goto-char (point-max)))) + (setq isearch-barrier (point) isearch-opoint (point)) + ;; After putting the next/prev history element, search + ;; the string in them again, until comint-next-input + ;; or comint-previous-input raises an error at the + ;; beginning/end of history. + (setq found (funcall search-fun string + (unless isearch-forward + ;; For backward search, don't search + ;; in the comint prompt + (comint-line-beginning-position)) + noerror))) + ;; Return point of the new search result + (point)) + ;; Return nil on the error "no next/preceding item" + (error nil))))))) (defun comint-history-isearch-message (&optional c-q-hack ellipsis) "Display the input history search prompt. @@ -1534,14 +1548,13 @@ Otherwise, it displays the standard Isearch message returned from "Wrap the input history search when search fails. Move point to the first history element for a forward search, or to the last history element for a backward search." - (unless isearch-word - ;; When `comint-history-isearch-search' fails on reaching the - ;; beginning/end of the history, wrap the search to the first/last - ;; input history element. - (if isearch-forward - (comint-goto-input (1- (ring-length comint-input-ring))) - (comint-goto-input nil)) - (setq isearch-success t)) + ;; When `comint-history-isearch-search' fails on reaching the + ;; beginning/end of the history, wrap the search to the first/last + ;; input history element. + (if isearch-forward + (comint-goto-input (1- (ring-length comint-input-ring))) + (comint-goto-input nil)) + (setq isearch-success t) (goto-char (if isearch-forward (comint-line-beginning-position) (point-max)))) (defun comint-history-isearch-push-state () @@ -1690,13 +1703,18 @@ Argument 0 is the command name." (defun comint-add-to-input-history (cmd) "Add CMD to the input history. Ignore duplicates if `comint-input-ignoredups' is non-nil." - (if (and (funcall comint-input-filter cmd) - (or (null comint-input-ignoredups) - (not (ring-p comint-input-ring)) - (ring-empty-p comint-input-ring) - (not (string-equal (ring-ref comint-input-ring 0) - cmd)))) - (ring-insert comint-input-ring cmd))) + (when (and (funcall comint-input-filter cmd) + (or (null comint-input-ignoredups) + (not (ring-p comint-input-ring)) + (ring-empty-p comint-input-ring) + (not (string-equal (ring-ref comint-input-ring 0) cmd)))) + ;; If `comint-input-ring' is full, maybe grow it. + (let ((size (ring-size comint-input-ring))) + (and (= size (ring-length comint-input-ring)) + (< size comint-input-ring-size) + (ring-extend comint-input-ring + (min size (- comint-input-ring-size size))))) + (ring-insert comint-input-ring cmd))) (defun comint-send-input (&optional no-newline artificial) "Send input to process. @@ -1750,7 +1768,7 @@ Similarly for Soar, Scheme, etc." (interactive) ;; Note that the input string does not include its terminal newline. (let ((proc (get-buffer-process (current-buffer)))) - (if (not proc) (error "Current buffer has no process") + (if (not proc) (user-error "Current buffer has no process") (widen) (let* ((pmark (process-mark proc)) (intxt (if (>= (point) (marker-position pmark)) @@ -1803,8 +1821,7 @@ Similarly for Soar, Scheme, etc." (add-text-properties beg end '(mouse-face highlight - help-echo "mouse-2: insert after prompt as new input" - field input)))) + help-echo "mouse-2: insert after prompt as new input")))) (unless (or no-newline comint-use-prompt-regexp) ;; Cover the terminating newline (add-text-properties end (1+ end) @@ -1981,7 +1998,7 @@ Make backspaces delete the previous character." ;; The point should float after any insertion we do. (saved-point (copy-marker (point) t))) - ;; We temporarly remove any buffer narrowing, in case the + ;; We temporarily remove any buffer narrowing, in case the ;; process mark is outside of the restriction (save-restriction (widen) @@ -2075,43 +2092,51 @@ This function should be a pre-command hook." (select-window selected)))) nil t)))))) +(defvar follow-mode) +(declare-function follow-comint-scroll-to-bottom "follow" ()) + (defun comint-postoutput-scroll-to-bottom (_string) "Go to the end of buffer in some or all windows showing it. -Does not scroll if the current line is the last line in the buffer. +Do not scroll if the current line is the last line in the buffer. Depends on the value of `comint-move-point-for-output' and `comint-scroll-show-maximum-output'. This function should be in the list `comint-output-filter-functions'." - (let* ((selected (selected-window)) - (current (current-buffer)) - (process (get-buffer-process current)) - (scroll comint-move-point-for-output)) + (let* ((current (current-buffer)) + (process (get-buffer-process current))) (unwind-protect - (if process - (walk-windows - (lambda (window) - (when (eq (window-buffer window) current) - (select-window window) - (if (and (< (point) (process-mark process)) - (or (eq scroll t) (eq scroll 'all) - ;; Maybe user wants point to jump to end. - (and (eq scroll 'this) (eq selected window)) - (and (eq scroll 'others) (not (eq selected window))) - ;; If point was at the end, keep it at end. - (and (marker-position comint-last-output-start) - (>= (point) comint-last-output-start)))) - (goto-char (process-mark process))) - ;; Optionally scroll so that the text - ;; ends at the bottom of the window. - (if (and comint-scroll-show-maximum-output - (= (point) (point-max))) - (save-excursion - (goto-char (point-max)) - (recenter (- -1 scroll-margin)))) - (select-window selected))) - nil t)) + (cond + ((null process)) + ((bound-and-true-p follow-mode) + (follow-comint-scroll-to-bottom)) + (t + (let ((selected (selected-window))) + (dolist (w (get-buffer-window-list current nil t)) + (select-window w) + (unwind-protect + (progn + (comint-adjust-point selected) + ;; Optionally scroll to the bottom of the window. + (and comint-scroll-show-maximum-output + (eobp) + (recenter (- -1 scroll-margin)))) + (select-window selected)))))) (set-buffer current)))) +(defun comint-adjust-point (selected) + "Move point in the selected window based on Comint settings. +SELECTED is the window that was originally selected." + (let ((process (get-buffer-process (current-buffer)))) + (and (< (point) (process-mark process)) + (or (memq comint-move-point-for-output '(t all)) + ;; Maybe user wants point to jump to end. + (eq comint-move-point-for-output + (if (eq (selected-window) selected) 'this 'others)) + ;; If point was at the end, keep it at end. + (and (marker-position comint-last-output-start) + (>= (point) comint-last-output-start))) + (goto-char (process-mark process))))) + (defun comint-truncate-buffer (&optional _string) "Truncate the buffer to `comint-buffer-maximum-size'. This function could be on `comint-output-filter-functions' or bound to a key." @@ -2152,8 +2177,10 @@ current line, if point is on an output field. If `comint-use-prompt-regexp' is non-nil, then return the current line with any initial string matching the regexp `comint-prompt-regexp' removed." - (let ((bof (field-beginning))) - (if (eq (get-char-property bof 'field) 'input) + (let (bof) + (if (and (not comint-use-prompt-regexp) + ;; Make sure we're in an input rather than output field. + (null (get-char-property (setq bof (field-beginning)) 'field))) (field-string-no-properties bof) (comint-bol) (buffer-substring-no-properties (point) (line-end-position))))) @@ -2165,7 +2192,7 @@ Calls `comint-get-old-input' to get old input." (let ((input (funcall comint-get-old-input)) (process (get-buffer-process (current-buffer)))) (if (not process) - (error "Current buffer has no process") + (user-error "Current buffer has no process") (goto-char (process-mark process)) (insert input)))) @@ -2472,8 +2499,8 @@ If N is negative, find the next or Nth next match." (save-excursion (while (/= n 0) (unless (re-search-backward regexp nil t dir) - (error "Not found")) - (when (eq (get-char-property (point) 'field) 'input) + (user-error "Not found")) + (unless (get-char-property (point) 'field) (setq n (- n dir)))) (field-beginning)))) (goto-char pos)))) @@ -2512,7 +2539,7 @@ text matching `comint-prompt-regexp'." (if (> n 0) (next-single-char-property-change pos 'field) (previous-single-char-property-change pos 'field))) - (cond ((or (null pos) (= pos prev-pos)) + (cond ((= pos prev-pos) ;; Ran off the end of the buffer. (when (> n 0) ;; There's always an input field at the end of the @@ -2520,7 +2547,7 @@ text matching `comint-prompt-regexp'." (setq input-pos (point-max))) ;; stop iterating (setq n 0)) - ((eq (get-char-property pos 'field) 'input) + ((null (get-char-property pos 'field)) (setq n (if (< n 0) (1+ n) (1- n))) (setq input-pos pos)))) (when input-pos @@ -2567,7 +2594,7 @@ This command is like `M-.' in bash." ;; First usage; initialize to a marker (setq comint-insert-previous-argument-last-start-pos (make-marker))))) - ;; Make sure we're not in the prompt, and add a beginning space if necess. + ;; Make sure we're not in the prompt, and add a beginning space if necessary. (if (<= (point) (comint-line-beginning-position)) (comint-bol) (just-one-space)) @@ -2932,19 +2959,20 @@ This is a good thing to set in mode hooks.") "Return the word of WORD-CHARS at point, or nil if none is found. Word constituents are considered to be those in WORD-CHARS, which is like the inside of a \"[...]\" (see `skip-chars-forward'), plus all non-ASCII characters." + ;; FIXME: Need to handle "..." and '...' quoting in shell.el! + ;; This should be combined with completion parsing somehow. (save-excursion (let ((here (point)) giveup) (while (not giveup) (let ((startpoint (point))) (skip-chars-backward (concat "\\\\" word-chars)) - ;; Fixme: This isn't consistent with Bash, at least -- not - ;; all non-ASCII chars should be word constituents. - (if (and (> (- (point) 2) (point-min)) - (= (char-after (- (point) 2)) ?\\)) + (if (and comint-file-name-quote-list + (eq (char-before (1- (point))) ?\\)) (forward-char -2)) - (if (and (> (- (point) 1) (point-min)) - (>= (char-after (- (point) 1)) 128)) + ;; FIXME: This isn't consistent with Bash, at least -- not + ;; all non-ASCII chars should be word constituents. + (if (and (not (bobp)) (>= (char-before) 128)) (forward-char -1)) (if (= (point) startpoint) (setq giveup t)))) @@ -2973,26 +3001,53 @@ interpreter (e.g., the percent notation of cmd.exe on Windows)." See `comint-word'." (comint-word comint-file-name-chars)) -(defun comint--unquote&expand-filename (filename) - ;; FIXME: The code below does unquote-then-expand which means that "\\$HOME" - ;; gets expanded to the same as "$HOME" - (comint-substitute-in-file-name - (comint-unquote-filename filename))) +(defun comint--unquote&requote-argument (qstr &optional upos) + (unless upos (setq upos 0)) + (let* ((qpos 0) + (ustrs '()) + (re (concat + "\\$\\(?:\\([[:alpha:]][[:alnum:]]*\\)" + "\\|{\\(?1:[^{}]+\\)}\\)" + (when (memq system-type '(ms-dos windows-nt)) + "\\|%\\(?1:[^\\\\/]*\\)%") + (when comint-file-name-quote-list + "\\|\\\\\\(.\\)"))) + (qupos nil) + (push (lambda (str end) + (push str ustrs) + (setq upos (- upos (length str))) + (unless (or qupos (> upos 0)) + (setq qupos (if (< end 0) (- end) (+ upos end)))))) + match) + (while (setq match (string-match re qstr qpos)) + (funcall push (substring qstr qpos match) match) + (cond + ((match-beginning 2) (funcall push (match-string 2 qstr) (match-end 0))) + ((match-beginning 1) (funcall push (getenv (match-string 1 qstr)) + (- (match-end 0)))) + (t (error "Unexpected case in comint--unquote&requote-argument!"))) + (setq qpos (match-end 0))) + (funcall push (substring qstr qpos) (length qstr)) + (list (mapconcat #'identity (nreverse ustrs) "") + qupos #'comint-quote-filename))) + +(defun comint--unquote-argument (str) + (car (comint--unquote&requote-argument str))) +(define-obsolete-function-alias 'comint--unquote&expand-filename + #'comint--unquote-argument "24.2") (defun comint-match-partial-filename () "Return the unquoted&expanded filename at point, or nil if none is found. Environment variables are substituted. See `comint-word'." (let ((filename (comint--match-partial-filename))) - (and filename (comint--unquote&expand-filename filename)))) + (and filename (comint--unquote-argument filename)))) (defun comint-quote-filename (filename) "Return FILENAME with magic characters quoted. Magic characters are those in `comint-file-name-quote-list'." (if (null comint-file-name-quote-list) filename - (let ((regexp - (format "[%s]" - (mapconcat 'char-to-string comint-file-name-quote-list "")))) + (let ((regexp (regexp-opt-charset comint-file-name-quote-list))) (save-match-data (let ((i 0)) (while (string-match regexp filename i) @@ -3005,11 +3060,13 @@ Magic characters are those in `comint-file-name-quote-list'." (if (null comint-file-name-quote-list) filename (save-match-data - (let ((i 0)) - (while (string-match "\\\\\\(.\\)" filename i) - (setq filename (replace-match "\\1" nil nil filename)) - (setq i (+ 1 (match-beginning 0))))) - filename))) + (replace-regexp-in-string "\\\\\\(.\\)" "\\1" filename t)))) +(make-obsolete 'comint-unquote-filename nil "24.2") + +(defun comint--requote-argument (upos qstr) + ;; See `completion-table-with-quoting'. + (let ((res (comint--unquote&requote-argument qstr upos))) + (cons (nth 1 res) (nth 2 res)))) (defun comint-completion-at-point () (run-hook-with-args-until-success 'comint-dynamic-complete-functions)) @@ -3043,79 +3100,30 @@ Returns t if successful." (when (comint--match-partial-filename) (comint--complete-file-name-data))) -;; FIXME: comint--common-suffix, comint--common-quoted-suffix, and -;; comint--table-subvert copied from pcomplete. And they don't fully solve -;; the problem, since selecting a file from *Completions* won't quote it. - -(defun comint--common-suffix (s1 s2) - (assert (not (or (string-match "\n" s1) (string-match "\n" s2)))) - ;; Since S2 is expected to be the "unquoted/expanded" version of S1, - ;; there shouldn't be any case difference, even if the completion is - ;; case-insensitive. - (let ((case-fold-search nil)) - (string-match ".*?\\(.*\\)\n.*\\1\\'" (concat s1 "\n" s2)) - (- (match-end 1) (match-beginning 1)))) - -(defun comint--common-quoted-suffix (s1 s2) - "Find the common suffix between S1 and S2 where S1 is the expanded S2. -S1 is expected to be the unquoted and expanded version of S1. -Returns (PS1 . PS2), i.e. the shortest prefixes of S1 and S2, such that -S1 = (concat PS1 SS1) and S2 = (concat PS2 SS2) and -SS1 = (unquote SS2)." - (let* ((cs (comint--common-suffix s1 s2)) - (ss1 (substring s1 (- (length s1) cs))) - (qss1 (comint-quote-filename ss1)) - qc) - (if (and (not (equal ss1 qss1)) - (setq qc (comint-quote-filename (substring ss1 0 1))) - (eq t (compare-strings s2 (- (length s2) cs (length qc) -1) - (- (length s2) cs -1) - qc nil nil))) - ;; The difference found is just that one char is quoted in S2 - ;; but not in S1, keep looking before this difference. - (comint--common-quoted-suffix - (substring s1 0 (- (length s1) cs)) - (substring s2 0 (- (length s2) cs (length qc) -1))) - (cons (substring s1 0 (- (length s1) cs)) - (substring s2 0 (- (length s2) cs)))))) - -(defun comint--table-subvert (table s1 s2 string pred action) - "Completion table that replaces the prefix S1 with S2 in STRING. -When TABLE, S1 and S2 are provided by `apply-partially', the result -is a completion table which completes strings of the form (concat S1 S) -in the same way as TABLE completes strings of the form (concat S2 S)." - (let* ((str (if (eq t (compare-strings string 0 (length s1) s1 nil nil - completion-ignore-case)) - (concat s2 (comint-unquote-filename - (substring string (length s1)))))) - (res (if str (complete-with-action action table str pred)))) - (when res - (cond - ((and (eq (car-safe action) 'boundaries)) - (let ((beg (or (and (eq (car-safe res) 'boundaries) (cadr res)) 0))) - (list* 'boundaries - (max (length s1) - ;; FIXME: Adjust because of quoting/unquoting. - (+ beg (- (length s1) (length s2)))) - (and (eq (car-safe res) 'boundaries) (cddr res))))) - ((stringp res) - (if (eq t (compare-strings res 0 (length s2) s2 nil nil - completion-ignore-case)) - (concat s1 (comint-quote-filename - (substring res (length s2)))))) - ((eq action t) - (let ((bounds (completion-boundaries str table pred ""))) - (if (>= (car bounds) (length s2)) - res - (let ((re (concat "\\`" - (regexp-quote (substring s2 (car bounds)))))) - (delq nil - (mapcar (lambda (c) - (if (string-match re c) - (substring c (match-end 0)))) - res)))))) - ;; E.g. action=nil and it's the only completion. - (res))))) +(defun comint-completion-file-name-table (string pred action) + (if (not (file-name-absolute-p string)) + (completion-file-name-table string pred action) + (cond + ((memq action '(t lambda)) + (completion-file-name-table + (concat comint-file-name-prefix string) pred action)) + ((null action) + (let ((res (completion-file-name-table + (concat comint-file-name-prefix string) pred action))) + (if (and (stringp res) + (string-match + (concat "\\`" (regexp-quote comint-file-name-prefix)) + res)) + (substring res (match-end 0)) + res))) + (t (completion-file-name-table string pred action))))) + +(defvar comint-unquote-function #'comint--unquote-argument + "Function to use for completion of quoted data. +See `completion-table-with-quoting' and `comint-requote-function'.") +(defvar comint-requote-function #'comint--requote-argument + "Function to use for completion of quoted data. +See `completion-table-with-quoting' and `comint-unquote-function'.") (defun comint--complete-file-name-data () "Return the completion data for file name at point." @@ -3127,14 +3135,11 @@ in the same way as TABLE completes strings of the form (concat S2 S)." (filename (comint--match-partial-filename)) (filename-beg (if filename (match-beginning 0) (point))) (filename-end (if filename (match-end 0) (point))) - (unquoted (if filename (comint--unquote&expand-filename filename) "")) (table - (let ((prefixes (comint--common-quoted-suffix - unquoted filename))) - (apply-partially - #'comint--table-subvert - #'completion-file-name-table - (cdr prefixes) (car prefixes))))) + (completion-table-with-quoting + #'comint-completion-file-name-table + comint-unquote-function + comint-requote-function))) (nconc (list filename-beg filename-end @@ -3342,7 +3347,7 @@ The process mark separates output, and input already sent, from input that has not yet been sent." (interactive) (let ((proc (or (get-buffer-process (current-buffer)) - (error "Current buffer has no process")))) + (user-error "Current buffer has no process")))) (goto-char (process-mark proc)) (when (called-interactively-p 'interactive) (message "Point is now at the process mark")))) @@ -3367,7 +3372,7 @@ the process mark is at the beginning of the accumulated input." "Set the process mark at point." (interactive) (let ((proc (or (get-buffer-process (current-buffer)) - (error "Current buffer has no process")))) + (user-error "Current buffer has no process")))) (set-marker (process-mark proc) (point)) (message "Process mark set"))) @@ -3421,7 +3426,7 @@ Also print a message when redirection is completed." :group 'comint :type 'boolean) -;; Directly analagous to comint-preoutput-filter-functions +;; Directly analogous to comint-preoutput-filter-functions (defvar comint-redirect-filter-functions nil "List of functions to call before inserting redirected process output. Each function gets one argument, a string containing the text received @@ -3719,14 +3724,6 @@ REGEXP-GROUP is the regular expression group in REGEXP to use." (match-end regexp-group)) results)) results))) - -(dolist (x '("^Not at command line$" - "^Empty input ring$" - "^No history$" - "^Not found$" ; Too common? - "^Current buffer has no process$")) - (add-to-list 'debug-ignored-errors x)) - ;; Converting process modes to use comint mode ;; ===========================================================================