お知らせ

VS Code での Blazor と Razor:.razor ファイルのための C# ツール

シアンの光に縁取られたガラスの層の上で、琥珀色の炎をまとって輝く金属製の @ の彫刻

SharpLsp は Razor を理解するようになりました。Blazor コンポーネント(.razor ファイル)、Razor Pages、MVC ビュー(.cshtml ファイル)が、残りの C# と同じ Roslyn エンジンを使えるようになりました。マークアップの中で、ホバー、補完、診断、定義へ移動、参照、名前の変更が使えます。このサポートは SharpLsp 0.30 で提供されます。

小さな Blazor Web App で見てみましょう。

ナレーション、オリジナル音楽、字幕付き。VS Code で Blazor Web App を使って収録:マークアップでの Roslyn のホバー、Razor の式の中での補完、HTML の補完とホバー、原因の Razor の行に表示される C# のエラー、F12 による @code ブロックへの移動、参照と名前の変更、Program.cs から App.razor への F12、そしてタグのリンクされた編集と折りたたみ。 単独のページで見る

Blazor とは?Blazor と Razor を 1 分で

2 つの名前は別の層を指しているため、混同しやすいのです。

この 3 つすべての下に、すべての Razor ファイルを C# に変換する 1 つの Razor コンパイラーがあります。だからこそ、SharpLsp の 1 つの実装が Blazor Server、Blazor WebAssembly、Razor Pages、MVC のすべてに対応します。

.razor ファイルで得られるもの

以下はすべて、動画のデモの Basket.razor ページで示したものであり、それぞれが自動テストでカバーされています。

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 はこれらのディレクティブから双方向のマップを作ります。

  1. Razor の位置へのリクエストは、生成された C# の同じ文字にマップされます。
  2. Roslyn がそこで応答します。.cs ファイルの場合とまったく同じです。
  3. 応答内のすべての位置が、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 を通じて同じように読み込まれます。

.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 で追跡しています。

試してみる

  1. VS Code Marketplace から、または Cursor、Windsurf、VSCodium では Open VSX から SharpLsp をインストールします。Razor のサポートにはバージョン 0.30 以降が必要です。
  2. dotnet new blazor -o BlazorDemo で Blazor Web App を作成し、そのフォルダーを開きます。
  3. Components/Pages/Counter.razor を開き、currentCount にホバーします。

Blazor と Razor のドキュメントには、すべての機能、サポートするプロジェクト、まだサポートしていない項目が掲載されています。

参考資料