Autocompletion Bash en mode shell Emacs

93

Dans le terminal GNOME, Bash effectue l'auto-complétion intelligente. Par exemple

apt-get in<TAB>

devient

apt-get install

En mode shell d'Emacs, cette auto-complétion ne fonctionne pas, même après avoir explicitement source /etc/bash_completion. L'exemple ci-dessus colle comme inou se complète automatiquement avec un nom de fichier dans le répertoire courant plutôt qu'une apt-getoption de commande valide . Vraisemblablement, c'est parce qu'Emacs intercepte la touche Tab. Comment activer la complétion automatique intelligente dans shell-mode?

Chris Conway
la source
Pour tous ceux qui découvrent emacs ... il existe d'autres modes shell dans emacs. Tels que eshell-modequi a l'achèvement de l'onglet. Plus d'infos ici: masteringemacs.org/articles/2010/11/01/…
Ross
3
L'auto-complétion d'eshell ne fonctionne que pour les répertoires locaux, si vous vous connectez à une autre machine, vous perdez soudainement cette capacité.
hajovonta

Réponses:

92

Je sais que cette question remonte à trois ans, mais c'est quelque chose que j'ai également voulu résoudre. Une recherche sur le Web m'a dirigé vers un élément d'elisp qui fait qu'Emacs utilise bash pour la complétion en mode shell. Cela fonctionne pour moi, en tout cas.

Découvrez-le sur https://github.com/szermatt/emacs-bash-completion .

David Christiansen
la source
6
+1 pour avoir eu le courage de répondre à une question de trois ans, mais toujours tout à fait pertinente. Merci!
djhaskin987
3
C'est déjà dans melpa melpa.org/#/bash-completion , l'installation est simple à utiliser M-x package-list-packages, mais à la fin cela n'a pas fonctionné pour moi, je ne sais pas pourquoi, c'est peut-être mon emacs (24.3.1) ou version bash (4.3.30). La documentation dit "bash-completion.el est assez sensible à l'OS et à la version BASH ..." alors est une liste où mes versions sont absentes.
boclodoa
Malheureusement, en attente du numéro 6, cela ne semble pas si utile pour la récolte actuelle d'outils CLI.
Jesse Glick
21

Dans le shell emacs, c'est en fait emacs qui effectue l'auto-complétion, pas bash. Si le shell et emacs ne sont pas synchronisés (par exemple en utilisant pushd, popd ou une fonction utilisateur bash qui change le répertoire courant du shell), alors l'auto-complétion cesse de fonctionner.

Pour résoudre ce problème, tapez simplement «dirs» dans le shell et les choses se synchronisent.

J'ai également les éléments suivants dans mes .emacs:

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

Ensuite, le simple fait d'appuyer sur Esc-return resynchronise l'auto-complétion.

Steve Lacey
la source
9
C'est une belle réponse à une question différente!
Chris Conway
Merci. J'utilise autojump et c'est le problème pour lequel les répertoires se désynchronisent.
Plankalkül
Chris Conway, oui, à celui-ci: emacs.stackexchange.com/questions/30550/… :-)
unhammer
15

Je ne connais pas la réponse à cela. Mais la raison pour laquelle cela ne fonctionne pas comme prévu est probablement parce que la complétion dans les shells emacs est gérée par emacs en interne (par la fonction comint-dynamic-complete), et n'a pas ces fonctions de complétion intelligente intégrées.

J'ai bien peur que ce ne soit pas une chose facile à résoudre.

Edit: la suggestion de njsf d'utiliser le mode terme est probablement aussi bonne que possible. Commencez avec

Terme MX
Il est inclus dans la distribution standard emacs (et au moins dans emacs21-common ou emacs22-common sur Ubuntu et Debian).

matli
la source
2
La complétion des onglets est encore pire avec M-x term.
mcandre
6

Comme Matli l'a dit, ce n'est pas une tâche facile, car bash est démarré avec --noediting et TAB est lié à comint-dynamic-complete.

On pourrait éventuellement relier TAB à self-insert-command dans shell-comand-hook avec local-set-key et faire en sorte que le mode shell ne commence pas par --noediting by Mx personnalise-variable RET explicit-bash-args, mais je soupçonne que cela ne conviendra pas à toutes les autres modifications.

