リファクタリングとコードアクション
コードアクションは、問題をコードに変えます。このレッスンでは、ショートカット、アクションの出どころ、F# の修正、名前の変更、C# の修正を扱い、最後に裏側で起きていることと、コードアクションではないものを説明します。ここに書いたことは macOS、Linux、Windows のどれでも同じように動きます。
このレッスンで学ぶこと:
- キーボードからコードアクションのメニューを開く
- メニューに出るアクションとその理由を予測する
- F# の match を補完し、診断ごとの F# クイックフィックスを使う
- ファイルをまたいでシンボルの名前を変更する
- 呼び出しから C# のメソッドを生成し、足りない
usingを追加する - アクションを選んだときに SharpLsp が何をするかを理解する
始める前に: SharpLsp をインストールし(エディター設定)、ソリューションまたはプロジェクトのあるフォルダーを開きます。コードアクションは C# と F# のコンパイラーが生成するため、プロジェクトの読み込みが必要です。
ステップ 1:メニューを開く
すべてのアクションは、ひとつのコマンド Quick Fix から始まります。
| 場所 | 操作 |
|---|---|
| macOS の VS Code | Cmd+. |
| Windows と Linux の VS Code | Ctrl+. |
| どのエディターでも | 余白の電球をクリック |
ほかのエディターでも、同じ LSP 要求がそれぞれのコードアクションのコマンドとして使えます。SharpLsp はそのすべてに textDocument/codeAction で応えます。
ステップ 2:アクションの出どころを知る
アクションはコンテキストに依存します。サイドカーは、そのコンパイラーまたはプロバイダーがカーソル位置のものに対して実行できる修正とリファクタリングだけを返します。
| コンテキスト | 得られるもの |
|---|---|
| 診断(警告またはエラー) | その診断の修正 |
| 型、メンバー、式の上のカーソル | それを作り変えるリファクタリング |
| 選択範囲 | その範囲に働くリファクタリング |
| 空行や、対応していない場所 | アクションなし。SharpLsp は推測しません |
期待したメニューが出ないときは、波線やシンボルの上にカーソルを移すか、コードを選択してから、もう一度開いてください。
ステップ 3:F# の match を補完する
どの F# プロジェクトでも試せます。共用体と、ケースがひとつ足りない match を書きます。
type Stage =
| Browsing
| CheckedOut of decimal
let describe stage =
match stage with
| Browsing -> "still browsing"
コンパイラーは stage に FS0025(不完全なパターンマッチ)の波線を引きます。そこにカーソルを置いて Cmd+. を押すと、二つの修正が出ます。
- Generate 1 missing union case(s) は、足りないケースごとに分岐を書きます
- Add wildcard case は、代わりに
| _ ->の分岐を追加します
Generate を選ぶと:
let describe stage =
match stage with
| Browsing -> "still browsing"
| CheckedOut _ -> failwith "todo"
F# コンパイラーは共用体のすべてのケースを知っているので、分岐は正確です。failwith "todo" を本体に置き換えてください。
ステップ 4:診断ごとの F# の修正
F# の修正は、それぞれコンパイラーの診断か、カーソル位置のコードに結びついています。
| トリガーまたはコンテキスト | アクション |
|---|---|
FS0039 |
名前空間を特定できる場合、解決用の open を追加 |
FS1182 |
未使用のバインディングに _ を付ける |
FS0020 |
` |
FS0025 |
ワイルドカードの match 分岐を追加 |
FS0026 |
冗長なパターンを削除 |
対応する FS0001 の型不一致 |
既知の変換を挿入 |
| 判別共用体の match | 不足しているケースを生成 |
| レコード式 | 不足しているフィールドを生成 |
| インターフェイス実装 | メンバーのスタブを生成 |
SLSPF0102 / SLSPF0103 |
未使用の open を削除/名前を簡略化 |
F# コンパイラーが FS1182 を報告するのは、プロジェクトでこの警告を有効にした場合だけです。たとえばプロジェクトファイルに <OtherFlags>--warnon:1182</OtherFlags> を書きます。
試してみる: List.map (fun x -> x + 1) [1; 2] を単独の行に書きます。結果が捨てられるので、コンパイラーは FS0020 を警告します。Cmd+. で |> ignore が出ます。
コンパイラーのタイプミス修正と一部の FSAC アクションは、まだ実装されていません。F# 言語サポートを参照してください。
ステップ 5:ファイルをまたいで名前を変更する
名前の変更はコードアクションではありません。シンボルにカーソルを置いて F2(Rename Symbol)を押し、新しい名前を入力して Enter を押します。SharpLsp は textDocument/prepareRename を提供するので、エディターは名前を変更できるかを先に知り、そのあと textDocument/rename がすべての参照を編集します。
- C# の名前変更は、読み込まれた Roslyn ソリューションを使います
- F# の名前変更は、読み込まれた F# プロジェクトを使い、シンボルを使うすべてのファイルを編集します
レッスンでは、Sessions.fs の F# 関数 remember を rememberOrder に変えると、Program.fs の呼び出しも書き換わります。
ステップ 6:呼び出しから C# のメソッドを生成する
C# サイドカーは、同梱の Roslyn 機能アセンブリから Roslyn のコード修正プロバイダーとリファクタリングプロバイダーを検出するので、C# の修正は Roslyn 自身のものです。便利なのは、先に呼び出しを書く習慣です。
var receipt = Receipt(invoice);
Receipt はまだ存在しないので、コンパイラーはエラーを報告します。そこで Cmd+. を押すと Generate method 'Receipt' が出て、Roslyn が呼び出しからパラメーターの型を決めてスタブを書きます。
private object Receipt(string invoice)
{
throw new NotImplementedException();
}
ステップ 7:足りない using を追加する
名前空間なしで型を使います。
var builder = new StringBuilder();
StringBuilder で Cmd+. を押すと、最初に using System.Text; が出ます。その下には、完全修飾名、新しい StringBuilder クラスの生成、ブロックスコープの名前空間への変換など、その場所で使える Roslyn のリファクタリングが並びます。
ほかに動作を確認済みの C# のアクションには、未使用のローカル変数の削除や、有効な型または選択範囲に対する Roslyn のリファクタリングがあります。実際の一覧は、構文、診断、Roslyn のバージョン、利用可能なプロバイダーによって変わります。カテゴリーがあっても、Visual Studio のすべてのリファクタリングが使えるわけではありません。
裏側:一覧、そして解決
コードアクションは二つの要求で動きます。
- 問い合わせ。 エディターが、カーソル位置または選択範囲について
textDocument/codeActionを送ります。 - 一覧。 サイドカー(C# は Roslyn、F# は F# コンパイラー)は、編集を含まない軽量な項目(タイトルと種類)を返します。だからメニューは速いのです。
- 解決。 ひとつを選ぶと、
codeAction/resolveがそのアクションだけの編集を計算します。 - 適用。 結果はひとつのワークスペース編集で、既存の複数のドキュメントを一度に変更できます。
アクションの期限が切れていた場合、サイドカーが再起動した場合、またはプロバイダーが編集を生成できない場合、resolve は推測せずに編集なしを返します。
コードアクションではないもの
- 整形。 SharpLsp は意図的に LSP の整形を提供しません。C# には CSharpier、F# には Fantomas を使ってください。
- Sort Members。 tree-sitter で動く、独立した C# のコマンドです。ソリューションエクスプローラーで C# の型を右クリックして Sort Members を選ぶと、設定に従ってアクセシビリティ、カテゴリー、名前の順にメンバーを並べ替えます。
- 名前の変更。 ステップ 5 の
F2です。
おさらい
Cmd+.(またはCtrl+.)で、カーソル、選択範囲、診断に当てはまるすべてのアクションが開きます。- F# の修正はコンパイラーの診断に従い、match のケース、レコードのフィールド、インターフェイスのスタブを丸ごと書けます。
F2で、F# でも C# でもファイルをまたいで名前を変更できます。- C# では、先に呼び出しを書き、あとから生成します。
ワンテイクのウォークスルー
同じ機能を、VS Code で途切れなく一度に収録したものです。