[Emacs] TrampでもLSPしたい
Emacs Advent Calendar 2023の25日目です。
長年Emacs使っているのにElispをまともに書けない私ですが
TrampでもLSPしたい、という気持ちはしばらく忘れて別の解決策でしのいでいました。
最近Macの仕様が変わったのか、上手く行かなくなってきたので、改めて向き合ってみようと思います。
🖥️ 環境
OS: macOS Sonoma 14.2
Emacs: 29.1
💡先に解決方法
'tramp-remote-path
にPATH通したいリモートマシン上のディレクトリを入れていく
(add-to-list 'tramp-remote-path "/home/xxxx/.cargo/bin")
✏️ まえがき
私は普段Macを使っているんですが
リモートワークなのもあって、Mac上ではたくさんのアプリケーションを立ち上げています。
- ブラウザ
- Gather
- Slack
- Emacs
- Skitch
- etc...
弊社ではGatherでカメラとマイクも常時繋ぎっぱなしで仕事しているので
CPUもGPUもメモリもわりといろいろ持っていかれます。
そうなるとMac上で開発しているとリソースが不足してきて、いろいろつらくなります。
なので、 1台別のLinuxマシンを用意し、普段はそのマシンにsshで繋いで開発 しています。
※これはDockerDesktop for Macが遅すぎてLinux上でdocker使ったほうが速くて快適だったのもあります。
ですが、 EmacsはGUI版を使いたいのでMac上で起動したい のです。
そこで、今までは sshfs
を使って、リモートマシン上のディレクトリをMac上にマウントし
Emacsで、 マウントしたディレクトリのファイルを編集する ことで対応していました。
❯ sshfs -o noappledouble,volname=xxx server:/home/xxx/workspace/xxx ~/workspace/server/xxx
この方法の利点としては LSPがMac上で起動できる
こと。
これに尽きます。
というか、 trampでsshxで開いたファイルだとLSPがエラーになって起動しない ことを解決する為にこうしていました。
正直逃げました。
🔥 問題発生
最近困ったことが起きました。
マウントしたディレクトリのファイルを開くとLSPがエラーを吐いて、
Emacs上からマウントしたディレクトリが全て見れなくなってしまいました。
そのファイルがあるディレクトリだけでなく、マウントしたディレクトリ全体が見れなくなる。
これはおそらく権限的な問題でNetworkVolumeが見れなくなっているのだろうと思い
いろいろ調査した結果、LSPで起動するプログラムに対して、
個別に Macのセキュリティ設定で Network Volumes
に対するアクセス権限を与えていくことで解決することがわかりました。
※問題ないのであれば Full Disk Access
を許可してもOK
Emacsを許可する例
ただし、この方法だと、言語毎にLSPのプログラムを個別に権限を与えていくことになり
めちゃくそしんどいです。
特にTypeScriptはいくつかのプロセスが上がるので、 nodeだけを許可してもダメで
nodeから起動される別プロセスのtsファイル毎 に権限を与えていく必要がありました。。。
しかもsymlinkを許可してもダメなので、 複数バージョンのnode.jsを使っている場合はバージョン毎に許可 していく必要があります。
これは耐えられない。
そこでやはり Trampに移行すること を決意しました。
🤔 Trampでの問題点
※つい最近まで lsp-mode
を使っていたんですが、lsp-mode
の恩恵をあまり受けていなかったので eglot
に移行しました。
Trampでファイルを開くと、eglotがLSPのプログラムを見つけられずに起動に失敗していました。
以下、TrampでRustのファイルを開いた時のeglotのログです。
...
[internal] Wed Dec 20 22:41:05 2023:
(:message "Running language server: /bin/sh -c stty raw > /dev/null; rust-analyzer")
[stderr] /bin/sh: 1: rust-analyzer: not found
[internal] Wed Dec 20 22:41:05 2023:
(:message "Connection state changed" :change "exited abnormally with code 127\n")
----------b---y---e---b---y---e----------
eshellを開いて本当に見つからないのか確認してみます。
/sshx:server:~/workspace/opensources/diesel-derive-enum/src $ which rust-analyzer
which: no rust-analyzer in (/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin)
見つからないし、PATHが少ないですね。
sshで直接入って確認してみます。
❯ which rust-analyzer
/home/xxxx/.cargo/bin/rust-analyzer
sshで入った時はPATHが通ってました。
'tramp-remote-path
にPATH通したいディレクトリを入れていく
💡解決方法 多分 zshenv
あたりでPATH通すことも出来る気がしたんですが、上手く行かなかったので
今回は 'tramp-remote-path
を使うことにしました。
Remote programs (TRAMP 2.6.0.29.1 User Manual)
私はleaf.elを使わせて頂いているので、こんな感じにしてみました。
(leaf tramp
:ensure t
:custom
(tramp-default-method . "sshx")
:init
(with-eval-after-load "tramp"
(add-to-list 'tramp-remote-path 'tramp-own-remote-path)
(add-to-list 'tramp-remote-path "/home/xxxx/.cargo/bin")
(add-to-list 'tramp-remote-path "/home/xxxx/.nvm/versions/node/v16.18.0/bin")
(add-to-list 'tramp-remote-path "/home/xxxx/.rbenv/shims")
)
)
'tramp-remote-path
に対してリモートマシン上のPATH通したい場所を1つずつ追加しています。
Rustで試してみる
先程と同じファイルを開いてみます。
eglotのログを見ると
[internal] Wed Dec 20 23:02:18 2023:
(:message "Running language server: /bin/sh -c stty raw > /dev/null; rust-analyzer")
[client-request] (id:1) Wed Dec 20 23:02:18 2023:
(:jsonrpc "2.0" :id 1 :method "initialize" :params
...
なんか起動してそうな予感ですね。
まとめ
- TrampでLSPしたいときは
'tramp-remote-path
にPATH通したい対象を入れていくとなんとかなる- ローカルで起動するよりは、やはりもっさりする(若干ラグがある)
- たまにエラーになる(おそらくリモートのLSPとの通信エラー)
- 相変わらずLispはよく分からない
- でもEmacsをやめられない
Discussion