チュートリアル: 余白のグリフを作成する

カスタム エディター拡張機能を使用して、エディターの余白の外観をカスタマイズできます。 このチュートリアルでは、コード コメントに "todo" という単語が表れるたびに、インジケーターの余白にカスタム グリフを配置します。

MEF プロジェクトを作成する

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

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

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

グリフを定義する

IGlyphFactory インターフェイスを実行してグリフを定義します。

グリフを定義するには

  1. クラス ファイルを追加し、その名前を TodoGlyphFactoryにします。

  2. 宣言を使用して次のコードを追加します。

    using System.ComponentModel.Composition;
    using System.Windows;
    using System.Windows.Shapes;
    using System.Windows.Media;
    using System.Windows.Controls;
    using Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Text.Formatting;
    using Microsoft.VisualStudio.Text.Tagging;
    using Microsoft.VisualStudio.Utilities;
    
  3. IGlyphFactory を実装する、TodoGlyphFactory という名前のクラスを追加します。

    internal class TodoGlyphFactory : IGlyphFactory
    
  4. グリフのディメンションを定義するプライベート フィールドを追加します。

    const double m_glyphSize = 16.0;
    
  5. グリフのユーザー インターフェイス (UI) 要素を定義して、GenerateGlyph を実装します。 TodoTag については、このチュートリアルで後ほど定義します。

    public UIElement GenerateGlyph(IWpfTextViewLine line, IGlyphTag tag)
    {
        // Ensure we can draw a glyph for this marker.
        if (tag == null || !(tag is TodoTag))
        {
            return null;
        }
    
        System.Windows.Shapes.Ellipse ellipse = new Ellipse();
        ellipse.Fill = Brushes.LightBlue;
        ellipse.StrokeThickness = 2;
        ellipse.Stroke = Brushes.DarkBlue;
        ellipse.Height = m_glyphSize;
        ellipse.Width = m_glyphSize;
    
        return ellipse;
    }
    
  6. IGlyphFactoryProvider を実装する、TodoGlyphFactoryProvider という名前のクラスを追加します。 NameAttribute として "TodoGlyph"、OrderAttribute として After VsTextMarker、ContentTypeAttribute として "code"、TagTypeAttribute として TodoTag を指定して、このクラスをエクスポートします。

    [Export(typeof(IGlyphFactoryProvider))]
    [Name("TodoGlyph")]
    [Order(After = "VsTextMarker")]
    [ContentType("code")]
    [TagType(typeof(TodoTag))]
    internal sealed class TodoGlyphFactoryProvider : IGlyphFactoryProvider
    
  7. TodoGlyphFactory をインスタンス化することで GetGlyphFactory メソッドを実装します。

    public IGlyphFactory GetGlyphFactory(IWpfTextView view, IWpfTextViewMargin margin)
    {
        return new TodoGlyphFactory();
    }
    

Todo タグとタガーを定義する

以前の手順で定義した UI 要素とインジケーター マージンとの間の関係を定義します。 タグの種類とタガーを作成し、それを、タガー プロバイダーを使用してエクスポートします。

Todo タグとタガーを定義するには

  1. 新しいクラス ファイルをプロジェクトに追加し、TodoTagger という名前を付けます。

  2. 次のインポートを追加します。

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Text.Tagging;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Text.Classification;
    using Microsoft.VisualStudio.Utilities;
    
  3. TodoTagという名前のクラスを追加します。

    internal class TodoTag : IGlyphTag
    
  4. TodoTag 型の ITagger<T> を実装する、TodoTagger という名前のクラスを変更します。

    internal class TodoTagger : ITagger<TodoTag>
    
  5. TodoTagger クラスに対して、IClassifier と、分類範囲内で検索するテキストのためのプライベート フィールドを追加します。

    private IClassifier m_classifier;
    private const string m_searchText = "todo";
    
  6. 分類子を設定するコンストラクターを追加します。

    internal TodoTagger(IClassifier classifier)
    {
        m_classifier = classifier;
    }
    
  7. その名前に "comment" という単語が含まれていて、そのテキストに検索テキストが含まれているすべての分類範囲を検索することで、GetTags メソッドを実装します。 検索テキストが見つかるたびに、TodoTag 型の新しい TagSpan<T> が返されます。

    IEnumerable<ITagSpan<TodoTag>> ITagger<TodoTag>.GetTags(NormalizedSnapshotSpanCollection spans)
    {
        foreach (SnapshotSpan span in spans)
        {
            //look at each classification span \
            foreach (ClassificationSpan classification in m_classifier.GetClassificationSpans(span))
            {
                //if the classification is a comment
                if (classification.ClassificationType.Classification.ToLower().Contains("comment"))
                {
                    //if the word "todo" is in the comment,
                    //create a new TodoTag TagSpan
                    int index = classification.Span.GetText().ToLower().IndexOf(m_searchText);
                    if (index != -1)
                    {
                        yield return new TagSpan<TodoTag>(new SnapshotSpan(classification.Span.Start + index, m_searchText.Length), new TodoTag());
                    }
                }
            }
        }
    }
    
  8. TagsChanged イベントを宣言します。

    public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
    
  9. ITaggerProvider を実装する、TodoTaggerProvider という名前のクラスを追加し、ContentTypeAttribute として "code"、TagTypeAttribute として TodoTag を指定してそれをエクスポートします。

    [Export(typeof(ITaggerProvider))]
    [ContentType("code")]
    [TagType(typeof(TodoTag))]
    class TodoTaggerProvider : ITaggerProvider
    
  10. IClassifierAggregatorService をインポートします。

    [Import]
    internal IClassifierAggregatorService AggregatorService;
    
  11. TodoTagger をインスタンス化することで CreateTagger メソッドを実装します。

    public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
    {
        if (buffer == null)
        {
            throw new ArgumentNullException("buffer");
        }
    
        return new TodoTagger(AggregatorService.GetClassifier(buffer)) as ITagger<T>;
    }
    

コードのビルドとテスト

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

TodoGlyphTest ソリューションをビルドしてテストするには

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

  2. F5 キーを押してプロジェクトを実行します。 Visual Studio の 2 番目のインスタンスが開始されます。

  3. インジケーター マージンが表示されていることを確認します。 ([ツール] メニューの [オプション]をクリックします。[テキスト エディター] ページで、[インジケーター マージン] が選択されていることを確認します。)

  4. コメントが含まれているコード ファイルを開きます。 コメント セクションの 1 つに "todo" という単語を追加します。

  5. コード ウィンドウの左側のインジケーター マージンに、濃い青の輪郭の付いた薄い青の円が表示されます。