ChatGPT解决这个技术问题 Extra ChatGPT

Bash autocompletion in Emacs shell-mode

In the GNOME Terminal, Bash does smart auto-completion. For example

apt-get in<TAB>

becomes

apt-get install

In Emacs shell-mode, this auto-completion doesn't work, even after I explicitly source /etc/bash_completion. The above example sticks as in or auto-completes with a filename in the current directory rather than a valid apt-get command option. Presumably, this is because Emacs is intercepting the Tab key-press. How do I enable smart auto-completion in shell-mode?

For anyone new to emacs... there are other shell modes in emacs. Such as eshell-mode which has tab completion. More info here: masteringemacs.org/articles/2010/11/01/…
eshell's autocompletion only works for local directories, if you ssh to another machine, you suddenly lose this ability.

D
David Christiansen

I know this question is three years old, but it's something that I've also been interested in solving. A Web search directed me to a piece of elisp that makes Emacs use bash for completion in shell mode. It works for me, in any case.

Check it out at https://github.com/szermatt/emacs-bash-completion .


+1 for having the courage to answer a three-year-old, but still totally relevant, question. Thanks!
This is already in melpa melpa.org/#/bash-completion, installation is straightforward using M-x package-list-packages, but in the end it didn't work for me, I don't know why, it may be my emacs (24.3.1) or bash (4.3.30) version. The documentation says "bash-completion.el is quite sensitive to the OS and BASH version..." then is a list where my versions are absent.
Unfortunately pending issue #6 this seems not so useful for the current crop of CLI tools.
That issue 6 was closed as (paraphrased).
S
Steve Lacey

In the emacs shell, it's actually emacs doing the auto-completion, not bash. If the shell and emacs are out of sync (e.g. by using pushd, popd or some bash user function that changes the shell's current directory), then auto-completion stops working.

To fix this, just type 'dirs' into the shell and things get back in sync.

I also have the following in my .emacs:

(global-set-key "\M-\r" 'shell-resync-dirs)

Then just hitting Esc-return resyncs the auto-completion.


This is a nice answer to a different question!
Thanks. I use autojump and that was the problem why the dirs got out of sync.
Chris Conway, yes, to this one: emacs.stackexchange.com/questions/30550/… :-)
m
matli

I don't know the answer to this. But the reason that it doesn't work as you expect is probably because the completion in emacs shells is handled by emacs internally (by the comint-dynamic-complete function), and doesn't have those smart completion functions built-in.

I'm afraid it is not an easy thing to fix.

Edit: njsf's suggestion of using term-mode is probably as good as it gets. Start it with

M-x term


Tab completion is even worse with M-x term.
g
gavenkoa

Please, consider another mode M-x term, like I did this when hit problem in 2011. I tried to gather all efforts over Inet at that time to make shell work with Bash completion, including this question. But since discovering alternative in face of term-mode I don't even want to try eshell.

It is full terminal emulator, so you can run interactive program inside, like Midnight commander. Or switch to zsh completion so you won't lose time on Emacs configuration.

You get TAB completion in bash for free. But more important you get full Readline power, like incremental or prefixed command search. To make this setup more convenient check my .inputrc, .bashrc, .emacs.

Essential part of .inputrc:

# I like this!
set editing-mode emacs

# Don't strip characters to 7 bits when reading.
set input-meta on

# Allow iso-latin1 characters to be inserted rather than converted to
# prefix-meta sequences.
set convert-meta off

# Display characters with the eighth bit set directly rather than as
# meta-prefixed characters.
set output-meta on

# Ignore hidden files.
set match-hidden-files off

# Ignore case (on/off).
set completion-ignore-case on

set completion-query-items 100

# First tab suggests ambiguous variants.
set show-all-if-ambiguous on

# Replace common prefix with ...
set completion-prefix-display-length 1

set skip-completed-text off

# If set to 'on', completed directory names have a slash appended. The default is 'on'.
set mark-directories on
set mark-symlinked-directories on

# If set to 'on', a character denoting a file's type is appended to the
# filename when listing possible completions. The default is 'off'.
set visible-stats on

set horizontal-scroll-mode off

$if Bash
"\C-x\C-e": edit-and-execute-command
$endif

# Define my favorite Emacs key bindings.
"\C-@": set-mark
"\C-w": kill-region
"\M-w": copy-region-as-kill

# Ctrl+Left/Right to move by whole words.
"\e[1;5C": forward-word
"\e[1;5D": backward-word
# Same with Shift pressed.
"\e[1;6C": forward-word
"\e[1;6D": backward-word

# Ctrl+Backspace/Delete to delete whole words.
"\e[3;5~": kill-word
"\C-_": backward-kill-word

# UP/DOWN filter history by typed string as prefix.
"\e[A": history-search-backward
"\C-p": history-search-backward
"\eOA": history-search-backward
"\e[B": history-search-forward
"\C-n": history-search-forward
"\eOB": history-search-forward

# Bind 'Shift+TAB' to complete as in Python TAB was need for another purpose.
"\e[Z": complete
# Cycling possible completion forward and backward in place.
"\e[1;3C": menu-complete                    # M-Right
"\e[1;3D": menu-complete-backward           # M-Left
"\e[1;5I": menu-complete                    # C-TAB

.bashrc (YEA! There is dabbrev in Bash from any word in ~/.bash_history):

set -o emacs

if [[ $- == *i* ]]; then
  bind '"\e/": dabbrev-expand'
  bind '"\ee": edit-and-execute-command'
