Preenchimento automático do Bash no modo shell do Emacs

93

No Terminal GNOME, o Bash faz o preenchimento automático inteligente. Por exemplo

apt-get in<TAB>

torna-se

apt-get install

No modo shell do Emacs, esse preenchimento automático não funciona, mesmo depois que eu explicitamente o código /etc/bash_completion. O exemplo acima se fixa inou se completa automaticamente com um nome de arquivo no diretório atual em vez de uma apt-getopção de comando válida . Presumivelmente, isso ocorre porque o Emacs está interceptando o pressionamento da tecla Tab. Como habilito o preenchimento automático inteligente shell-mode?

Chris Conway
fonte
Para quem é novo no emacs ... existem outros modos de shell no emacs. Por exemplo, eshell-modeque tem preenchimento com guia. Mais informações aqui: masteringemacs.org/articles/2010/11/01/…
Ross
3
O autocompletar do eshell só funciona para diretórios locais, se você fizer o ssh para outra máquina, de repente perde essa capacidade.
hajovonta

Respostas:

92

Sei que essa pergunta tem três anos, mas é algo que também estou interessado em resolver. Uma pesquisa na Web me direcionou para um pedaço de elisp que faz o Emacs usar o bash para completar no modo shell. Funciona para mim, em qualquer caso.

Confira em https://github.com/szermatt/emacs-bash-completion .

David Christiansen
fonte
6
+1 por ter a coragem de responder a uma pergunta de três anos, mas ainda totalmente relevante. Obrigado!
djhaskin987
3
Isso já está em melpa melpa.org/#/bash-completion , a instalação é simples de usar M-x package-list-packages, mas no final não funcionou para mim, não sei porque, pode ser meu emacs (24.3.1) ou versão bash (4.3.30). A documentação diz "bash-completed.el é bastante sensível ao sistema operacional e à versão do BASH ..." então é uma lista onde minhas versões estão ausentes.
boclodoa
Infelizmente, a questão pendente nº 6 não parece tão útil para a safra atual de ferramentas CLI.
Jesse Glick
21

No shell do emacs, na verdade é o emacs fazendo o preenchimento automático, não o bash. Se o shell e o emacs estiverem fora de sincronia (por exemplo, usando pushd, popd ou alguma função de usuário bash que muda o diretório atual do shell), então o autocompletar para de funcionar.

Para corrigir isso, basta digitar 'dirs' no shell e as coisas voltam a sincronizar.

Eu também tenho o seguinte em meu .emacs:

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

Em seguida, apenas pressionar Esc-return sincroniza novamente o preenchimento automático.

Steve Lacey
fonte
9
Esta é uma boa resposta para uma pergunta diferente!
Chris Conway,
Obrigado. Eu uso o autojump e esse foi o problema por que o dirs saiu de sincronia.
Plankalkül
Chris Conway, sim, para este aqui: emacs.stackexchange.com/questions/30550/… :-)
unhammer
15

Eu não sei a resposta para isso. Mas a razão pela qual não funciona como você espera é provavelmente porque o preenchimento em shells emacs é tratado pelo emacs internamente (pela função comint-dynamic-complete) e não tem essas funções de conclusão inteligente embutidas.

Receio que não seja uma coisa fácil de consertar.

Edit: a sugestão do njsf de usar o modo term é provavelmente o melhor que pode acontecer. Comece com

Mx term
Ele está incluído na distribuição emacs padrão (e no emacs21-common ou emacs22-common no Ubuntu e Debian pelo menos).

matli
fonte
2
O preenchimento da guia é ainda pior com M-x term.
março
6

Como Matli disse, não é uma tarefa fácil, já que o bash é iniciado com --noediting e TAB é vinculado a comint-dynamic-complete.

É possível religar TAB ao comando self-insert no shell-comand-hook com local-set-key e fazer com que o modo shell não comece com --noediting por Mx customize-variable RET explicit-bash-args, mas eu suspeito que não vai se encaixar bem com todas as outras edições.

