Emacsのセッション情報と履歴を保存する
Emacsを再起動すると通常は、ファイル自体のデータは保存されても、どのファイルを開いていたのかや、ウィンドウやフレームやバッファの状態、さまざまな履歴などの変数といったデータは消えてしまいます。そのため、Emacsを再起動した直後には不便な思いをすることも多くあります。というか、消えるのが当たり前で不便だと感じることさえなくなってるかもしれません。
こうした不便を解消するために使えるのが、Emacsに標準でついてくるdesktopライブラリーとsavehistライブラリーです。
このdesktopとsavehistについては、GNU Emacs Manual 29.1(日本語版)の45 Saving Emacs Sessions(日本語版)でdesktopを中心に紹介されています。
このうち、desktopでは開いているファイルや、ウィンドウやフレームやバッファの位置、変数の値などを保存できます。一方、savehistでは、変数の値を保存します。なので、変数の値だけを保存したい場合は、savehistを使い、開いているファイルやウィンドウ、フレームなどの状態を保存したい場合はdesktopを使うといいでしょう。ただしdesktopで保存されるファイルやウィンドウなどの情報は時々クリアする必要が生じるため、そうした場面も含めて変数の値をきちんと保存したい場合はdesktopとsavehistを併用した方がよさそうです。
desktopとsavehistを有効にする
desktopとsavehistを有効にするには、カスタム変数設定画面(日本語版)から設定して保存する方法と、init.elなどの初期化ファイルに直接記述する方法があります。私は、初期化ファイルに直接記述した方がわかりやすくて便利だと思っています。
初期化ファイルでdesktopとsavehistを有効にする
次の記述を追加すると、desktop-save-modeとsavehist-modeをカスタム変数として設定します。
(custom-set-variables
'(desktop-save-mode nil)
'(savehist-mode nil)
)
あるいは、Emacs 29.1から登場したsetoptを使った次の記述でも同じ結果になります。
(setopt desktop-save-mode t)
(setopt savehist-mode t)
カスタム変数を使わずに、関数desktop-save-modeとsavehist-modeを使う記述もあります。
(desktop-save-mode 1)
(savehist-mode 1)
desktopとsavehistを無効にする
Emacsを使っている途中でdesktopとsavehistを無効にする場合は、M-x
からdesktop-save-mode
やsavehist-mode
を実行するのがかんたんでしょう。無効にしたあともう一度同じコマンドを実行すると、desktopやsavehistが有効になります。
またカスタム変数設定画面を使い、M-x
customize-variable
からdesktop-save-mode
やsavehist-mode
を編集する方法もあります。
この方法は、状態を確認しながら設定できるメリットがありそうです。
すべての履歴変数を保存する
こうしたdesktopとsavehistの設定をして使っていたところ、ちょっと不満を覚えるようになりました。実はdesktopやsavehistが登場する前にsessionというパッケージを使って変数の保存をしていました。そのsessionとくれべて、desktopやsavehistでは限られた変数しか保存してくれなかったのです。ググると同じ不満を持っていた人がいて、鮮やかな解決方法を示していました。desktop-globals-to-saveにすべての履歴系変数を突っ込むです。個人的には、desktopの変数であるdesktop-globals-to-save
よりもsavehistの変数savehist-additional-variables
を使ったほうが良いと考え、次の処理を試してみました。
(setopt savehist-additional-variables (apropos-internal "-\\(\\(history\\)\\|\\(ring\\)\\)\\'" 'boundp))
ただし、これでも保存されない履歴変数があります。調べてみたら、あとからロードされたライブラリーの履歴変数が保存されていませんでした。そこで、上記の処理をmy-set-sevehist-additional-variables
という名前の関数にし、ファイルをロードしたあとに実行されるフックafter-load-functions
に追加するようにしました。
(defun my-set-sevehist-additional-variables (&optional file)
(ignore file)
(setopt savehist-additional-variables (apropos-internal "-\\(\\(history\\)\\|\\(ring\\)\\)\\'" 'boundp)))
(add-hook 'after-load-functions 'my-set-sevehist-additional-variables)
after-load-functions
は引数として引数1つ(file)が必要なので、関数に引数を設定します。ignoreは何もしない関数で、引数fileに対して何もしないことを明示しています。
また、savehistのsavehist-minibuffer-history-variables
やsavehist-ignored-variables
、desktopを有効にした場合のdesktop-globals-to-save
やdesktop-locals-to-save
に含まれる変数を二重に保存することは無駄だし有害な場合もありそうなので、savehist-additional-variables
からは排除することにしました。この辺りになると、EmacsLispの本格的なリスト処理が必要になってきますね。
(defun my-set-savehist-additional-variables (&optional file)
(let (histvars othervars)
(ignore file)
(setq histvars (apropos-internal "-\\(\\(history\\)\\|\\(ring\\)\\)\\'" 'boundp))
(setq othervars
(append othervars
(when desktop-save-mode
(append
desktop-globals-to-save
desktop-locals-to-save
))
savehist-minibuffer-history-variables
savehist-ignored-variables
))
(dolist (ovar othervars)
(setq histvars (delete ovar histvars)))
(setopt savehist-additional-variables histvars)))
(add-hook 'after-load-functions 'my-set-sevehist-additional-variables)
まとめ
desktopとsavehistを有効にすると、Emacsを終了した場合でも様々な情報を保存できて便利です。savehist-additional-variables
などの変数を適切に設定するとさらに便利になります。