ZedでPyreflyを拡張なしで使う:有効化と型エラーを表示する設定

ZedでPyreflyを拡張なしで使う:有効化と型エラーを表示する設定

Zedに組み込まれたPyrefly言語サーバーを有効にする設定、Zedがpyreflyを探す順序、型エラーを表示するpyrefly.tomlとtypeCheckingModeの設定を、Zed 1.28.1とPyrefly 1.4.1で確かめ、エディタの画面とあわせて整理します。
2026.11.13

どうも!オペ部の西村祐二です!

Zed で Python を書くとき、型チェックや定義ジャンプを担う言語サーバーはデフォルトで basedpyright です。Meta の型チェッカー Pyrefly を使いたい場合、これまでは Pyrefly の拡張機能を入れる必要がありました。2026年10月28日リリースの Zed 1.26.2 で Pyrefly が組み込みの言語サーバーとして追加され、設定を足すだけで使えるようになりました。

ただし、pyrefly.toml などの設定ファイルがないプロジェクトでは、有効にしただけでは型の不一致が診断として返ってきません。その対処も含めて、2026年11月13日時点の最新版である Zed 1.28.1 と Pyrefly 1.4.1 で確かめた結果を、エディタの画面とあわせて整理します。

今回やること

Pyrefly は型チェッカー兼言語サーバーで、README では高速な型チェックを特徴に挙げています。コマンドラインで pyrefly check を使っているプロジェクトなら、エディタでも同じ型チェッカーの結果を見られます。

この記事で分かること:

  • Zed で basedpyright の代わりに Pyrefly を使う設定
  • Zed が pyrefly を探す順序(プロジェクトの .venv、PATH、GitHub Releases)
  • 型エラーを表示するための pyrefly.toml と typeCheckingMode の設定

Pyrefly と basedpyright の速度や検出内容の比較は扱いません。

前提条件

動作環境

  • macOS 26.7.2(Apple Silicon)
  • Zed 1.28.1(2026年11月11日リリース。組み込みの Pyrefly が追加された 1.26.2 より後のバージョン)
  • Pyrefly 1.4.1(Zed が GitHub Releases から取得したもの)、1.3.0(プロジェクトの .venv に入れたもの)、1.3.2(PATH 上に置いたもの)。どの pyrefly が起動したか区別できるよう、バージョンを変えています
  • uv 0.13.6(.venv の作成と pyrefly の導入に使用)

確認した範囲

設定方法

やることは次の2つです。

  1. languages.Python.language_servers に "pyrefly" を足す
  2. 型エラーまで表示するなら、プロジェクトに pyrefly.toml を置くか、Zed の設定で typeCheckingMode を default にする

1. language_servers に pyrefly を追加する

Python の言語サーバーは languages.Python.language_servers で選びます。特定のプロジェクトだけで切り替えるならプロジェクト直下の .zed/settings.json に、すべてのプロジェクトで使うならユーザー設定(~/.config/zed/settings.json)に書きます。

.zed/settings.json
{
  "languages": {
    "Python": {
      "language_servers": ["pyrefly", "!basedpyright", "..."]
    }
  }
}

それぞれの要素の意味は次のとおりです。

要素 意味
"pyrefly" Pyrefly を有効にする
"!basedpyright" デフォルトの basedpyright を無効にする
"..." ほかにデフォルトで有効な言語サーバー(Ruff など)をそのまま使う

Zed のドキュメントによると、Pyrefly、ty、Pyright、PyLSP はデフォルトでは無効なので "..." には含まれず、language_servers に名前を書いたときだけ有効になります。

初めて開くプロジェクトでは信頼を求めるダイアログが出て、「Trust and Continue」を選ぶまで .zed/settings.json は読み込まれません(詳しくはつまずきポイントで説明します)。

設定を書いたプロジェクトを開き、ログに出た言語サーバーを、.zed/settings.json がないプロジェクトの場合と比べました。macOS のログは ~/Library/Logs/Zed/Zed.log にあり、pyrefly で絞り込むと starting language server process. binary path: ... の行から、起動したバイナリが分かります。

