From: Stefan Monnier Date: Wed, 20 Apr 2011 22:31:06 +0000 (-0300) Subject: * lisp/shell.el: Use lexical-binding and std completion UI. X-Git-Tag: emacs-pretest-24.0.90~104^2~275^2~215^2~51 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=c0a193ea2017fbaa6fb3e64a07125878656da156;p=emacs.git * lisp/shell.el: Use lexical-binding and std completion UI. (shell-filter-ctrl-a-ctrl-b): Work as a preoutput filter. (shell-mode): Put shell-filter-ctrl-a-ctrl-b on comint-preoutput-filter-functions rather than on comint-output-filter-functions. (shell-command-completion, shell--command-completion-data) (shell-filename-completion, shell-environment-variable-completion) (shell-c-a-p-replace-by-expanded-directory): New functions. (shell-dynamic-complete-functions, shell-dynamic-complete-command) (shell-dynamic-complete-filename, shell-replace-by-expanded-directory) (shell-dynamic-complete-environment-variable): Use them. (shell-dynamic-complete-as-environment-variable) (shell-dynamic-complete-as-command): Remove. (shell-match-partial-variable): Match past point. * lisp/comint.el: Clean up use of completion-at-point-functions. (comint-completion-at-point): New function. (comint-mode): Use it completion-at-point-functions. (comint-dynamic-complete): Make it obsolete. (comint-replace-by-expanded-history-before-point): Add dry-run arg. (comint-c-a-p-replace-by-expanded-history): New function. (comint-dynamic-complete-functions) (comint-replace-by-expanded-history): Use it. * lisp/minibuffer.el (completion-table-with-terminator): Allow dynamic termination strings. Try harder to avoid second try-completion. (completion-in-region-mode-map): Disable bindings that don't work yet. --- diff --git a/etc/NEWS b/etc/NEWS index bc85b3223ed..aed90764fa1 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -370,6 +370,8 @@ $ESHELL nor variable `explicit-shell-file-name' is set. * Changes in Specialized Modes and Packages in Emacs 24.1 +** comint and modes derived from it use the generic completion code. + ** The compile.el mode can be used without font-lock-mode. `compilation-parse-errors-function' is now obsolete. diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 214376b817c..70bb631df9e 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,5 +1,31 @@ 2011-04-20 Stefan Monnier + * shell.el: Use lexical-binding and std completion UI. + (shell-filter-ctrl-a-ctrl-b): Work as a preoutput filter. + (shell-mode): Put shell-filter-ctrl-a-ctrl-b on + comint-preoutput-filter-functions rather than on + comint-output-filter-functions. + (shell-command-completion, shell--command-completion-data) + (shell-filename-completion, shell-environment-variable-completion) + (shell-c-a-p-replace-by-expanded-directory): New functions. + (shell-dynamic-complete-functions, shell-dynamic-complete-command) + (shell-dynamic-complete-filename, shell-replace-by-expanded-directory) + (shell-dynamic-complete-environment-variable): Use them. + (shell-dynamic-complete-as-environment-variable) + (shell-dynamic-complete-as-command): Remove. + (shell-match-partial-variable): Match past point. + * comint.el: Clean up use of completion-at-point-functions. + (comint-completion-at-point): New function. + (comint-mode): Use it completion-at-point-functions. + (comint-dynamic-complete): Make it obsolete. + (comint-replace-by-expanded-history-before-point): Add dry-run arg. + (comint-c-a-p-replace-by-expanded-history): New function. + (comint-dynamic-complete-functions) + (comint-replace-by-expanded-history): Use it. + * minibuffer.el (completion-table-with-terminator): Allow dynamic + termination strings. Try harder to avoid second try-completion. + (completion-in-region-mode-map): Disable bindings that don't work yet. + * comint.el: Use lexical-binding. Require CL. (comint-dynamic-complete-functions): Use comint-filename-completion. (comint-completion-addsuffix): Tweak custom type. @@ -9,6 +35,7 @@ (comint-dynamic-complete-as-filename, comint-dynamic-complete-filename) (comint-dynamic-list-filename-completions): Use them. (comint-dynamic-simple-complete): Make obsolete. + * minibuffer.el (completion-in-region-mode): Keep completion-in-region-mode--predicate global. (completion-in-region--postch): diff --git a/lisp/comint.el b/lisp/comint.el index 735770a8908..8608c0d31e9 100644 --- a/lisp/comint.el +++ b/lisp/comint.el @@ -367,7 +367,7 @@ text matching `comint-prompt-regexp', depending on the value of `comint-use-prompt-regexp'.") (defvar comint-dynamic-complete-functions - '(comint-replace-by-expanded-history comint-filename-completion) + '(comint-c-a-p-replace-by-expanded-history comint-filename-completion) "List of functions called to perform completion. Works like `completion-at-point-functions'. See also `comint-dynamic-complete'. @@ -493,7 +493,7 @@ executed once when the buffer is created." (define-key map [menu-bar completion complete-file] '("Complete File Name" . comint-dynamic-complete-filename)) (define-key map [menu-bar completion complete] - '("Complete Before Point" . comint-dynamic-complete)) + '("Complete at Point" . completion-at-point)) ;; Input history: (define-key map [menu-bar inout] (cons "In/Out" (make-sparse-keymap "In/Out"))) @@ -683,6 +683,7 @@ Entry to this mode runs the hooks on `comint-mode-hook'." (setq font-lock-defaults '(nil t)) (add-hook 'change-major-mode-hook 'font-lock-defontify nil t) (add-hook 'isearch-mode-hook 'comint-history-isearch-setup nil t) + (add-hook 'completion-at-point-functions 'comint-completion-at-point nil t) ;; This behavior is not useful in comint buffers, and is annoying (set (make-local-variable 'next-line-add-newlines) nil)) @@ -1231,6 +1232,12 @@ See `comint-magic-space' and `comint-replace-by-expanded-history-before-point'. Returns t if successful." (interactive) + (let ((f (comint-c-a-p-replace-by-expanded-history silent start))) + (if f (funcall f)))) + +(defun comint-c-a-p-replace-by-expanded-history (&optional silent start) + "Expand input command history at point. +For use on `completion-at-point-functions'." (if (and comint-input-autoexpand (if comint-use-prompt-regexp ;; Use comint-prompt-regexp @@ -1240,20 +1247,28 @@ Returns t if successful." ;; Use input fields. User input that hasn't been entered ;; yet, at the end of the buffer, has a nil `field' property. (and (null (get-char-property (point) 'field)) - (string-match "!\\|^\\^" (field-string))))) - ;; 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) - (/= previous-modified-tick (buffer-modified-tick))))) - - -(defun comint-replace-by-expanded-history-before-point (silent &optional start) + (string-match "!\\|^\\^" (field-string)))) + (catch 'dry-run + (comint-replace-by-expanded-history-before-point + silent start 'dry-run))) + (lambda () + ;; 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) + (/= previous-modified-tick (buffer-modified-tick)))))) + + +(defun comint-replace-by-expanded-history-before-point + (silent &optional start dry-run) "Expand directory stack reference before point. See `comint-replace-by-expanded-history'. Returns t if successful. If the optional argument START is non-nil, that specifies the start of the text to scan for history references, rather -than the logical beginning of line." +than the logical beginning of line. + +If DRY-RUN is non-nil, throw to DRY-RUN before performing any +actual side-effect." (save-excursion (let ((toend (- (line-end-position) (point))) (start (or start (comint-line-beginning-position)))) @@ -1274,10 +1289,12 @@ than the logical beginning of line." (goto-char (1+ (point)))) ((looking-at "![0-9]+\\($\\|[^-]\\)") ;; We cannot know the interpreter's idea of input line numbers. + (if dry-run (throw dry-run 'message)) (goto-char (match-end 0)) (message "Absolute reference cannot be expanded")) ((looking-at "!-\\([0-9]+\\)\\(:?[0-9^$*-]+\\)?") ;; Just a number of args from `number' lines backward. + (if dry-run (throw dry-run 'history)) (let ((number (1- (string-to-number (buffer-substring (match-beginning 1) (match-end 1)))))) @@ -1293,6 +1310,7 @@ than the logical beginning of line." (message "Relative reference exceeds input history size")))) ((or (looking-at "!!?:?\\([0-9^$*-]+\\)") (looking-at "!!")) ;; Just a number of args from the previous input line. + (if dry-run (throw dry-run 'expand)) (replace-match (comint-args (comint-previous-input-string 0) (match-beginning 1) (match-end 1)) t t) @@ -1301,6 +1319,7 @@ than the logical beginning of line." "!\\??\\({\\(.+\\)}\\|\\(\\sw+\\)\\)\\(:?[0-9^$*-]+\\)?") ;; Most recent input starting with or containing (possibly ;; protected) string, maybe just a number of args. Phew. + (if dry-run (throw dry-run 'expand)) (let* ((mb1 (match-beginning 1)) (me1 (match-end 1)) (mb2 (match-beginning 2)) (me2 (match-end 2)) (exp (buffer-substring (or mb2 mb1) (or me2 me1))) @@ -1322,6 +1341,7 @@ than the logical beginning of line." (message "History item: %d" (1+ pos))))) ((looking-at "\\^\\([^^]+\\)\\^?\\([^^]*\\)\\^?") ;; Quick substitution on the previous input line. + (if dry-run (throw dry-run 'expand)) (let ((old (buffer-substring (match-beginning 1) (match-end 1))) (new (buffer-substring (match-beginning 2) (match-end 2))) (pos nil)) @@ -1334,7 +1354,8 @@ than the logical beginning of line." (replace-match new t t) (message "History item: substituted")))) (t - (forward-char 1))))))) + (forward-char 1))))) + nil)) (defun comint-magic-space (arg) @@ -1740,9 +1761,9 @@ Similarly for Soar, Scheme, etc." (insert copy) copy))) (input (if (not (eq comint-input-autoexpand 'input)) - ;; Just whatever's already there + ;; Just whatever's already there. intxt - ;; Expand and leave it visible in buffer + ;; Expand and leave it visible in buffer. (comint-replace-by-expanded-history t pmark) (buffer-substring pmark (point)))) (history (if (not (eq comint-input-autoexpand 'history)) @@ -2990,16 +3011,12 @@ Magic characters are those in `comint-file-name-quote-list'." (setq i (+ 1 (match-beginning 0))))) filename))) +(defun comint-completion-at-point () + (run-hook-with-args-until-success 'comint-dynamic-complete-functions)) -(defun comint-dynamic-complete () - "Dynamically perform completion at point. -Calls the functions in `comint-dynamic-complete-functions' to perform -completion until a function returns non-nil, at which point completion is -assumed to have occurred." - (interactive) - (let ((completion-at-point-functions comint-dynamic-complete-functions)) - (completion-at-point))) - +(define-obsolete-function-alias + 'comint-dynamic-complete + 'completion-at-point "24.1") (defun comint-dynamic-complete-filename () "Dynamically complete the filename at point. diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 0adf2a1d8b8..e012c324012 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -247,7 +247,9 @@ TERMINATOR can also be a cons cell (TERMINATOR . TERMINATOR-REGEXP) in which case TERMINATOR-REGEXP is a regular expression whose submatch number 1 should match TERMINATOR. This is used when there is a need to distinguish occurrences of the TERMINATOR strings which are really terminators -from others (e.g. escaped)." +from others (e.g. escaped). In this form, the car of TERMINATOR can also be, +instead of a string, a function that takes the completion and returns the +\"terminated\" string." ;; FIXME: This implementation is not right since it only adds the terminator ;; in try-completion, so any completion-style that builds the completion via ;; all-completions won't get the terminator, and selecting an entry in @@ -258,22 +260,28 @@ from others (e.g. escaped)." (bounds (completion-boundaries string table pred suffix)) (terminator-regexp (if (consp terminator) (cdr terminator) (regexp-quote terminator))) - (max (string-match terminator-regexp suffix))) + (max (and terminator-regexp + (string-match terminator-regexp suffix)))) (list* 'boundaries (car bounds) (min (cdr bounds) (or max (length suffix)))))) ((eq action nil) (let ((comp (try-completion string table pred))) (if (consp terminator) (setq terminator (car terminator))) (if (eq comp t) - (concat string terminator) - (if (and (stringp comp) - ;; FIXME: Try to avoid this second call, especially since + (if (functionp terminator) + (funcall terminator string) + (concat string terminator)) + (if (and (stringp comp) (not (zerop (length comp))) + ;; Try to avoid the second call to try-completion, since ;; it may be very inefficient (because `comp' made us ;; jump to a new boundary, so we complete in that ;; boundary with an empty start string). - ;; completion-boundaries might help. + (let ((newbounds (completion-boundaries comp table pred ""))) + (< (car newbounds) (length comp))) (eq (try-completion comp table pred) t)) - (concat comp terminator) + (if (functionp terminator) + (funcall terminator comp) + (concat comp terminator)) comp)))) ((eq action t) ;; FIXME: We generally want the `try' and `all' behaviors to be @@ -1294,6 +1302,8 @@ Point needs to be somewhere between START and END." (defvar completion-in-region-mode-map (let ((map (make-sparse-keymap))) + ;; FIXME: Only works if completion-in-region-mode was activated via + ;; completion-at-point called directly. (define-key map "?" 'completion-help-at-point) (define-key map "\t" 'completion-at-point) map) diff --git a/lisp/shell.el b/lisp/shell.el index 57187b6d7f9..d6bc685618c 100644 --- a/lisp/shell.el +++ b/lisp/shell.el @@ -1,4 +1,4 @@ -;;; shell.el --- specialized comint.el for running the shell +;;; shell.el --- specialized comint.el for running the shell -*- lexical-binding: t -*- ;; Copyright (C) 1988, 1993-1997, 2000-2011 Free Software Foundation, Inc. @@ -79,7 +79,7 @@ ;; Shell Mode Commands: ;; shell Fires up the shell process -;; tab comint-dynamic-complete Complete filename/command/history +;; tab completion-at-point Complete filename/command/history ;; m-? comint-dynamic-list-filename-completions ;; List completions in help buffer ;; m-c-f shell-forward-command Forward a shell command @@ -96,6 +96,7 @@ ;;; Code: +(eval-when-compile (require 'cl)) (require 'comint) ;;; Customization and Buffer Variables @@ -181,12 +182,12 @@ shell buffer. The value may depend on the operating system or shell. This is a fine thing to set in your `.emacs' file.") (defvar shell-dynamic-complete-functions - '(comint-replace-by-expanded-history - shell-dynamic-complete-environment-variable - shell-dynamic-complete-command - shell-replace-by-expanded-directory - shell-dynamic-complete-filename - comint-dynamic-complete-filename) + '(comint-c-a-p-replace-by-expanded-history + shell-environment-variable-completion + shell-command-completion + shell-c-a-p-replace-by-expanded-directory + shell-filename-completion + comint-filename-completion) "List of functions called to perform completion. This variable is used to initialize `comint-dynamic-complete-functions' in the shell buffer. @@ -312,7 +313,7 @@ This mirrors the optional behavior of tcsh (its autoexpand and histlit). If the value is `input', then the expansion is seen on input. If the value is `history', then the expansion is only when inserting into the buffer's input ring. See also `comint-magic-space' and -`comint-dynamic-complete'. +`comint-dynamic-complete-functions'. This variable supplies a default for `comint-input-autoexpand', for Shell mode only." @@ -339,7 +340,7 @@ Thus, this does not include the shell's current directory.") (let ((map (nconc (make-sparse-keymap) comint-mode-map))) (define-key map "\C-c\C-f" 'shell-forward-command) (define-key map "\C-c\C-b" 'shell-backward-command) - (define-key map "\t" 'comint-dynamic-complete) + (define-key map "\t" 'completion-at-point) (define-key map (kbd "M-RET") 'shell-resync-dirs) (define-key map "\M-?" 'comint-dynamic-list-filename-completions) (define-key map [menu-bar completion] @@ -486,7 +487,7 @@ buffer." (t "dirs"))) ;; Bypass a bug in certain versions of bash. (when (string-equal shell "bash") - (add-hook 'comint-output-filter-functions + (add-hook 'comint-preoutput-filter-functions 'shell-filter-ctrl-a-ctrl-b nil t))) (when shell-dir-cookie-re ;; Watch for magic cookies in the output to track the current dir. @@ -494,7 +495,7 @@ buffer." 'shell-dir-cookie-watcher nil t)) (comint-read-input-ring t))) -(defun shell-filter-ctrl-a-ctrl-b (_string) +(defun shell-filter-ctrl-a-ctrl-b (string) "Remove `^A' and `^B' characters from comint output. Bash uses these characters as internal quoting characters in its @@ -504,15 +505,10 @@ started with the `--noediting' option and Select Graphic Rendition (SGR) control sequences (formerly known as ANSI escape sequences) are used to color the prompt. -This function can be put on `comint-output-filter-functions'. -The argument STRING is ignored." - (let ((pmark (process-mark (get-buffer-process (current-buffer))))) - (save-excursion - (goto-char (or (and (markerp comint-last-output-start) - (marker-position comint-last-output-start)) - (point-min))) - (while (re-search-forward "[\C-a\C-b]" pmark t) - (replace-match ""))))) +This function can be put on `comint-preoutput-filter-functions'." + (if (string-match "[\C-a\C-b]" string) + (replace-regexp-in-string "[\C-a\C-b]" "" string t t) + string)) (defun shell-write-history-on-exit (process event) "Called when the shell process is stopped. @@ -1011,30 +1007,36 @@ candidates. Note that this may not be the same as the shell's idea of the path. Completion is dependent on the value of `shell-completion-execonly', plus -those that effect file completion. See `shell-dynamic-complete-as-command'. +those that effect file completion. Returns t if successful." (interactive) + (let ((data (shell-command-completion))) + (if data + (prog2 (unless (window-minibuffer-p (selected-window)) + (message "Completing command name...")) + (apply #'completion-in-region data))))) + +(defun shell-command-completion () + "Return the completion data for the command at point, if any." (let ((filename (comint-match-partial-filename))) (if (and filename (save-match-data (not (string-match "[~/]" filename))) (eq (match-beginning 0) (save-excursion (shell-backward-command 1) (point)))) - (prog2 (unless (window-minibuffer-p (selected-window)) - (message "Completing command name...")) - (shell-dynamic-complete-as-command))))) + (shell--command-completion-data)))) - -(defun shell-dynamic-complete-as-command () - "Dynamically complete at point as a command. -See `shell-dynamic-complete-filename'. Returns t if successful." +(defun shell--command-completion-data () + "Return the completion data for the command at point." (let* ((filename (or (comint-match-partial-filename) "")) + (start (if (zerop (length filename)) (point) (match-beginning 0))) + (end (if (zerop (length filename)) (point) (match-end 0))) (filenondir (file-name-nondirectory filename)) - (path-dirs (cdr (reverse exec-path))) + (path-dirs (cdr (reverse exec-path))) ;FIXME: Why `cdr'? (cwd (file-name-as-directory (expand-file-name default-directory))) (ignored-extensions (and comint-completion-fignore - (mapconcat (function (lambda (x) (concat (regexp-quote x) "$"))) + (mapconcat (function (lambda (x) (concat (regexp-quote x) "\\'"))) comint-completion-fignore "\\|"))) (dir "") (comps-in-dir ()) (file "") (abs-file-name "") (completions ())) @@ -1058,18 +1060,31 @@ See `shell-dynamic-complete-filename'. Returns t if successful." (setq comps-in-dir (cdr comps-in-dir))) (setq path-dirs (cdr path-dirs))) ;; OK, we've got a list of completions. - (let ((success (let ((comint-completion-addsuffix nil)) - (comint-dynamic-simple-complete filenondir completions)))) - (if (and (memq success '(sole shortest)) comint-completion-addsuffix - (not (file-directory-p (comint-match-partial-filename)))) - (insert " ")) - success))) + (list + start end + (lambda (string pred action) + (completion-table-with-terminator + " " (lambda (string pred action) + (if (string-match "/" string) + (completion-file-name-table string pred action) + (complete-with-action action completions string pred))) + string pred action))))) + +;; (defun shell-dynamic-complete-as-command () +;; "Dynamically complete at point as a command. +;; See `shell-dynamic-complete-filename'. Returns t if successful." +;; (apply #'completion-in-region shell--command-completion-data)) (defun shell-dynamic-complete-filename () "Dynamically complete the filename at point. This completes only if point is at a suitable position for a filename argument." (interactive) + (let ((data (shell-filename-completion))) + (if data (apply #'completion-in-region data)))) + +(defun shell-filename-completion () + "Return the completion data for file name at point, if any." (let ((opoint (point)) (beg (comint-line-beginning-position))) (when (save-excursion @@ -1077,24 +1092,21 @@ filename argument." (match-end 0) beg)) (re-search-forward "[^ \t][ \t]" opoint t)) - (comint-dynamic-complete-as-filename)))) + (comint-filename-completion)))) (defun shell-match-partial-variable () "Return the shell variable at point, or nil if none is found." (save-excursion - (let ((limit (point))) - (if (re-search-backward "[^A-Za-z0-9_{}]" nil 'move) - (or (looking-at "\\$") (forward-char 1))) - ;; Anchor the search forwards. - (if (or (eolp) (looking-at "[^A-Za-z0-9_{}$]")) - nil - (re-search-forward "\\$?{?[A-Za-z0-9_]*}?" limit) - (buffer-substring (match-beginning 0) (match-end 0)))))) + (if (re-search-backward "[^A-Za-z0-9_{(]" nil 'move) + (or (looking-at "\\$") (forward-char 1))) + (if (or (eolp) (looking-at "[^A-Za-z0-9_{($]")) + nil + (looking-at "\\$?[{(]?[A-Za-z0-9_]*[})]?") + (buffer-substring (match-beginning 0) (match-end 0))))) (defun shell-dynamic-complete-environment-variable () "Dynamically complete the environment variable at point. Completes if after a variable, i.e., if it starts with a \"$\". -See `shell-dynamic-complete-as-environment-variable'. This function is similar to `comint-dynamic-complete-filename', except that it searches `process-environment' for completion candidates. Note that this may @@ -1106,38 +1118,69 @@ called `shell-dynamic-complete-process-environment-variable'. Returns non-nil if successful." (interactive) - (let ((variable (shell-match-partial-variable))) - (if (and variable (string-match "^\\$" variable)) + (let ((data (shell-environment-variable-completion))) + (if data (prog2 (unless (window-minibuffer-p (selected-window)) (message "Completing variable name...")) - (shell-dynamic-complete-as-environment-variable))))) - - -(defun shell-dynamic-complete-as-environment-variable () - "Dynamically complete at point as an environment variable. -Used by `shell-dynamic-complete-environment-variable'. -Uses `comint-dynamic-simple-complete'." - (let* ((var (or (shell-match-partial-variable) "")) - (variable (substring var (or (string-match "[^$({]\\|$" var) 0))) - (variables (mapcar (function (lambda (x) - (substring x 0 (string-match "=" x)))) - process-environment)) - (addsuffix comint-completion-addsuffix) - (comint-completion-addsuffix nil) - (success (comint-dynamic-simple-complete variable variables))) - (if (memq success '(sole shortest)) - (let* ((var (shell-match-partial-variable)) - (variable (substring var (string-match "[^$({]" var))) - (protection (cond ((string-match "{" var) "}") - ((string-match "(" var) ")") - (t ""))) - (suffix (cond ((null addsuffix) "") - ((file-directory-p - (comint-directory (getenv variable))) "/") - (t " ")))) - (insert protection suffix))) - success)) - + (apply #'completion-in-region data))))) + + +(defun shell-environment-variable-completion () + "Completion data for an environment variable at point, if any." + (let* ((var (shell-match-partial-variable)) + (end (match-end 0))) + (when (and (not (zerop (length var))) (eq (aref var 0) ?$)) + (let* ((start + (save-excursion + (goto-char (match-beginning 0)) + (looking-at "\\$?[({]*") + (match-end 0))) + (variables (mapcar (lambda (x) + (substring x 0 (string-match "=" x))) + process-environment)) + (suffix (case (char-before start) (?\{ "}") (?\( ")") (t "")))) + (list + start end + (apply-partially + #'completion-table-with-terminator + (cons (lambda (comp) + (concat comp + suffix + (if (file-directory-p + (comint-directory (getenv comp))) + "/"))) + "\\`a\\`") + variables)))))) + + +(defun shell-c-a-p-replace-by-expanded-directory () + "Expand directory stack reference before point. +For use on `completion-at-point-functions'." + (when (comint-match-partial-filename) + (save-excursion + (goto-char (match-beginning 0)) + (let ((stack (cons default-directory shell-dirstack)) + (index (cond ((looking-at "=-/?") + (length shell-dirstack)) + ((looking-at "=\\([0-9]+\\)/?") + (string-to-number + (buffer-substring + (match-beginning 1) (match-end 1))))))) + (when index + (let ((start (match-beginning 0)) + (end (match-end 0)) + (replacement (file-name-as-directory (nth index stack)))) + (lambda () + (cond + ((>= index (length stack)) + (error "Directory stack not that deep")) + (t + (save-excursion + (goto-char start) + (insert replacement) + (delete-char (- end start))) + (message "Directory item: %d" index) + t))))))))) (defun shell-replace-by-expanded-directory () "Expand directory stack reference before point. @@ -1146,24 +1189,8 @@ See `default-directory' and `shell-dirstack'. Returns t if successful." (interactive) - (if (comint-match-partial-filename) - (save-excursion - (goto-char (match-beginning 0)) - (let ((stack (cons default-directory shell-dirstack)) - (index (cond ((looking-at "=-/?") - (length shell-dirstack)) - ((looking-at "=\\([0-9]+\\)/?") - (string-to-number - (buffer-substring - (match-beginning 1) (match-end 1))))))) - (cond ((null index) - nil) - ((>= index (length stack)) - (error "Directory stack not that deep")) - (t - (replace-match (file-name-as-directory (nth index stack)) t t) - (message "Directory item: %d" index) - t)))))) + (let ((f (shell-c-a-p-replace-by-expanded-directory))) + (if f (funcall f)))) (provide 'shell)