* 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.
This commit is contained in:
parent
2011338029
commit
c0a193ea20
5 changed files with 203 additions and 120 deletions
2
etc/NEWS
2
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.
|
||||
|
||||
|
|
|
@ -1,5 +1,31 @@
|
|||
2011-04-20 Stefan Monnier <monnier@iro.umontreal.ca>
|
||||
|
||||
* 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):
|
||||
|
|
|
@ -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)))))
|
||||
(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)
|
||||
(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.
|
||||
|
|
|
@ -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)
|
||||
|
|
211
lisp/shell.el
211
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,39 +1118,70 @@ 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)))))
|
||||
(apply #'completion-in-region data)))))
|
||||
|
||||
|
||||
(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))
|
||||
(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.
|
||||
Directory stack references are of the form \"=digit\" or \"=-\".
|
||||
|
@ -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)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue