@kyanny's blog

流行はつねに前進していく。そして、精神の偽りの自由が絶えずせり上がっていく - ロマン・ロラン

Emacs 内で対話的なシェルを動かす方法の分類

違いを理解していなかったので整理した。

以下の三種類に大別できる。

  1. M-x shell
  2. M-x term
    • ansi-term, multi-term, vterm などの亜種がある
  3. M-x eshell

M-x shell

  • 38.2 Interactive Subshell
  • Emacs のバッファと I/O が繋がったサブシェルを立ち上げる
  • メジャーモードは shell-mode
  • 大半の Emacs キーバインドが使える
    • そのかわり端末エミュレーター上でシェルを使う際の一般的なキーバインドは使えない(C-r とか)
    • shell-mode 内でシェルの履歴を操作するキーバインド 38.5.1 Shell History Ring
  • なんだかんだで使い勝手が良いが vim とか less を実行すると終了できずに慌てたりする
    • q だけではサブシェルにキーが送られないので q RET とすればよい

M-x term

  • Emacs 内で動く端末エミュレーター 38.8 Emacs Terminal Emulator
    • 他に ansi-term, multi-term, vterm などの亜種があるが、どれも「Emacs 内で動く端末エミュレーター」というコンセプトは同じ
  • Emacs のバッファと I/O が繋がったサブシェルを立ち上げる
  • メジャーモードは term-mode
  • term-mode は内部に二つのモードを持つ
    • The terminal emulator uses Term mode, which has two input modes. In line mode, Term basically acts like Shell mode (see Shell Mode). In char mode, each character is sent directly to the subshell, as terminal input; the sole exception is the terminal escape character, which by default is C-c (see Term Mode).
    • In term-mode (which multi-term uses) there are two input modes:
      • この StackOverflow コメントで知った
    • line mode
      • shell-mode と同じような挙動をする
    • char mode
      • 大半のキーは直接端末エミュレーターを介してシェルに送られる
        • 大半の Emacs キーバインドは使えない(C-p はカーソルの移動ではなくコマンド履歴を遡る)
    • char mode から line mode へ切り替え
      • C-c C-j(M-x term-line-mode)
    • line mode から char mode へ切り替え
      • C-c C-k (M-x term-char-mode)
  • line mode と char mode を使い分ければ shell-mode を内包するので万能
    • うっかり line mode のとき vimless を実行しても C-c C-k で char mode に切り替えて q で終了したりできる

M-x eshell

余談

  • 歴史的には ansi-termterm の改良版という位置づけだったが、現在は実質的に大差ないらしい
  • Spacemacs 内で一度でも multi-term を立ち上げると C-c C-j (term-line-mode) のキーバインドが消えてしまい、その後 termansi-term を立ち上げ直しても消えたままになってしまった。元に戻すには Emacs の再起動が必要。
  • term-mode の char mode で C-h が効かないのがストレス
    • なんとなく bind-key* との兼ね合いな予感。もう少し調べる
    • ビンゴ。しかし bind-key にしたら、 ivy を有効化した C-x C-f (find-file) バッファ内で C-h が効かなくなってしまった
      • ivy が C-x C-fcounsel-find-file に置き換えるが、このモードに C-h が効いてない
      • (bind-key "C-h" 'counsel-up-directory counsel-find-file-map) を足すことで解決した
        • helm 使ってたとき似たような設定をしていた名残があった (bind-key "C-h" nil helm-map)
(defun dotspacemacs/user-config ()
  (bind-key "C-h" 'backward-delete-char)
  ;; (bind-key* "C-h" 'backward-delete-char) ;; NG
  (bind-key "C-h" 'counsel-up-directory counsel-find-file-map)

  (add-hook
   'term-mode-hook
   (lambda ()
     (bind-key "C-h" 'term-send-backspace term-raw-map) ;; 上記のように bind-key と組み合わせると OK。 bind-key* と組み合わせると NG。
     (bind-key "C-y" 'term-paste term-raw-map)
     ))
  )