次郎の貝塚

技術ブログのような何か

About | Profile | Diary | Application | Source Code | RSS

投稿日時 2026-06-17 23:19:28 +0900 | カテゴリー 技術

前の記事 React.js のアプリを素の HTML と JS で書きなおした | 次の記事

Doom Emacs を試している

最近、Emacs の設定ディストリビューションの 1 つの Doom Emacs を試しています。 使ってみるとこれが結構快適で、もうしばらく使い続けると思います。 ということで、Emacs の話をします。

なぜ今更 Emacs?

僕はもう10年くらいは Vim を使いつづけています。

開発環境は tmux + fish + vim/neovim という環境でここ数年は開発を続けていました。 で、最近 tmux を zellij に変更しようとしたのですが、そこで Vim のキーバインドと衝突して zellij の主要なキーが優先されるようになりました。

zellij の設定について調べて設定ファイルを調整しないといけないのですが、思えばターミナル上で複数のツールを使うとき毎回同じことに悩んでいることに気付きました。 ターミナル上で使うツールはすべて tmux や zellij のキーバインドと衝突しないように調整しないといけない。

さらに、設定ファイルの書式はツールによってそれぞれ異なったりします。 数年ぶりに開いた tmux.conf の設定は、自分で書いたはずなのにちんぷんかんぷんでした。 そして、それを今度は zellij 用に再設定しないといけないので、zellij の設定ファイルについて理解しないといけないです。 この設定ファイルの書式の違いも、割と手間です。

そこで「もしかしたら Emacs だとこの辺りの惱みって少ないのではないか?」と思い、触りはじめました。

VSCode で良いのでは?

VSCode に Vim 拡張を入れて使ったりもしていたのですが、 キーボードだけで全部の操作ができる感じではなくてやめてしまった。

Vim を使ってると基本的にマウスを触らずにあらゆる操作ができるので、 マウス操作の必要なエディタは手間に感じてしまう。

Spacemacs or Doom Emacs

実は数年前に一瞬だけ Spacemacs という設定ディストリビューションを試したことがあったのですが、 そのときはすぐ Vim に戻ってしまいました。 今回 Emacs を使うにあたって、以前触って諦めたものを再度採用するのはどうかと思い、別のディストリビューションの Doom Emacs を採用しました。 ChatGTP に聞いた感じでは、Doom Emacs の方が今は活発らしいです。(本当なのか?)

ただ、今のところ Doom Emacs で不便していないため、このまま使い続けると思います。

Doom Emacs の魅力

さて、ここからは tmux + fish + vim で長年開発を続けてきた人から見た Doom Emacs の魅力を語ります。 素の Emacs を触ったことがほぼ無いので、どこからが Doom Emacs 固有の機能なのか分からないです。 Doom Emacs の機能、Emacs 元々の機能が混在しているかと思いますが、ご容赦いただければと。

Vim Mode 標準搭載 (Evil)

Evil という拡張がデフォルトで有効になっていて、最初から Vim とほぼ同じ操作ができます。 僕は Vim の操作体系が大好きなので、これは外せません。

この Evil が非常に優秀で、他の Emacs プラグイン上の操作も Vim と同様に操作できます。 操作方法が統一されているため、とても快適です。

さらに、この Evil でのコマンドですが、途中まで入力したときに候補となるコマンドを表示してくれます。 たとえば、ノーマルモード中に Space f r と続けて入力すると、過去に開いたファイルを検索して開くことができます。 そこで、Space だけ入力するとその次に用意されているキーがサジェストされます。 f もそうですし、それ以外の Space トリガーのコマンド郡が全て表示される。

このサジェストを見ているだけで「へーこんなコマンドもあるんだ」と新しい発見が得られます。

M-x によるコマンドのインクリメンタルサーチ

M-x (Alt-x) というキーで、使用できるコマンド郡をインクリメンタルサーチできます。 これがなにげに凄い。

大量にプラグインを入れていると、使えるコマンドやショートカットキーが分からなかったり、忘れてしまうことがありますが、 M-x による検索はこれを解消します。

