次の方法で共有


チュートリアル: 電球アイコンによる提案の表示

電球は Visual Studio エディターのアイコンで、組み込みのコード アナライザーやコード リファクタリングによって特定された問題の修正など、一連のアクションを表示するために展開されます。

Visual C# エディターと Visual Basic エディターでは、.NET コンパイラ プラットフォーム ("Roslyn") を使用して、電球を自動的に表示するアクションを含む独自のコード アナライザーを記述してパッケージ化することもできます。 詳細については、以下を参照してください。

  • 方法: C# 診断とコード修正 を記述する

  • 方法: Visual Basic 診断とコード修正 を記述する

    C++ などの他の言語では、その関数のスタブ実装を作成するための提案など、一部のクイック アクション用の電球も用意されています。

    電球の外観を次に示します。 Visual Basic または Visual C# プロジェクトでは、変数名が無効な場合、その名前の下に赤い波線が表示されます。 無効な識別子の上にマウスを置くと、カーソルの近くに電球が表示されます。

    電球 電球

    電球の下向き矢印をクリックすると、一連の推奨アクションと、選択したアクションのプレビューが表示されます。 この場合、アクションを実行した場合にコードに加えられた変更が表示されます。

    電球のプレビュー

    電球を使用して、独自の推奨アクションを提供できます。 たとえば、左中かっこを新しい行に移動したり、前の行の末尾に移動したりするアクションを提供できます。 次の手順では、現在の単語に表示される電球を作成し、2 つの推奨アクションを示す方法を説明します。大文字に変換小文字に変換

Managed Extensibility Framework (MEF) プロジェクトを作成する

  1. C# VSIX プロジェクトを作成します。 ([新しいプロジェクト] ダイアログで、[Visual C#]、[拡張機能][VSIX プロジェクト] の順に選択します。) ソリューションに LightBulbTest という名前を付けます。

  2. エディター分類子 項目テンプレートをプロジェクトに追加します。 詳細については、「エディター項目テンプレートを使用して拡張機能を作成する」を参照してください。

  3. 既存のクラス ファイルを削除します。

  4. プロジェクトに次の参照を追加し、ローカルコピーFalseに設定します。

    Microsoft.VisualStudio.Language.Intellisense

  5. 新しいクラス ファイルを追加し、LightBulbTest名前を付けます。

  6. ディレクティブを使用して以下を追加します。

    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Microsoft.VisualStudio.Language.Intellisense;
    using Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Text.Operations;
    using Microsoft.VisualStudio.Utilities;
    using System.ComponentModel.Composition;
    using System.Threading;
    
    

電球ソース プロバイダーを実装する

  1. LightBulbTest.cs クラス ファイルで、LightBulbTest クラスを削除します。 TestSuggestedActionsSourceProvider という名前のクラスを ISuggestedActionsSourceProviderを実装する形で追加します。 Test Suggested Actions という名前を付けて、ContentTypeAttribute に "text" を設定して、それをエクスポートします。

    [Export(typeof(ISuggestedActionsSourceProvider))]
    [Name("Test Suggested Actions")]
    [ContentType("text")]
    internal class TestSuggestedActionsSourceProvider : ISuggestedActionsSourceProvider
    
  2. ソース プロバイダー クラス内で、ITextStructureNavigatorSelectorService をインポートし、プロパティとして追加します。

    [Import(typeof(ITextStructureNavigatorSelectorService))]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
  3. CreateSuggestedActionsSource メソッドを実装して、ISuggestedActionsSource オブジェクトを返します。 ソースについては、次のセクションで説明します。

    public ISuggestedActionsSource CreateSuggestedActionsSource(ITextView textView, ITextBuffer textBuffer)
    {
        if (textBuffer == null || textView == null)
        {
            return null;
        }
        return new TestSuggestedActionsSource(this, textView, textBuffer);
    }
    

ISuggestedActionSource を実装する

