Emacsのセッション情報と履歴を保存する

# Emacsのセッション情報と履歴を保存する

Emacsを再起動すると通常は、ファイル自体のデータは保存されても、どのファイルを開いていたのかや、ウィンドウやフレームやバッファの状態、さまざまな履歴などの変数といったデータは消えてしまいます。そのため、Emacsを再起動した直後には不便な思いをすることも多くあります。というか、消えるのが当たり前で不便だと感じることさえなくなってるかもしれません。

こうした不便を解消するために使えるのが、Emacsに標準でついてくるdesktopライブラリーとsavehistライブラリーです。

このdesktopとsavehistについては、[GNU Emacs Manual 29.1](https://www.gnu.org/software/emacs/manual/html_mono/emacs.html)（[日本語版](https://ayatakesi.github.io/emacs/29.1/emacs-ja.html)）の[45 Saving Emacs Sessions](https://www.gnu.org/software/emacs/manual/html_mono/emacs.html#Saving-Emacs-Sessions)（[日本語版](https://ayatakesi.github.io/emacs/29.1/emacs-ja.html#Saving-Emacs-Sessions)）でdesktopを中心に紹介されています。

このうち、desktopでは開いているファイルや、ウィンドウやフレームやバッファの位置、変数の値などを保存できます。一方、savehistでは、変数の値を保存します。なので、変数の値だけを保存したい場合は、savehistを使い、開いているファイルやウィンドウ、フレームなどの状態を保存したい場合はdesktopを使うといいでしょう。ただしdesktopで保存されるファイルやウィンドウなどの情報は時々クリアする必要が生じるため、そうした場面も含めて変数の値をきちんと保存したい場合はdesktopとsavehistを併用した方がよさそうです。

## desktopとsavehistを有効にする
desktopとsavehistを有効にするには、[カスタム変数設定画面](https://www.gnu.org/software/emacs/manual/html_mono/emacs.html#Easy-Customization)（[日本語版](https://ayatakesi.github.io/emacs/29.1/emacs-ja.html#Easy-Customization)）から設定して保存する方法と、init.elなどの初期化ファイルに直接記述する方法があります。私は、初期化ファイルに直接記述した方がわかりやすくて便利だと思っています。

### 初期化ファイルでdesktopとsavehistを有効にする


次の記述を追加すると、desktop-save-modeとsavehist-modeをカスタム変数として設定します。

```:初期化ファイルに追加(1)
(custom-set-variables
 '(desktop-save-mode nil)
 '(savehist-mode nil)
)
```

あるいは、Emacs 29.1から登場したsetoptを使った次の記述でも同じ結果になります。

```:初期化ファイルに追加(2)
(setopt desktop-save-mode t)
(setopt savehist-mode t)
```

カスタム変数を使わずに、関数desktop-save-modeとsavehist-modeを使う記述もあります。

```:初期化ファイルに追加(3)
(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にすべての履歴系変数を突っ込む](https://www.robario.com/2014/12/11)です。個人的には、desktopの変数である``desktop-globals-to-save``よりもsavehistの変数``savehist-additional-variables``を使ったほうが良いと考え、次の処理を試してみました。

```:savehist-additional-variablesの設定:第1版
(setopt savehist-additional-variables (apropos-internal "-\\(\\(history\\)\\|\\(ring\\)\\)\\'" 'boundp))
```

ただし、これでも保存されない履歴変数があります。調べてみたら、あとからロードされたライブラリーの履歴変数が保存されていませんでした。そこで、上記の処理を`my-set-sevehist-additional-variables`という名前の関数にし、ファイルをロードしたあとに実行されるフック`after-load-functions`に追加するようにしました。

```:savehist-additional-variablesの設定:第2版
(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の本格的なリスト処理が必要になってきますね。

```:savehist-additional-variablesの設定:第3版
(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``などの変数を適切に設定するとさらに便利になります。