そして、この M-x、なんとショートカットキーも表示してくれます。 Emacs 触りたてでキーも全然暗記できていない僕には、このサポートはありがたい。

consult によるファイル検索

最初から有効になっていた consult という検索機能がとても便利でした。 peco や fzf みたいなインクリメンタルサーチ UI を提供する機能で、ファイル名の検索やファイル内の grep 検索などもできます。 consult-find, consult-grep, cunsult-ripgrep を頻繁に使ってます。

Magit (Git インタフェース)

僕は git を lazygit で操作していたのですが、Emacs では Magit という Emacs プラグインが主流です。 まだ使いはじめたばかりですが、便利です。 ブランチの切りなおし操作とかは lazygit のほうが楽な感じはしますが、それ以外はとくに不満ありません。 (これも僕がまだ Magit を使いこなせていないからかもしれませんが)

僕が特に気にいっているのは以下です。

eshell

Emacs Lisp で実装されたシェル。 反応速度は遅い感じがありますが、簡単なコマンド実行程度なら十分です。 ClaudeCode などの TUI を操作する場合は、後述の eat で操作しています。 「ターミナル操作はターミナルでやればいいじゃん」と思うので避けていたのですが、使ってみると結構便利です。

まず、ターミナルを終了してもバッファ上にターミナル上のログがそのまま残る。 この状態は普通にバッファ上にテキストがあるのと同じなので、コピーしたりできる。 ここでも Evil のノーマルモードが使えるので、Vim でテキストを操作するのと同じように扱える。

tmux もターミナル上を移動してログをコピーしたりできるのですが、 あくまでも Vim などの操作を模倣しているだけで、細かい振舞は異なります。

一方、zellij はログをエディタで開いてコピーさせるアプローチをとっています。 こちらのほうが Emacs の方法に近いですね。

次に現在開いているファイルの位置と同期される。 ようはファイルを開いた状態で eshell とかを開くと、そのファイルのある位置でシェルが起動する。 これが地味に便利で、リポジトリをまたがって移動したいときに 「とりあえず履歴からそのリポジトリのファイルを適当に開いてから eshell」とするだけで、目的の場所で端末を操作できます。

あと、ターミナルを閉じてもバッファ上は残っているので、バッファを開きなおしたらログを再び見られるのも地味に嬉しい。

eat

Emacs Lisp で実装されたターミナルエミュレータ。 ClaudeCode などの TUI を使う場合はこちらを使ってます。 eshell だと時間がかかりそうな、重い処理や複雑なことをやるときに使ってます。

ターミナルエミュレータとしては vterm or eat が良く比較されているようです。 vterm も良さそうだったのですが、使うために CMake が必要なのが嫌で見送りました。

ファイル操作と移動

標準で有効になっている dired が便利です。 ディレクトリ移動やファイル選択は、これか consult でやってます。 ファイルの場所が分かっているときは dired、あいまいなときは consult で検索と使いわけています。

あと treemacs も有効にしました。 ファイルエクスプローラをサイドバーで表示してくれるパッケージです。 使いそうと思って有効にしましたが、dired で十分な気がしてきている。 まぁ、たまにツリー上で見たくなるときがあるので、有効なままにしています。

何気に mkdir というコマンドも用意されてます。 さくっとディレクトリを作れます。 単純かつ便利。

org-mode

Emacs といえばこれ、という機能の 1 つだと思う。 まだ使いはじめたばかりで機能を全然把握できていないので、 Markdown の延長程度にしか使えていない。 メモはリモートにとる習慣があって、あんまり org-mode でメモを取る機会がないのも理由。

使いこなすとスケジュール管理とかもできるらしいが、そこには至っていない。

org-roam

org-mode の拡張で、メモを管理する機能。 メモファイル同士をリンクしたり、検索しやすくする機能を持っている。 これも有効にしたんだけれど、ローカルでメモを取る習慣がないせいで、あまり活用できていない。

出先でも読みたくなるかっちりしたメモは Google Docs で書いてるし、 このブログは Markdown で書いている。 基本的にメモはスマホでも読みたいんで、ローカルに閉じた状態でメモを取りたくない気持ちがある。 同じ理由で Obsidian を使ったときもすぐに止めてしまった。

