emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] /srv/bzr/emacs/trunk r103962: * lisp/shell.el: Use lexical


From: Stefan Monnier
Subject: [Emacs-diffs] /srv/bzr/emacs/trunk r103962: * lisp/shell.el: Use lexical-binding and std completion UI.
Date: Wed, 20 Apr 2011 19:31:06 -0300
User-agent: Bazaar (2.3.1)

------------------------------------------------------------
revno: 103962
committer: Stefan Monnier <address@hidden>
branch nick: trunk
timestamp: Wed 2011-04-20 19:31:06 -0300
message:
  * 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.
modified:
  etc/NEWS
  lisp/ChangeLog
  lisp/comint.el
  lisp/minibuffer.el
  lisp/shell.el
=== modified file 'etc/NEWS'
--- a/etc/NEWS  2011-04-20 17:33:09 +0000
+++ b/etc/NEWS  2011-04-20 22:31:06 +0000
@@ -370,6 +370,8 @@
 
 * 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.
 

=== modified file 'lisp/ChangeLog'
--- a/lisp/ChangeLog    2011-04-20 19:05:50 +0000
+++ b/lisp/ChangeLog    2011-04-20 22:31:06 +0000
@@ -1,5 +1,31 @@
 2011-04-20  Stefan Monnier  <address@hidden>
 
+       * 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):

=== modified file 'lisp/comint.el'
--- a/lisp/comint.el    2011-04-20 19:05:50 +0000
+++ b/lisp/comint.el    2011-04-20 22:31:06 +0000
@@ -367,7 +367,7 @@
 `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 @@
     (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 @@
   (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 @@
 
 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 @@
             ;; 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 @@
               (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 @@
                   (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 @@
                "!\\??\\({\\(.+\\)}\\|\\(\\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 @@
                   (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 @@
                   (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 @@
                         (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 @@
          (setq i (+ 1 (match-beginning 0)))))
       filename)))
 
-
-(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)))
-
+(defun comint-completion-at-point ()
+  (run-hook-with-args-until-success 'comint-dynamic-complete-functions))
+
+(define-obsolete-function-alias
+  'comint-dynamic-complete
+  'completion-at-point "24.1")
 
 (defun comint-dynamic-complete-filename ()
   "Dynamically complete the filename at point.

=== modified file 'lisp/minibuffer.el'
--- a/lisp/minibuffer.el        2011-04-20 19:05:50 +0000
+++ b/lisp/minibuffer.el        2011-04-20 22:31:06 +0000
@@ -247,7 +247,9 @@
 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 @@
            (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 @@
 
 (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)

=== modified file 'lisp/shell.el'
--- a/lisp/shell.el     2011-04-19 13:44:55 +0000
+++ b/lisp/shell.el     2011-04-20 22:31:06 +0000
@@ -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 @@
 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 @@
 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 @@
   (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 @@
                  (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 @@
                '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 @@
 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 @@
 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)))))
-
-
-(defun shell-dynamic-complete-as-command ()
-  "Dynamically complete at point as a command.
-See `shell-dynamic-complete-filename'.  Returns t if successful."
+       (shell--command-completion-data))))
+
+(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 @@
        (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 @@
                           (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 @@
 
 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 @@
 
 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)
 


reply via email to

[Prev in Thread] Current Thread [Next in Thread]