Vous voudrez peut-être essayer le mode terme, mais il présente un autre ensemble de problèmes, car certaines des autres combinaisons de touches régulières sont dépassées par le mode terme.

EDIT: Par d'autres keybidings réguliers dépassés par le mode terme, je veux dire tout sauf Cc qui devient l'évasion pour pouvoir changer de tampon. Donc, au lieu de Cx k pour tuer le tampon, vous devrez Cc Cx k. Ou pour passer à un autre tampon 'Cc Cx o' ou 'Cc Cx 2'

njsf
la source
self-insert-command n'est-ce pas: il insère le caractère TAB plutôt que de passer la touche TAB à Bash.
Chris Conway
Je n'ai pas de commande en mode terme et je ne sais pas où l'obtenir.
Chris Conway
6

S'il vous plaît, considérez un autre mode M-x term, comme je l'ai fait lorsque j'ai rencontré un problème en 2011. J'ai essayé de rassembler tous les efforts sur Inet à ce moment-là pour faire fonctionner le shell avec l'achèvement de Bash, y compris cette question. Mais depuis que term-modej'ai découvert une alternative face à moi, je n'ai même pas envie d'essayer eshell.

C'est un émulateur de terminal complet, vous pouvez donc exécuter un programme interactif à l'intérieur, comme Midnight Commander. Ou passez à la zshfin pour ne pas perdre de temps sur la configuration d'Emacs.

Vous obtenez la complétion TAB dans bash gratuitement. Mais plus important encore, vous bénéficiez de toute la puissance de Readline, comme la recherche de commandes incrémentielle ou préfixée . Pour rendre cette configuration plus pratique, vérifiez mon .inputrc , .bashrc , .emacs .

Partie essentielle de .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(OUI! Il y a dabbrev dans Bash de n'importe quel mot dans ~/.bash_history):

set -o emacs

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

.emacs pour rendre la navigation confortable dans le tampon de termes:

(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)

Comme toutes les commandes habituelles C-x one fonctionnent pas en mode d'émulation de terminal, j'ai étendu le keymap avec:

(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)

Notez que j'utilise la superclé term-raw-mapet peut-être que toute autre mappe de touches n'est pas en conflit avec mes raccourcis clavier. Pour créer la superclé à partir de la touche gauche, Winj'utilise .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

Vous devez juste vous souvenir de 2 commandes: C-c C-j- pour entrer dans le mode d'édition normal d'Emacs (pour copier ou grepping dans le texte du tampon), C-c C-k- pour revenir en mode d'émulation de terminal.

Sélection de la souris et Shift-Inserttravail comme dans xterm.

gavenkoa
la source
1
Merci, c'est de l'or! surtout la .inputrcpartie!
cmantas
1

J'utilise Prelude et quand je clique sur Meta + Tab, ça se termine pour moi.

De plus, Ctrl + i semble faire la même chose.

douma
la source
1

Je sais que ce message a plus de 11 ans maintenant. Mais j'ai créé une fonction pour compléter le shell natif dans Emacs. Il envoie simplement une touche de tabulation au processus sous-jacent et intercepte la sortie, donc c'est exactement la même chose que vous obtiendriez dans le shell lui-même.

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

Prgrm.celeritas
la source
0

J'utilise le mode barre. Il a cette fonctionnalité (après avoir appuyé sur "TAB"): entrez la description de l'image ici

a_subscriber
la source
-2

Je ne prétends pas être un expert emacs mais cela devrait résoudre votre problème:

Créer: ~ / .emacs

Ajoutez-y:

(nécessite 'shell-command) (shell-command-completion-mode)

Emacs prend en charge le shell afin que les paramètres BASH ne soient pas appliqués. Cela définira la complétion automatique pour EMACS lui-même.

Scott Alan Miller
la source
Non, cela ne résout pas le problème. Cela ne fonctionne que sur la commande shell, pas en mode shell. Et en plus, il n'active pas la complétion intelligente qui a été demandée dans la question (il ne complétera que les commandes et les noms de fichiers).
matli
1
shell-command-completion-mode effectue la complétion du nom de fichier et est activé par défaut. Je veux la fonction de complétion de Bash (qui est extensible pour inclure, par exemple, des sous-commandes pour apt-get et svn).
Chris Conway