Don't Repeat Yourself

Don't Repeat Yourself (DRY) is a principle of software development aimed at reducing repetition of all kinds. -- wikipedia

Zedの設定をちゃんとやってみる

ここ数回の記事を見返してみると、書評ばかりしていてコード書いてるのか…?となったので、久しぶりにちゃんとコード(設定ファイル)を書く記事を書こうと思いました。いえ、コードは書いてるんですが、まとまった成果になっていないか、あまり新しいことをやっていないだけです。

Zed

Zed(ゼッド)というエディタが最近話題ですね。私も実は最近会社のPC上のNeovimが壊れてしまって、直している時間がないので一旦Zedを使ってその場しのぎをしています[*1]VS Codeを使わなかったのは、あんまりVimバインディングが強くないとわかっていたからです。あとで説明しますが、ZedはVimバインディングがまずまずよくできています。

zed.dev

ZedはRust製のエディタで、今のところ非常に高速に動作するのがウリです。プラグインを入れだすと遅くなるんじゃない?という話はあるとは思うんですが、プラグインを全然入れていない状態のZedとVS Codeを比較しても、VS Codeの方はちょっともっさりしていると感じることはある一方で、Zedはもっさりすることはまずないですね。

Rust製のツールはZed以外にも、たとえばCLIやTUIツールなどで人気ですが、特徴としてとにかく高速に動作し、画面の止まる感覚がないというのはあると思います。既存ツールをPythonやGoなどの言語からRustに置き換えたようなものを比較でいくつか使ってみていますが、圧倒的に画面が止まることが少ないです。こういうところで、やっぱりRustって速いんだなあと感じることが多いですね。

とってもどうでもいい話ですが、個人的には「ゼッド」と言われるとZeddの方が思い浮かんでしまって、開くたびにZeddを聴いてしまいます。チクタク。

www.youtube.com

Zedの設定方法と今回目標

settings.jsonとkeymap.json

設定ファイルの記述方法についてはドキュメントに詳しくまとまっているので、そちらを参照すると良いでしょう。

zed.dev

settings.jsonはZed全体の設定を記述するファイルです。たとえばテーマやフォント、vimモードをオンにするかといった設定を行うことができます。

keymap.jsonはキーマッピングの設定を記述するファイルです。私は今回は自分のNeovimに近い挙動をさせようとしていますが、たとえばIntelliJVS Codeなど使い慣れたエディタがあり、そのキーマップを引き継ぎたい場合は、サンプルとなるkeymap.jsonが用意されているようなので、自分の手元に落として使うとよいと思います。

snippet

この記事では一旦スキップしようと思いますが、いわゆる「スニペット」も設定可能なようです。要するに、よく書くコードのパターンをカスタマイズして登録しておくことができるというものです。throw Error(...)のような頻出イディオムを登録しておくことができます。最近はCopilotに完全に委ねていますが、あると便利なのかもしれません。

設定の目標

目標は手元のNeovimとほとんど同じキーマッピングであらゆる操作をできるようにすることです。テーマ設定をはじめとする普段の開発状況も可能な限り揃えておきたいと思います。

私の設定

今回は下記をやってみました。

  • Vimモードをオン。
  • いくつかの必要な設定をオン。
    • inlay hints
  • テーマをcatppuccinに変更。
  • フォントの調整。
    • サイズを変更する。
    • JetBrainsMono Nerd Font (JetBrainsMono NF)にする。
    • リガチャは有効にする。
  • キーマッピング

最終形態はこんな感じです。

セットアップ完了後

完成させると次のような設定ファイルになりました。まずはsettings.jsonからです。

{
  "theme": "Catppuccin Mocha - No Italics",
  "ui_font_size": 14,
  "buffer_font_family": "JetBrainsMono Nerd Font",
  "buffer_font_size": 14,
  "vim_mode": true,
  "relative_line_numbers": true,
  "scrollbar": {
    "show": "never"
  },
  "buffer_line_height": {
    "custom": 1.5
  },
  "inlay_hints": {
    "enabled": true,
    "show_type_hints": true,
    "show_parameter_hints": true,
    "show_other_hints": true,
    "edit_debounce_ms": 700,
    "scroll_debounce_ms": 50
  },
  "lsp": {
    "rust-analyzer": {
      "check": {
        "extraArgs": ["--target-dir", "target/ra"]
      },
      "initialization_options": {
        "check": {
          "command": "check"
        }
      }
    }
  },
  "terminal": {
    "alternate_scroll": "off",
    "blinking": "terminal_controlled",
    "copy_on_select": true,
    "font_family": "JetBrainsMono Nerd Font",
    "toolbar": {
      "title": true
    },
    "line_height": {
      "custom": 1.5
    },
    "working_directory": "current_project_directory"
  }
}

