JetBrainsのクロスプラットフォーム対応.NET IDE「Rider」誕生までの歴史とそのアーキテクチャ
こんにちは。JetBrains堀岡です。
「最近JetBrainsは.NET関連ツールに注力してないのでは?JetBrains Night Tokyoでもセッション無いし。」というご意見を頂きました。そんなことありません。実際のところJetBrainsの.NET系製品群はIntelliJ IDEAに次ぐ代表製品で、日本でも多くのお客様にご利用いただいております。つい先日、弊社Developer Adovocate(@cwoodruff、@maartenballiauw)によるRiderに関する英文記事がCODE Magazineで公開されました。せっかくなのでCODE Magazine 様から了承を頂き、英文記事の抄訳を作成しました。Riderをお使いのお客様や、これから使ってみたいと考えているお客様にRiderの設計思想やメリット、JetBrainsの.NET製品への継続的な取り組みをご理解頂くために参考にして頂ければ幸いです(ただし、かなりの長文です)。英語の原文で読みたい方は以下をご覧ください。
“This article originally appeared in CODE Magazine – Nov/Dec 2018: https://www.codemag.com/Article/1811091/Building-a-.NET-IDE-with-JetBrains-Rider”
JetBrains Riderについて
JetBrains Riderは.NET/Mono/.NET Core環境、およびASP.NET、ASP.NET Core、Xamarin、WPFなどのフレームワークを使用するテクノロジをサポートするクロスプラットフォーム(Windows, Linux, macOS)対応IDEです。Riderは、C#、VB.NET、F#、JavaScript、TypeScriptなどの多くの言語に対応しており、コンソールアプリケーション、ライブラリ、Unityゲーム、Xamarinモバイルアプリケーション、ASP.NET MVCの構築、及び、Angular/React/Vue.jsなどを用いたWebアプリケーション作成をサポートしています。
Riderには、.NET開発者にとって日々のコーディングを快適にし、品質の高いコードを書くために役立つ多くの機能があります。例えば、Riderは他のJetBrains IDEと同レベルの優れたコード補完、コード生成、リファクタリング、ナビゲーション、2,300を超えるコード検査(静的コード解析)機能を備えています。これらのコーディング支援機能に加えて、.NET開発者がIDEの基本機能として期待するような、デバッガ、単体テスト実行、(高速な)NuGetクライアント、データベースツール、WPF XAMLプレビューウィンドウ、バージョンコントロールシステムとの連携機能も備えています。さらに、Dockerとの連携やUnity Editorのような最新のテクノロジーにも対応しています。しかしながら、特筆すべきことは、これらの豊富な機能が全て備わっていても、Riderはメモリ効率が良く、高速で機敏に動作するということです。
Rider誕生までの歴史
Riderの技術とアーキテクチャの話に入る前に、その歴史について触れなければなりません。 2004年、JetBrainsはVisual StudioアドインReSharperのスタンドアロンアプリケーション版の作成を検討していました。 結果としてリリースされることはありませんでしたが完全に機能するプロトタイプが既に存在していました。
以下の図1がReSharper 2.0 IDE UIのスナップショットです。ソリューションエクスプローラ、エディタ、使用箇所の表示、コード補完、リファクタリング機能は既に実装されていました。 ユーザーインターフェイスはモダンではありませんが、エディタ上でドキュメンテーションをインラインで装飾表示するための機能は.NET WinFormsをベースに実現されており、WPF(Windows Presentation Foundation)が存在しなかった当時としては革新的な取り組みでした。
図1: ReSharper 2.0 IDE UI
その後スタンドアロン版ReSharperプロジェクトは一旦中止となってしまいましたが、そこで開発された要素技術(アクションシステム、テキストコントロール実装、ツールウィンドウおよびツールバーコントロール、単体テストランナー、ReSharperコマンドラインツール)は、その後のReSharperの進化とRiderの誕生に応用され、決して無駄になることはありませんでした。
ReSharper 2.0 IDEにおいて1つの重要なデザイン上の決定は、ReSharperのコア機能と(エディタとしての)IDE機能をデザイン上分離しておくということでした。この決定はその後のReSharperの開発の効率化(Visual Studio 2010, 2013, 2015, 2017の同時サポートを共通のReSharperコア機能上に個別のIDE対応レイヤーを作成することにより実現)に寄与しました。このデザインはRiderの開発にとっても重要な役割を果たしました。簡単に言ってしまえば、Riderの開発に必要なことは、ReSharperコアに別の(IntelliJ向けの)インテグレーションレイヤーを追加することのみに出来たからです。
ReSharper 2.0 IDEの開発同様、JetBrainsは新しいIDEを開発する上で、既存の技術とツールを可能な限り再利用したいと考えていました。それは論理的にはJetBrainsが長い年月をかけて構築してきたIDEプラットフォーム(IntelliJ IDEA)を再利用するということでした。IntelliJ IDEAは様々な開発シナリオをサポートし、WebStormやIntelliJ IDEA Ultimateのような他のJetBrains IDEの基礎となっています。一方で、JetBrainsには、他のIDEではサポートしていないC#やVB.NET向けVisual Studio用アドオン製品としてReSharperがありました。
そこでJetBrainsはIntelliJとReSharperを融合するという挑戦を開始しました。IntelliJのリッチなフロントエンド機能やバージョンコントロールシステム連携などの共通ツール機能、ReSharperの.NETツール機能の融合です。しかしそれぞれのツールは異なるテクノロジー・スタックの上に実現されています。具体的には、IntelliJ はJava Virtual Machine上で動作し、ReSharperは.NETベースで動作します。そのため我々はこれら2つのプロセスを協調して動作させる必要があったのです。
軽量かつスマートなUIレイヤー
RiderはIntelliJをReSharper上で動作する軽量なユーザインタフェース層として主に利用しています。すなわち、IntelliJはRiderにおいては、エディターとしてのユーザインターフェースを提供します。ただし、JavaScriptやTypeScriptのような言語に対しては、言語解析等の全ての機能を提供します。一方で、IntelliJのフロントエンドはC#やVB.NET、F#のような言語に対しては、知識を持ち合わせていません。それらの知識はReSharperバックエンドから提供される必要があります。
ユーザがソースファイルを編集するとき、IntelliJはユーザの操作を追跡します。 例えば、ステートメントの記述を完了するタイミングで、IntelliJは現在の言語サービスに補完項目を要求します。 C#、VB.NET、F#、および他のいくつかの言語に対しては、Riderのフロントエンドは、実際の処理を行う代わりにReSharperバックエンドから情報を取得するファサード言語サービスを呼び出します。 そこで得られた情報がフロントエンドに渡され、補完候補リストとして表示されます。
このような情報の流れは逆方向にも起こりえます。RiderのIntelliJフロントエンドはコードインスペクション結果を波線で表示する仕組みを持っていますが、C#用のインスペクション機能自体は持っていません。Riderは、ソースファイルを開いたタイミングで、ファイル情報をReSharperのバックエンドプロセスに通知し、ReSharperがファイル解析を行い、インスペクションの実行が完了するのを待ちます。ReSharperはインスペクション結果として指摘箇所、重要度、tooltip(吹き出し)テキストのリストを出力し、フロントエンドはその情報を表示するだけです。
まとめると、RiderにおいてはIntelliJフロントエンドの役割は特定の言語(JavaScript等)向けの処理、Tool Window群の提供、バージョンコントロールシステムとの連携となります。一方、.NET言語に対しては、IntelliJフロントエンドは編集機能の提供やその他の共通機能のフレームワークとして動作し、必要な情報はバックエンドプロセス(ReSharper)から取得します。
カスタムプロトコルの必要性
前述の通り、RiderのIntelliJフロントエンドには、編集、コード補完、メニューエントリの表示、コード検査結果の表示など、多くの機能に対するフレームワークが用意されています。 一方で、ReSharperのバックエンドプロセスも、コード補完、コード検査、リファクタリング等の同様の概念を持っています。
これらの類似点のため、JetBrainsは、フロントエンドとバックエンドの間で共有されるシンプルなモデルを利用できることに気付きました。 例えば、ユーザがコードを編集した時に、少量のテキストと差分情報をフロントエンドからバックエンドに渡すことができます。 コード検査結果を表示するために、バックエンドからフロントエンドにドキュメント範囲、重要度、および説明を提供すれば良いのです。 ということは、もし、あるコード検査結果をReSharperからIntelliJのフロントエンドで表示することができれば、その他のコード検査結果も同様に表示することができるはずです。 また、もし、エディタでコードの一部を変更し、小さなコードの部分データをReSharperに渡してモデルを更新することにより、1つのリファクタリング機能を動作させることができれば、外のすべてのリファクタリングも同様に動作させることができるはずです。
そこでJetBrainsはフロントエンドとバックエンドの間で、どのような手法でアクションやデータを受け渡すのが良いのかを決定するために、以下を検討しました。
- 初めに、Language Server Protocol (LSP)の利用を評価しました。 – 結局採用しませんでした。LSPは素晴らしく、多くの機能がありますが、我々の用途とはあまりマッチしませんでした。例えば、ReSharperのいくつかのリファクタリング機能をLSPを用いて実装するためには、多くのカスタマイズが必要であることが判明しました。 また、C#/ VB.NETとHTML、CSS、JavaScriptを混在させたRazorのような言語をLSPがどのように扱うべきかも我々にとって不透明でした。 恐らく、別々の言語のLSPコンポーネントが必要であり、組み合わせ言語のLSPコンポーネントも必要でしょう。結果として、 LSPは多くの複雑さをもたらし、我々の特定のユースケースに対してほとんど利益をもたらさないと判断しました。
-
次に、各プロセスが別のプロセスに呼び出すことができるカスタムRESTプロトコルを構築し、評価しました。 JetBrainsは、JSONやProtoBufなどのさまざまなトランスポートメカニズムとシリアライザを試していました。 残念なことに、これらのプロトコルは遅く、カスタマイズが難しく、効率的な開発には適さない事が判明しました。 これらアプローチの主な欠点は、リモートプロシージャコール(RPC)タイプのプロトコルを使用していることです。 これらの手法の場合、常に「要求 – アクション – レスポンス」フローで考える必要があります。一方で我々は、フロントエンドとバックエンドの間に、同じ概念で構築された、より自然なフローを持たせる方法を探していました。例えば、 フロントエンドがバックエンドに「add file to solution」メッセージを送信し、状態が更新されたという応答を待つ代わりに、solution.Add(filename)というコードを書いたら、両方のプロセスをが自動的に同期され、 競合の解消やその時点で例外が発生した場合に何が起きるかをあまり気にせずプログラミングできるようにしたいのです。
もう一つの実装における重要な検討事項は、互いに受け渡されるデータの型でした。コード検査の場合、交換されるデータは、ドキュメントの範囲、重要度、および説明のみになります。 RPCスタイルでは、実際にコード検査を実現するには詳細な情報が必要です。例えば、このコード検査はどのソリューション対するものなのか?ソリューションのどのプロジェクトものなのか?そして、どのファイルに対するものなのか?あらゆる呼び出しには、そのようなコンテキスト情報が必要となり、RPC呼び出しが大量になり、開発者に多くのオーバーヘッドが必要になります。
そこでModel-View-ViewModel(MVVM)パターンとしてこのプロトコルをモデリングしようとしたとき、私たちに気づきの瞬間が訪れました。 Wikipediaの定義によれば、MVVMは、ビューモデル(VM)をバリューコンバーターとして使用して、バックエンド(M:モデル)の開発からユーザーインターフェイス(V:ビュー)の開発を分離することを容易にします。 私たちの場合、IntelliJはビューであり、ReSharperはモデルを提供しています。そして「プロトコル」が、UIコンポーネントに必要な軽量のデータとデータを共有するビューモデル(VM)です。
このデザインでは、(フロントエンドとバックエンドの)両方のプロセスに状態を持ち、それらを同期させようとするのではなく、両者から操作できる共有モデルが存在することが特徴です。私たちが最初に欲しかったのはまさにこのデザインでした。開発者がフロントエンドのプロジェクトに新しいファイルを追加すると、共有モデルが更新され、フロントエンドとバックエンドの両方のプロセスがそれに反応します。 ReSharperで動作しているリファクタリングで新しいファイルが追加されると、共有モデルが更新され、再び両方のプロセスがそれに反応できます。共有モデルはシステムの状態であり、両プロセスはモデルの変更を監視して反応することができます。
競合の解決
プロセス間通信において、MVVMは競合を解決しません。変更はフロントエンドまたはバックエンドのいずれかから来る可能性があり、競合が発生する可能性があります。たとえば、バックエンドがリファクタリングを実行中に、フロントエンドはファイルを削除する可能性があります。その結果、バックエンドは、モデル内の削除されたファイルに対して、レポートおよび更新を行おうとします。 ReSharperのバックエンドがリファクタリングを実行中に、コードを変更する場合、矛盾する変更をどのように解決すればよいでしょうか?コードを書いている人間による変更が優先されるのでしょうか?バックエンドプロセスが勝つのでしょうか?このような場合、どのようにモデルに対する動作を同期させれば良いでしょうか?
1つの解決方法は、ロックの概念を導入して、このような競合の発生を防止することです。しかし、リファクタリングの実行中にIDEが応答を停止するのを見たい人はいないでしょう。開発者は、長時間実行されているタスクがまだ完了していなくても、そのファイルを削除したり、新しいファイルを作成したり、モデルを更新したいはずです。我々はIDEが常にサクサク動作することを期待するのです!
そこで、JetBrainsは、競合の発生を防ぐために、以下のいくつかの基本的なルールを決めました:
- クライアントとサーバーを定義します。Riderの場合、クライアントはIntelliJで、サーバーはReSharperです。
- ビューモデルに格納される各値にはバージョンがあります。
- クライアントによって値が更新されるたびに、バージョンが増分されます。
- サーバー側の更新はバージョンを増やしません。
- (ビューモデルは)バージョンが同じかそれより新しい場合にのみ、変更された値を受け入れます。
クライアントによって値が変更されると、ビューモデルはそれを受け入れます。一方、サーバーが処理を行い、値を変更しようとしたときに、変更した値のバージョン番号が現在のバージョンより低いと、その変更はビューモデルで受け入れられません。これにより、競合が発生したときに、クライアント側の変更プロトコルが常に優先されるようになります。
Riderプロトコル
JetBrainsは、MVVMのアプローチと競合解消のルールを組み合わせたRiderプロトコルを構築しオープンソース化しました。しかし、私たちは多くのプロトコルの詳細が開発者に面倒をもたらすことを避けたいと思っていました。代わりに、私たちはプロトコルを図2のようにしたいと思っていました。
プロトコルの最下層は通信自体を提供し、ソケットで動作します。Riderは、差分情報を送信するバイナリワイヤプロトコルを使用します。必要に応じてバッチ処理を提供し、ワイヤを通過するすべてのデータをダンプするためのロギングをサポートします。それでも、このプロトコルは、開発者にとって詳細すぎると考えました。そこで、 JetBrainsはこのプロトコルを使用するためのフレームワークを構築しました。
IntelliJフロントエンドはJVM上で実行され、ReSharperバックエンドは.NET上で実行されます。そこで我々は、Kotlin(JVM言語)ライブラリとC#ライブラリを作成し、両方のライブラリが共通で使用するプリミティブを定義しました。また、これらのプリミティブを使用して、ドメイン固有の言語でビューモデルを定義し、フロントエンドとバックエンドで使用できるコードを生成できるようにする(Kotlinベースの)コードジェネレータも作成しました。
(フレームワーク又はライブラリによって)JetBrains開発チームは、プロトコルで動作する基本的なプリミティブについてのみ知っていればよく、ライブラリの背後にある他のすべての魔法を習得する必要はありません。このフレームワークを使用する別のメリットは、プロトコルレイヤは自動生成されるので、reflectionやintrospectionについて工数をかけることなく、パフォーマンスよく開発できることです。
このプロトコルではまた、オブジェクト階層を管理するためのアプローチとして、ライフタイムの概念をサポートしています。オブジェクトをメモリから、いつ、どこで削除するかをコントロールする代わりに、オブジェクトはライフタイムに付けられ、ライフタイムが消滅するタイミングで同時に削除されます。例えば、コード検査結果をエディタ・タブのライフタイムに付けることができます。これにより、エディタ・タブを閉じて(ライフタイムが終了すると)コード検査結果もメモリから削除されます。また、そのエディタのタブ自体をソリューションライフタイムに関連付けることができるため、ソリューションが終了して親ライフタイムが終了すると、すべての存在するオブジェクトも破棄されます。 IntelliJとReSharperはどちらもこのアプローチを利用しており、プロトコルのビューモデルはこれに従う必要があります。
ライフタイム以外にも、Riderプロトコルは次の概念をサポートしています:
- シグナル:何かが発生したときに生成されるイベント
- プロパティ:Observableな値
- マップ:Observableなコレクション
- フィールド:Immutableの値
- 呼び出し:ケースバイケースで必要となるRPCスタイルの呼び出し
これらはすべて、string、 enum、classdefまたはaggregatedef(ビューモデル内のノード)、およびstructdef(データを保持するオブジェクト)といったデータタイプの受け渡しに使用することが出来ます。
概念のみだと曖昧に聞こえるかもしれないので、簡単な例を見てみましょう。 RiderのNuGetクライアントは、さまざまなパッケージソースの使用をサポートしています。 これらはすべて名前とURLを持っています。
このプロトコルでは、NuGetクライアント情報(RdNuGetHost)を保持するビューモデルノードを定義します。このノードには、NuGet configuration(configManager)を管理するノードがあり、NuGetのソース名とURLを保持するプロパティ(knownFeeds)があります。
object RdNuGetHost : Ext(SolutionModel.Solution, extName = "nuGetHost") { field("configManager", ("RdNuGetConfigManager") { property("knownFeeds", immutableList( structdef("RdNuGetFeed") { field("name", string) field("url", string) })) }) }
コード生成を実行すると、図3に示すように、IntelliJフロントエンドが使用できるKotlinのクラス定義と、ReSharperバックエンドが使用できるC#のクラス定義が取得されます。
これにより、開発者は生成されたプロトコルに対して作業することができます。 たとえば、フロントエンド側では、以下のようにKotlinプログラミング言語でフィードのリストを設定できます:
configManager.knownFeeds.set(arrayListOf( RdNuGetFeed("NuGet.org", "https://api.nuget.org/v3/index.json") ))
一方、バックエンドの.NETコードでこのリストを購読し、変更に反応することができます。 または、指定した時点でリストの値を列挙できます。 図4に示すように、プロトコルコードが生成されているため、コード補完も得られます。
まとめると、プロトコルに対するコード生成のおかげで、IntelliJフロントエンドとReSharperバックエンド間のコミュニケーションや状態について詳細をケアすること無く、共有モデル用いてコードを書くことができるのです。
マイクロサービス
Riderは、IntelliJのフロントエンドとReSharperのバックエンドの2つのプロセスで構成されていることをすでに説明しました。両方とも異なるテクノロジスタック(JVMと.NET)で動作しているため、Riderは、Riderプロトコルを使用して相互に通信する複数のプロセスを実行する必要があります。複数プロセスによる実行にはいくつかの利点があります。1つの利点は、それぞれのプロセスが独自の64ビットメモリ空間を持ち、マルチコアマシンでは、これらのプロセスが独自のCPUコア上で実行される可能性が高いため、パフォーマンスが向上することです。
もう一つ興味深い利点があります。プロセスの独立性です。複数のプロセスが独立して実行されるので(プロトコル経由での通信は別として)、独立したガベージコレクションが実行可能です。また、起動や停止に関しても独立して実行可能です。この特徴はとても興味深いです!独自のメモリスペース、独自のガベージコレクタ、および潜在的に独自のCPUコアを使用して、必要に応じてプロセスを開始および停止できるのです。
Riderは、使用しているソリューションのタイプに応じて、2つ以上のプロセスを実行することがあります。図5のような、Windows Presentation Foundation(WPF)プロジェクトで作業する場合、3つのプロセスが実行されている可能性があります。
まず、エディタ自体はRiderのIntelliJフロントエンドであり1つのプロセスです。次に、コード補完、コード解析、クイックフィックスなどは、RiderのReSharperバックエンドプロセスとして実行されます。 そして、画面下部のXAML Previewウィンドウが3つ目のプロセスです。ReSharperのバックエンドは、WPFアプリケーション向けに作業していることを検出し、別のインスタンスのプロトコルでReSharperとビットマップ表現を通信するレンダリングプロセスを開始します(図6) 。
図6:XAML Previewレンダラと動作するRiderのプロセス
RiderはRoslynアナライザーの実行もサポートしています。多くの開発チームは独自のアナライザを作成して、構築したフレームワークに追加のツールを提供します。Riderでは、ReSharperバックエンドがRoslynを使用してコードを解析する別のプロセスを開始します。Roslynによる解析結果がIntelliJのフロントエンドに渡され表示されます(図7)。
デバッガもまた別のプロセスでも実行され、ReSharperバックエンドによって生成され、Riderプロトコルを使用して通信します。したがって、Roslynアナライザを使用するプロジェクトでXAMLプレビューツールウィンドウを開いて、それをデバッグしようとすると、さらに多くのプロセスが実行されます(図8)。
図8:デバッガを使用したRiderのプロセス
まとめると、オンデマンドでプロセスを開始および停止できるアーキテクチャでは、次のような利点があります。
- 独立したガベージコレクションとメモリスペースの分離
- オンデマンドでの機能の開始/停止、ソリューションで必要なときにのみ、完全な機能セットをロード
- 独立したクラッシュ。これは、XAMLプレビューレンダラのようなプロセスで重要になります。例えば、問題のあるユーザーデータをレンダリングする必要がある場合です。そのような場合、IDE全体が停止するのではなく、1つのプロセスを正常に失敗させることができます。
まだ説明していないもう1つの利点があります。 Riderプロトコルはソケットベースなので、必ずしも同じコンピュータ上で実行する必要はありません。たとえば、Dockerデバッグサポートでは、デバッガプロセスはDockerコンテナ(基本的に別のコンピュータ)で実行され、RiderのReSharperバックエンドプロセスと通信します。これは、現在と将来に対して多くの柔軟性をもたらします。
これまで見てきた例では、Riderがプロセスを所有し、起動/停止を行いました。一方、 Unity Editorとの統合においては、Riderは(Unity Editorの)プロセスを所有していません。 RiderとUnity Editorは独立して起動することができますが、両方のプロセスが互いのRider Protocol接続を探すことができます。両方とも起動し、接続が許可されている場合、RiderはそのビューモデルをUnity Editorと共有することができ、その逆も可能です。これにより、RiderはUnityの再生、一時停止、停止ボタンをコントロールすることができます。また、Unity Editorで実行中でもRiderでコードをデバッグすることができます(図9)。
図9:macOSで動作しているRiderによるUnityデバッグ
Unityプラグインの内部に興味がある場合は、https://github.com/JetBrains/resharper-unityをご覧ください。IntelliJフロントエンド、ReSharperバックエンド、およびUnity Editorプラグインによるマイクロサービスの実現を見ることができますよ!
Riderの独自UI拡張のためのデザイン
ここまではRiderのIntelliJフロントエンドとReSharperバックエンド間における、ビューモデルの共有について重点を置いて説明してきました。ビュー自体はどうでしょうか?確かに、Riderにはビュー側で開発されたいくつかの独自のユーザーインターフェイス要素とツールウィンドウが必要がですよね?
その通りです。コード検査やクイックフィックスの表示においては、ドキュメントの範囲、重要度、および説明といった、IntelliJで既に使用されているものを再利用するため、フロントエンドでの変更を必要としません。一方、他の機能、例えばNuGetツールウィンドウでは、構築したプロトコルに基づきにすべての機能を実装する必要があります。
最近のバージョンのRiderを開発中に、JetBrainsは、多くのユーザーインターフェースとユーザーインターフェース要素が本質的に似ていることに気づきました。たとえば、すべてのコード検査設定は、検査の概要、重要度、およびそれらをオンまたはオフに切り替えるブール値です。これらのすべての設定は、そのリストをグリッドビューにバインドする同じユーザーコントロールを共有します。
他の多くの設定では、ユーザーインターフェイスは多くの場合ヘッダーの後に1つ以上のテキストが続き、その後にチェックボックス、テキストボックス、またはドロップダウンが続きます。そこで、それらを個別に再構築するのではなく、プロトコルを使用して実装できるいくつかの標準ビューを構築し始めました。たとえば、RiderのC#インタラクティブ設定は、次のコードスニペットを使用してReSharperバックエンドで定義されます。
AddHeader("Tool settings"); AddToolPathFileChooserOption(lifetime, commonFileDialogs); AddEmptyLine(); AddStringOption( (CSharpInteractiveOptions s) => s.ToolArguments, "Tool arguments:") AddHeader("Tool window behavior"); AddBoolOption( (CSharpInteractiveOptions s) => s.FocusOnOpenToolWindow, "Focus tool window on open"); AddBoolOption( (CSharpInteractiveOptions s) => s.FocusOnSendLineText, "Focus tool window on Send Line"); AddBoolOption( (CSharpInteractiveOptions s) => s.MoveCaretOnSendLineText, "Move caret down on Send Line");
RiderのIntelliJフロントエンドはこれらをレンダリングし、適切な設定ペインが表示されます(図10)。
ReSharper側でユーザーインターフェイスを定義する利点は2つあります。まず、UIを手動で構築する必要はなく、代わりにプロトコルモデルからUIを生成できます。第2に、RiderとReSharperは同じコードベースなので、ReSharperの将来のバージョンでもこれらをレンダリングできるのです!
Riderの開発がReSharperの進化にもたらすメリット
RiderはReSharperの機能をできる限り再利用して作られているという話をしました。このようなデザインにより、JetBrainsがRiderに追加する機能は、ReSharperでも利用可能であり、ReSharperへの機能追加は同様にRiderでも利用可能であるという利点があります。
これは機能に限った話だけではなく、パフォーマンスの最適化においても、一方での改善が他方にもメリットをもたらします。 例えば、ReSharperにおいて、ソリューションをロードする速度を最適化すれば、Riderはそのメリットを得るでしょう。 RiderがIDEコンポーネントの読み込みを最適化すれば、ReSharperも同様の改善によるメリットを享受することができます。
現在のRider開発において、JetBrainsは今後ReSharperバックエンドを.NET Core CLRの上で動作させることに重点を置いています。これは、.NET Framework(Windowsの場合)またはMono(macOSおよびLinuxの場合)を必要としないようにするためです。この取り組みは、現在のような2つの異なるランタイム環境ではなく、すべてのプラットフォームのランタイム環境が.NET Coreになるという点でRiderにとって有益です。Riderはまた、ReSharperと同様に、.NET Coreで行われた多くのパフォーマンス強化の恩恵を受けるでしょう。
Rider開発において我々はUIとは別のプロセスとしてReSharperを実行させるデザインに取り組んできましたが、この取り組みはVisual Studioにおいても、ReSharperを別プロセスとして実行させることに応用されようとしています。 JetBrainsはこれに積極的に取り組んでおり、Visual Studioにおいてもこの記事で紹介している利点をもたらします。Visual StudioとReSharperはそれぞれ独立したプロセスで動作し、独自のメモリスペースと潜在的に独自のCPUコアを使うことができるようになります。この取り組みは .NET Core CLR上でReSharperを実行するためのサポートの変更とともに、Visual Studio上におけるReSharperにおいてもパフォーマンスの大幅な向上をもたらすでしょう!
さらなる機能:DataGrip、WebStorm、およびdotToolsとの統合
Riderの歴史から学んだように、Rider IDEの強みは、IntelliJファミリの他のIDEの機能を包含している点にもあります。 .NETとASP.NETの開発者にとって、JetBrainsはDataGripとWebStorm IDEの多くの機能をRiderに統合しました。これは基本的に既存のプラグインをRiderにバンドルすることによって実現しました。
DataGripのデータベースツールをバンドルすることで、RiderはOracle、MySQL、Microsoft SQL Server、Azure SQL Databaseなどをサポートするデータベースおよびデータに対する豊富な機能を提供します。Riderのユーザーは、SQLテキストエディタのインテリジェンスを使用してSQLコードを作成し、SQL文を実行し、結果セットを表示し、データベーススキーマを迅速にナビゲートして、使用しているデータベースでテーブル、ビュー、プロシージャ等を確認することができます。
JetBrainsは、単に既存のプラグインと機能を組み合わせるだけでなく、1+1=3になるように機能を拡張しようと積極的に考えています。例えば、 C#ファイルの文字列の中にSQLコード補完を提供することができれば、どれくらいうれしいですか?または、そのクエリを実行するAlt + Enterアクションを提供しますか?これはまさに私たちが取り組んでいることです(Rider 2018.3でSQLに対するLanguage Injection機能が拡充されます)。両方のプロセス(さらに、おそらく、他に協調するマイクロサービス)からインテリジェンスが得られるため、IDEはさらにスマートになります。
その他の例は、バンドルされているWebStormの機能にあります。ここ10年間のWebクライアントベースの開発の増加に伴い、ほとんどのASP.NETおよびASP.NETコア開発者は、プロジェクトやソリューションでJavaScriptの使用を強化する必要性を認識しました。 RiderのエディタはJavaScriptだけでなく、Angular、React、Vue.jsなど、現在利用されている代表的なWebフレームワークも理解しています。Riderは、また独自のコード解析機能と、現在利用可能な最も人気のあるオープンソースのLinterと組み合わせて、Webコードをチェックすることができます。 WebStormの組み込みデバッガとユニットテスト機能を使用して、すべてのJavaScriptコードを.NETコードとは別にデバッグしてテストすることもできます。 ASP.NETとASP.NET Coreを使用して構築されたWeb APIをテストするためのRESTクライアントもあります。
現在、Rider上でTypeScriptファイルをコーディングする場合、関連するすべての機能は、フロントエンドのWebStormプラグインによって提供されます。しかし、ReSharperにも、JavaScript、TypeScript、HTMLなどの多くの機能もあります。そこで現在、私たちは、フロントエンドとバックエンドの両方でコード分析、完了、コード生成などを行うことで、両方の世界のベストな機能を組み合わせることに取り組んでいます。
Rider 2018.2では、コードカバレッジと継続的テストが統合されました。これは別のバックエンドプロセスであるdotCoverによって実行されます。今後Riderに追加されうるその他の機能としては、dotTrace(JetBrainsのパフォーマンスプロファイラ)とdotMemory(JetBrainsのメモリプロファイラ)があります。現在、これらはWindows上でのみ実行されます。今後の我々のゴールは、これらのツールをmacOSおよびLinux上でも実行できるようにし、Riderの豊富なプロファイリング機能もそれらのプラットフォームでも利用可能にすることです。(※これらプロファイリング系機能を使用するためにはReSharper Ultimate + RiderまたはAll Products Packのご購入が必要です。)
まとめ
.NETとASP.NET開発の将来は非常にエキサイティングであり、Riderはその一部になりたいと考えています。 JetBrainsはAzure、WinForms、WPF、Xamarin、.NET Coreなど.NET開発者が現在使用している多くのテクノロジのサポートを追加し、継続的に改善にも取り組んでいきます。 現在は、.NETソリューションを開く際の起動時間の改善や、エディタ上でのタイピング時におけるゼロ遅延機能を追加することによる、Riderのパフォーマンス向上に取り組んでいます。
また、将来的な機能拡張としては、ソリューションやプロジェクト内でC ++コードのサポート、プラグイン開発者がRiderのIntelliJフロントエンド、ReSharperバックエンドを拡張できるようにする、より簡単なSDKを提供することによるプラグインエコシステムの構築することを検討しています。
Rider IDE誕生までの歴史やアーキテクチャ、異なるテクノロジスタック上で動作する既存の製品を組み合わせる際に直面する課題を紹介するしました。これにより Riderに対する理解を深めていただけたら幸いです。
RiderはWindows、Mac OS X、およびLinuxで使用できる非常に豊富で有益な.NET IDEです。製品の詳細や無料トライアル、購入に関するお問い合わせはJetBrains Riderウェブサイト(英語、日本語)または、日本の販売代理店までお知らせください。
お知らせ
2018年11月20日(火)午後6時より、JetBrainsの本社メンバーや日本における第一人者からベストプラクティスや最先端の現場からのユーザ事例を知ることができる貴重なイベント「JetBrains Night Tokyo 2018」を開催します。本記事の作者の一人である.NET関連製品スペシャリストの@maartenballiauwとも懇親会のAsk the specialistでお会いいただけます。以下から引き続きお申込み受付中です!
(2018年11月8日 追記)
突然ですが、2018年11月19日(月)午後7時より、株式会社ライフベア様のご厚意により、会場をお借りして、JetBrains “.NET” night Tokyo 2018を開催します。会場のキャパシティに限りがあり抽選による参加となる可能性がございますが、興味がございましたらぜひお申込みください!
https://lifebear.connpass.com/event/107559/