diff options
| author | Richard M. Stallman | 1994-03-03 23:37:27 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1994-03-03 23:37:27 +0000 |
| commit | 988a4d60ceddc04f86a5d691b165f257e17a8633 (patch) | |
| tree | 41f104c7214c9588560bd13e951255227cae27b0 | |
| parent | 1b4218268dcde0a1311dca7870884fc787717597 (diff) | |
| download | emacs-988a4d60ceddc04f86a5d691b165f257e17a8633.tar.gz emacs-988a4d60ceddc04f86a5d691b165f257e17a8633.zip | |
(shell-dynamic-complete-functions): New variable.
(shell-mode): Use it to set comint-dynamic-complete-functions.
(shell-mode-map): Define menu-bars for command,
variable and directory completion/expansion.
(shell-get-current-command, shell-after-partial-filename): Functions deleted.
(shell-dynamic-complete-environment-variable,
shell-replace-by-expanded-directory): New commands.
(shell-match-partial-variable,
shell-dynamic-complete-as-environment-variable): New functions.
| -rw-r--r-- | lisp/shell.el | 245 |
1 files changed, 172 insertions, 73 deletions
diff --git a/lisp/shell.el b/lisp/shell.el index 45107e67f67..3a6a9c03818 100644 --- a/lisp/shell.el +++ b/lisp/shell.el | |||
| @@ -160,6 +160,18 @@ shell buffer. The default is (?\\| ?& ?< ?> ?\\( ?\\) ?\\;). | |||
| 160 | 160 | ||
| 161 | This is a fine thing to set in your `.emacs' file.") | 161 | This is a fine thing to set in your `.emacs' file.") |
| 162 | 162 | ||
| 163 | (defvar shell-dynamic-complete-functions | ||
| 164 | '(comint-replace-by-expanded-history | ||
| 165 | shell-dynamic-complete-environment-variable | ||
| 166 | shell-dynamic-complete-command | ||
| 167 | shell-replace-by-expanded-directory | ||
| 168 | comint-dynamic-complete-filename) | ||
| 169 | "List of functions called to perform completion. | ||
| 170 | This variable is used to initialise `comint-dynamic-complete-functions' in the | ||
| 171 | shell buffer. | ||
| 172 | |||
| 173 | This is a fine thing to set in your `.emacs' file.") | ||
| 174 | |||
| 163 | (defvar shell-command-regexp "\\((.*)\\|[^;&|]\\)+" | 175 | (defvar shell-command-regexp "\\((.*)\\|[^;&|]\\)+" |
| 164 | "*Regexp to match shell commands. | 176 | "*Regexp to match shell commands. |
| 165 | Elements of pipes are considered as separate commands, forks and redirections | 177 | Elements of pipes are considered as separate commands, forks and redirections |
| @@ -236,7 +248,20 @@ Thus, this does not include the shell's current directory.") | |||
| 236 | (define-key shell-mode-map "\C-c\C-b" 'shell-backward-command) | 248 | (define-key shell-mode-map "\C-c\C-b" 'shell-backward-command) |
| 237 | (define-key shell-mode-map "\t" 'comint-dynamic-complete) | 249 | (define-key shell-mode-map "\t" 'comint-dynamic-complete) |
| 238 | (define-key shell-mode-map "\M-?" | 250 | (define-key shell-mode-map "\M-?" |
| 239 | 'comint-dynamic-list-filename-completions))) | 251 | 'comint-dynamic-list-filename-completions) |
| 252 | ;; Undefine the general completion first. | ||
| 253 | (define-key shell-mode-map [menu-bar completion complete] nil) | ||
| 254 | (define-key shell-mode-map [menu-bar completion expand-directory] | ||
| 255 | '("Expand Directory Reference" . shell-replace-by-expanded-directory)) | ||
| 256 | (define-key shell-mode-map [menu-bar completion complete-env-variable] | ||
| 257 | '("Complete Env. Variable Name" . | ||
| 258 | shell-dynamic-complete-environment-variable)) | ||
| 259 | (define-key shell-mode-map [menu-bar completion complete-command] | ||
| 260 | '("Complete Command Name" . shell-dynamic-complete-command)) | ||
| 261 | ;; Redefine (requires a new key) so that it is at the top. | ||
| 262 | (define-key shell-mode-map [menu-bar completion complete-shell] | ||
| 263 | '("Complete Before Point" . comint-dynamic-complete)) | ||
| 264 | )) | ||
| 240 | 265 | ||
| 241 | (defvar shell-mode-hook '() | 266 | (defvar shell-mode-hook '() |
| 242 | "*Hook for customising Shell mode.") | 267 | "*Hook for customising Shell mode.") |
| @@ -266,8 +291,9 @@ M-x dirtrack-toggle turns directory tracking on and off. | |||
| 266 | 291 | ||
| 267 | \\{shell-mode-map} | 292 | \\{shell-mode-map} |
| 268 | Customization: Entry to this mode runs the hooks on `comint-mode-hook' and | 293 | Customization: Entry to this mode runs the hooks on `comint-mode-hook' and |
| 269 | `shell-mode-hook' (in that order). After each shell output, the hooks on | 294 | `shell-mode-hook' (in that order). Before each input, the hooks on |
| 270 | `comint-output-filter-functions' are run. | 295 | `comint-input-sentinel-functions' are run. After each shell output, the hooks |
| 296 | on `comint-output-sentinel-functions' are run. | ||
| 271 | 297 | ||
| 272 | Variables `shell-cd-regexp', `shell-pushd-regexp' and `shell-popd-regexp' | 298 | Variables `shell-cd-regexp', `shell-pushd-regexp' and `shell-popd-regexp' |
| 273 | are used to match their respective commands, while `shell-pushd-tohome', | 299 | are used to match their respective commands, while `shell-pushd-tohome', |
| @@ -282,9 +308,10 @@ the behavior of command name completion. | |||
| 282 | Variables `comint-input-ring-file-name' and `comint-input-autoexpand' control | 308 | Variables `comint-input-ring-file-name' and `comint-input-autoexpand' control |
| 283 | the initialisation of the input ring history, and history expansion. | 309 | the initialisation of the input ring history, and history expansion. |
| 284 | 310 | ||
| 285 | Variables `comint-output-filter-functions', `comint-scroll-to-bottom-on-input', | 311 | Variables `comint-output-sentinel-functions', a hook, and |
| 286 | and `comint-scroll-to-bottom-on-output' control whether input and output | 312 | `comint-scroll-to-bottom-on-input',and `comint-scroll-to-bottom-on-output' |
| 287 | cause the window to scroll to the end of the buffer." | 313 | control whether input and output cause the window to scroll to the end of the |
| 314 | buffer." | ||
| 288 | (interactive) | 315 | (interactive) |
| 289 | (comint-mode) | 316 | (comint-mode) |
| 290 | (setq major-mode 'shell-mode) | 317 | (setq major-mode 'shell-mode) |
| @@ -292,9 +319,7 @@ cause the window to scroll to the end of the buffer." | |||
| 292 | (use-local-map shell-mode-map) | 319 | (use-local-map shell-mode-map) |
| 293 | (setq comint-prompt-regexp shell-prompt-pattern) | 320 | (setq comint-prompt-regexp shell-prompt-pattern) |
| 294 | (setq comint-delimiter-argument-list shell-delimiter-argument-list) | 321 | (setq comint-delimiter-argument-list shell-delimiter-argument-list) |
| 295 | (setq comint-after-partial-filename-command 'shell-after-partial-filename) | 322 | (setq comint-dynamic-complete-functions shell-dynamic-complete-functions) |
| 296 | (setq comint-get-current-command 'shell-get-current-command) | ||
| 297 | (setq comint-dynamic-complete-command-command 'shell-dynamic-complete-command) | ||
| 298 | (make-local-variable 'paragraph-start) | 323 | (make-local-variable 'paragraph-start) |
| 299 | (setq paragraph-start comint-prompt-regexp) | 324 | (setq paragraph-start comint-prompt-regexp) |
| 300 | (make-local-variable 'shell-dirstack) | 325 | (make-local-variable 'shell-dirstack) |
| @@ -302,7 +327,7 @@ cause the window to scroll to the end of the buffer." | |||
| 302 | (setq shell-last-dir nil) | 327 | (setq shell-last-dir nil) |
| 303 | (make-local-variable 'shell-dirtrackp) | 328 | (make-local-variable 'shell-dirtrackp) |
| 304 | (setq shell-dirtrackp t) | 329 | (setq shell-dirtrackp t) |
| 305 | (setq comint-input-sentinel 'shell-directory-tracker) | 330 | (add-hook 'comint-output-sentinel-functions 'shell-directory-tracker) |
| 306 | (setq comint-input-autoexpand shell-input-autoexpand) | 331 | (setq comint-input-autoexpand shell-input-autoexpand) |
| 307 | ;; shell-dependent assignments. | 332 | ;; shell-dependent assignments. |
| 308 | (let ((shell (car (process-command (get-buffer-process (current-buffer)))))) | 333 | (let ((shell (car (process-command (get-buffer-process (current-buffer)))))) |
| @@ -621,27 +646,6 @@ See `shell-command-regexp'." | |||
| 621 | (skip-chars-forward ";&|"))))) | 646 | (skip-chars-forward ";&|"))))) |
| 622 | 647 | ||
| 623 | 648 | ||
| 624 | (defun shell-get-current-command () | ||
| 625 | "Function that returns the current command including arguments." | ||
| 626 | (save-excursion | ||
| 627 | (if (looking-at "[\t ]*[^;&|\n]") | ||
| 628 | (goto-char (match-end 0))) | ||
| 629 | (buffer-substring | ||
| 630 | (progn (shell-backward-command 1) (point)) | ||
| 631 | (progn (shell-forward-command 1) (if (eolp) (point) (match-end 1)))))) | ||
| 632 | |||
| 633 | |||
| 634 | (defun shell-after-partial-filename () | ||
| 635 | "Returns t if point is after a file name. | ||
| 636 | File names are assumed to contain `/'s or not be the first item in the command. | ||
| 637 | |||
| 638 | See also `shell-backward-command'." | ||
| 639 | (let ((filename (comint-match-partial-filename))) | ||
| 640 | (or (save-match-data (string-match "/" filename)) | ||
| 641 | (not (eq (match-beginning 0) | ||
| 642 | (save-excursion (shell-backward-command 1) (point))))))) | ||
| 643 | |||
| 644 | |||
| 645 | (defun shell-dynamic-complete-command () | 649 | (defun shell-dynamic-complete-command () |
| 646 | "Dynamically complete the command at point. | 650 | "Dynamically complete the command at point. |
| 647 | This function is similar to `comint-dynamic-complete-filename', except that it | 651 | This function is similar to `comint-dynamic-complete-filename', except that it |
| @@ -650,10 +654,25 @@ candidates. Note that this may not be the same as the shell's idea of the | |||
| 650 | path. | 654 | path. |
| 651 | 655 | ||
| 652 | Completion is dependent on the value of `shell-completion-execonly', plus | 656 | Completion is dependent on the value of `shell-completion-execonly', plus |
| 653 | those that effect file completion. See `comint-dynamic-complete-filename'." | 657 | those that effect file completion. See `shell-dynamic-complete-as-command'. |
| 658 | |||
| 659 | Returns t if successful." | ||
| 654 | (interactive) | 660 | (interactive) |
| 661 | (let ((filename (comint-match-partial-filename))) | ||
| 662 | (if (and filename | ||
| 663 | (save-match-data (not (string-match "[~/]" filename))) | ||
| 664 | (eq (match-beginning 0) | ||
| 665 | (save-excursion (shell-backward-command 1) (point)))) | ||
| 666 | (prog2 (message "Completing command name...") | ||
| 667 | (shell-dynamic-complete-as-command))))) | ||
| 668 | |||
| 669 | |||
| 670 | (defun shell-dynamic-complete-as-command () | ||
| 671 | "Dynamically complete at point as a command. | ||
| 672 | See `shell-dynamic-complete-filename'. Returns t if successful." | ||
| 655 | (let* ((completion-ignore-case nil) | 673 | (let* ((completion-ignore-case nil) |
| 656 | (filename (comint-match-partial-filename)) | 674 | (success t) |
| 675 | (filename (or (comint-match-partial-filename) "")) | ||
| 657 | (pathnondir (file-name-nondirectory filename)) | 676 | (pathnondir (file-name-nondirectory filename)) |
| 658 | (paths (cdr (reverse exec-path))) | 677 | (paths (cdr (reverse exec-path))) |
| 659 | (cwd (file-name-as-directory (expand-file-name default-directory))) | 678 | (cwd (file-name-as-directory (expand-file-name default-directory))) |
| @@ -681,47 +700,127 @@ those that effect file completion. See `comint-dynamic-complete-filename'." | |||
| 681 | (setq paths (cdr paths))) | 700 | (setq paths (cdr paths))) |
| 682 | ;; OK, we've got a list of completions. | 701 | ;; OK, we've got a list of completions. |
| 683 | (cond ((null completions) | 702 | (cond ((null completions) |
| 684 | (message "No completions of %s" filename) | 703 | (message "No completions of %s" filename) |
| 685 | (ding)) | 704 | (setq success nil)) |
| 686 | ((= 1 (length completions)) ; Gotcha! | 705 | ((= 1 (length completions)) ; Gotcha! |
| 687 | (let ((completion (car completions))) | 706 | (let ((completion (car completions))) |
| 688 | (if (string-equal completion pathnondir) | 707 | (if (string-equal completion pathnondir) |
| 689 | (message "Sole completion") | 708 | (message "Sole completion") |
| 690 | (insert (substring (directory-file-name completion) | 709 | (insert (substring (directory-file-name completion) |
| 691 | (length pathnondir))) | 710 | (length pathnondir))) |
| 692 | (message "Completed")) | 711 | (message "Completed")) |
| 693 | (if comint-completion-addsuffix | 712 | (if comint-completion-addsuffix |
| 694 | (insert (if (file-directory-p completion) "/" " "))))) | 713 | (insert (if (file-directory-p completion) "/" " "))))) |
| 695 | (t ; There's no unique completion. | 714 | (t ; There's no unique completion. |
| 696 | (let ((completion | 715 | (let ((completion |
| 697 | (try-completion pathnondir (mapcar (function (lambda (x) | 716 | (try-completion pathnondir (mapcar (function (lambda (x) |
| 698 | (list x))) | 717 | (list x))) |
| 699 | completions)))) | 718 | completions)))) |
| 700 | ;; Insert the longest substring. | 719 | ;; Insert the longest substring. |
| 701 | (insert (substring (directory-file-name completion) | 720 | (insert (substring (directory-file-name completion) |
| 702 | (length pathnondir))) | 721 | (length pathnondir))) |
| 703 | (cond ((and comint-completion-recexact comint-completion-addsuffix | 722 | (cond ((and comint-completion-recexact comint-completion-addsuffix |
| 704 | (string-equal pathnondir completion) | 723 | (string-equal pathnondir completion) |
| 705 | (member completion completions)) | 724 | (member completion completions)) |
| 706 | ;; It's not unique, but user wants shortest match. | 725 | ;; It's not unique, but user wants shortest match. |
| 707 | (insert (if (file-directory-p completion) "/" " ")) | 726 | (insert (if (file-directory-p completion) "/" " ")) |
| 708 | (message "Completed shortest")) | 727 | (message "Completed shortest")) |
| 709 | ((or comint-completion-autolist | 728 | ((or comint-completion-autolist |
| 710 | (string-equal pathnondir completion)) | 729 | (string-equal pathnondir completion)) |
| 711 | ;; It's not unique, list possible completions. | 730 | ;; It's not unique, list possible completions. |
| 712 | (comint-dynamic-list-completions completions)) | 731 | (comint-dynamic-list-completions completions)) |
| 713 | (t | 732 | (t |
| 714 | (message "Partially completed")))))))) | 733 | (message "Partially completed")))))) |
| 715 | 734 | success)) | |
| 716 | ;;; Do the user's customization... | 735 | |
| 717 | ;;; | 736 | |
| 718 | ;;; Isn't this what eval-after-load is for? | 737 | (defun shell-match-partial-variable () |
| 719 | ;;;(defvar shell-load-hook nil | 738 | "Return the variable at point, or nil if non is found." |
| 720 | ;;; "This hook is run when shell is loaded in. | 739 | (save-excursion |
| 721 | ;;;This is a good place to put keybindings.") | 740 | (let ((limit (point))) |
| 722 | ;;; | 741 | (if (re-search-backward "[^A-Za-z0-9_{}]" nil 'move) |
| 723 | ;;;(run-hooks 'shell-load-hook) | 742 | (or (looking-at "\\$") (forward-char 1))) |
| 743 | ;; Anchor the search forwards. | ||
| 744 | (if (or (eolp) (looking-at "[^A-Za-z0-9_{}$]")) | ||
| 745 | nil | ||
| 746 | (re-search-forward "\\$?{?[A-Za-z0-9_]*}?" limit) | ||
| 747 | (buffer-substring (match-beginning 0) (match-end 0)))))) | ||
| 724 | 748 | ||
| 749 | |||
| 750 | (defun shell-dynamic-complete-environment-variable () | ||
| 751 | "Dynamically complete the environment variable at point. | ||
| 752 | Completes if after a variable, i.e., if it starts with a \"$\". | ||
| 753 | See `shell-dynamic-complete-as-environment-variable'. | ||
| 754 | |||
| 755 | This function is similar to `comint-dynamic-complete-filename', except that it | ||
| 756 | searches `process-environment' for completion candidates. Note that this may | ||
| 757 | not be the same as the interpreter's idea of variable names. The main problem | ||
| 758 | with this type of completion is that `process-environment' is the environment | ||
| 759 | which Emacs started with. Emacs does not track changes to the environment made | ||
| 760 | by the interpreter. Perhaps it would be more accurate if this function was | ||
| 761 | called `shell-dynamic-complete-process-environment-variable'. | ||
| 762 | |||
| 763 | Returns non-nil if successful." | ||
| 764 | (interactive) | ||
| 765 | (let ((variable (shell-match-partial-variable))) | ||
| 766 | (if (and variable (string-match "^\\$" variable)) | ||
| 767 | (prog2 (message "Completing variable name...") | ||
| 768 | (shell-dynamic-complete-as-environment-variable))))) | ||
| 769 | |||
| 770 | |||
| 771 | (defun shell-dynamic-complete-as-environment-variable () | ||
| 772 | "Dynamically complete at point as an environment variable. | ||
| 773 | Used by `shell-dynamic-complete-environment-variable'. | ||
| 774 | Uses `comint-dynamic-simple-complete'." | ||
| 775 | (let* ((var (or (shell-match-partial-variable) "")) | ||
| 776 | (variable (substring var (or (string-match "[^$({]\\|$" var) 0))) | ||
| 777 | (variables (mapcar (function (lambda (x) | ||
| 778 | (substring x 0 (string-match "=" x)))) | ||
| 779 | process-environment)) | ||
| 780 | (addsuffix comint-completion-addsuffix) | ||
| 781 | (comint-completion-addsuffix nil) | ||
| 782 | (success (comint-dynamic-simple-complete variable variables))) | ||
| 783 | (if (memq success '(sole shortest)) | ||
| 784 | (let* ((var (shell-match-partial-variable)) | ||
| 785 | (variable (substring var (string-match "[^$({]" var))) | ||
| 786 | (protection (cond ((string-match "{" var) "}") | ||
| 787 | ((string-match "(" var) ")") | ||
| 788 | (t ""))) | ||
| 789 | (suffix (cond ((null addsuffix) "") | ||
| 790 | ((file-directory-p | ||
| 791 | (comint-directory (getenv variable))) "/") | ||
| 792 | (t " ")))) | ||
| 793 | (insert protection suffix))) | ||
| 794 | success)) | ||
| 795 | |||
| 796 | |||
| 797 | (defun shell-replace-by-expanded-directory () | ||
| 798 | "Expand directory stack reference before point. | ||
| 799 | Directory stack references are of the form \"=digit\" or \"=-\". | ||
| 800 | See `default-directory' and `shell-dirstack'. | ||
| 801 | |||
| 802 | Returns t if successful." | ||
| 803 | (interactive) | ||
| 804 | (if (comint-match-partial-filename) | ||
| 805 | (save-excursion | ||
| 806 | (message "Expanding directory references...") | ||
| 807 | (goto-char (match-beginning 0)) | ||
| 808 | (let ((stack (cons default-directory shell-dirstack)) | ||
| 809 | (index (cond ((looking-at "=-/?") | ||
| 810 | (length shell-dirstack)) | ||
| 811 | ((looking-at "=\\([0-9]+\\)") | ||
| 812 | (string-to-number | ||
| 813 | (buffer-substring | ||
| 814 | (match-beginning 1) (match-end 1))))))) | ||
| 815 | (cond ((null index) | ||
| 816 | nil) | ||
| 817 | ((>= index (length stack)) | ||
| 818 | (error "Directory stack not that deep.")) | ||
| 819 | (t | ||
| 820 | (replace-match (file-name-as-directory (nth index stack)) t t) | ||
| 821 | (message "Directory item: %d" index) | ||
| 822 | t)))))) | ||
| 823 | |||
| 725 | (provide 'shell) | 824 | (provide 'shell) |
| 726 | 825 | ||
| 727 | ;;; shell.el ends here | 826 | ;;; shell.el ends here |