fi

.emacs to make navigation comfortable in term buffer:

(setq term-buffer-maximum-size (lsh 1 14))

(eval-after-load 'term
  '(progn
    (defun my-term-send-delete-word-forward () (interactive) (term-send-raw-string "\ed"))
    (defun my-term-send-delete-word-backward () (interactive) (term-send-raw-string "\e\C-h"))
    (define-key term-raw-map [C-delete] 'my-term-send-delete-word-forward)
    (define-key term-raw-map [C-backspace] 'my-term-send-delete-word-backward)
    (defun my-term-send-forward-word () (interactive) (term-send-raw-string "\ef"))
    (defun my-term-send-backward-word () (interactive) (term-send-raw-string "\eb"))
    (define-key term-raw-map [C-left] 'my-term-send-backward-word)
    (define-key term-raw-map [C-right] 'my-term-send-forward-word)
    (defun my-term-send-m-right () (interactive) (term-send-raw-string "\e[1;3C"))
    (defun my-term-send-m-left () (interactive) (term-send-raw-string "\e[1;3D"))
    (define-key term-raw-map [M-right] 'my-term-send-m-right)
    (define-key term-raw-map [M-left] 'my-term-send-m-left)
    ))

(defun my-term-mode-hook ()
  (goto-address-mode 1))
(add-hook 'term-mode-hook #'my-term-mode-hook)

As any usual commands as C-x o aren't working in terminal emulation mode I extended keymap with:

(unless
    (ignore-errors
      (require 'ido)
      (ido-mode 1)
      (global-set-key [?\s-d] #'ido-dired)
      (global-set-key [?\s-f] #'ido-find-file)
      t)
  (global-set-key [?\s-d] #'dired)
  (global-set-key [?\s-f] #'find-file))

(defun my--kill-this-buffer-maybe-switch-to-next ()
  "Kill current buffer. Switch to next buffer if previous command
was switching to next buffer or this command itself allowing
sequential closing of uninteresting buffers."
  (interactive)
  (let ( (cmd last-command) )
    (kill-buffer (current-buffer))
    (when (memq cmd (list 'next-buffer this-command))
      (next-buffer))))
(global-set-key [s-delete] 'my--kill-this-buffer-maybe-switch-to-next)
(defun my--backward-other-window ()
  (interactive)
  (other-window -1))
(global-set-key [s-up] #'my--backward-other-window)
(global-set-key [s-down] #'other-window)
(global-set-key [s-tab] 'other-window)

Note that I use super key so term-raw-map and possibly any other keymap don't conflict with my key bindings. To make super key from left Win key I use .xmodmaprc:

! To load this config run:
!   $ xmodmap .xmodmaprc

! Win key.
clear mod3
clear mod4

keycode 133 = Super_L
keycode 134 = Hyper_R
add mod3 = Super_L
add mod4 = Hyper_R

You just should remember 2 commands: C-c C-j - to enter to normal Emacs editing mode (for copying or grepping in buffer text), C-c C-k - to return to terminal emulation mode.

Mouse selection and Shift-Insert work as in xterm.


Thank you, this is gold! especially the .inputrc part!
n
njsf

Like Matli said, it is not an easy task, since bash is started with --noediting and TAB is bound to comint-dynamic-complete.

One could possibly rebind TAB to self-insert-command in shell-comand-hook with local-set-key and make shell-mode not start with --noediting by M-x customize-variable RET explicit-bash-args, but I suspect that it will not sit well with all other editing.

You might want to try term-mode, but it has another set of problems, because some of the other regular keybindings are overtaken by term-mode.

EDIT: By other regular keybidings being overtaken by term-mode, I mean all but C-c which becomes the escape to be able to switch buffers. So instead of C-x k to kill the buffer you'd have to C-c C-x k. Or to switch to another buffer 'C-c C-x o' or 'C-c C-x 2'


self-insert-command is not it: it inserts the TAB character rather than passing the TAB key-press through to Bash.
I don't have a term-mode command and I can't figure out where to get it.
P
Prgrm.celeritas

I know this post is over 11 years old now. But I have created a function to give native shell completion in Emacs. It just sends a tab key to the underlying process and intercepts the output, so it is the exact same as you would get in the shell itself.

https://github.com/CeleritasCelery/emacs-native-shell-complete


Wow! A m a z i n g ! (Initial impression based on the preview.)
I love it. Thank you so much. It would be even better if, when TAB can't be completed to a single command because there are multiple candidates, those candidates appeared in Emacs like they do in Bash.
You mean like in a big list in the terminal instead of using Emacs completion display?
d
duma

I use Prelude and when I hit Meta+Tab it completes for me.

Also, Ctrl+i seems to do the same thing.


A
Alexei

https://i.stack.imgur.com/OeILH.png


S
Scott Alan Miller

I make no claims to being an emacs expert but this should solve your problem:

Create: ~/.emacs

Add to it:

(require 'shell-command) (shell-command-completion-mode)

Emacs takes over the shell so BASH settings don't carry through. This will set auto completion for EMACS itself.


No, this doesn't solve the problem. It only works on shell-command, not in shell-mode. And besides, it doesn't enable the smart completion that was requested in the question (it will only complete commands and filenames).
shell-command-completion-mode does filename completion and is enabled by default. I want Bash's completion feature (which is extensible to include, e.g., sub-commands for apt-get and svn).