推奨されるアクション ソースは、提案されたアクションのセットを収集し、適切なコンテキストで追加する役割を担います。 この場合、コンテキストは現在の単語であり、推奨されるアクションは UpperCaseSuggestedAction および LowerCaseSuggestedActionです。これについては、次のセクションで説明します。

  1. を実装する TestSuggestedActionsSource クラスを追加します。

    internal class TestSuggestedActionsSource : ISuggestedActionsSource
    
  2. 推奨されるアクション ソース プロバイダー、テキスト バッファー、およびテキスト ビューのプライベートな読み取り専用フィールドを追加します。

    private readonly TestSuggestedActionsSourceProvider m_factory;
    private readonly ITextBuffer m_textBuffer;
    private readonly ITextView m_textView;
    
  3. プライベート フィールドを設定するコンストラクターを追加します。

    public TestSuggestedActionsSource(TestSuggestedActionsSourceProvider testSuggestedActionsSourceProvider, ITextView textView, ITextBuffer textBuffer)
    {
        m_factory = testSuggestedActionsSourceProvider;
        m_textBuffer = textBuffer;
        m_textView = textView;
    }
    
  4. 現在カーソルの下にある単語を返すプライベート メソッドを追加します。 次のメソッドは、カーソルの現在の位置を調び、テキスト構造ナビゲーターに単語の範囲を確認します。 カーソルが単語の上にある場合、TextExtent は out パラメーターで返されます。それ以外の場合、out パラメーターは null され、メソッドは falseを返します。

    private bool TryGetWordUnderCaret(out TextExtent wordExtent)
    {
        ITextCaret caret = m_textView.Caret;
        SnapshotPoint point;
    
        if (caret.Position.BufferPosition > 0)
        {
            point = caret.Position.BufferPosition - 1;
        }
        else
        {
            wordExtent = default(TextExtent);
            return false;
        }
    
        ITextStructureNavigator navigator = m_factory.NavigatorService.GetTextStructureNavigator(m_textBuffer);
    
        wordExtent = navigator.GetExtentOfWord(point);
        return true;
    }
    
  5. HasSuggestedActionsAsync メソッドを実装します。 エディターはこのメソッドを呼び出して、電球を表示するかどうかを調べます。 この呼び出しは、たとえば、カーソルが 1 行から別の行に移動したときや、エラー波線の上にマウス ポインターを置いたときによく行われます。 このメソッドが動作している間、他の UI 操作を引き継ぐために非同期です。 ほとんどの場合、このメソッドは現在の行の解析と分析を実行する必要があるため、処理に時間がかかる場合があります。

    この実装では、TextExtent を非同期的に取得し、エクステントが空白以外のテキストを持っているかどうかなど、エクステントが重要かどうかを判断します。

    public Task<bool> HasSuggestedActionsAsync(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() =>
        {
            TextExtent extent;
            if (TryGetWordUnderCaret(out extent))
            {
                // don't display the action if the extent has whitespace
                return extent.IsSignificant;
              }
            return false;
        });
    }
    
  6. 異なる ISuggestedAction オブジェクトを含む SuggestedActionSet オブジェクトの配列を返す GetSuggestedActions メソッドを実装します。 このメソッドは、電球が展開されるときに呼び出されます。

    警告

    HasSuggestedActionsAsync()GetSuggestedActions() の実装が一貫していることを確認する必要があります。つまり、HasSuggestedActionsAsync()trueを返す場合は、GetSuggestedActions() に表示するアクションが必要です。 多くの場合、HasSuggestedActionsAsync()GetSuggestedActions()の直前に呼び出されますが、必ずしもそうであるとは限りません。 たとえば、ユーザーが (Ctrl +) キーを押して電球アクションを呼び出した場合、 のみが呼び出されます。

    public IEnumerable<SuggestedActionSet> GetSuggestedActions(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken)
    {
        TextExtent extent;
        if (TryGetWordUnderCaret(out extent) && extent.IsSignificant)
        {
            ITrackingSpan trackingSpan = range.Snapshot.CreateTrackingSpan(extent.Span, SpanTrackingMode.EdgeInclusive);
            var upperAction = new UpperCaseSuggestedAction(trackingSpan);
            var lowerAction = new LowerCaseSuggestedAction(trackingSpan);
            return new SuggestedActionSet[] { new SuggestedActionSet(new ISuggestedAction[] { upperAction, lowerAction }) };
        }
        return Enumerable.Empty<SuggestedActionSet>();
    }
    
  7. SuggestedActionsChanged イベントを定義します。

    public event EventHandler<EventArgs> SuggestedActionsChanged;
    
  8. 実装を完了するには、Dispose() メソッドと TryGetTelemetryId() メソッドの実装を追加します。 テレメトリを実行したくないので、false 返して GUID を Emptyに設定するだけです。

    public void Dispose()
    {
    }
    
    public bool TryGetTelemetryId(out Guid telemetryId)
    {
        // This is a sample provider and doesn't participate in LightBulb telemetry
        telemetryId = Guid.Empty;
        return false;
    }
    

