Bash pelengkapan otomatis dalam mode shell Emacs


93

Di Terminal GNOME, Bash melakukan penyelesaian otomatis cerdas. Sebagai contoh

apt-get in<TAB>

menjadi

apt-get install

Dalam mode shell Emacs, pelengkapan otomatis ini tidak berfungsi, bahkan setelah saya mencari sumber secara eksplisit /etc/bash_completion. Contoh di atas tetap sebagai inatau pelengkapan otomatis dengan nama file di direktori saat ini daripada apt-getopsi perintah yang valid . Agaknya, ini karena Emacs mencegat penekanan tombol Tab. Bagaimana cara mengaktifkan pelengkapan otomatis pintar di shell-mode?


Bagi siapa pun yang baru mengenal emacs ... ada mode shell lain di emacs. Seperti eshell-modeyang memiliki penyelesaian tab. Info lebih lanjut di sini: masteringemacs.org/articles/2010/11/01/…
Ross

3
Pelengkapan otomatis eshell hanya bekerja untuk direktori lokal, jika Anda ssh ke komputer lain, Anda tiba-tiba kehilangan kemampuan ini.
hajovonta

Jawaban:


93

Saya tahu pertanyaan ini berumur tiga tahun, tetapi itu adalah sesuatu yang juga ingin saya pecahkan. Pencarian Web mengarahkan saya ke bagian elisp yang membuat Emacs menggunakan bash untuk penyelesaian dalam mode shell. Bagaimanapun, itu berhasil untuk saya.

Lihat di https://github.com/szermatt/emacs-bash-completion .


6
+1 karena memiliki keberanian untuk menjawab pertanyaan anak berusia tiga tahun, tetapi masih sangat relevan. Terima kasih!
djhaskin987

3
Ini sudah ada di melpa melpa.org/#/bash-completion , penginstalan langsung digunakan M-x package-list-packages, tetapi pada akhirnya tidak berhasil untuk saya, saya tidak tahu mengapa, mungkin emacs saya (24.3.1) atau versi bash (4.3.30). Dokumentasi mengatakan "bash-complete.el cukup sensitif terhadap OS dan versi BASH ..." maka ada daftar di mana versi saya tidak ada.
boclodoa

Sayangnya masalah tertunda # 6 ini tampaknya tidak begitu berguna untuk alat CLI saat ini.
Jesse Glick

Masalah 6 itu ditutup karena <setidaknya sebagian besar diperbaiki / contoh tidak berfungsi diperlukan> (diparafrasekan).
Matthew Elvey

21

Di shell emacs, sebenarnya emacs melakukan penyelesaian otomatis, bukan bash. Jika shell dan emacs tidak sinkron (mis. Dengan menggunakan pushd, popd atau beberapa fungsi pengguna bash yang mengubah direktori shell saat ini), maka pelengkapan otomatis berhenti bekerja.

Untuk memperbaikinya, cukup ketik 'dirs' ke dalam shell dan semuanya kembali sinkron.

Saya juga memiliki yang berikut ini di .emacs saya:

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

Kemudian cukup menekan Esc-return menyinkronkan ulang penyelesaian otomatis.


9
Ini adalah jawaban yang bagus untuk pertanyaan berbeda!
Chris Conway

Terima kasih. Saya menggunakan autojump dan itulah masalah mengapa dir menjadi tidak sinkron.
Plankalkül

Chris Conway, ya, untuk yang ini: emacs.stackexchange.com/questions/30550/… :-)
unhammer

15

Saya tidak tahu jawabannya. Tetapi alasan mengapa itu tidak berfungsi seperti yang Anda harapkan mungkin karena penyelesaian di shell emacs ditangani oleh emacs secara internal (oleh fungsi comint-dynamic-complete), dan tidak memiliki fungsi penyelesaian pintar bawaan.

Saya khawatir ini bukan hal yang mudah untuk diperbaiki.

Edit: saran njsf untuk menggunakan term-mode mungkin sebagus yang didapat. Mulailah dengan

Istilah mx
Ini termasuk dalam distribusi emacs standar (dan dalam emacs21-common atau emacs22-common setidaknya di Ubuntu dan Debian).


2
Penyelesaian tab bahkan lebih buruk dengan M-x term.
mcandre

7

