2025-12-09

Emacsのコンテキストメニューに「Close」を追加する

AndroidのEmacsを使っていると、なぜかふとバッファを長押ししてコンテキストメニューを表示し、そこから「Close」を選びたくなるときがあるんです。もちろんそこには「Close」なんて無いので「あっ……」と思うわけですが。

バッファを閉じたいならツールバーに×ボタンがありますし、メニューバーも出しているので「File」→「Close」を選ぶという手もあります。でもなぜかコンテキストメニューの中から「Close」を選びたくなるときがあるのです。

それがなぜかはよく分からないのですが、とりあえず追加してみます。

私はcontext-menu-mode(Emacs 28で追加)を有効にしていて、以前書いたようにタッチの長押しでそれが表示されるようにしているのでコンテキストメニュー自体はすでに表示できます。

長押しでコンテキストメニューを開く(Emacs 30) | Misohena Blog

なので後は単純にコンテキストメニューに項目を追加すればいいだけです。

コンテキストメニューに項目を追加するにはcontext-menu-functions変数に関数を追加すれば良いのでした。

(defun my-close-buffer-and-window ()
  (interactive)
  (let ((window (selected-window)))
    (when (kill-buffer)
      ;; すぐに削除するとタッチ処理部分のwith-selected-windowが削除
      ;; したウィンドウを選択しようとしてエラーになる。
      ;; なのでタイマーで遅延する。
      (run-with-timer
       0 nil
       (lambda ()
         (when (and (window-live-p window) (window-deletable-p window))
           (delete-window window)))))))

(defun my-context-menu-function (menu _click)
  (define-key menu [my-close-buffer-and-window]
              '(menu-item "Close" my-close-buffer-and-window))
  menu)

(add-hook 'context-menu-functions 'my-context-menu-function 0)

望んでいる動作というのはおそらくただバッファを閉じるだけでなくウィンドウも閉じたいのだと思います。

ウィンドウを閉じるだけなら以前モードラインをドラッグしてウィンドウを消せるようにしましたが、それだとバッファが残ってしまいますからね。

モードラインをドラッグしてウィンドウを消す | Misohena Blog

それに今はサイドウィンドウで仮想キーボードを表示しているので、うまく狙ったウィンドウが消せないことがあるのです。

最初はプレフィックス付きでquit-windowを呼び出そうと思いました。私はデスクトップ上では表示専用のバッファをよくC-u qで閉じることがあります。ポップアップしたウィンドウをバッファも含めて消してくれるので。

でもこの「Close」にquit-windowを割り当ててみてもどうもしっくりこないのです。quit-windowは前の状態を復元しようとしますが必ずウィンドウを閉じてくれませんからね。

なので上のコードでは素直にバッファとウィンドウを同時に削除することにしました。

ただし、長押しで無理矢理コンテキストメニューを出した弊害で、そこからウィンドウを削除するとどこからともなくwindowがらみのエラーが発生します。調べてみると、touch-screen.elの中でwith-selected-windowを使用しているところがあって、それが私が削除したウィンドウを選択しようとしてエラーが出るみたいなのです。なのでkill-buffer-and-windowは使えません。仕方が無いのでタイマーで後から削除するようにしました。

コンテキストメニューに「Close」を追加したところ
図1: コンテキストメニューに「Close」を追加したところ