プロジェクトの設定 起動した言語サーバー
.zed/settings.json なし Ruff、basedpyright
上の .zed/settings.json Ruff、Pyrefly

basedpyright が起動しなくなり、Ruff はそのまま残りました。フォーマットとリントは Ruff、型チェックや定義ジャンプは Pyrefly という分担になります。pyrefly を別途インストールしていなくても、Zed が自動で取得します。

2. Zed がどの pyrefly を起動するか

Pyrefly を有効にすると、Zed は起動する pyrefly を下の図の順に探します。図は、Zed 1.28.1 のソースコード(PyreflyLspAdapter)を読んで自作した概念図です。公式のものではありません。

自作した概念図。Zed 1.28.1がpyreflyを探す順序を示す。Pythonツールチェーン(.venv/bin/pyrefly)にあればそれを起動し、なければPATHのpyreflyを探し、それもなければGitHub Releasesから最新版をSHA-256照合付きで取得してlanguages/pyrefly/に保存し、pyrefly lspとして起動する。

  1. Zed が選んだ Python ツールチェーン(プロジェクトの .venv など)の中の pyrefly
  2. PATH 上の pyrefly
  3. どちらもなければ、GitHub Releases から取得した最新版の pyrefly(ダウンロード時に SHA-256 を照合します。保存先は Zed のデータフォルダで、macOS では ~/Library/Application Support/Zed/languages/pyrefly/)

3つとも実際に動かして確かめました。用意した状態は次のとおりです。

  • .venv にも PATH にも pyrefly がないプロジェクト
  • uv venv で作った .venv に、uv pip install pyrefly==1.3.0 で pyrefly を入れたプロジェクト
  • uv tool install pyrefly==1.3.2 で入れた pyrefly の場所を PATH に含めて Zed を起動した場合

次のログは、Zed のログから pyrefly の主な行を抜き出したものです。日付、ログレベル、working directory を省き、パスは <data-dir> などに置き換えています。# の行は説明のために足しました。

# .venv にも PATH にも pyrefly がない場合
10:55:11 [github_releases] downloading https://github.com/meta/pyrefly/releases/download/1.4.1/pyrefly-macos-arm64.tar.gz into "<data-dir>/languages/pyrefly/pyrefly-1.4.1"
10:55:16 [lsp] starting language server process. binary path: "<data-dir>/languages/pyrefly/pyrefly-1.4.1/pyrefly", args: ["lsp"]

# .venv/bin/pyrefly がある場合
10:57:25 [language] found user-installed language server for pyrefly. path: "<project>/.venv/bin/pyrefly", arguments: ["lsp"]
10:57:25 [lsp] starting language server process. binary path: "<project>/.venv/bin/pyrefly", args: ["lsp"]

# PATH 上に pyrefly がある場合
10:57:59 [language] found user-installed language server for pyrefly. path: "<PATH 上のフォルダ>/pyrefly", arguments: ["lsp"]
10:57:59 [lsp] starting language server process. binary path: "<PATH 上のフォルダ>/pyrefly", args: ["lsp"]

.venv か PATH に pyrefly がある場合、Zed はダウンロードせずに、見つけた pyrefly をそのまま起動しています。PATH 上にある場合は、同じ pyrefly が起動直後に1回起動し直していました(抜粋では省いています)。

.venv と PATH の両方に pyrefly がある状態も1回試しました。最初に PATH 上の pyrefly(1.3.2)が起動し、直後に停止されて、代わりに .venv/bin/pyrefly(1.3.0)が起動しました。最終的に動いていたのは .venv 側です。上の 1〜3 は、最終的に使われる pyrefly の優先順位にあたります。

なお、参考リンクに挙げた PR #44262 の説明には PyPI から取得するとありますが、1.28.1 のコードと実際の動作では GitHub Releases から取得していました。

3. pyrefly.toml を置いて型エラーを表示する

次の main.py を開いて、エディタの表示と、Pyrefly が Zed に返した診断を確認しました。4行目と5行目が型の誤り、6行目が未定義の名前、7行目が存在しないモジュールの import です。

main.py
def add(a: int, b: int) -> int:
    return a + b