Harap pertimbangkan mode lain M-x term, seperti yang saya lakukan saat terkena masalah pada tahun 2011. Saya mencoba mengumpulkan semua upaya dari Inet pada saat itu untuk membuat shell berfungsi dengan penyelesaian Bash, termasuk pertanyaan ini. Tetapi karena menemukan alternatif di hadapan term-modesaya, saya bahkan tidak ingin mencoba eshell.

Ini adalah emulator terminal penuh, sehingga Anda dapat menjalankan program interaktif di dalamnya, seperti komandan Midnight. Atau alihkan ke zshpenyelesaian sehingga Anda tidak akan kehilangan waktu pada konfigurasi Emacs.

Anda mendapatkan penyelesaian TAB dalam bash secara gratis. Tetapi yang lebih penting Anda mendapatkan kekuatan Readline penuh, seperti pencarian perintah inkremental atau prefiks . Untuk membuat konfigurasi ini lebih nyaman saya cek .inputrc , .bashrc , emacs .

Bagian penting dari .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! Ada dabbrev di Bash dari kata mana pun di ~/.bash_history):

set -o emacs

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

.emacs untuk membuat navigasi nyaman dalam istilah penyangga:

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

Karena perintah biasa apa pun C-x oyang tidak berfungsi dalam mode emulasi terminal, saya memperluas keymap dengan:

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

Perhatikan bahwa saya menggunakan superkunci sehingga term-raw-mapdan mungkin peta kunci lainnya tidak bertentangan dengan ikatan kunci saya. Untuk membuat superkunci dari Winkunci kiri saya menggunakan .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

Anda hanya harus mengingat 2 perintah: C-c C-j- untuk masuk ke mode pengeditan Emacs normal (untuk menyalin atau meng-grep dalam teks buffer), C-c C-k- untuk kembali ke mode emulasi terminal.

Pemilihan mouse dan Shift-Insertbekerja seperti pada xterm.


1
Terima kasih, ini emas! terutama .inputrcbagiannya!
cmantas

6

Seperti yang dikatakan Matli, ini bukanlah tugas yang mudah, karena bash dimulai dengan --noediting dan TAB terikat pada comint-dynamic-complete.

Seseorang mungkin bisa me-rebind TAB ke self-insert-command di shell-comand-hook dengan local-set-key dan membuat shell-mode tidak dimulai dengan --noediting oleh Mx customize-variable RET eksplisit-bash-args, tapi saya curiga itu tidak akan cocok dengan semua pengeditan lainnya.

Anda mungkin ingin mencoba term-mode, tetapi memiliki masalah lain, karena beberapa keybindings reguler lainnya diambil alih oleh term-mode.

EDIT: Dengan keybidings reguler lainnya yang diambil alih oleh term-mode, maksud saya semua kecuali Cc yang menjadi jalan keluar untuk dapat beralih buffer. Jadi, alih-alih Cx k untuk membunuh buffer, Anda harus Cc Cx k. Atau untuk beralih ke buffer lain 'Cc Cx o' atau 'Cc Cx 2'


self-insert-command bukan: ia menyisipkan karakter TAB daripada meneruskan tombol TAB melalui Bash.
Chris Conway

Saya tidak memiliki perintah term-mode dan saya tidak tahu di mana mendapatkannya.
Chris Conway


1

Saya menggunakan Prelude dan ketika saya menekan Meta + Tab itu selesai untuk saya.

Selain itu, Ctrl + i tampaknya melakukan hal yang sama.


0

Saya menggunakan mode helm. Ini memiliki fungsi ini (setelah tekan "TAB"): masukkan deskripsi gambar di sini


-2

Saya tidak mengklaim sebagai ahli emacs tetapi ini akan menyelesaikan masalah Anda:

Buat: ~ / .emacs

Tambahkan ke dalamnya:

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

Emacs mengambil alih shell sehingga pengaturan BASH tidak dapat dijalankan. Ini akan mengatur penyelesaian otomatis untuk EMACS itu sendiri.


Tidak, ini tidak menyelesaikan masalah. Ini hanya berfungsi pada perintah shell, bukan dalam mode shell. Dan selain itu, itu tidak memungkinkan penyelesaian cerdas yang diminta dalam pertanyaan (itu hanya akan menyelesaikan perintah dan nama file).
matli

1
shell-command-complete-mode melakukan penyelesaian nama file dan diaktifkan secara default. Saya ingin fitur penyelesaian Bash (yang dapat diperluas untuk menyertakan, misalnya, sub-perintah untuk apt-get dan svn).
Chris Conway
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.