電球アクションを実装する

  1. プロジェクトで、Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll への参照を追加し、を"ローカルにコピー"、Falseに設定します。

  2. 2 つのクラスを作成します。1 つ目の名前は UpperCaseSuggestedAction で、2 つ目は LowerCaseSuggestedActionという名前です。 どちらのクラスも ISuggestedActionを実装します。

    internal class UpperCaseSuggestedAction : ISuggestedAction
    internal class LowerCaseSuggestedAction : ISuggestedAction
    

    どちらのクラスも同じですが、一方は ToUpper を呼び出し、もう一方は ToLowerを呼び出します。 次の手順では大文字のアクション クラスのみを対象としていますが、両方のクラスを実装する必要があります。 小文字操作を実装するためのパターンとして、大文字操作を実装するための手順を使用します。

  3. これらのクラスに次の using ディレクティブを追加します。

    using Microsoft.VisualStudio.Imaging.Interop;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Media;
    
    
  4. プライベート フィールドのセットを宣言します。

    private ITrackingSpan m_span;
    private string m_upper;
    private string m_display;
    private ITextSnapshot m_snapshot;
    
  5. フィールドを設定するコンストラクターを追加します。

    public UpperCaseSuggestedAction(ITrackingSpan span)
    {
        m_span = span;
        m_snapshot = span.TextBuffer.CurrentSnapshot;
        m_upper = span.GetText(m_snapshot).ToUpper();
        m_display = string.Format("Convert '{0}' to upper case", span.GetText(m_snapshot));
    }
    
  6. GetPreviewAsync メソッドを実装して、アクションのプレビューを表示します。

    public Task<object> GetPreviewAsync(CancellationToken cancellationToken)
    {
        var textBlock = new TextBlock();
        textBlock.Padding = new Thickness(5);
        textBlock.Inlines.Add(new Run() { Text = m_upper });
        return Task.FromResult<object>(textBlock);
    }
    
  7. 空の SuggestedActionSet 列挙型を返すように、GetActionSetsAsync メソッドを実装します。

    public Task<IEnumerable<SuggestedActionSet>> GetActionSetsAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult<IEnumerable<SuggestedActionSet>>(null);
    }
    
  8. 次のようにプロパティを実装します。

    public bool HasActionSets
    {
        get { return false; }
    }
    public string DisplayText
    {
        get { return m_display; }
    }
    public ImageMoniker IconMoniker
    {
       get { return default(ImageMoniker); }
    }
    public string IconAutomationText
    {
        get
        {
            return null;
        }
    }
    public string InputGestureText
    {
        get
        {
            return null;
        }
    }
    public bool HasPreview
    {
        get { return true; }
    }
    
  9. スパン内のテキストを大文字に置き換えて、Invoke メソッドを実装します。

    public void Invoke(CancellationToken cancellationToken)
    {
        m_span.TextBuffer.Replace(m_span.GetSpan(m_snapshot), m_upper);
    }
    

    警告

    電球アイコンのアクションである Invoke メソッドでは、UI を表示することが想定されていません。 アクションで新しい UI (プレビューや選択ダイアログなど) が表示される場合は、Invoke メソッド内から直接 UI を表示するのではなく、Invokeから戻った後に UI を表示するようにスケジュールします。

  10. 実装を完了するには、Dispose() メソッドと TryGetTelemetryId() メソッドを追加します。

    public void Dispose()
    {
    }
    
    public bool TryGetTelemetryId(out Guid telemetryId)
    {
        // This is a sample action and doesn't participate in LightBulb telemetry
        telemetryId = Guid.Empty;
        return false;
    }
    
  11. LowerCaseSuggestedAction に対しても同様の作業を行い、表示テキストを "Convert '{0}' to lower case" に変更し、呼び出しを ToUpper から ToLowerに変えることを忘れないでください。

コードをビルドしてテストする

このコードをテストするには、LightBulbTest ソリューションをビルドし、実験用インスタンスで実行します。

  1. ソリューションをビルドします。

  2. デバッガーでこのプロジェクトを実行すると、Visual Studio の 2 番目のインスタンスが開始されます。

  3. テキスト ファイルを作成し、テキストを入力します。 テキストの左側に電球が表示されます。

    電球をテスト

  4. 電球をポイントします。 下矢印が表示されます。

  5. 電球をクリックすると、選択したアクションのプレビューと共に、2 つの推奨アクションが表示されます。

    テスト電球 、TestLIghtBulbExpanded展開

  6. 最初のアクションをクリックすると、現在の単語のすべてのテキストが大文字に変換されます。 2 番目のアクションをクリックすると、すべてのテキストが小文字に変換されます。