Worker.cs 5.6 KB
Newer Older
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
P
Pilchie 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Collections;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp.Classification
{
    /// <summary>
    /// Worker is an utility class that can classify a list of tokens or a tree within a
    /// requested span The implementation is generic and can produce any kind of classification
    /// artifacts T T is normally either ClassificationSpan or a Tuple (for testing purposes) 
    /// and constructed via provided factory.
    /// </summary>
    internal partial class Worker
    {
27 28 29
        private readonly TextSpan _textSpan;
        private readonly List<ClassifiedSpan> _result;
        private readonly CancellationToken _cancellationToken;
P
Pilchie 已提交
30 31 32

        private Worker(TextSpan textSpan, List<ClassifiedSpan> result, CancellationToken cancellationToken)
        {
33 34 35
            _result = result;
            _textSpan = textSpan;
            _cancellationToken = cancellationToken;
P
Pilchie 已提交
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
        }

        internal static void CollectClassifiedSpans(
            IEnumerable<SyntaxToken> tokens, TextSpan textSpan, List<ClassifiedSpan> result, CancellationToken cancellationToken)
        {
            var worker = new Worker(textSpan, result, cancellationToken);
            foreach (var tk in tokens)
            {
                worker.ClassifyToken(tk);
            }
        }

        internal static void CollectClassifiedSpans(SyntaxNode
            node, TextSpan textSpan, List<ClassifiedSpan> result, CancellationToken cancellationToken)
        {
            var worker = new Worker(textSpan, result, cancellationToken);
            worker.ClassifyNode(node);
        }

        private void AddClassification(TextSpan span, string type)
        {
57
            _result.Add(new ClassifiedSpan(type, span));
P
Pilchie 已提交
58 59 60 61
        }

        private void AddClassification(SyntaxTrivia trivia, string type)
        {
62
            if (trivia.Width() > 0 && _textSpan.OverlapsWith(trivia.Span))
P
Pilchie 已提交
63 64 65 66 67 68 69
            {
                AddClassification(trivia.Span, type);
            }
        }

        private void AddClassification(SyntaxToken token, string type)
        {
70
            if (token.Width() > 0 && _textSpan.OverlapsWith(token.Span))
P
Pilchie 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
            {
                AddClassification(token.Span, type);
            }
        }

        private void ClassifyNodeOrToken(SyntaxNodeOrToken nodeOrToken)
        {
            if (nodeOrToken.IsToken)
            {
                ClassifyToken(nodeOrToken.AsToken());
                return;
            }

            ClassifyNode(nodeOrToken.AsNode());
        }

        private void ClassifyNode(SyntaxNode node)
        {
89
            foreach (var token in node.DescendantTokens(span: _textSpan, descendIntoTrivia: false))
P
Pilchie 已提交
90
            {
91
                _cancellationToken.ThrowIfCancellationRequested();
P
Pilchie 已提交
92 93 94 95 96 97 98
                ClassifyToken(token);
            }
        }

        private void ClassifyToken(SyntaxToken token)
        {
            var span = token.Span;
99
            if (span.Length != 0 && _textSpan.OverlapsWith(span))
P
Pilchie 已提交
100 101 102 103 104 105 106 107 108 109 110
            {
                var type = ClassificationHelpers.GetClassification(token);

                if (type != null)
                {
                    AddClassification(span, type);
                }
            }

            foreach (var trivia in token.LeadingTrivia)
            {
111
                _cancellationToken.ThrowIfCancellationRequested();
P
Pilchie 已提交
112 113 114 115 116
                ClassifyTrivia(trivia);
            }

            foreach (var trivia in token.TrailingTrivia)
            {
117
                _cancellationToken.ThrowIfCancellationRequested();
P
Pilchie 已提交
118 119 120 121 122 123
                ClassifyTrivia(trivia);
            }
        }

        private void ClassifyTrivia(SyntaxTrivia trivia)
        {
124
            if (trivia.IsRegularComment())
P
Pilchie 已提交
125 126 127
            {
                AddClassification(trivia, ClassificationTypeNames.Comment);
            }
128
            else if (trivia.Kind() == SyntaxKind.DisabledTextTrivia)
P
Pilchie 已提交
129 130 131
            {
                AddClassification(trivia, ClassificationTypeNames.ExcludedCode);
            }
132
            else if (trivia.Kind() == SyntaxKind.SkippedTokensTrivia)
P
Pilchie 已提交
133 134 135 136 137 138 139
            {
                ClassifySkippedTokens((SkippedTokensTriviaSyntax)trivia.GetStructure());
            }
            else if (trivia.IsDocComment())
            {
                ClassifyDocumentationComment((DocumentationCommentTriviaSyntax)trivia.GetStructure());
            }
140
            else if (trivia.Kind() == SyntaxKind.DocumentationCommentExteriorTrivia)
P
Pilchie 已提交
141 142 143
            {
                AddClassification(trivia, ClassificationTypeNames.XmlDocCommentDelimiter);
            }
144
            else if (SyntaxFacts.IsPreprocessorDirective(trivia.Kind()))
P
Pilchie 已提交
145 146 147 148 149 150 151
            {
                ClassifyPreprocessorDirective((DirectiveTriviaSyntax)trivia.GetStructure());
            }
        }

        private void ClassifySkippedTokens(SkippedTokensTriviaSyntax skippedTokens)
        {
152
            if (!_textSpan.OverlapsWith(skippedTokens.Span))
P
Pilchie 已提交
153 154 155 156 157 158 159 160 161 162 163 164
            {
                return;
            }

            var tokens = skippedTokens.Tokens;
            foreach (var tk in tokens)
            {
                ClassifyToken(tk);
            }
        }
    }
}