vim_mode、フォント、テーマ

最初のUIに関連する設定についてまずは見ていきます。主にはテーマ、フォント、そしてvim_modeに関する設定を施しています。

{
  "theme": "Catppuccin Mocha - No Italics",
  "ui_font_size": 16,
  "buffer_font_family": "JetBrainsMono Nerd Font",
  "buffer_font_size": 14,
  "vim_mode": true,
  "relative_line_numbers": true,
  "scrollbar": {
    "show": "never"
  },
  "buffer_line_height": {
    "custom": 1.5
  },
...

themeではCatppuccin Mochaというテーマを指定しています。私はNeovimやVS Code、ターミナルなどが軒並み全部Catppuccinを使っています。かわいくて好きです。

デフォルトでOne Darkは入っていそうですが、Catppuccinは追加でいろいろ設定が必要でした。Extensionとして配布されているCatpuccinをインストールしておく必要があります。ちなみにですが、settings.jsonに設定内容があれば何もせずとも読み取ってくれそうではあるものの、たとえばsettings.jsonで何も設定していない初回のセットアップの際などは、下記のようにテーマを手で選んでやる必要があります。

Select Themeでテーマを選ぶ。裏でsettings.jsonが修正されます。

フォントにはJetBrainsMono Nerd Fontをいつも使っています。リガチャありで使っていますが、普通の文字の方にはあまり飾りが多くなくて好きかなあという印象です。

"vim_mode": trueVimモードを利用できます。最初はこれだけsettings.jsonに書いて、あとでいろいろ調整しても遅くないと思います。Vimモードですが、定義ジャンプなど必要な機能はだいたい標準のキーマップで実行できるように設定されています。後で若干キーマップは調整を加えます。

relative_line_numbersで相対的な行番号表示に切り替えられます。これもNeovim側でオンにしていたのでこちらも合わせてオンにしています。

inlay hints

inlay hints利用シーンとして代表的なのは、変数の型情報の表示でしょう。近年のプログラミング言語は、型を明示せずとも型を推論して解決できるようになってきている関係で、変数に対する型の情報をコードに落とす必要がなくなってきています。しかし一方で、コードに型情報が落ちてこないと言うことは、ぱっと見でその変数にどのような型付けがされているかわかりにくくなることもあります。代表的なのはTypeScriptやRustでしょうか。私は普段Rustを使いますが、Rustも比較的激し目にいろいろ型付けする言語で、最終的にどのような型が生成されているのかをコードから追うのが難しい時があります。

inlay hintsを利用すると、変数の横に型情報が表示されるようになります。他には関数のパラメータ名を表示してくれたり、メソッドチェーンが連なる横に今どのような型付けになっているかを表示してくれたりします。メソッドチェーンの多いRustでは手放せない機能でもあります。

inlay hints自体は実はLSPに定義があります。したがって、使っているLanguage Serverがinlay hintsに対応している必要があります。Zedはその情報を受け取って表示しているに過ぎません。

私はinlay hintsそれ自体は普通に便利だと思っているので、NeovimでもVS CodeでもJet Brainsでもオンにしていた派です。したがって下記のように設定しました。基本的に全部オンにしています。

  "inlay_hints": {
    "enabled": true,
    "show_type_hints": true,
    "show_parameter_hints": true,
    "show_other_hints": true,
    "edit_debounce_ms": 700,
    "scroll_debounce_ms": 50
  },

注意点ですが、Zedのinlay hintsの表示はVS Codeのそれと比べると結構ノイジーです。TypeScriptなどを書いていると、型情報が100文字くらい平気でいくことがありますが、現状のZedの実装ではそれをすべて表示してしまいます。一応Issueは立てられていますが、まだ進む気配はないですね。試してはいないんですが、Language Server側に設定があるようであればそちらで表示数を削る設定を有効化しなければならないかもしれません。

rust-analyzer

Zedとは直接関係がないんですが、lspというセクションにLSPごとの設定も仕込めるようです。Zedそれ自体は正直RustかGoを書く時くらいしか使わないかなと思っているのですが、一旦rust-analyzerの設定だけ仕込んであります。

  "lsp": {
    "rust-analyzer": {
      "check": {
        "extraArgs": ["--target-dir", "target/ra"]
      }
    }
  },

Rustacean.nvimなんかではcargo clippyをrust-analyzerの解析で走らせてたりするみたいなんですが、そうしたい場合には下記のようにclippyを設定します。私は重くて嫌なのでcargo checkを走らせています。たしかデフォルトはcargo checkだったと思ってるんですが、念の為設定を上書きしています。extraArgs--target-dirを設定してやると、rust-analyzer専用の成果物を置くディレクトリを用意させられます。これをやっておくと、rust-analyzerの解析とターミナル等で走らせたcargo runなどがバッティングして、ビルドがブロックされてしまう問題を回避することができます。

      "initialization_options": {
        "check": {
          "command": "check"
        }
      }

キーバインディング

さてキーバインディングですが、下記のように設定しています。ひとつひとつつまんでいると記事を書くのが大変そうなので、要所だけ押さえておきます。

[
  {
    "context": "Editor && (vim_mode == normal || vim_mode == visual) && !VimWaiting && !menu",
    "bindings": {
      "ctrl-h": ["workspace::ActivatePaneInDirection", "Left"],
      "ctrl-l": ["workspace::ActivatePaneInDirection", "Right"],
      "ctrl-k": ["workspace::ActivatePaneInDirection", "Up"],
      "ctrl-j": ["workspace::ActivatePaneInDirection", "Down"],
      "space c": "pane::CloseActiveItem",
      "space b c": "pane::CloseInactiveItems",
      "space b C": "pane::CloseAllItems",
      "space e": "workspace::ToggleLeftDock",
      "space f f": "file_finder::Toggle",
      "space l a": "editor::ToggleCodeActions",
      "space l d": "diagnostics::Deploy",
      "space l f": "editor::Format",
      "space l s": "outline::Toggle",
      "space l r": "editor::Rename",
      "space o": "tab_switcher::Toggle",
      "space t h": "workspace::OpenInTerminal",
      "space t f": "workspace::NewCenterTerminal",
      "space /": "editor::ToggleComments",
      "g d v": "editor::GoToDefinitionSplit",
      "g r": "editor::FindAllReferences",
      ">": "vim::Indent"
    }
  },
  {
    "context": "Terminal",
    "bindings": {
      "ctrl-k": ["workspace::ActivatePaneInDirection", "Up"],
      "ctrl-j": ["workspace::ActivatePaneInDirection", "Down"]
    }
  }
]

キーバインディングの設定は、二つのセクションからなるオブジェクトで構成されています。

  • context: コンテクスト。Zedのどの場所にどの状態で差し掛かったらそのキーバーインディングを使用するか、でしょうか。contextは幅広く指定できるようで、Editorはコードを記述する場所、TerminalはZed内で起動されるターミナルにいる際、他は左に表示されるファイルエクスプローラー的な場所にいる場面、などほとんどすべてのZed上の要素を指定できそうです。このページで指定できるcontextの一覧を見ることができます→Key bindings - Zed
  • bindings: そのコンテクストで有効化するキーマッピングを定義します。

IntelliJVimバインディングだと正直コートエディタ内くらいまでしかまともに動かせない印象ですが、Zedの場合ほとんどすべてに対してキーマッピングを指定できます。非常に柔軟性が高く、手元のNeovimとほとんど同じ動作をさせるところまでたどり着くことができました。いわゆるリーダーキーを使った操作(私の場合は、<leader> = spaceでした)も定義することができて割と満足です。

Vimプラグインをたくさん入れている方の場合は、設定の自由度に物足りなさが多少あるかもしれませんが、私はそんなに詳しくないのもあり、あまりプラグインを多くは入れていない状態でNeovimを使っていたので一旦このキーマッピングで満足しています。

設定してみての感想

JetBrains系やVS Codeよりかは自由度高く設定できる印象です。おまけに今のところは非常に高速に動作するので満足しました。

欲しいのはFloating Windowなんですがこれは難しいんですかね。現状はターミナルを立ち上げ後、Shift + Escを押すと擬似的にそれっぽいことはできるんですが、Floating Window欲しいなという気持ちはあります。

git周りのサポートはまだこれから発展途上という感じではあるんですが、そもそも普段lazygitを使っているのであんまり問題ありませんでした。ターミナルを立ち上げ後、↑で示した方法でlazygitを操作すればほとんどNeovimのころと変わらない体験が得られました。

本当は仕事でも使いたいんですが、Kotlinはあんまりいい感じに動いてくれないのでまだIntelliJが手放せそうです。GoやTypeScriptの開発には積極的に利用したいんですが、TypeScriptでの開発時にはinlay hintsを切らないと型情報でコードエディタが爆発します。

それでは、Neovimの壊れてしまった部分を直す旅に出てきます。

参考資料

*1:日中の8割〜9割はIntelliJで過ごしていますが、時々Neovimで開いた方が早かったり一瞬使いたくなったりするケースがあります。ただ、そうした小時間のために直していると時間がもったいないので…といった理由です。