Você pode querer tentar o modo de termo, mas ele tem outro conjunto de problemas, porque alguns dos outros atalhos de teclado regulares são substituídos pelo modo de termo.

EDIT: Por outros keybidings regulares sendo ultrapassados ​​pelo modo de termo, quero dizer todos, exceto Cc, que se torna a saída para ser capaz de alternar os buffers. Então, em vez de Cx k para matar o buffer, você teria que Cc Cx k. Ou para mudar para outro buffer 'Cc Cx o' ou 'Cc Cx 2'

njsf
fonte
self-insert-command não é: ele insere o caractere TAB em vez de passar o pressionamento da tecla TAB para o Bash.
Chris Conway,
Não tenho um comando term-mode e não consigo descobrir onde obtê-lo.
Chris Conway,
6

Por favor, considere outro modo M-x term, como eu fiz quando atingi o problema em 2011. Eu tentei reunir todos os esforços no Inet naquela época para fazer o shell funcionar com a conclusão do Bash, incluindo esta questão. Mas desde que descobri alternativas diante de term-modenão quero nem tentar eshell.

É um emulador de terminal completo, para que você possa executar um programa interativo dentro dele, como o Midnight commander. Ou mude para a zshconclusão para não perder tempo na configuração do Emacs.

Você obtém a conclusão TAB no bash gratuitamente. Mas o mais importante é que você obtenha o poder total do Readline, como pesquisa de comando incremental ou prefixada . Para tornar esta configuração mais conveniente, verifique meus .inputrc , .bashrc , .emacs .

Parte essencial 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(SIM! Há dabbrev em Bash de qualquer palavra em ~/.bash_history):

set -o emacs

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

.emacs para tornar a navegação confortável no buffer de termo:

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

Como qualquer comando usual, pois C-x onão está funcionando no modo de emulação de terminal, estendi o mapa de teclado com:

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

Observe que eu uso a superchave para que, term-raw-mappossivelmente, qualquer outro mapa de teclado não entre em conflito com minhas combinações de teclas. Para fazer a superchave a partir da esquerda, Wineu uso .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

Você apenas deve se lembrar de 2 comandos: C-c C-j- para entrar no modo de edição normal do Emacs (para copiar ou fazer o grep no texto do buffer), C-c C-k- para retornar ao modo de emulação de terminal.

Seleção do mouse e Shift-Inserttrabalho como em xterm.

Gavenkoa
fonte
1
Obrigado, isso é ouro! especialmente a .inputrcparte!
cmantas
1

Eu uso o Prelude e quando clico em Meta + Tab, ele é concluído para mim.

Além disso, Ctrl + i parece fazer a mesma coisa.

duma
fonte
1

Eu sei que este post já tem mais de 11 anos. Mas eu criei uma função para fornecer a conclusão do shell nativo no Emacs. Ele apenas envia uma tecla tab para o processo subjacente e intercepta a saída, de modo que é exatamente o mesmo que você obteria no próprio shell.

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

Prgrm.celeritas
fonte
0

Eu uso o modo de leme. Possui esta funcionalidade (após pressionar "TAB"): insira a descrição da imagem aqui

a_subscriber
fonte
-2

Não reivindico ser um especialista em emacs, mas isso deve resolver o seu problema:

Crie: ~ / .emacs

Adicione a ele:

(requer 'shell-command) (shell-command-complete-mode)

O Emacs assume o controle do shell para que as configurações do BASH não sejam executadas. Isso definirá o preenchimento automático para o próprio EMACS.

Scott Alan Miller
fonte
Não, isso não resolve o problema. Ele só funciona no comando shell, não no modo shell. Além disso, não habilita o preenchimento inteligente que foi solicitado na pergunta (só completará comandos e nomes de arquivos).
matli,
1
O modo de conclusão do comando do shell completa o nome do arquivo e é habilitado por padrão. Eu quero o recurso de conclusão do Bash (que é extensível para incluir, por exemplo, subcomandos para apt-get e svn).
Chris Conway,