テンプレートを使い始めるためにOKですが、通常、それはあなたがに行くしているものの方向を知っていれば、より直接的に、すべてを再実装する簡単です
は、ここですべてのピースを組み合わせる方法は次のとおりです。
- クラシファイア(実際には
IClassificationTag
タグ)は、必要に応じてテキストバッファの特定のセクションの分類タグスパンを生成します。
- 分類タグスパンは、タグが適用されるバッファ内のスパンと分類タグ自体で構成されます。分類タグは、単に適用する分類タイプを指定します。
- 分類タイプは、その分類のタグを所定のフォーマットに関連付けるために使用されます。
- 形式(具体的には
ClassificationFormatDefinition
)は、MEF(EditorFormatDefinition
)としてエクスポートされるため、VSはそれらを検出し、関連付けられた分類タイプを持つカラースパンに使用できます。それらは、オプションで、フォント&の色オプションにも表示されます。
- クラシファイアプロバイダは、VSがそれを検出するためにMEF経由でエクスポートされます。 VSは、開いているバッファごとに分類子をインスタンス化する手段を提供します(したがって、その中のタグを検出します)。
これは、それぞれ2つの分類タイプに関連付けられた2つの分類フォーマット定義を定義してエクスポートするコードです。それで、分類器は両方の型のタグをそれに応じて生成する必要があります。ここでの例では、(未テスト)です:
public static class Classifications
{
// These are the strings that will be used to form the classification types
// and bind those types to formats
public const string ArchiveKey = "MyProject/ArchiveKey";
public const string ArchiveKeyVar = "MyProject/ArchiveKeyVar";
// These MEF exports define the types themselves
[Export]
[Name(ArchiveKey)]
private static ClassificationTypeDefinition ArchiveKeyType = null;
[Export]
[Name(ArchiveKeyVar)]
private static ClassificationTypeDefinition ArchiveKeyVarType = null;
// These are the format definitions that specify how things will look
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = ArchiveKey)]
[UserVisible(true)] // Controls whether it appears in Fonts & Colors options for user configuration
[Name(ArchiveKey)] // This could be anything but I like to reuse the classification type name
[Order(After = Priority.Default, Before = Priority.High)] // Optionally include this attribute if your classification should
// take precedence over some of the builtin ones like keywords
public sealed class ArchiveKeyFormatDefinition : ClassificationFormatDefinition
{
public ArchiveKeyFormatDefinition()
{
ForegroundColor = Color.FromRgb(0xFF, 0x69, 0xB4); // pink!
DisplayName = "This will display in Fonts & Colors";
}
}
[Export(typeof(EditorFormatDefinition))]
[ClassificationType(ClassificationTypeNames = ArchiveKeyVar)]
[UserVisible(true)]
[Name(ArchiveKeyVar)]
[Order(After = Priority.Default, Before = Priority.High)]
public sealed class ArchiveKeyVarFormatDefinition : ClassificationFormatDefinition
{
public ArchiveKeyVarFormatDefinition()
{
ForegroundColor = Color.FromRgb(0xB0, 0x30, 0x60); // maroon
DisplayName = "This too will display in Fonts & Colors";
}
}
}
プロバイダ:
[Export(typeof(ITaggerProvider))]
[ContentType("text")] // or whatever content type your tagger applies to
[TagType(typeof(ClassificationTag))]
public class ArchiveKeyClassifierProvider : ITaggerProvider
{
[Import]
public IClassificationTypeRegistryService ClassificationTypeRegistry { get; set; }
public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
{
return buffer.Properties.GetOrCreateSingletonProperty(() =>
new ArchiveKeyClassifier(buffer, ClassificationTypeRegistry)) as ITagger<T>;
}
}
は最後に、鬼自体:
public class ArchiveKeyClassifier : ITagger<ClassificationTag>
{
public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
private Dictionary<string, ClassificationTag> _tags;
public ArchiveKeyClassifier(ITextBuffer subjectBuffer, IClassificationTypeRegistryService classificationRegistry)
{
// Build the tags that correspond to each of the possible classifications
_tags = new Dictionary<string, ClassificationTag> {
{ Classifications.ArchiveKey, BuildTag(classificationRegistry, Classifications.ArchiveKey) },
{ Classifications.ArchiveKeyVar, BuildTag(classificationRegistry, Classifications.ArchiveKeyVar) }
};
}
public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
{
if (spans.Count == 0)
yield break;
foreach (var span in spans) {
if (span.IsEmpty)
continue;
foreach (var identSpan in LexIdentifiers(span)) {
var ident = identSpan.GetText();
if (!ident.StartsWith("Archive") || !ident.EndsWith("Key"))
continue;
var varSpan = new SnapshotSpan(
identSpan.Start + "Archive".Length,
identSpan.End - "Key".Length);
yield return new TagSpan<ClassificationTag>(new SnapshotSpan(identSpan.Start, varSpan.Start), _tags[Classifications.ArchiveKey]);
yield return new TagSpan<ClassificationTag>(varSpan, _tags[Classifications.ArchiveKeyVar]);
yield return new TagSpan<ClassificationTag>(new SnapshotSpan(varSpan.End, identSpan.End), _tags[Classifications.ArchiveKey]);
}
}
}
private static IEnumerable<SnapshotSpan> LexIdentifiers(SnapshotSpan span)
{
// Tokenize the string into identifiers and numbers, returning only the identifiers
var s = span.GetText();
for (int i = 0; i < s.Length;) {
if (char.IsLetter(s[i])) {
var start = i;
for (++i; i < s.Length && IsTokenChar(s[i]); ++i);
yield return new SnapshotSpan(span.Start + start, i - start);
continue;
}
if (char.IsDigit(s[i])) {
for (++i; i < s.Length && IsTokenChar(s[i]); ++i);
continue;
}
++i;
}
}
private static bool IsTokenChar(char c)
{
return char.IsLetterOrDigit(c) || c == '_';
}
private static ClassificationTag BuildTag(IClassificationTypeRegistryService classificationRegistry, string typeName)
{
return new ClassificationTag(classificationRegistry.GetClassificationType(typeName));
}
}
もう一つ注意:スタートアップを加速するために、VSはA続けますMEFエクスポートのキャッシュ。ただし、このキャッシュは、必要なときに無効化されないことがよくあります。さらに、既存の分類フォーマット定義のデフォルトの色を変更した場合、VSは以前の値をレジストリに保存するため、変更が取得されない可能性があります。これを軽減するには、MEFや書式関連の変更があった場合に、コンパイル時にバッチスクリプトを実行してオブジェクトをクリアするのが最善です。ここでVS2013と(VSIXesをテストする際にデフォルトで使用)経験ルートサフィックスのための例です。同じ[名前(ArchiveKey)]は、これは素晴らしい作品属性を使用して、両方のClassificationFormatDefinitionタイプから離れて
@echo off
del "%LOCALAPPDATA%\Microsoft\VisualStudio\12.0Exp\ComponentModelCache\Microsoft.VisualStudio.Default.cache" 2> nul
rmdir /S /Q "%LOCALAPPDATA%\Microsoft\VisualStudio\12.0Exp\ComponentModelCache" 2> nul
reg delete HKCU\Software\Microsoft\VisualStudio\12.0Exp\FontAndColors\Cache\{75A05685-00A8-4DED-BAE5-E7A50BFA929A} /f
。私は間違いなくこの上に構築することができます。 –
おっと、良いキャッチ。編集。あなたがそれが役に立つとうれしい! – Cameron
@Cameron:VSはレジストリに以前の値が保存されているため、変更が反映されない可能性があります。これを軽減するには、コンパイル時にバッチスクリプトを実行することをお勧めします。物事をクリアするためのフォーマット関連の変更が含まれています。これらのキャッシュされた値を拡張機能から無効にする方法を知っていますか? – HJLebbink