total: int = add(1, "2")
print(total.upper())
prnt(total)
import not_installed_pkg

Pyrefly の設定ファイル(pyrefly.toml など)を置かない状態では、波線が出たのは6行目と7行目だけでした。4行目と5行目には何も出ていません。

設定ファイルなしのプロジェクトでmain.pyを開いたZedの画面。6行目のprntと7行目のnot_installed_pkgにだけ波線が出ていて、4行目と5行目には出ていない。ステータスバーはエラー3件、警告1件。

画面の波線とステータスバーの件数には、Ruff の診断も含まれます。Pyrefly が返した診断だけを通信の記録から抜き出すと、次の3件でした。

main.py diagnostics (3):
  6:1 [unknown-name] Could not find name `prnt`
  7:8 [missing-import] Cannot find module `not_installed_pkg`
  7:8 [unused-import] Import `not_installed_pkg` may be unused

未定義の名前(unknown-name)のほか、7行目の import については、モジュールが見つからないエラー(missing-import)と未使用のヒント(unused-import)が出ています。一方、4行目の、引数の型が合わない誤り(bad-argument-type)と、5行目の、存在しない属性の参照(missing-attribute)は出ませんでした。

ステータスバーのエラー3件は、Pyrefly の unknown-name と missing-import に Ruff の F821(未定義の名前)を足したもので、警告1件は Ruff の F401(未使用の import)です。Pyrefly の unused-import はヒントとして返っていて、この件数には入っていません。

この動きは、Pyrefly のドキュメントにある typeCheckingMode の説明と一致します。デフォルトの auto では、Pyrefly の設定ファイルがなければ近くにある mypy や Pyright の設定を読み込んで使い、それも見つからなければ確度の高いエラーだけを出す basic プリセットで動きます。今回のサンプルには mypy や Pyright の設定もないため、basic の範囲の3件だけになったと考えられます。

プロジェクト直下に pyrefly.toml を置くと、4行目と5行目にも波線が出ました。中身は Zed のドキュメントにある最小の例です。

pyrefly.toml
# Check all Python files in your project
project-includes = ["."]

pyrefly.tomlを置いたプロジェクトでmain.pyを開いたZedの画面。4行目から7行目に波線が出ていて、4行目の文字列にマウスを重ねるとPyreflyのbad-argument-typeのメッセージがポップアップに表示されている。ステータスバーはエラー5件、警告1件。

上の画面は、4行目の "2" にマウスを重ねたところです。Pyrefly の bad-argument-type がポップアップに出ています。Pyrefly が返した診断は、bad-argument-type と missing-attribute が加わって5件になっていました。ステータスバーのエラー5件は、Pyrefly のエラー4件に Ruff の F821 を足したものです。

Project Diagnostics パネル(cmd-shift-m)を開くと、診断ごとに Pyrefly と Ruff のどちらが出したものか、ルール名とあわせて確認できます。

pyrefly.tomlを置いた状態のProject Diagnosticsパネル。4行目にPyreflyのbad-argument-type、5行目にPyreflyのmissing-attribute、6行目にPyreflyのunknown-nameとRuffのF821、7行目にPyreflyのmissing-importとRuffのF401が並んでいる。

Pyrefly のドキュメントでは [tool.pyrefly] を書いた pyproject.toml も設定ファイルとして扱うとされています。こちらは Zed を介さず、pyrefly 1.4.1 に LSP で直接つないで試し、pyrefly.toml と同じく bad-argument-type と missing-attribute が返ることを確認しました。

4. 設定ファイルを置かずに型エラーを表示する

リポジトリに pyrefly.toml を足せない場合や、個人の Zed 設定だけで済ませたい場合は、ユーザー設定の lsp で typeCheckingMode を default にします。

Pyrefly 1.4.1 のドキュメントが説明しているのは、LSP の initializationOptions で設定を送る方法です。VS Code 以外のエディタから送るキーは、VS Code の設定名から先頭の python. を外したもの(pyrefly.typeCheckingMode)とされています。Zed では lsp.pyrefly.initialization_options がこれにあたります。