Google Docs に同期するとか、GitHub のプライベートリポジトリでメモを管理するとか、いろいろやりようはあると思うが、 未だ答えが見付かっていない。 GitHub で管理してスマホから見る場合、GitHub アプリじゃなくてブラウザで見れば org-mode がレンダリングされるので、 リポジトリ管理が一番相性良さそうではある。 だけど、個人的なメモを GitHub で管理するのもなんかなぁ、と思っている。

scratch buffer

使い捨てのバッファ。 いつでも捨てられるメモ帳みたいなもの。

テキストファイルを作って残すほどではないものをさっとメモできる。

LSP

LSP に対応されているので、コード補完がバリバリ効きます。 それだけだと Vim の LSP と一緒なんですが、コード定義ジャンプがちょっと特殊です。

LSP が効くファイルの場合は普通に関数定義にジャンプします。 一方で LSP が効いてないファイルでもある程度探してくれるっぽいです。 同じプロジェクト内で、カーソルのあたっているキーワードを探して検索してジャンプできるようにしてくれる。 これが結構便利。

Emacs Lisp

Vim のプラグインも自作したことがあるのですが、個人的には Vimscript よりも Emacs Lisp の方が好き。 もともと、趣味で Clojure という Lisp 系言語に触っていたこともあり、Emacs Lisp の実装にもほとんど抵抗はありませんでした。

ちょっとしたスクリプト程度なら ChatGPT や Gemini に聞きながら作れるし、作った関数をすぐに実行して動作確認できるのも良い。 この記事を書いているときに、Markdown ファイルのファイル名の日付と、ヘッダーの date の更新を半自動化したくなって、それも Emacs Lisp で関数を作って解決したりしました。 それ以前はシェルスクリプトで date の埋めこみとリネームをやってました。 こういう簡易な処理を Emacs 内で完結させたくなるのも、Emacs ならではな体験な気がします。

ちなみに、作った関数は以下です。

(defun github-pages-page-set-date-header ()
  "Set a today to date of header."
  (save-excursion
    (goto-char (point-min))
    ;; 先頭から10行目の末尾の文字位置(ポイント)を取得
    (let ((end-point (save-excursion
                       (forward-line 10)
                       (point)))
          (today-str (format-time-string "%Y-%m-%d %H:%M:%S +0900")))
      ;; 先頭10行の範囲内で、行頭の "date:" を検索
      (while (re-search-forward "^date:\\(.*\\)$" end-point t)
        ;; "date: " の後ろに日付を埋め込む(既存の日付があれば置換する)
        (replace-match (concat "date: " today-str))))))

(defun github-pages-page-rename-file-to-today ()
  "Rename a current buffer to today file."

  ;; バッファがファイルに保存済みでない場合は異常終了させる
  (unless (and (buffer-file-name) (file-exists-p (buffer-file-name)))
    (user-error "このバッファはディスク上に保存されたファイルではありません"))

  (let* ((current-file (buffer-file-name))
         (dir (file-name-directory current-file))
         (file-name (file-name-nondirectory current-file))
         (today-str (format-time-string "%Y-%m-%d"))
         (new-file (file-name-concat dir (concat today-str (substring file-name 10)))))
    (unless (string= current-file new-file)
      (rename-file current-file new-file 1)
      (set-visited-file-name new-file)
      (set-buffer-modified-p nil)
      (kill-buffer (current-buffer))
      (find-file new-file)
      (message "ファイルを %s にリネームして開き直しました" (file-name-nondirectory new-file)))))

(defun github-pages-page-update-date ()
  "Update date to github pages posts."
  (interactive)
  (github-pages-page-rename-file-to-today)
  (github-pages-page-set-date-header))

config.el にベタ書きしています。 interactive も設定しているので、M-x で検索できます。 もしかしたら自分用のパッケージに切りだすかも。

また後日書く予定なのですが、Emacs パッケージを自作して公開したりもしました。 Emacs Lisp へ慣れるために作ったので、使われるとは思っていません。

