Windows & Emacs Tramp でオートセーブに失敗する時の対処法

シェアする

どうも、ながやすです。

以前に、Emacs Tramp について下記の記事を公開しました。

Emacs Tramp でリモート上のファイルを直接編集する方法|Windowsにも対応
ローカルの使いなれたEmacs 環境からリモート上のファイルを直接編集する Tramp 機能についての説明です。Windowsの場合の注意点、ポート番号がデフォルト22以外の場合の使い方、root権限での開き方など、一通りの使い方を説明しています

このWindows環境で使っていて、リモートのファイル編集中にやたらに重くなることが頻発したので、その原因と解決方法を調べました。もし同じ問題で悩んでいる方、参考にしてみてください。
解決法の他に、参考情報としてオートセーブについての簡単な説明と、Tramp の lisp コードのリーディングを載せています。ご参考までに。

スポンサーリンク

動作が重くなるときに起きていること

Windows環境で Tramp モードを使っていて、ときどきやたらに動作が重くなることがありました。*Message* バッファを確認してみると下記のエラーが出ていました。
(*Message* バッファの表示方法は C-x b *Message* です)

Auto-saving...
Auto-saving hoge.txt: Opening output file: Invalid argument,
c:/Users/username/AppData/Local/Temp/#!plink:user_name@hostname:!home!user_name!hoge.txt#

……オートセーブでこけていました。ファイル名に「:」があるのがダメです。GNU/Linuxでは「:」があっても大丈夫ですが、Windows はファイル名に「:」を使えません

解決法

さまざまな解決法がありそうですが、ここでは簡単にできる2つの方法を示します。

オートセーブを無効化してしまう

えいやっとオートセーブを無効にしてしまうことで回避可能です。emacs初期化ファイル(.emacs, .d.emacs/init.el)に下記の1行を追加するだけです。これでオートセーブが無効化されます。「オートセーブなんか使ったことねえぜ!」という方はこれが一番お手軽かなと思います。

(setq auto-save-default nil)

Tramp モード時のみ有効なオートセーブファイルの作成場所を明示的に指定する

もう少しスマートな方法が tramp-auto-save-directory 変数を設定する方法です。変数名の通り、Trampモード時のみオートセーブファイルの場所を指定するものです。これを指定すると、tramp がいい感じにオートセーブファイルの名前を変更してくれるようになります。これで問題が解決されます。

(setq tramp-auto-save-directory temporary-file-directory)

【参考】そもそもオートセーブとは?

Emacs にはオートセーブという機能があります。編集中のファイルを自動で保存する機能です。このオートセーブファイルは編集中のファイルと同じディレクトリに作られます。例えば/home/user_name/hoge.txt を編集しているときは /home/user_name/#hoge.txt# というファイル名で自動で作成されます。
(デフォルトでは、ファイル内容を変更して「30秒経過」もしくは「300回タイプ」すれば作成されます)
ファイルを編集後にセーブすれば、勝手にオートセーブファイルは削除されます。

このオートセーブファイルですが、編集中のファイルと同じ場所に作成されると散らばってうっとしいので、下記のように特定のディレクトリにまとめるような設定をよくします

;; オートセーブファイルの作成場所をシステムの Temp ディレクトリに変更する.
(setq auto-save-file-name-transforms
      `((".*" ,temporary-file-directory t)))

temporary-file-directory はシステムのテンポラリディレクトリです。 Windows7 ですと c:/Users/username/AppData/Local/Temp/ です(隠しフォルダ設定されているかもしれません)。

【参考】頑張ってどこで問題が起こっているのかlispをみてみる

問題は、オートセーブファイル名に「:」があることです。よくみると、「/」は「!」に変更してくれています。きっとどこかにファイル名を変更する処理があるはずです。そこに「:」を「!」に変更する処理を追加すればいけそうです。

c:/Users/username/AppData/Local/Temp/#!plink:user_name@hostname:!home!user_name!hoge.txt#

ということで、Tramp の Lisp コードを眺めてみます。 Emacs を C:/Programs/emacs/ にインストールした場合は下記の場所にあります。

C:/Programs/emacs/share/emacs/25.1/lisp/net/tramp.el.gz

.gz で固められていますので、msys2などで解凍しましょう。

$ gzip.exe -d tramp.el.gz

すると tramp.el が取得できます。

その中をガンバって読んでいくと、下記のコードが見つかります。 auto saving がどうのこうのとコメントがありますね。
「if (null tramp-auto-save-directory)」だと「buffer-file-name」をそのママ使い、null でない場合は「_ / : | [ ] 」の文字をを置換するようです。

(setq tramp-auto-save-directory temporary-file-directory)

;;; Auto saving to a special directory:
(defvar auto-save-file-name-transforms)

(defun tramp-handle-make-auto-save-file-name ()
  "Like `make-auto-save-file-name' for Tramp files.
Returns a file name in `tramp-auto-save-directory' for autosaving
this file, if that variable is non-nil."
  (when (stringp tramp-auto-save-directory)
    (setq tramp-auto-save-directory
          (expand-file-name tramp-auto-save-directory)))
  ;; Create directory.
  (unless (or (null tramp-auto-save-directory)
              (file-exists-p tramp-auto-save-directory))
    (make-directory tramp-auto-save-directory t))

  (let ((system-type 'not-windows)
        (auto-save-file-name-transforms
         (if (and (null tramp-auto-save-directory)
                  (boundp 'auto-save-file-name-transforms))
             (symbol-value 'auto-save-file-name-transforms)))
        (buffer-file-name
         (if (null tramp-auto-save-directory)    ;; ☆ ココで置換するかどうか判定!
             buffer-file-name
           (expand-file-name
            (tramp-subst-strs-in-string          ;; ☆ 置換処理!
             '(("_" . "|")
               ("/" . "_a")
               (":" . "_b")
               ("|" . "__")
               ("[" . "_l")
               ("]" . "_r"))
             (buffer-file-name))
            tramp-auto-save-directory))))
    ;; Run plain `make-auto-save-file-name'.  There might be an advice when
    ;; it is not a magic file name operation (since Emacs 22).
    ;; We must deactivate it temporarily.
    (if (not (ad-is-active 'make-auto-save-file-name))
        (tramp-run-real-handler 'make-auto-save-file-name nil)
      ;; else
      (ad-deactivate 'make-auto-save-file-name)
      (prog1
          (tramp-run-real-handler 'make-auto-save-file-name nil)
        (ad-activate 'make-auto-save-file-name)))))

ここまでくれは、 tramp-auto-save-directory でググってみれば、何かしら情報を得られるはずです。例えば下記のサイトがヒットします。

“set the variable tramp-auto-save-directory to direct all auto saves to that location.”と記載がありました。わざわざtramp.el を変更する必要はなさそうですね。

まとめ

この記事では、 Windows で Tramp を使うときにオートセーブで動作が重くなる時の解決法を書きました。特に難しい設定は必要なく、init.el に1つ設定を追加すればいいだけです。ついでに、オートセーブの簡単な説明とtrampのlispコードも見てみました。

Windows で Emacs Tramp を使っているときに動作が重くなるという方、ご参考ください!

@shima_tetsuo さんに教えていただきました!ありがとうございます!