summaryrefslogtreecommitdiff
path: root/lisp/shell.el
diff options
context:
space:
mode:
authorStefan Monnier <monnier@iro.umontreal.ca>2011-04-20 19:31:06 -0300
committerStefan Monnier <monnier@iro.umontreal.ca>2011-04-20 19:31:06 -0300
commitc0a193ea2017fbaa6fb3e64a07125878656da156 (patch)
tree52ecaae3b27b33e9de36b354cbc2d17e2334f411 /lisp/shell.el
parent201133802956936332f1c4ce04eac42dfd1cf1c6 (diff)
downloademacs-c0a193ea2017fbaa6fb3e64a07125878656da156.tar.gz
* 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.
Diffstat (limited to 'lisp/shell.el')
-rw-r--r--lisp/shell.el217
1 files changed, 122 insertions, 95 deletions
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)