2014-07-10 9 views
11

Sto cercando di aggiungere colorazione personalizzata solo per determinate parole chiave nel mio editor di Visual Studio per il codice C#. Voglio essere in grado di colorare qualsiasi tipo che implementa IDisposable come un colore diverso. Idealmente mi piacerebbe creare un semplice elenco di classi/interfacce che derivano da IDisposable in una sorta di configurazione che posso modificare. (Anche se hai detto che c'era un metodo/plugin che avrebbe trovato automaticamente tutti i tipi usa e getta e li colorerebbe in modo indipendente che sarebbe il Santo Graal).Colorazione parole chiave personalizzate in Visual Studio 2010 +

Ho fatto un sacco di ricerche e sembra che un'estensione "editor di classificatore" potrebbe fare il trucco. Tuttavia ne ho creato uno che cerca semplicemente di colorare la parola "Stream" e sebbene colpisca il mio codice che tenta di evidenziare quella parola, non finisce evidenziato nell'editor.

ho aggiunto la mia estensione VS per Github here

Questo sembra davvero come questo dovrebbe essere abbastanza semplice, ma io sono andato giù molti vicoli su questo solo per scoprire vicoli ciechi. C'è un modo più semplice per farlo, o la mia estensione è rotta?

Aggiornamento

Molto strano. Ho appena eseguito di nuovo la mia estensione e sebbene non evidenzi il testo nell'editor, esso evidenzia tutte le istanze di "Stream" nel testo popup quando passi con il mouse su un tipo/variabile! C'è un modo per farlo applicare all'editor?

enter image description here

+3

Questo sarebbe meraviglioso. – TyCobb

+0

Ho scaricato il codice dell'interno e l'ho eseguito localmente, sembra che funzioni correttamente per me. L'unica differenza che posso pensare è che sto correndo in VS2012 rispetto al 2010, quindi ho dovuto rifare un progetto e copiare il codice. il tuo vsixmanifest non ha funzionato bene sul mio computer, forse l'aggiornamento 2010-> 2012 l'ha rotto in qualche modo –

+0

Vedi i miei aggiornamenti sul testo del tooltip. Sto fraintendendo cosa sia un editor di classificatore? –

risposta

3

seconda del tempo che si sta utilizzando JetBrains ReSharper o non si può scrivere un plugin per questo. In questo modo non solo puoi aggiungere notifiche visive di IDisposable su una variabile, ma anche fornire quickfix se, e solo se, non viene chiamato, che è quello che presumo tu voglia catturare. Intendiamoci che posso immaginare che ci sia già un plugin R # per questo. So di aver preso in considerazione anche questo, ma ero troppo pigro per scrivere un plugin per questo.

Non fraintendermi btw - Se non stai usando r #, dovresti provare a provarlo.

Tra gli altri che ci si dovrà lavorare con questo: API-QuickFix

ci sono anche modi per definire parole chiave personalizzate, come ReSharper fa, data da una marcatura personalizzata e applicare quickfixes a quello.

PS: No, non lavoro a jetbrains. è solo che bene :)

UPDATE:

potenziale VS Extension risolvere?

controllare questo fuori: MSDN Link Highlighting Text

ho provato ad aprire il vostro progetto GitHub, ma non poteva così ho pensato che mi limiterò a controllare MSDN invece. sembra che tu stia derivando dalla classe sbagliata per soddisfare i tuoi bisogni?

MSDN parola chiave "Editors - Estendere l'Editor - Soluzione: Il testo Evidenziando"

che conosco così vuole il codice sul sito, ma è piuttosto improbabile collegamenti MSDN che va verso il basso e con le informazioni fornite il contenuto può essere trovato facilmente abbastanza :)

+0

Love Resharper ma non ha reso il budget l'ultimo pugno di anni. Sperando in una soluzione gratuita o fai-da-te –

+0

@mjmarsh hey. qual è il tuo stato allora? ha funzionato? non ha ancora visto un aggiornamento github – Dbl

1

Quindi, una soluzione possibile (credo che questo funziona):

1) creare un tipo di contenuto che eredita da csharp.

2) Creare un nuovo TextViewCreationListener che sostituirà tutti i tipi di contenuto "csharp" con il proprio, quindi "disarmando" potenzialmente tutti gli altri classificatori.

3) Registrare il classificatore per gestire il proprio tipo di contenuto.

Ecco parte del codice:

[Export(typeof(IVsTextViewCreationListener))] 
[ContentType("csharp")] 
[TextViewRole(PredefinedTextViewRoles.Editable)] 
class TextViewCreationListener : IVsTextViewCreationListener { 
    internal readonly IVsEditorAdaptersFactoryService _adaptersFactory; 

    [Import] internal IContentTypeRegistryService ContentTypeRegistryService = null; 

    [ImportingConstructor] 
    public TextViewCreationListener(IVsEditorAdaptersFactoryService adaptersFactory) { 
     _adaptersFactory = adaptersFactory; 
    } 

    #region IVsTextViewCreationListener Members 

    public void VsTextViewCreated(VisualStudio.TextManager.Interop.IVsTextView textViewAdapter) { 
     var textView = _adaptersFactory.GetWpfTextView(textViewAdapter); 

     var myContent = ContentTypeRegistryService.GetContentType(MyContentType); 
     if(myContent == null) 
     { 
      ContentTypeRegistryService.AddContentType(MyContentType, new[] {"csharp"}); 
      myContent = ContentTypeRegistryService.GetContentType(MyContentType); 
     } 

     // some kind of check if the content type is not already MyContentType. 
     textView.TextBuffer.ChangeContentType(myContent, null); 
    } 

    #endregion 
} 

Ed ora, basta modificare l'IClassifierProvider di registrarsi con il proprio tipo di contenuto, come ad esempio: [ContentType(MyContentType)]

Iin il proprio IClassifier, si può sostanzialmente fare il tuo calcolo e una volta che pensi di non poter gestire il materiale, potresti passare il controllo ad altri classificatori.

Se si utilizza MEF e si importa IClassifierAggregatorService, è possibile ottenere un "classificatore MASTER" che eseguirà tutta la logica per voi. Non l'ho ancora implementato, ma ho suggerito qualcosa di simile in passato, e sembrava funzionare. In alternativa potresti usare [ImportMany] con List<IClassifier> e filtrare quelli di csharp ?!

+0

L'attributo 'Ordine' nelle definizioni del formato dell'editor influisce solo sull'ordine in cui sono visualizzati nelle opzioni Caratteri e colori. Per quanto riguarda l'applicazione di 'Order' al provider stesso, è possibile utilizzare' Order (After = Priority.Default) ', ma non sono del tutto sicuro che qualcuno guardi quell'attributo quando esegue i classificatori. – Cameron

+0

Ho modificato la mia risposta con una nuova possibile risposta. Per quanto riguarda il tuo commento, è vero. Prima di postare effettivamente la risposta precedente, ho fatto un po 'di decompilazione e non ho trovato alcuna prova che suggerisse che Order [()] avrebbe funzionato per i classificatori, sebbene l'avessi visto in alcuni codici Microsoft e ho pensato che valesse la pena sparare . –

2

Sono un po 'in ritardo alla festa, ma hey, perché non gettare i miei 2 centesimi in

Come si è spiegato nella sua interrogazione, il progetto ha due parti fondamentali:.

  1. Trovare le classi che implementano IDisposable
  2. Evidenziando loro

il primo è di gran lunga la più difficile, ma non impossibile. Un approccio basato su elenco di parole è probabilmente il più semplice, anche se dovrebbe essere possibile con Roslyn per capire al volo quali classi ereditano lo IDisposible.

Si potrebbe anche sempre ricorrere al caricamento del file .exe/.dll compilato del progetto in background dopo una compilazione e capire quali sono i tipi lì, ma si dovrebbe comunque scrivere un codice di colla magica per capire cosa nomi di classi brevi nel codice si riferiscono a quali sono le attuali classi complete nell'assembly.

La seconda parte, evidenziando, è abbastanza facile una volta che si sa come farlo (è utile che abbia trascorso gli ultimi mesi a lavorare a tempo pieno sull'estensione del VS). Naturalmente, con Visual Studio, nulla è così semplice come sembra (nonostante gli sforzi di Microsoft per cercare di renderlo di facile utilizzo). Quindi, ho creato un'estensione di esempio che mette in evidenza solo le classi denominate "Stream" all'interno di file C# per iniziare.

highlighting at work

Il codice in questione è al di sotto, e la piena project source is on GitHub). Si inizia con un fornitore di classificazione-tagger:

[Export(typeof(ITaggerProvider))] 
[ContentType("CSharp")] 
[TagType(typeof(ClassificationTag))] 
[Name("HighlightDisposableTagger")] 
public class HighlightDisposableTaggerProvider : ITaggerProvider 
{ 
    [Import] 
    private IClassificationTypeRegistryService _classificationRegistry = null; 

    [Import] 
    private IClassifierAggregatorService _classifierAggregator = null; 

    private bool _reentrant; 

    public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag 
    { 
     if (_reentrant) 
      return null; 

     try { 
      _reentrant = true; 
      var classifier = _classifierAggregator.GetClassifier(buffer); 
      return new HighlightDisposableTagger(buffer, _classificationRegistry, classifier) as ITagger<T>; 
     } 
     finally { 
      _reentrant = false; 
     } 
    } 
} 

Poi il tagger stesso:

public class HighlightDisposableTagger : ITagger<ClassificationTag> 
{ 
    private const string DisposableFormatName = "HighlightDisposableFormat"; 

    [Export] 
    [Name(DisposableFormatName)] 
    public static ClassificationTypeDefinition DisposableFormatType = null; 

    [Export(typeof(EditorFormatDefinition))] 
    [Name(DisposableFormatName)] 
    [ClassificationType(ClassificationTypeNames = DisposableFormatName)] 
    [UserVisible(true)] 
    public class DisposableFormatDefinition : ClassificationFormatDefinition 
    { 
     public DisposableFormatDefinition() 
     { 
      DisplayName = "Disposable Format"; 
      ForegroundColor = Color.FromRgb(0xFF, 0x00, 0x00); 
     } 
    } 

    public event EventHandler<SnapshotSpanEventArgs> TagsChanged = delegate { }; 

    private ITextBuffer _subjectBuffer; 
    private ClassificationTag _tag; 
    private IClassifier _classifier; 
    private bool _reentrant; 

    public HighlightDisposableTagger(ITextBuffer subjectBuffer, IClassificationTypeRegistryService typeService, IClassifier classifier) 
    { 
     _subjectBuffer = subjectBuffer; 

     var classificationType = typeService.GetClassificationType(DisposableFormatName); 
     _tag = new ClassificationTag(classificationType); 
     _classifier = classifier; 
    } 

    public IEnumerable<ITagSpan<ClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans) 
    { 
     if (_reentrant) { 
      return Enumerable.Empty<ITagSpan<ClassificationTag>>(); 
     } 

     var tags = new List<ITagSpan<ClassificationTag>>(); 
     try { 
      _reentrant = true; 

      foreach (var span in spans) { 
       if (span.IsEmpty) 
        continue; 

       foreach (var token in _classifier.GetClassificationSpans(span)) { 
        if (token.ClassificationType.IsOfType(/*PredefinedClassificationTypeNames.Identifier*/ "User Types")) { 
         // TODO: Somehow figure out if this refers to a class which implements IDisposable 
         if (token.Span.GetText() == "Stream") { 
          tags.Add(new TagSpan<ClassificationTag>(token.Span, _tag)); 
         } 
        } 
       } 
      } 
      return tags; 
     } 
     finally { 
      _reentrant = false; 
     } 
    } 
} 

ho testato solo questo su VS2010, ma dovrebbe funzionare per VS2013 troppo (l'unica cosa che potrebbe essere diverso è il nome della classificazione di classe, ma è facile da scoprire con un breakpoint ben piazzato). Non ho mai scritto un'estensione per VS2012, quindi non posso commentare su questo, ma so che è abbastanza vicino a VS2013 per molti aspetti.