VS Code での Blazor と Razor:.razor ファイルのための C# ツール
SharpLsp チーム
Nimblesite エンジニアリング
SharpLsp は Razor を理解するようになりました。Blazor コンポーネント(.razor ファイル)、Razor Pages、MVC ビュー(.cshtml ファイル)が、残りの C# と同じ Roslyn エンジンを使えるようになりました。マークアップの中で、ホバー、補完、診断、定義へ移動、参照、名前の変更が使えます。このサポートは SharpLsp 0.30 で提供されます。
小さな Blazor Web App で見てみましょう。
Blazor とは?Blazor と Razor を 1 分で
2 つの名前は別の層を指しているため、混同しやすいのです。
- Razor は構文です。HTML と C# を混ぜ合わせます。
@式、@ifや@foreachのブロック、@codeブロックなどです。Microsoft の Razor 構文リファレンスがそのすべてを説明しています。 - ASP.NET Core の Razor Pages と MVC ビューは、リクエストごとにサーバーがレンダリングする
.cshtmlファイルです。ASP.NET Core の Razor Pagesと ASP.NET Core MVC のビューをご覧ください。 - Blazor はコンポーネントフレームワークです。Razor コンポーネントは
.razorファイルです。Blazor Web App は .NET 8 以降のdotnet new blazorテンプレートで、コンポーネントをサーバー上またはブラウザー内の WebAssembly で実行します。各コンポーネントはレンダーモードを選びます。静的なサーバーレンダリング、InteractiveServer、InteractiveWebAssembly、InteractiveAutoです。
この 3 つすべての下に、すべての Razor ファイルを C# に変換する 1 つの Razor コンパイラーがあります。だからこそ、SharpLsp の 1 つの実装が Blazor Server、Blazor WebAssembly、Razor Pages、MVC のすべてに対応します。
.razor ファイルで得られるもの
以下はすべて、動画のデモの Basket.razor ページで示したものであり、それぞれが自動テストでカバーされています。
- ホバー。
@Total()にホバーすると、Roslyn がBasket.Total()のシグネチャーと XML サマリーを表示します。レイアウトの@Bodyにホバーすると、それがレンダリングするLayoutComponentBase.Bodyプロパティが表示されます。 - 補完。
@式と@codeブロックの中で、Roslyn によるメンバーの補完が働きます。編集はすべて.razorファイルに反映され、補完が追加するusingは@using行になります。 - 診断。 C# の誤りは、その原因となった Razor の行に、コンパイラーのコードとともに、エディターと問題パネルに報告されます。ソリューション全体のスキャンには Razor ファイルも含まれます。
- 定義へ移動。
@onclick="() => Add(product)"のAddで F12 を押すと、@codeブロックに移動します。Catalog.Productsで F12 を押すとCatalog.csが開きます。C# 側から、app.MapRazorComponents<App>()のAppで F12 を押すと、生成されたクラスではなくApp.razorが開きます。 - 参照とハイライト。 Razor ファイル内のすべての使用箇所を表示し、コンパイラーが生成した隠れた足場のコードは含みません。
- 名前の変更。
@codeのフィールドの名前を変更すると、マークアップ内のすべての使用箇所が追従します。 - 構造。 複数行の要素と
@codeブロックが折りたためるほか、アウトラインがディレクティブと要素を一覧表示し、開始タグの名前を変更すると終了タグも変わります。 - 呼び出しの中のシグネチャヘルプと、ファイル内の C# に対するセマンティックハイライト。
C# を囲むマークアップは、やはり HTML です。VS Code では、SharpLsp が要素と属性の補完、および HTML のホバーを VS Code 自身の HTML 言語サービスへ転送します。C# の断片をすべて空白にした HTML ビューを送るため、HTML サービスが C# をマークアップと取り違えることはありません。
仕組み:Razor コンパイラーこそが信頼できる唯一の情報源
Razor ツールを作るとき、つい Razor パーサーを書いてしまいがちです。SharpLsp はそうしません。Razor のセマンティクスは Razor コンパイラーに属しており、2 つ目のパーサーはリリースのたびにそれからずれていきます。
.NET 6 以降、Razor コンパイラーは C# のソースジェネレーターです。それは .NET SDK に同梱され、C# サイドカーのデザインタイムビルドが dotnet build とまったく同じように実行します。ジェネレーターは各 Razor ファイルを C# クラスに変換します。C# の断片はすべて、Razor のテキストを指し示す拡張された #line ディレクティブの中に、そのままそのクラスへコピーされます。
SharpLsp はこれらのディレクティブから双方向のマップを作ります。
- Razor の位置へのリクエストは、生成された C# の同じ文字にマップされます。
- Roslyn がそこで応答します。
.csファイルの場合とまったく同じです。 - 応答内のすべての位置が、Razor ファイルへ逆マップされます。
マップはディレクティブを鵜呑みにはしません。Roslyn はマップされた各スパンの終端を、断片の 1 文字先として報告するため、SharpLsp は Razor のテキストと生成されたテキストを 1 文字ずつ比較して、すべての断片を測ります。マークアップをレンダリングする __builder 呼び出しのように、ジェネレーター自身が書いたコード内の位置には Razor のテキストがなく、利用者に届くことはありません。
ジェネレーターがコンパイラー自身のものであるため、それがマップするあらゆる構文の中の C# が同じ扱いを受けます。@bind の値、イベントハンドラー、ジェネリックコンポーネント、@code ブロックも含まれます。折りたたみ、アウトライン、リンクされた編集、HTML ビューを駆動するマークアップ側は、再実装ではなく、コンパイラーの公開された中間表現から来ています。
作りながら見つけたこと
新しい dotnet new blazor アプリで実際のホストを動かすと、1 つの誤報告が見つかりました。_Imports.razor のすべての @using が、不要なものとして淡色表示されていたのです。Roslyn は各ファイル自身の生成された C# しか見ないため、インポートファイルの using が、その隣や下にあるコンポーネントに役立っていることを知りません。コンポーネント自身の using も、マークアップだけがそれを必要とする場合には同じ問題を抱えていました。現在、Razor ファイルは不要な using を報告せず、C# ファイルはコードビハインドを含めて引き続き報告します。
2 つ目の発見は SDK のバージョンについてです。Razor ジェネレーターはプロジェクトをビルドする SDK に由来し、global.json で別のバージョンを固定していない限り、インストールされている最新のものが使われます。SDK 10.0.401 のジェネレーターは SharpLsp が同梱する Roslyn より新しいバージョンを必要とするため、当初はその Razor ファイルで C# の機能が使えませんでした(issue #399)。現在の SharpLsp は、そのジェネレーターの代わりに同梱の Razor コンパイラーを読み込み、両方の Roslyn バージョンを示す警告をログに記録します。そのため、新しい SDK でもバージョンを固定する必要はありません。詳しくは Blazor と Razor のドキュメントをご覧ください。
.NET 10 の Blazor:Server、WebAssembly、Razor クラスライブラリ
SharpLsp は、ソリューションに既にあるプロジェクトを読み取ります。どれも Microsoft.NET.Sdk.Web、Microsoft.NET.Sdk.BlazorWebAssembly、Microsoft.NET.Sdk.Razor を通じて同じように読み込まれます。
- Blazor Server または Blazor WebAssembly の対話性を持つ Blazor Web App
- スタンドアロンの Blazor WebAssembly アプリ
- Razor Pages または MVC のアプリ
- 共有コンポーネントの Razor クラスライブラリ
.NET 10 SDK 上の ASP.NET Core 10 は検証済みで、新しい dotnet new blazor アプリも含まれます。net8.0 と net9.0 のプロジェクトも同じソースジェネレーターで Razor をコンパイルするため動作するはずですが、まだ検証していません。.NET 6 より前のプロジェクトはソースジェネレーターなしで Razor をコンパイルするためサポート対象外であり、.NET Framework の MVC 5 と Web Forms も同様です。
今後の予定
このリリースは Razor ファイル内の C# を対象にしています。次はマークアップ自体で、issue #397 で追跡しています。
- コンポーネントタグ、コンポーネントパラメーター、
@bind-Value、@onclickなどのディレクティブ属性に対するホバー、定義へ移動、補完、参照 - 未知のコンポーネントなど、コンパイラー自身の
RZ診断 asp-forなど、Razor Pages と MVC ビューのタグヘルパー- Blazor フロントエンドの背後にある F# ライブラリ:Razor からの F# の型に対するホバー、補完、定義へ移動、および Razor の使用箇所にまで届く F# の名前の変更
- フォーマット、コンポーネントの抽出などのコードアクション、ホットリロード
試してみる
- VS Code Marketplace から、または Cursor、Windsurf、VSCodium では Open VSX から SharpLsp をインストールします。Razor のサポートにはバージョン 0.30 以降が必要です。
dotnet new blazor -o BlazorDemoで Blazor Web App を作成し、そのフォルダーを開きます。Components/Pages/Counter.razorを開き、currentCountにホバーします。
Blazor と Razor のドキュメントには、すべての機能、サポートするプロジェクト、まだサポートしていない項目が掲載されています。