~/.config/zed/settings.json
{
  "lsp": {
    "pyrefly": {
      "initialization_options": {
        "pyrefly": { "typeCheckingMode": "default" }
      }
    }
  }
}

この設定で開くと、Pyrefly の診断は pyrefly.toml を置いたときと同じ5件になり、エディタでも4行目と5行目に波線が出ました(画面は、つまずきポイントに載せた比較画像の下側です)。

initialization_options の代わりに settings に書いても、同じ結果でした。その場合は、次のように python の下に書きます。Pyrefly は python という名前のセクションの設定を Zed に問い合わせ、Zed は settings 内の python の値を返すためです。書くのはどちらか一方で足ります。

~/.config/zed/settings.json
{
  "lsp": {
    "pyrefly": {
      "settings": {
        "python": {
          "pyrefly": { "typeCheckingMode": "default" }
        }
      }
    }
  }
}

Pyrefly のドキュメントによると、pyrefly.toml や pyproject.toml の [tool.pyrefly] が適用されるファイルでは、typeCheckingMode よりもそちらの設定が優先されます。pyrefly.toml を置いたプロジェクトに、すべてのエラーを止める typeCheckingMode: "off" を送っても、bad-argument-type と missing-attribute は返ってきました。これは pyrefly 1.4.1 に LSP で直接つないで確かめた結果で、Zed の設定経由では試していません。

設定のポイント

  1. リポジトリに設定を残せるなら pyrefly.toml を置く

    • Zed のドキュメントも設定ファイルを推奨しています。pyrefly check を手元や CI で実行したときも、同じ pyrefly.toml が読まれます(バージョンのそろえ方はポイント 3 で説明します)。mypy や Pyright の設定がある場合は、pyrefly init で移行できると README に書かれています
  2. 個人の設定だけで済ませるなら typeCheckingMode を使う

    • リポジトリに手を入れずに済みますが、型エラーが出るのは設定を書いた自分の Zed だけです。pyrefly.toml があるプロジェクトでは、設定方法 4 で書いたとおり設定ファイル側が優先されます
  3. Pyrefly のバージョンを固定したいならプロジェクトの .venv に入れる

    • Zed が GitHub Releases から取得するのは、その時点の最新版です。2026年10月31日に試したときは 1.4.0 を、11月13日は 1.4.1 を取得しました。Pyrefly の README には、マイナーバージョンが毎月出ること、どのバージョンでも新しい型エラーが出る可能性があることが書かれています。バージョンを固定した pyrefly を .venv に入れておくと、Zed もそのバージョンを使うので、CI の pyrefly check と結果を合わせやすくなります

つまずきポイント

プロジェクトを信頼するまで言語サーバーが起動しない

Zed のドキュメントでは、初めて開くプロジェクトをユーザーが信頼するまでの状態を Restricted Mode と呼び、その間は .zed/settings.json の読み込みと、プロジェクトの言語サーバーの導入・起動を止めると説明しています。

信頼していない状態で、.zed/settings.json に Pyrefly を設定したプロジェクトを開くと、次のダイアログが出ました。

信頼していないプロジェクトを開いたときにZedが表示するUnrecognized Projectダイアログ。Restricted Modeではプロジェクトの設定が適用されず、言語サーバーも起動しないと書かれていて、Stay in Restricted ModeとTrust and Continueのボタンがある。

このときのログには Waiting for worktree ... to be trusted, before starting language server basedpyright と出ていました。Pyrefly ではなく basedpyright の起動を待っているので、プロジェクトの設定がまだ読み込まれていないことが分かります。「Trust and Continue」を選ぶと、プロジェクトの設定が読み込まれ、Pyrefly と Ruff が起動しました。

ドキュメントによると、あとから信頼する場合は、タイトルバーの感嘆符のアイコン(1.28.1 では「Restricted Mode」と表示されています)か workspace::ToggleWorktreeSecurity アクションでこのダイアログを開けます。

すべてのプロジェクトを信頼済みとして扱う session.trust_all_worktrees という設定もありますが、ドキュメントでは推奨されていません。有効にした状態で開くと、確認なしで .zed/settings.json が読み込まれ、.venv/bin/pyrefly のようなプロジェクト内のバイナリが起動しました。

