カスタム エディター拡張機能を使用して、エディターの余白の外観をカスタマイズできます。 このチュートリアルでは、コード コメントに "todo" という単語が表れるたびに、インジケーターの余白にカスタム グリフを配置します。
MEF プロジェクトを作成する
C# VSIX プロジェクトを作成します。 ([新しいプロジェクト] ダイアログで、[Visual C#]、[拡張機能]、[VSIX プロジェクト] の順に選択します。) ソリューションに TodoGlyphTest
という名前を付けます。
エディター分類子のプロジェクト項目を追加します。 詳細については、「エディター項目テンプレートを使用して拡張機能を作成する」を参照してください。
既存のクラス ファイルを削除します。
グリフを定義する
IGlyphFactory インターフェイスを実行してグリフを定義します。
グリフを定義するには
クラス ファイルを追加し、その名前を TodoGlyphFactory
にします。
宣言を使用して次のコードを追加します。
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;
Imports System.ComponentModel.Composition
Imports System.Windows
Imports System.Windows.Shapes
Imports System.Windows.Media
Imports System.Windows.Controls
Imports Microsoft.VisualStudio.Text
Imports Microsoft.VisualStudio.Text.Editor
Imports Microsoft.VisualStudio.Text.Formatting
Imports Microsoft.VisualStudio.Text.Tagging
Imports Microsoft.VisualStudio.Utilities
IGlyphFactory を実装する、TodoGlyphFactory
という名前のクラスを追加します。
internal class TodoGlyphFactory : IGlyphFactory
Friend Class TodoGlyphFactory
Implements IGlyphFactory
グリフのディメンションを定義するプライベート フィールドを追加します。
const double m_glyphSize = 16.0;
Const m_glyphSize As Double = 16.0
グリフのユーザー インターフェイス (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;
}
Public Function GenerateGlyph(ByVal line As IWpfTextViewLine, ByVal tag As IGlyphTag) As System.Windows.UIElement Implements IGlyphFactory.GenerateGlyph
' Ensure we can draw a glyph for this marker.
If tag Is Nothing OrElse Not (TypeOf tag Is TodoTag) Then
Return Nothing
End If
Dim ellipse As Ellipse = New Ellipse()
ellipse.Fill = Brushes.LightBlue
ellipse.StrokeThickness = 2
ellipse.Stroke = Brushes.DarkBlue
ellipse.Height = m_glyphSize
ellipse.Width = m_glyphSize
Return ellipse
End Function
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
<Export(GetType(IGlyphFactoryProvider)), Name("TodoGlyph"), Order(After:="VsTextMarker"), ContentType("code"), TagType(GetType(TodoTag))>
Friend NotInheritable Class TodoGlyphFactoryProvider
Implements IGlyphFactoryProvider
TodoGlyphFactory
をインスタンス化することで GetGlyphFactory メソッドを実装します。
public IGlyphFactory GetGlyphFactory(IWpfTextView view, IWpfTextViewMargin margin)
{
return new TodoGlyphFactory();
}
Public Function GetGlyphFactory(ByVal view As IWpfTextView, ByVal margin As IWpfTextViewMargin) As IGlyphFactory Implements IGlyphFactoryProvider.GetGlyphFactory
Return New TodoGlyphFactory()
End Function
Todo タグとタガーを定義する
以前の手順で定義した UI 要素とインジケーター マージンとの間の関係を定義します。 タグの種類とタガーを作成し、それを、タガー プロバイダーを使用してエクスポートします。
Todo タグとタガーを定義するには
新しいクラス ファイルをプロジェクトに追加し、TodoTagger
という名前を付けます。
次のインポートを追加します。
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;
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel.Composition
Imports Microsoft.VisualStudio.Text
Imports Microsoft.VisualStudio.Text.Tagging
Imports Microsoft.VisualStudio.Text.Editor
Imports Microsoft.VisualStudio.Text.Classification
Imports Microsoft.VisualStudio.Utilities
TodoTag
という名前のクラスを追加します。
internal class TodoTag : IGlyphTag
Friend Class TodoTag
Implements IGlyphTag
Public Sub New()
MyBase.New()
End Sub
End Class
TodoTag
型の ITagger<T> を実装する、TodoTagger
という名前のクラスを変更します。
internal class TodoTagger : ITagger<TodoTag>
Friend Class TodoTagger
Implements ITagger(Of TodoTag)
TodoTagger
クラスに対して、IClassifier と、分類範囲内で検索するテキストのためのプライベート フィールドを追加します。
private IClassifier m_classifier;
private const string m_searchText = "todo";
Private m_classifier As IClassifier
Private Const m_searchText As String = "todo"
分類子を設定するコンストラクターを追加します。
internal TodoTagger(IClassifier classifier)
{
m_classifier = classifier;
}
Friend Sub New(ByVal classifier As IClassifier)
m_classifier = classifier
End Sub
その名前に "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());
}
}
}
}
}
Private Function GetTags(ByVal spans As NormalizedSnapshotSpanCollection) As IEnumerable(Of ITagSpan(Of TodoTag)) Implements ITagger(Of TodoTag).GetTags
Dim list As List(Of ITagSpan(Of TodoTag))
list = New List(Of ITagSpan(Of TodoTag))()
For Each span As SnapshotSpan In spans
'look at each classification span \
For Each classification As ClassificationSpan In m_classifier.GetClassificationSpans(span)
'if the classification is a comment
If classification.ClassificationType.Classification.ToLower().Contains("comment") Then
'if the word "todo" is in the comment,
'create a new TodoTag TagSpan
Dim index As Integer = classification.Span.GetText().ToLower().IndexOf(m_searchText)
If index <> -1 Then
list.Add(New TagSpan(Of TodoTag)(New SnapshotSpan(classification.Span.Start + index, m_searchText.Length), New TodoTag()))
End If
End If
Next classification
Next span
Return list
End Function
TagsChanged
イベントを宣言します。
public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
Public Event TagsChanged(ByVal sender As Object, ByVal e As Microsoft.VisualStudio.Text.SnapshotSpanEventArgs) Implements Microsoft.VisualStudio.Text.Tagging.ITagger(Of TodoTag).TagsChanged
ITaggerProvider を実装する、TodoTaggerProvider
という名前のクラスを追加し、ContentTypeAttribute として "code"、TagTypeAttribute として TodoTag を指定してそれをエクスポートします。
[Export(typeof(ITaggerProvider))]
[ContentType("code")]
[TagType(typeof(TodoTag))]
class TodoTaggerProvider : ITaggerProvider
<Export(GetType(ITaggerProvider)), ContentType("code"), TagType(GetType(TodoTag))>
Friend Class TodoTaggerProvider
Implements ITaggerProvider
IClassifierAggregatorService をインポートします。
[Import]
internal IClassifierAggregatorService AggregatorService;
<Import()>
Friend AggregatorService As IClassifierAggregatorService
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>;
}
Public Function CreateTagger(Of T As Microsoft.VisualStudio.Text.Tagging.ITag)(ByVal buffer As Microsoft.VisualStudio.Text.ITextBuffer) As Microsoft.VisualStudio.Text.Tagging.ITagger(Of T) Implements Microsoft.VisualStudio.Text.Tagging.ITaggerProvider.CreateTagger
If buffer Is Nothing Then
Throw New ArgumentNullException("buffer")
End If
Return TryCast(New TodoTagger(AggregatorService.GetClassifier(buffer)), ITagger(Of T))
End Function
コードのビルドとテスト
このコードをテストするには、TodoGlyphTest ソリューションをビルドし、それを実験用インスタンスで実行します。
TodoGlyphTest ソリューションをビルドしてテストするには
ソリューションをビルドします。
F5 キーを押してプロジェクトを実行します。 Visual Studio の 2 番目のインスタンスが開始されます。
インジケーター マージンが表示されていることを確認します。 ([ツール] メニューの [オプション]をクリックします。[テキスト エディター] ページで、[インジケーター マージン] が選択されていることを確認します。)
コメントが含まれているコード ファイルを開きます。 コメント セクションの 1 つに "todo" という単語を追加します。
コード ウィンドウの左側のインジケーター マージンに、濃い青の輪郭の付いた薄い青の円が表示されます。