ESM アジャイル事業部 開発者ブログ

永和システムマネジメント アジャイル事業部の開発者ブログです。

Emacs でだって Docker で開発したい!

こんにちは。wat-aro です。

Docker 環境で開発する際に VSCode の Remote Container はとても便利ですね。
でも今まで Emacs で開発してきた人は VSCode ではなく Emacs を使いたいはずです。
ここでは僕が Emacs + Docker 環境でどのように開発しているかを紹介します。

docker コマンド

まずは docker コマンドを使えなくてはなりません。
Emacs 使いのみなさんはターミナルでなく Emacs から docker コマンドを叩きたいですよね。
そんなときは docker.el です。
https://github.com/Silex/docker.el
docker image コマンドや docker compose コマンドが Emacs から実行できます。
docker compose up で立ち上げたコンテナをリスタートすることなどもできます。
まずはこれでコンテナを立ち上げましょう。

docker コンテナに繋ぐ

コンテナを立ち上げたら Emacs からコンテナに入り、コンテナ内でファイルを編集します。
開発用のコンテナ内に Emacs を入れてしまう人もいるかもしれませんが、余計な依存を開発環境にも入れたくないためその方法は取りません。
また、Emacs からコンテナにアクセスせず、ホストの Emacs から docker や docker compose コマンドを叩けばいいのかと思われるかもしれません。
僕も始めは docker compose コマンドを叩く方法を検討しました。
しかし Emacs のパッケージの多くが外部コマンドにファイルの情報を渡す際にローカルの絶対パスを渡します。
コンテナにローカルの絶対パスを渡してもそのファイルがないため動きません。
プロジェクトルートからの相対パスを渡すことも可能かもしれませんが、flycheck などを見るとファイルパスを渡している部分がマクロになっていて変更が厳しそうだったため、Emacs からコンテナ内のファイルを編集する方法を取るようになりました。
仮にプロジェクトルートからの相対パスを渡せたとしても、様々なパッケージを入れるたびに相対パスを渡すために試行錯語するくらいならコンテナ内に入ってファイルを編集するほうが楽です。

Emacs からコンテナに入る方法です。
普段 Emacs を使っていて、ssh 先に繋ぐ際には tramp.el を使いますよね。
tramp.el ではホスト側の Emacs をそのまま使えるため、使い慣れた設定やパッケージを使えます。
ただし、外部コマンドを実行する場合はリモート先にその外部コマンドがインストールされている必要があります。

コンテナに繋ぐ際には docker-tramp.el が便利です。
https://github.com/emacs-pe/docker-tramp.el

C-x C-f /docker:user@container:/path/to/file  

でコンテナに繋ぐことができます。
毎回接続先を入力するのは大変なため、emacs-helm-trampcounsel-tramp を使うと便利です。
僕は counsel-tramp を使っていますが、コンテナを起動した状態で counsel-tramp を実行すると接続できるコンテナが補完候補として表示されます。
counsel-tramp でコンテナに入った時点では / ディレクトリにいるため Dockerfile に指定した WORKDIR に移動する必要があります。。

lsp-mode

昨今はLSPを使って開発する人が多いと思います。
僕は lsp-mode を使っていますが、 docker-tramp.el でコンテナに入っている際にローカルのままの設定の lsp-mode では Language Server が立ち上がりません。

https://emacs-lsp.github.io/lsp-mode/page/remote/#sample-configuration
emacs-lsp のドキュメントを見ると :new-connectionlsp-tramp-connection を使い :remote?t 設定するとよいようです。
これを参考に solargraph の設定を書くとこのようになります。

(with-eval-after-load "lsp-solargraph"  
  (lsp-register-client  
    (make-lsp-client :new-connection (lsp-tramp-connection '("solargraph" "stdio"))  
      :major-modes '(ruby-mode enh-ruby-mode)  
      :priority 1  
      :remote? t  
      :multi-root t  
      :server-id 'ruby-ls-remote  
      :initialized-fn (lambda (workspace)  
                        (with-lsp-workspace workspace  
                          (lsp--set-configuration  
                            (lsp-configuration-section "solargraph")))))))  

lsp-mode の設定にこれを追加することでリモートの solargraph を使うことができるようになります。

まとめ

僕の Emacs + Docker での設定を紹介しました。
よいプログラミングライフを!