2016-06-01 3 views
2

Используя стандартный шаблон, мне удалось создать пользовательский маркер, который превращает все вхождения строки «Архив ????? ключ» (где ???? - это любая коллекция символов, разрешенных в имена переменных) розовые. Однако мне бы очень хотелось, чтобы части «Архив» и «Ключ» стали розовыми, а для «????» - чтобы стать темно-бордовым. Насколько я понимаю, VSIX-маркеры (и я действительно этого не делаю), это означает определение двух ClassificationFormatDefinition, но каждый раз, когда я пытаюсь, я просто разбиваю проект.Классификатор VSIX, назначающий несколько классов классификации

Мой GetClassificationSpans метод (который является единственным существенным отклонением от стандартного шаблона) выглядит следующим образом:

public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span) 
{ 
    List<ClassificationSpan> spans = new List<ClassificationSpan>(); 

    string text = span.GetText(); 
    int idx0 = 0; 
    int idx1; 

    while (true) 
    { 
    idx0 = text.IndexOf(keyPrefix, idx0); 
    if (idx0 < 0) 
     break; 

    idx1 = text.IndexOf(keySuffix, idx0 + 6); 
    if (idx1 < 0) 
     break; 

    // TODO: make sure the prefix and suffix are part of the same object identifier. 
    string name = text.Substring(idx0 + lengthPrefix, idx1 - idx0 - lengthPrefix); 
    string full = text.Substring(idx0, idx1 - idx0 + keySuffix.Length); 

    SnapshotSpan span0 = new SnapshotSpan(span.Start + idx0, idx1 - idx0 + lengthSuffix); 
    SnapshotSpan span1 = new SnapshotSpan(span.Start + idx0 + lengthPrefix, idx1 - idx0 - lengthPrefix); 
    SnapshotSpan span2 = new SnapshotSpan(span.Start + idx1, lengthSuffix); 

    spans.Add(new ClassificationSpan(span0, classificationType)); 
    spans.Add(new ClassificationSpan(span1, classificationType)); // I'd like to assign a different IClassificationType to this span. 
    spans.Add(new ClassificationSpan(span2, classificationType)); 
    idx0 = idx1 + 5; 
    } 
    return spans; 
} 

И span1, где я хочу, чтобы назначить другой стиль. Я не понимаю, как классы Classifier, Format, Provider и Definition, необходимые для создания этой (!) Вещи, относятся друг к другу и какие из них могут быть осведомлены о нескольких стилях.

ответ

6

шаблоны OK для начала, но, как правило, это проще переопределять все более непосредственно, как только вы знаете, в каком направлении вы собираетесь в

Вот как все части подходят друг к другу:.

  • Классификатор (на самом деле, теггер IClassificationTag) дает классификационные тэги для определенного раздела текстового буфера по требованию.
  • Классификация тегов состоит из диапазона в буфере, к которому применяется тег, и самого классификационного тега. В теге классификации просто указывается тип классификации.
  • Типы классификации используются для связывания тегов этой классификации с заданным форматом.
  • Форматы (в частности, ClassificationFormatDefinition s) экспортируются через MEF (как EditorFormatDefinition s), чтобы VS мог их обнаружить и использовать для цветных пространств, имеющих соответствующий тип классификации. Они также (необязательно) отображаются в шрифтах & Варианты цветов.
  • Поставщик классификатора экспортируется через MEF для того, чтобы VS мог его обнаружить; он дает VS средство создания вашего классификатора для каждого открытого буфера (и, таким образом, обнаруживает в нем теги).

Итак, что вам нужно, это код, который определяет и экспортирует два определения формата классификации, связанные с двумя типами классификации, соответственно. Тогда ваш классификатор должен соответствующим образом создавать теги обоих типов. Вот пример (непроверенные):

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 держит кеш экспорта MEF. Однако этот кеш часто не является недействительным, когда он должен быть. Кроме того, если вы измените цвет по умолчанию существующего определения формата классификации, есть хорошая вероятность, что ваши изменения не будут получены, потому что VS сохраняет предыдущие значения в реестре. Чтобы смягчить это, лучше всего запускать пакетный скрипт между компиляторами, когда все изменения, связанные с MEF- или форматированием, становятся ясными.Вот пример для VS2013 и корня суффикса Exp (используется по умолчанию при тестировании VSIXes):

@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 
+0

кроме обоих типов ClassificationFormatDefinition, используя тот же [Имя (ArchiveKey)] связывают это работает. Я определенно могу основываться на этом. –

+0

Упс, хороший улов. Редактирование. Рад, что вы сочтете это полезным! – Cameron

+0

@Cameron: В вашем ответе вы говорите: «Есть хороший шанс, что ваши изменения не будут получены, потому что VS сохраняет прежние значения в реестре. Чтобы смягчить это, лучше всего запустить пакетный скрипт между компиляторами, когда что-то MEF- или изменения, связанные с форматом, чтобы очистить вещи ». Вы знаете способ аннулирования этих кешированных значений из внутреннего расширения? – HJLebbink

Смежные вопросы