Zed のドキュメントにある displayTypeErrors の例では型エラーが返らなかった

Zed のドキュメントには、設定ファイルを置かずに型エラーを表示する方法として、lsp.pyrefly.initialization_options に {"python": {"pyrefly": {"displayTypeErrors": "force-on"}}} を書く例があります(2026年11月13日時点の main ブランチでも同じ記述です)。同じ日に、Zed 1.28.1 と Pyrefly 1.4.1 の組み合わせで、この例をユーザー設定に書いて試しました。Zed はこの値をそのまま Pyrefly に送っていましたが、Pyrefly の診断は設定ファイルを置かないときと同じ3件のままで、4行目と5行目に波線は出ませんでした。

ユーザー設定のinitialization_optionsを変えてmain.pyを開いたZedの画面を上下に並べた比較。上のZedのドキュメントの例では6行目と7行目にだけ波線が出ていてエラー3件、下のpyrefly.typeCheckingModeをdefaultにした場合は4行目から7行目に波線が出ていてエラー5件。

上が Zed のドキュメントの例、下が {"pyrefly": {"typeCheckingMode": "default"}} を initialization_options に書いた場合です。どちらも pyrefly.toml は置いていません。

切り分けとして、pyrefly 1.4.1 に LSP で直接つないだ場合も試しました。python の下に置いた {"python": {"pyrefly": {"displayTypeErrors": "force-on"}}} では型エラーが返らず、python を外した {"pyrefly": {"displayTypeErrors": "force-on"}} では返りました。設定方法 4 のとおり、initialization_options で Pyrefly 1.4.1 が読むキーは python. を外した位置にあります。Zed のドキュメントの例はキーを python の下に置いているため、読まれていないと考えられます。

また、Pyrefly 1.4.1 のドキュメントでは、displayTypeErrors が非推奨になり、typeCheckingMode と disableTypeErrors に置き換えられています(force-on は typeCheckingMode = "default" に相当します)。Zed のドキュメントの例が python. を外した書き方に変われば、この項は当てはまらなくなります。

GitHub API のレート制限に当たると初回の取得に失敗する

取得済みの pyrefly がない状態で GitHub API のレート制限に当たると、ログに Failed to start language server "pyrefly": status error 403 と API rate limit exceeded が出て、Pyrefly が起動しません。Zed は GitHub Releases の最新バージョンを GitHub API に問い合わせていて、未認証のリクエストは IP アドレスごとに1時間60回までに制限されているためです。

取得済みの pyrefly がデータフォルダにある場合は、同じ 403 が返っても、ログに failed to fetch newest version of language server と出たうえで取得済みのバイナリが起動しました。制限に当たっている間も、.venv に入れた pyrefly は起動しました。

今回は、出口の IP アドレスをほかの利用者と共有する VPN 経由で、空のデータフォルダでの起動を繰り返していたときに発生しました。普段の使い方でどの程度起きるかは確かめていません。PATH だけに pyrefly を置いた場合も、制限中には試していません。

まとめ

Zed 1.26.2 以降では、languages.Python.language_servers に "pyrefly" を足すだけで、拡張機能を入れずに Pyrefly を使えます。basedpyright を止めるなら "!basedpyright" を、Ruff を残すなら "..." を一緒に書きます。

Zed は、起動する pyrefly をプロジェクトの .venv、PATH の順に探し、どちらにもなければ GitHub Releases の最新版を取得します。コマンドラインや CI とバージョンをそろえたい場合は、.venv に入れておくと合わせやすくなります。

型の不一致まで表示するには、プロジェクトに pyrefly.toml を置くか、Zed の lsp 設定で typeCheckingMode を default にします。Zed のドキュメントにある displayTypeErrors の例を書いても、Zed 1.28.1 と Pyrefly 1.4.1 の組み合わせでは診断が変わりませんでした。設定後は、この記事と同じ main.py の4行目と5行目に波線が出るか、Project Diagnostics パネルに bad-argument-type と missing-attribute が並ぶかで確認できます。

誰かの参考になれば幸いです。


参考リンク:

この記事をシェアする

関連記事