init.el でサポートされているパッケージが多い

terraform と nim がサポートされてたのは正直驚いた。 僕が触りうる言語は全部サポートされてましたし、それ以外にも人気どころのパッケージはだいたいコメントアウトされて残されてました。 外すだけで有効にできます。

詰まったところ、改善したいところ

Git 操作

慣れもあると思うけれど、ブランチの切りなおしとかは lazygit の方がすぐに出来るので ついそっちで済ませてしまう。 Magit での操作に慣れていきたい。

SKK

WSL2 上で Emacs を起動しているのですが、IME が反応しません。 mozc とか入れて調整しようかと思ったのですが、ddskk で日本語入力することにしました。 SKK は以前使ったことがあったので、さくっと慣れることができました。 この記事も SKK 入力で書いてます。

なお、会社の PC では IME が反応してくれるので、WSL2 上で動かすときだけ SKK 入力をしています。 本当は IME に統一したいのですが、今は妥協しています。

外部コマンドに PATH を通す

nix でインストールした Emacs から nix 上のコマンドを呼びだそうとしてるのが悪いのか、 外部コマンドに PATH が通ってなくて躓きました。

packages.el に exec-path-from-shell を導入して解決しました。

(package! exec-path-from-shell :pin "dae820da35ad46234cbca31626ffb6da7928694a")

コマンドを覚える

M-x で検索できるとはいえ、頻繁に使う操作はやはり暗記したいところ。 初めて Vim を覚えようとしたときにチートシートを書いたように、 Emacs でもチートシートを書きはじめている。

このへんのコマンドを頻繁に使う。

コマンド 説明
doom sync -u パッケージの更新
SPC SPC ファイル検索
SPC f r 最近開いたファイルを検索
SPC . カレントディレクトリのファイルを検索
SPC p p プロジェクト検索
SPC g g magit
C-x d dired
C-x C-e カーソル位置の Emacs Lisp 式を評価
C-z 一時的に Evil Mode を OFF
M-: ミニバッファで Emacs Lisp を実行
M-! 外部コマンド呼び出し
g R バッファ全体の Emacs Lisp を読みこむ
M-x eshell eshell
M-x eat eat
M-x consult-ripgrep consult-ripgrep
M-x calculator 電卓を起動
M-x calendar カレンダー

その他

Vim では Vim 内でタブを複数起動して、それを行き来する使い方をよくしていたのだが、 Emacs ではタブをあまり使わないらしい(ChatGPT より)。

実際、使ってみると自然とそういう使い方をあんまりしなくなった。 ウィンドウ分割は使うけれど、タブは全然使わない。 タブを開いて残しておくのではなく、都度最近開いたファイルを検索して見つけるほうがずっと早いので。 多分、そういう使い方が多いんだと思う。

おわりに

今まで苦手意識のあった Emacs は、ただの食わず嫌いだったことが分かりました。 真面目に Emacs に向きあってみて実感しました。 もちろん、今のパッケージ管理の仕組みや Evil Mode があるおかげで、ですが。

今のところ、使っていて手触りがとても良いので、もうしばらく使ってみようと思います。 zellij + fish + neovim は今でも好きなので、完全移行するかは分かりません。 併用する形に落ちつく可能性もあります。

ちなみに、最近あまり技術的なことをプライベートの時間でやっていなかったのですが、 Emacs に触りはじめてから時間が増えました。

実は Neovim の方はほぼ満足する設定が整っており、年単位で設定を変更しない状態が続いていたんですよね。 今回 Emacs を使いはじめたことで、エディタ設定のチューニングという楽しみが得られた、そのおかげです。

普段と違うことをする新鮮さもありますし、なによりエディタを調整する時間って楽しいです。 設定調整の延長でコードを書いたりしています。 エディタが目的の一部に含まれているのかもしれません。

Emacs という歴史あるエディタから新しい風を受けつつも、 使いこんでいきたいと思います。

前の記事 React.js のアプリを素の HTML と JS で書きなおした | 次の記事