简体   繁体   中英

Determining if the current span is an attribute deleration using Roslyn APIs

I'm building a visual studio extension for coloring various language constructs using Roslyn APIs, I want to change the color of attribute declarations, like Asp.Net MVC [Require] attribute for example. I have access to SyntaxNode and ISymbol , my current check to find out if the current node is an attribute declaration is:

public static bool IsCSharpAttributeSyntaxKind(this SyntaxNode node)
{
        return node.Kind() == SyntaxKind.Attribute;
}

And use it like:

if (node.IsCSharpAttributeSyntaxKind())
  {
     classificationTypeDictionary.TryGetValue(ColorCoderClassificationName.Attribute, out IClassificationType classificationValue);

     return new TagSpan<IClassificationTag>(new SnapshotSpan(snapshot, span.TextSpan.Start, span.TextSpan.Length), new ClassificationTag(classificationValue));
   }

I get the Roslyn info by:

public IEnumerable<ITagSpan<IClassificationTag>> GetTags(NormalizedSnapshotSpanCollection spans)
    {
        if (spans.Count == 0)
        {
            return Enumerable.Empty<ITagSpan<IClassificationTag>>();
        }

        var cacheStatus = _colorCoderTaggerServices.ManageCache(ref _cache, spans, _buffer);

        if (cacheStatus == CacheState.NotResolved)
        {
            return Enumerable.Empty<ITagSpan<IClassificationTag>>();
        }

        return _colorCoderTaggerServices.GetClassificationTags(_cache, spans, classificationTypeDictionary);
    }

Also the method for retrieving the identifiers:

internal IEnumerable<ClassifiedSpan> GetIdentifiersInSpans(Workspace workspace, SemanticModel model, NormalizedSnapshotSpanCollection spans)
 {
    var comparer = StringComparer.InvariantCultureIgnoreCase;

    var classifiedSpans = spans.SelectMany(span =>
    {
       var textSpan = TextSpan.FromBounds(span.Start, span.End);
        return Classifier.GetClassifiedSpans(model, textSpan, workspace);
    });

    return classifiedSpans.Where(c => comparer.Compare(c.ClassificationType, "identifier") == 0);
}

I don't know if the attribute declaration is part of the identifiers that I return form GetIdentifiersInSpans , but I did it without the Where with no success.

And for my caching mechanism I use:

public class ProviderCache
{
    public Workspace Workspace { get; private set; }
    public Document Document { get; private set; }
    public SemanticModel SemanticModel { get; private set; }
    public SyntaxNode SyntaxRoot { get; private set; }
    public ITextSnapshot Snapshot { get; private set; }

    public static async Task<ProviderCache> Resolve(ITextBuffer buffer, ITextSnapshot snapshot)
    {
        var workspace = buffer.GetWorkspace();
        var document = snapshot.GetOpenDocumentInCurrentContextWithChanges();
        if (document == null)
        {
            return null;
        }

        var semanticModel = await document.GetSemanticModelAsync().ConfigureAwait(false);
        var syntaxRoot = await document.GetSyntaxRootAsync().ConfigureAwait(false);
        return new ProviderCache
        {
            Workspace = workspace,
            Document = document,
            SemanticModel = semanticModel,
            SyntaxRoot = syntaxRoot,
            Snapshot = snapshot
        };
    }
}

I've tried various other things but none of them worked, I believe I miss something else here. I understand that my question is not good enough, I'm sorry for that, that's because I lack the terminology to ask a good question, and visual studio's extensibility frameworks are usually under-documented, if you need any more detail please let me know.

You don't mention how you are getting the roslyn info in your extension, but because you mention SyntaxNode Workspace and SemanticModel in your code samples, I assume you can get the current Document . The approach I would take is to get the root node for the document you are attempting to classify. A good starting place would be somthing like the code below and this VSSDK sample.

public static async Task<IEnumerable<ClassificationSpan>> ClassifyAttributes(Document currentDocument, CancellationToken token)
{
    // Get all attribute nodes in the current document
    var rootNode = await currentDocument.GetSyntaxRootAsync(token).ConfigureAwait(false);
    var attributesInDocument = from descendantNode in rootNode.DescendantNodesAndSelf()
                               where descendantNode.IsKind(SyntaxKind.Attribute)
                               select (AttributeSyntax)descendantNode;

    // Check to see if the attribute binds to a type (I assume you do not want to classify attributes with errors)
    var model = await currentDocument.GetSemanticModelAsync(token).ConfigureAwait(false);
    var attributeSpans = from attributeNode in attributesInDocument
                         let typeInfo = model.GetTypeInfo(attributeNode, token)
                         where typeInfo.Type.Kind != SymbolKind.ErrorType
                         select new ClassificationSpan(attributeNode.Span, _classificationType);

    // returns a set of ClassifiedSpans that your extensions classifer will colorize
    return attributeSpans;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM