Spacemacs導入

Emacsの拡張ディストリビューション Spacemacsは、アグレッシブにLispパッケージを追加して高機能なエディタをセットアップします。

  • emacs風のエディタではなく、emacs
    • ターミナルでも動作する。Android上のTermuxのほか、SSHなどのリモート接続を介した構成もとれる
  • Language Server支援を手軽にセットアップできる(developブランチ)
  • emacs + vi + 独自のキーバインドに拡張されている。vi同様、モードがある
    • viの基礎的なキーバインドをエミュレートしている(evil)
      • 置換などのexコマンドが動作する
      • z m, z rなどのコード折りたたみが動作する
    • emacsのキーバインドも生きている
  • 独自バインドは、スペースキー(SPC)を起点とする複数ストロークでコマンドを呼び出す。
    • SPC bはバッファ操作、SPC wはウインドウ、SPC pはプロジェクト、SPC fはファイルなど系統分類されている
    • マルチストロークの途中まで入力すると候補ヘルプ表示
    • M-xSPC SPCで実行可能
  • 目的別のLayerを提供する

といった特長があります。

極力カスタマイズせず、emacsとviを共用する目的に最適です。
emacsもviもカスタムのキーバインドやスクリプトで際限なく拡張でき、その方向で自分にとっての最適を目指せるエディタではあります。
一方、カスタマイズは標準環境の操作に困難をもたらすため、spacemacsのようにコミュニティ標準指向のセットアップにも意義があります。

ディスク容量は200MBほど消費しますが、VSCodeのようにコード支援機能を持つエディタがサーバーなどのCLI環境でも動作します。
起動時に簡易プロファイルが表示され、“298 packages loaded in 1.790s"といったスピードです。emacsの動作が重いというのは主流のCPUがx86やPentiumあたりの時代の話で、過去のイメージに過ぎないようです(当時はたしかに起動待ちが長かった印象です)。

セットアップ手順は以下のような流れです。詳細は、 GitHubのプロジェクトトップに記載されています。

  1. ディストリビューションのパッケージからEmacsをインストールする(v24.4以降)
  2. git clone https://github.com/syl20bnr/spacemacs ~/.emacs.d
  3. cd ~/.emacs.d; git checkout develop(develop版を使う場合)
  4. emacsを起動。初回起動時、Spacemacsのセットアップ開始

~/.emacs.d/はemacsのカスタマイズコードを置くPathで、Spacemacsは拡張部分を一式提供します。Spacemacsの設定は~/.spacemacs(初回起動時に生成される)を編集します。

develop版のアップグレード

develop版は更新配布がとくになく、適宜リポジトリを更新します。間違えてorigin masterをpullするとリポジトリが混ざって崩壊するため注意します。

$ cd .emacs.d
$ git pull origin develop

develop版の動作は安定しており、masterでは利用できない主要機能もあるため、developを採用するケースは多いでしょう。

パッケージ追加

Spacemacsは検証済のパッケージ集を"layer"という単位で提供しています。.spacemacsdotspacemacs-configuration-layersにlayerを追加することで、Spacemacs再起動時にインストール、バイトコンパイルされます。また、アンインストールについても設定と連動して自動実行されるため、パッケージを直接操作する必要がありません。
代表的なレイヤーについては、デフォルトの.spacemacsにコメント記載されていますが、他にも存在しています。

emacsにはMELPAというリポジトリもあり、Layerは存在していないがMELPAにあるパッケージは、dotspacemacs-additional-packagesに記載すると再起動時にインストールされます。たとえば日本語入力機能として代表的なSKKは、 ddskkパッケージをMELPAからインストールできます。

キーバインドの確認

編集するファイルに応じてモードが切り替わるため、 各レイヤーのドキュメントを参照するのが手軽です。

C-h bを実行するとキーバインドを検索できます。採用パッケージが多いため、機能の由来を特定する際に活用できます。

LSP(Language Server Protocol)のセットアップ

LSPは、IDE(統合開発環境)のコード編集支援機能を分離して標準化したプロトコルです。メジャーな例ではVSCodeのバックエンドは主にLSPであり、SpacemacsをLSP接続するとコード支援がVSCodeと同等になります。
Spacemacsは LSPレイヤーを有効にすることでコーディング支援のバックエンドに各種Language Serverを利用できるようになります。

なお、emacs29で本体に同根されたeglotとtree-sitterについては、 emacs29以降のコード支援機能で解説しています。

レイヤーは~/.spacemacsファイルのdotspacemacs-configuration-layersにレイヤー名を書くと有効になります。以下の例は関係する箇所の抜粋です。

   dotspacemacs-configuration-layers
   '(
     lsp
     (ruby :variables ruby-backend 'lsp)
     (rust :variables rust-backend 'lsp)
     (typescript :variables typescript-backend 'lsp)

この例は、lspモードの有効化と、ruby, rust, typescriptモードのバックエンド設定をしています。

先ほどのLSPのドキュメントには、lspを有効にすると各言語のバックエンドのデフォルトはlspになる、という説明があるのですが、このrubyの例では、デフォルトのrubyという設定では有効にならず、(ruby :variables ruby-backend 'lsp)と明示的にバックエンドを指定するとLSPが動作します。

他の言語も、 Layer一覧にリストされているドキュメントに導入方法が書かれています。基本的には同様の設定で動作するでしょう。

なお、LSPは一般的に言語別のLanguage Serverプログラムがあり、そのセットアップは別途必要です。各言語向けのコマンドをインストールするだけです。
複数の実装がある言語は、PATHに存在するコマンドを探して実行するため、利用したいものだけインストールすると手軽です。

  • rubyは、 Solargraphをgemからインストールしておきます。
  • rustは、 rust-analyzerが動作します。PATHのとおった場所に relaesesから取得したバイナリを置きます。rust-analyzerというコマンド名で実行できる必要があります
  • TypeScriptは、バックエンドの選択肢がいくつかあります。deno lspもlsp-modeで動作します。 typescript-language-serverを使う場合には、npm install -gにより、tsserverコマンドが実行可能になっている必要があります

起動しない場合のデバッグ

LSPサーバーのコマンドにPATHが通っていない場合などに、起動失敗メッセージがモードラインに出ることがあります。
SPC b bでバッファ一覧を表示すると、*lsp-log**rust-analyzer::stderr*(rustの例)などのバッファがあり、コンソールログを分析に使えます。

サブ機能の設定

lsp-modeはサブ機能の集合体になっています。
A guide on disabling/enabling lsp-mode featuresにスクリーンショット付きの解説があり、ON/OFFのための設定変数もリストされています。

Spacemacsでは、.spacemacsuser-configに記載しておくと各設定が有効になります。
さらに詳細なオプションは、 lsp-uiプロジェクトにリファレンスがあります。

lsp-modeの配色

デフォルトテーマは主要なモードをサポートしていますが、未設定の配色もあります。 たとえば、LSPのポップアップはテーマ配色とは独立しており、custom-set-facesに設定することで変更できます。

(custom-set-faces
 '(lsp-ui-doc-background ((t :background "#ff00ff")))
 '(lsp-ui-doc-header ((t :foreground "#ff8000" :background "#00ff00")))
)

配色カスタマイズについては、 Emacsの色設定を確認するで補足しています。

flycheckの無効化

flycheckが動くと不定のタイミングでフリーズする挙動になりがちです。
LSP layerにはflycheckが組込まれていますが、ON/OFFを切り替える設定がありません。そこで、user-init()に以下の変数を指定することで、自動チェックを停止できます。

(setq flycheck-check-syntax-automatically nil)

ファイル操作

卑近なファイル操作は、 projectileによるプロジェクト操作を基礎としています。
projectileはgitなどのリポジトリを自動識別するほか、.projectileという空ファイルを置いたディレクトリをプロジェクトトップと見なします。

SPC p SPCでカレントプロジェクト内ファイル、SPC p pでプロジェクト切り替えできます。プロジェクトの追加/削除は、M-x projectile-add-known-projectM-x projectile-remove-known-projectでインタラクティブに編集できます。

ivyのファイル検索インターフェースは counsel を通じて行うようです。

  • M-x bookmark-set (C-x r m)
    • 現在のディレクトリをブックマークにセット。C-x C-fでディレクトリに移動した上で使用。Trampで開いたリモートファイルもブックマーク可能
  • M-x counsel-bookmark (SPC f b)
    • ブックマークをインクリメンタルサーチして開く
  • M-x bookmark-list (C-x r l)
    • 登録済のブックマーク一覧を表示
  • M-x neotree-toggle (SPC f t)
    • ディレクトリツリーを表示/非表示きりかえ
  • M-x list-buffers (SPC b b)
    • 編集バッファ切り替え。起動直後でもファイルの編集履歴新着になっている
  • M-x search-project (SPC /)
    • 全文検索。 ripgrepなどをインストールして、rg系コマンドを使える状態にしておく必要がある。サブディレクトリ以下も再帰的に探す
  • M-x counsel-projectile (SPC p SPC)
    • ファイル名の部分一致による絞り込み検索。サブディレクトリも再帰的に探す
  • M-x counsel-projectile-switch-project (SPC p p)
    • プロジェクトを指定したうえで、ファイル名の部分一致による絞り込み検索

Tramp-mode向け接続設定

emacsにはTramp-modeという標準機能があり、ssh経由でリモートファイルを編集できます。先ほどの全文検索なども期待どおりリモートリポジトリを対象に動作します。

標準機能であるためspacemacs側では追加設定は不要ですが、リモートホストのssh設定が素朴な構成になっていないと接続に失敗します。
tmuxの自動起動などシェル起動時の追加設定がある構成では、$TERMdumbで接続されたケースを除外する必要があります。~/.bashrcの記述例は以下のようになります。

if [ $TERM = "dumb" ]; then
  # Tramp-mode接続向けの設定。プロンプトも簡素な方が安全
  export PS1="$ "
else
  # 通常のSSH接続向けの設定
fi

なお、リモートファイル編集時にShell-layerの機能でターミナルを開くとリモートシェルが起動しますが、ターミナルの種類がeshell(SPC a t s e)やshell(SPC a t s i)である必要があります。
vtermやansi-termなどを起動するとローカルシェルに接続する挙動になります。

クリップボード

xclipboardレイヤーを導入することでemacsのバッファ操作とOSのクリップボードを連動できます。 なお、Linuxの場合はxselをインストールしておく必要があります。

カラーテーマ

カラーテーマは theme-mega-packレイヤーを有効にすると沢山入り、切り替えて利用できます。
または、 draculaのように、 色設定custom-set-variablesに追加するだけの配色設定もあり、コンパクトでカスタマイズも容易です。 Spacemacs-themeに変数のリファレンスがあります。

なお、.spacemacsにはhookタイミングが多数ありますが、テーマ変数spacemacs-theme-custom-colorsについてはLispパッケージのload前に設定する必要があり、user-initに定義すると動作します。

ターミナルの配色と透過

ターミナル内のemacsは環境変数TERMの影響を受けます。export TERM=xterm-256colorは比較的無難な設定です。
カラーコードはHTMLカラーと同じ24ビットであるため、テーマを完全に再現するためにはTrueColorで動作するターミナルが必要でしょう。ターミナルの色数不足の場合、変色します。

またSpacemacsには背景透過の設定があるのですが、スタンドアロンで起動した場合の設定で、ターミナル内で起動するemacsの場合には動作していません。

テーマの背景色がつくとターミナルの透過設定が無効になり表示が荒れるケースがあります。spacemacsのテーマ配色のうちbg1プロパティにnilを設定するとターミナル背景色そのものになります。

または、 Emacs: disable theme background color in terminalで紹介されている以下のフック処理を、.spacemacsのdotspacemasc/user-initに記載するとテーマの背景色をオフにできます。

(defun set-background-for-terminal (&optional frame)
  (or frame (setq frame (selected-frame)))
  "unsets the background color in terminal mode"
  (unless (display-graphic-p frame)
    (set-face-background 'default "unspecified-bg" frame)))
(add-hook 'after-make-frame-functions 'set-background-for-terminal)
(add-hook 'window-setup-hook 'set-background-for-terminal)

この場合の背景色は、ターミナルの設定で変更することになります。

さらにテーマ外の配色の影響を受けるようであれば、以下のように背景設定をX環境のみに限定する手があります。

(when (equal window-system 'x)
  (set-face-background 'default "#000000")
  (set-frame-parameter nil 'alpha 75))

emacsの透過設定は前景も透過しますが、ターミナル(emacs -nw)は背景色のみ透過します。

中馬崇尋
Chuma Takahiro