aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/shell.el
diff options
context:
space:
mode:
authorStefan Monnier2011-04-20 19:31:06 -0300
committerStefan Monnier2011-04-20 19:31:06 -0300
commitc0a193ea2017fbaa6fb3e64a07125878656da156 (patch)
tree52ecaae3b27b33e9de36b354cbc2d17e2334f411 /lisp/shell.el
parent201133802956936332f1c4ce04eac42dfd1cf1c6 (diff)
downloademacs-c0a193ea2017fbaa6fb3e64a07125878656da156.tar.gz
emacs-c0a193ea2017fbaa6fb3e64a07125878656da156.zip
* 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 @@
1;;; shell.el --- specialized comint.el for running the shell 1;;; shell.el --- specialized comint.el for running the shell -*- lexical-binding: t -*-
2 2
3;; Copyright (C) 1988, 1993-1997, 2000-2011 Free Software Foundation, Inc. 3;; Copyright (C) 1988, 1993-1997, 2000-2011 Free Software Foundation, Inc.
4 4
@@ -79,7 +79,7 @@
79 79
80;; Shell Mode Commands: 80;; Shell Mode Commands:
81;; shell Fires up the shell process 81;; shell Fires up the shell process
82;; tab comint-dynamic-complete Complete filename/command/history 82;; tab completion-at-point Complete filename/command/history
83;; m-? comint-dynamic-list-filename-completions 83;; m-? comint-dynamic-list-filename-completions
84;; List completions in help buffer 84;; List completions in help buffer
85;; m-c-f shell-forward-command Forward a shell command 85;; m-c-f shell-forward-command Forward a shell command
@@ -96,6 +96,7 @@
96 96
97;;; Code: 97;;; Code:
98 98
99(eval-when-compile (require 'cl))
99(require 'comint) 100(require 'comint)
100 101
101;;; Customization and Buffer Variables 102;;; Customization and Buffer Variables
@@ -181,12 +182,12 @@ shell buffer. The value may depend on the operating system or shell.
181This is a fine thing to set in your `.emacs' file.") 182This is a fine thing to set in your `.emacs' file.")
182 183
183(defvar shell-dynamic-complete-functions 184(defvar shell-dynamic-complete-functions
184 '(comint-replace-by-expanded-history 185 '(comint-c-a-p-replace-by-expanded-history
185 shell-dynamic-complete-environment-variable 186 shell-environment-variable-completion
186 shell-dynamic-complete-command 187 shell-command-completion
187 shell-replace-by-expanded-directory 188 shell-c-a-p-replace-by-expanded-directory
188 shell-dynamic-complete-filename 189 shell-filename-completion
189 comint-dynamic-complete-filename) 190 comint-filename-completion)
190 "List of functions called to perform completion. 191 "List of functions called to perform completion.
191This variable is used to initialize `comint-dynamic-complete-functions' in the 192This variable is used to initialize `comint-dynamic-complete-functions' in the
192shell buffer. 193shell buffer.
@@ -312,7 +313,7 @@ This mirrors the optional behavior of tcsh (its autoexpand and histlit).
312If the value is `input', then the expansion is seen on input. 313If the value is `input', then the expansion is seen on input.
313If the value is `history', then the expansion is only when inserting 314If the value is `history', then the expansion is only when inserting
314into the buffer's input ring. See also `comint-magic-space' and 315into the buffer's input ring. See also `comint-magic-space' and
315`comint-dynamic-complete'. 316`comint-dynamic-complete-functions'.
316 317
317This variable supplies a default for `comint-input-autoexpand', 318This variable supplies a default for `comint-input-autoexpand',
318for Shell mode only." 319for Shell mode only."
@@ -339,7 +340,7 @@ Thus, this does not include the shell's current directory.")
339 (let ((map (nconc (make-sparse-keymap) comint-mode-map))) 340 (let ((map (nconc (make-sparse-keymap) comint-mode-map)))
340 (define-key map "\C-c\C-f" 'shell-forward-command) 341 (define-key map "\C-c\C-f" 'shell-forward-command)
341 (define-key map "\C-c\C-b" 'shell-backward-command) 342 (define-key map "\C-c\C-b" 'shell-backward-command)
342 (define-key map "\t" 'comint-dynamic-complete) 343 (define-key map "\t" 'completion-at-point)
343 (define-key map (kbd "M-RET") 'shell-resync-dirs) 344 (define-key map (kbd "M-RET") 'shell-resync-dirs)
344 (define-key map "\M-?" 'comint-dynamic-list-filename-completions) 345 (define-key map "\M-?" 'comint-dynamic-list-filename-completions)
345 (define-key map [menu-bar completion] 346 (define-key map [menu-bar completion]
@@ -486,7 +487,7 @@ buffer."
486 (t "dirs"))) 487 (t "dirs")))
487 ;; Bypass a bug in certain versions of bash. 488 ;; Bypass a bug in certain versions of bash.
488 (when (string-equal shell "bash") 489 (when (string-equal shell "bash")
489 (add-hook 'comint-output-filter-functions 490 (add-hook 'comint-preoutput-filter-functions
490 'shell-filter-ctrl-a-ctrl-b nil t))) 491 'shell-filter-ctrl-a-ctrl-b nil t)))
491 (when shell-dir-cookie-re 492 (when shell-dir-cookie-re
492 ;; Watch for magic cookies in the output to track the current dir. 493 ;; Watch for magic cookies in the output to track the current dir.
@@ -494,7 +495,7 @@ buffer."
494 'shell-dir-cookie-watcher nil t)) 495 'shell-dir-cookie-watcher nil t))
495 (comint-read-input-ring t))) 496 (comint-read-input-ring t)))
496 497
497(defun shell-filter-ctrl-a-ctrl-b (_string) 498(defun shell-filter-ctrl-a-ctrl-b (string)
498 "Remove `^A' and `^B' characters from comint output. 499 "Remove `^A' and `^B' characters from comint output.
499 500
500Bash uses these characters as internal quoting characters in its 501Bash uses these characters as internal quoting characters in its
@@ -504,15 +505,10 @@ started with the `--noediting' option and Select Graphic
504Rendition (SGR) control sequences (formerly known as ANSI escape 505Rendition (SGR) control sequences (formerly known as ANSI escape
505sequences) are used to color the prompt. 506sequences) are used to color the prompt.
506 507
507This function can be put on `comint-output-filter-functions'. 508This function can be put on `comint-preoutput-filter-functions'."
508The argument STRING is ignored." 509 (if (string-match "[\C-a\C-b]" string)
509 (let ((pmark (process-mark (get-buffer-process (current-buffer))))) 510 (replace-regexp-in-string "[\C-a\C-b]" "" string t t)
510 (save-excursion 511 string))
511 (goto-char (or (and (markerp comint-last-output-start)
512 (marker-position comint-last-output-start))
513 (point-min)))
514 (while (re-search-forward "[\C-a\C-b]" pmark t)
515 (replace-match "")))))
516 512
517(defun shell-write-history-on-exit (process event) 513(defun shell-write-history-on-exit (process event)
518 "Called when the shell process is stopped. 514 "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
1011path. 1007path.
1012 1008
1013Completion is dependent on the value of `shell-completion-execonly', plus 1009Completion is dependent on the value of `shell-completion-execonly', plus
1014those that effect file completion. See `shell-dynamic-complete-as-command'. 1010those that effect file completion.
1015 1011
1016Returns t if successful." 1012Returns t if successful."
1017 (interactive) 1013 (interactive)
1014 (let ((data (shell-command-completion)))
1015 (if data
1016 (prog2 (unless (window-minibuffer-p (selected-window))
1017 (message "Completing command name..."))
1018 (apply #'completion-in-region data)))))
1019
1020(defun shell-command-completion ()
1021 "Return the completion data for the command at point, if any."
1018 (let ((filename (comint-match-partial-filename))) 1022 (let ((filename (comint-match-partial-filename)))
1019 (if (and filename 1023 (if (and filename
1020 (save-match-data (not (string-match "[~/]" filename))) 1024 (save-match-data (not (string-match "[~/]" filename)))
1021 (eq (match-beginning 0) 1025 (eq (match-beginning 0)
1022 (save-excursion (shell-backward-command 1) (point)))) 1026 (save-excursion (shell-backward-command 1) (point))))
1023 (prog2 (unless (window-minibuffer-p (selected-window)) 1027 (shell--command-completion-data))))
1024 (message "Completing command name..."))
1025 (shell-dynamic-complete-as-command)))))
1026 1028
1027 1029(defun shell--command-completion-data ()
1028(defun shell-dynamic-complete-as-command () 1030 "Return the completion data for the command at point."
1029 "Dynamically complete at point as a command.
1030See `shell-dynamic-complete-filename'. Returns t if successful."
1031 (let* ((filename (or (comint-match-partial-filename) "")) 1031 (let* ((filename (or (comint-match-partial-filename) ""))
1032 (start (if (zerop (length filename)) (point) (match-beginning 0)))
1033 (end (if (zerop (length filename)) (point) (match-end 0)))
1032 (filenondir (file-name-nondirectory filename)) 1034 (filenondir (file-name-nondirectory filename))
1033 (path-dirs (cdr (reverse exec-path))) 1035 (path-dirs (cdr (reverse exec-path))) ;FIXME: Why `cdr'?
1034 (cwd (file-name-as-directory (expand-file-name default-directory))) 1036 (cwd (file-name-as-directory (expand-file-name default-directory)))
1035 (ignored-extensions 1037 (ignored-extensions
1036 (and comint-completion-fignore 1038 (and comint-completion-fignore
1037 (mapconcat (function (lambda (x) (concat (regexp-quote x) "$"))) 1039 (mapconcat (function (lambda (x) (concat (regexp-quote x) "\\'")))
1038 comint-completion-fignore "\\|"))) 1040 comint-completion-fignore "\\|")))
1039 (dir "") (comps-in-dir ()) 1041 (dir "") (comps-in-dir ())
1040 (file "") (abs-file-name "") (completions ())) 1042 (file "") (abs-file-name "") (completions ()))
@@ -1058,18 +1060,31 @@ See `shell-dynamic-complete-filename'. Returns t if successful."
1058 (setq comps-in-dir (cdr comps-in-dir))) 1060 (setq comps-in-dir (cdr comps-in-dir)))
1059 (setq path-dirs (cdr path-dirs))) 1061 (setq path-dirs (cdr path-dirs)))
1060 ;; OK, we've got a list of completions. 1062 ;; OK, we've got a list of completions.
1061 (let ((success (let ((comint-completion-addsuffix nil)) 1063 (list
1062 (comint-dynamic-simple-complete filenondir completions)))) 1064 start end
1063 (if (and (memq success '(sole shortest)) comint-completion-addsuffix 1065 (lambda (string pred action)
1064 (not (file-directory-p (comint-match-partial-filename)))) 1066 (completion-table-with-terminator
1065 (insert " ")) 1067 " " (lambda (string pred action)
1066 success))) 1068 (if (string-match "/" string)
1069 (completion-file-name-table string pred action)
1070 (complete-with-action action completions string pred)))
1071 string pred action)))))
1072
1073;; (defun shell-dynamic-complete-as-command ()
1074;; "Dynamically complete at point as a command.
1075;; See `shell-dynamic-complete-filename'. Returns t if successful."
1076;; (apply #'completion-in-region shell--command-completion-data))
1067 1077
1068(defun shell-dynamic-complete-filename () 1078(defun shell-dynamic-complete-filename ()
1069 "Dynamically complete the filename at point. 1079 "Dynamically complete the filename at point.
1070This completes only if point is at a suitable position for a 1080This completes only if point is at a suitable position for a
1071filename argument." 1081filename argument."
1072 (interactive) 1082 (interactive)
1083 (let ((data (shell-filename-completion)))
1084 (if data (apply #'completion-in-region data))))
1085
1086(defun shell-filename-completion ()
1087 "Return the completion data for file name at point, if any."
1073 (let ((opoint (point)) 1088 (let ((opoint (point))
1074 (beg (comint-line-beginning-position))) 1089 (beg (comint-line-beginning-position)))
1075 (when (save-excursion 1090 (when (save-excursion
@@ -1077,24 +1092,21 @@ filename argument."
1077 (match-end 0) 1092 (match-end 0)
1078 beg)) 1093 beg))
1079 (re-search-forward "[^ \t][ \t]" opoint t)) 1094 (re-search-forward "[^ \t][ \t]" opoint t))
1080 (comint-dynamic-complete-as-filename)))) 1095 (comint-filename-completion))))
1081 1096
1082(defun shell-match-partial-variable () 1097(defun shell-match-partial-variable ()
1083 "Return the shell variable at point, or nil if none is found." 1098 "Return the shell variable at point, or nil if none is found."
1084 (save-excursion 1099 (save-excursion
1085 (let ((limit (point))) 1100 (if (re-search-backward "[^A-Za-z0-9_{(]" nil 'move)
1086 (if (re-search-backward "[^A-Za-z0-9_{}]" nil 'move) 1101 (or (looking-at "\\$") (forward-char 1)))
1087 (or (looking-at "\\$") (forward-char 1))) 1102 (if (or (eolp) (looking-at "[^A-Za-z0-9_{($]"))
1088 ;; Anchor the search forwards. 1103 nil
1089 (if (or (eolp) (looking-at "[^A-Za-z0-9_{}$]")) 1104 (looking-at "\\$?[{(]?[A-Za-z0-9_]*[})]?")
1090 nil 1105 (buffer-substring (match-beginning 0) (match-end 0)))))
1091 (re-search-forward "\\$?{?[A-Za-z0-9_]*}?" limit)
1092 (buffer-substring (match-beginning 0) (match-end 0))))))
1093 1106
1094(defun shell-dynamic-complete-environment-variable () 1107(defun shell-dynamic-complete-environment-variable ()
1095 "Dynamically complete the environment variable at point. 1108 "Dynamically complete the environment variable at point.
1096Completes if after a variable, i.e., if it starts with a \"$\". 1109Completes if after a variable, i.e., if it starts with a \"$\".
1097See `shell-dynamic-complete-as-environment-variable'.
1098 1110
1099This function is similar to `comint-dynamic-complete-filename', except that it 1111This function is similar to `comint-dynamic-complete-filename', except that it
1100searches `process-environment' for completion candidates. Note that this may 1112searches `process-environment' for completion candidates. Note that this may
@@ -1106,38 +1118,69 @@ called `shell-dynamic-complete-process-environment-variable'.
1106 1118
1107Returns non-nil if successful." 1119Returns non-nil if successful."
1108 (interactive) 1120 (interactive)
1109 (let ((variable (shell-match-partial-variable))) 1121 (let ((data (shell-environment-variable-completion)))
1110 (if (and variable (string-match "^\\$" variable)) 1122 (if data
1111 (prog2 (unless (window-minibuffer-p (selected-window)) 1123 (prog2 (unless (window-minibuffer-p (selected-window))
1112 (message "Completing variable name...")) 1124 (message "Completing variable name..."))
1113 (shell-dynamic-complete-as-environment-variable))))) 1125 (apply #'completion-in-region data)))))
1114 1126
1115 1127
1116(defun shell-dynamic-complete-as-environment-variable () 1128(defun shell-environment-variable-completion ()
1117 "Dynamically complete at point as an environment variable. 1129 "Completion data for an environment variable at point, if any."
1118Used by `shell-dynamic-complete-environment-variable'. 1130 (let* ((var (shell-match-partial-variable))
1119Uses `comint-dynamic-simple-complete'." 1131 (end (match-end 0)))
1120 (let* ((var (or (shell-match-partial-variable) "")) 1132 (when (and (not (zerop (length var))) (eq (aref var 0) ?$))
1121 (variable (substring var (or (string-match "[^$({]\\|$" var) 0))) 1133 (let* ((start
1122 (variables (mapcar (function (lambda (x) 1134 (save-excursion
1123 (substring x 0 (string-match "=" x)))) 1135 (goto-char (match-beginning 0))
1124 process-environment)) 1136 (looking-at "\\$?[({]*")
1125 (addsuffix comint-completion-addsuffix) 1137 (match-end 0)))
1126 (comint-completion-addsuffix nil) 1138 (variables (mapcar (lambda (x)
1127 (success (comint-dynamic-simple-complete variable variables))) 1139 (substring x 0 (string-match "=" x)))
1128 (if (memq success '(sole shortest)) 1140 process-environment))
1129 (let* ((var (shell-match-partial-variable)) 1141 (suffix (case (char-before start) (?\{ "}") (?\( ")") (t ""))))
1130 (variable (substring var (string-match "[^$({]" var))) 1142 (list
1131 (protection (cond ((string-match "{" var) "}") 1143 start end
1132 ((string-match "(" var) ")") 1144 (apply-partially
1133 (t ""))) 1145 #'completion-table-with-terminator
1134 (suffix (cond ((null addsuffix) "") 1146 (cons (lambda (comp)
1135 ((file-directory-p 1147 (concat comp
1136 (comint-directory (getenv variable))) "/") 1148 suffix
1137 (t " ")))) 1149 (if (file-directory-p
1138 (insert protection suffix))) 1150 (comint-directory (getenv comp)))
1139 success)) 1151 "/")))
1140 1152 "\\`a\\`")
1153 variables))))))
1154
1155
1156(defun shell-c-a-p-replace-by-expanded-directory ()
1157 "Expand directory stack reference before point.
1158For use on `completion-at-point-functions'."
1159 (when (comint-match-partial-filename)
1160 (save-excursion
1161 (goto-char (match-beginning 0))
1162 (let ((stack (cons default-directory shell-dirstack))
1163 (index (cond ((looking-at "=-/?")
1164 (length shell-dirstack))
1165 ((looking-at "=\\([0-9]+\\)/?")
1166 (string-to-number
1167 (buffer-substring
1168 (match-beginning 1) (match-end 1)))))))
1169 (when index
1170 (let ((start (match-beginning 0))
1171 (end (match-end 0))
1172 (replacement (file-name-as-directory (nth index stack))))
1173 (lambda ()
1174 (cond
1175 ((>= index (length stack))
1176 (error "Directory stack not that deep"))
1177 (t
1178 (save-excursion
1179 (goto-char start)
1180 (insert replacement)
1181 (delete-char (- end start)))
1182 (message "Directory item: %d" index)
1183 t)))))))))
1141 1184
1142(defun shell-replace-by-expanded-directory () 1185(defun shell-replace-by-expanded-directory ()
1143 "Expand directory stack reference before point. 1186 "Expand directory stack reference before point.
@@ -1146,24 +1189,8 @@ See `default-directory' and `shell-dirstack'.
1146 1189
1147Returns t if successful." 1190Returns t if successful."
1148 (interactive) 1191 (interactive)
1149 (if (comint-match-partial-filename) 1192 (let ((f (shell-c-a-p-replace-by-expanded-directory)))
1150 (save-excursion 1193 (if f (funcall f))))
1151 (goto-char (match-beginning 0))
1152 (let ((stack (cons default-directory shell-dirstack))
1153 (index (cond ((looking-at "=-/?")
1154 (length shell-dirstack))
1155 ((looking-at "=\\([0-9]+\\)/?")
1156 (string-to-number
1157 (buffer-substring
1158 (match-beginning 1) (match-end 1)))))))
1159 (cond ((null index)
1160 nil)
1161 ((>= index (length stack))
1162 (error "Directory stack not that deep"))
1163 (t
1164 (replace-match (file-name-as-directory (nth index stack)) t t)
1165 (message "Directory item: %d" index)
1166 t))))))
1167 1194
1168(provide 'shell) 1195(provide 'shell)
1169 1196