ClassificationHelpers.cs 25.8 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

using System.Threading;
using Microsoft.CodeAnalysis.Classification;
5
using Microsoft.CodeAnalysis.CSharp.Extensions;
P
Pilchie 已提交
6
using Microsoft.CodeAnalysis.CSharp.Syntax;
T
Tomas Matousek 已提交
7
using Microsoft.CodeAnalysis.PooledObjects;
P
Pilchie 已提交
8 9 10 11 12 13 14 15 16
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.CSharp.Classification
{
    internal static class ClassificationHelpers
    {
        private const string FromKeyword = "from";
        private const string ValueKeyword = "value";
        private const string VarKeyword = "var";
17 18
        private const string UnmanagedKeyword = "unmanaged";
        private const string DynamicKeyword = "dynamic";
19
        private const string AwaitKeyword = "await";
P
Pilchie 已提交
20 21 22 23 24 25 26 27

        /// <summary>
        /// Determine the classification type for a given token.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <returns>The correct syntactic classification for the token.</returns>
        public static string GetClassification(SyntaxToken token)
        {
J
Joey Robichaud 已提交
28
            if (IsControlKeyword(token))
P
Pilchie 已提交
29
            {
J
Joey Robichaud 已提交
30 31 32 33 34
                return ClassificationTypeNames.ControlKeyword;
            }
            else if (SyntaxFacts.IsKeywordKind(token.Kind()) || token.IsKind(SyntaxKind.DiscardDesignation))
            {
                // When classifying `_`, IsKeywordKind handles UnderscoreToken, but need to additional check for DiscardDesignation
P
Pilchie 已提交
35 36
                return ClassificationTypeNames.Keyword;
            }
37
            else if (SyntaxFacts.IsPunctuation(token.Kind()))
P
Pilchie 已提交
38 39 40
            {
                return GetClassificationForPunctuation(token);
            }
41
            else if (token.Kind() == SyntaxKind.IdentifierToken)
P
Pilchie 已提交
42
            {
C
Charles Stoner 已提交
43
                return GetClassificationForIdentifier(token);
P
Pilchie 已提交
44
            }
45
            else if (IsStringToken(token))
P
Pilchie 已提交
46
            {
47
                return IsVerbatimStringToken(token)
P
Pilchie 已提交
48 49 50
                    ? ClassificationTypeNames.VerbatimStringLiteral
                    : ClassificationTypeNames.StringLiteral;
            }
51
            else if (token.Kind() == SyntaxKind.NumericLiteralToken)
P
Pilchie 已提交
52 53 54 55 56 57 58
            {
                return ClassificationTypeNames.NumericLiteral;
            }

            return null;
        }

J
Joey Robichaud 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
        private static bool IsControlKeyword(SyntaxToken token)
        {
            if (token.Parent is null || !IsControlKeywordKind(token.Kind()))
            {
                return false;
            }

            return IsControlStatementKind(token.Parent.Kind());
        }

        private static bool IsControlKeywordKind(SyntaxKind kind)
        {
            switch (kind)
            {
                case SyntaxKind.IfKeyword:
                case SyntaxKind.ElseKeyword:
                case SyntaxKind.WhileKeyword:
                case SyntaxKind.ForKeyword:
                case SyntaxKind.ForEachKeyword:
                case SyntaxKind.DoKeyword:
                case SyntaxKind.SwitchKeyword:
                case SyntaxKind.CaseKeyword:
                case SyntaxKind.TryKeyword:
                case SyntaxKind.CatchKeyword:
                case SyntaxKind.FinallyKeyword:
                case SyntaxKind.GotoKeyword:
                case SyntaxKind.BreakKeyword:
                case SyntaxKind.ContinueKeyword:
                case SyntaxKind.ReturnKeyword:
                case SyntaxKind.ThrowKeyword:
                case SyntaxKind.YieldKeyword:
                case SyntaxKind.DefaultKeyword: // Include DefaultKeyword as it can be part of a DefaultSwitchLabel
                case SyntaxKind.InKeyword: // Include InKeyword as it can be part of an ForEachStatement
92
                case SyntaxKind.WhenKeyword: // Include WhenKeyword as it can be part of a CatchFilterClause or a pattern WhenClause
J
Joey Robichaud 已提交
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
                    return true;
                default:
                    return false;
            }
        }

        private static bool IsControlStatementKind(SyntaxKind kind)
        {
            switch (kind)
            {
                // Jump Statements
                case SyntaxKind.GotoStatement:
                case SyntaxKind.GotoCaseStatement:
                case SyntaxKind.GotoDefaultStatement:
                case SyntaxKind.BreakStatement:
                case SyntaxKind.ContinueStatement:
                case SyntaxKind.ReturnStatement:
                case SyntaxKind.YieldReturnStatement:
                case SyntaxKind.YieldBreakStatement:
                case SyntaxKind.ThrowStatement:
                case SyntaxKind.WhileStatement:
                case SyntaxKind.DoStatement:
                case SyntaxKind.ForStatement:
                case SyntaxKind.ForEachStatement:
117
                case SyntaxKind.ForEachVariableStatement:
J
Joey Robichaud 已提交
118 119 120 121 122 123 124 125 126 127 128 129
                // Checked Statements
                case SyntaxKind.IfStatement:
                case SyntaxKind.ElseClause:
                case SyntaxKind.SwitchStatement:
                case SyntaxKind.SwitchSection:
                case SyntaxKind.CaseSwitchLabel:
                case SyntaxKind.CasePatternSwitchLabel:
                case SyntaxKind.DefaultSwitchLabel:
                case SyntaxKind.TryStatement:
                case SyntaxKind.CatchClause:
                case SyntaxKind.CatchFilterClause:
                case SyntaxKind.FinallyClause:
130 131 132
                case SyntaxKind.SwitchExpression:
                case SyntaxKind.ThrowExpression:
                case SyntaxKind.WhenClause:
J
Joey Robichaud 已提交
133 134 135 136 137 138
                    return true;
                default:
                    return false;
            }
        }

139
        private static bool IsStringToken(SyntaxToken token)
140
        {
141 142 143 144 145 146
            return token.IsKind(SyntaxKind.StringLiteralToken)
                || token.IsKind(SyntaxKind.CharacterLiteralToken)
                || token.IsKind(SyntaxKind.InterpolatedStringStartToken)
                || token.IsKind(SyntaxKind.InterpolatedVerbatimStringStartToken)
                || token.IsKind(SyntaxKind.InterpolatedStringTextToken)
                || token.IsKind(SyntaxKind.InterpolatedStringEndToken);
147 148
        }

149 150 151 152 153 154 155
        private static bool IsVerbatimStringToken(SyntaxToken token)
        {
            if (token.IsVerbatimStringLiteral())
            {
                return true;
            }

156
            switch (token.Kind())
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
            {
                case SyntaxKind.InterpolatedVerbatimStringStartToken:
                    return true;
                case SyntaxKind.InterpolatedStringStartToken:
                    return false;

                case SyntaxKind.InterpolatedStringEndToken:
                    {
                        var interpolatedString = token.Parent as InterpolatedStringExpressionSyntax;

                        return interpolatedString != null
                            && interpolatedString.StringStartToken.IsKind(SyntaxKind.InterpolatedVerbatimStringStartToken);
                    }

                case SyntaxKind.InterpolatedStringTextToken:
                    {
                        var interpolatedStringText = token.Parent as InterpolatedStringTextSyntax;
                        if (interpolatedStringText == null)
                        {
                            return false;
                        }

                        var interpolatedString = interpolatedStringText.Parent as InterpolatedStringExpressionSyntax;

                        return interpolatedString != null
                            && interpolatedString.StringStartToken.IsKind(SyntaxKind.InterpolatedVerbatimStringStartToken);
                    }
            }

            return false;
        }

C
Charles Stoner 已提交
189
        private static string GetClassificationForIdentifier(SyntaxToken token)
P
Pilchie 已提交
190
        {
C
CyrusNajmabadi 已提交
191
            if (token.Parent is BaseTypeDeclarationSyntax typeDeclaration && typeDeclaration.Identifier == token)
P
Pilchie 已提交
192 193 194
            {
                return GetClassificationForTypeDeclarationIdentifier(token);
            }
J
jasonmalinowski 已提交
195
            else if (token.Parent.IsKind(SyntaxKind.DelegateDeclaration) && ((DelegateDeclarationSyntax)token.Parent).Identifier == token)
P
Pilchie 已提交
196 197 198
            {
                return ClassificationTypeNames.DelegateName;
            }
J
jasonmalinowski 已提交
199
            else if (token.Parent.IsKind(SyntaxKind.TypeParameter) && ((TypeParameterSyntax)token.Parent).Identifier == token)
P
Pilchie 已提交
200 201 202
            {
                return ClassificationTypeNames.TypeParameterName;
            }
203 204 205 206
            else if (token.Parent is MethodDeclarationSyntax methodDeclaration && methodDeclaration.Identifier == token)
            {
                return IsExtensionMethod(methodDeclaration) ? ClassificationTypeNames.ExtensionMethodName : ClassificationTypeNames.MethodName;
            }
J
Joey Robichaud 已提交
207 208
            else if (token.Parent is ConstructorDeclarationSyntax constructorDeclaration && constructorDeclaration.Identifier == token)
            {
209 210 211
                return constructorDeclaration.IsParentKind(SyntaxKind.ClassDeclaration)
                    ? ClassificationTypeNames.ClassName
                    : ClassificationTypeNames.StructName;
J
Joey Robichaud 已提交
212 213 214
            }
            else if (token.Parent is DestructorDeclarationSyntax destructorDeclaration && destructorDeclaration.Identifier == token)
            {
215 216 217
                return destructorDeclaration.IsParentKind(SyntaxKind.ClassDeclaration)
                    ? ClassificationTypeNames.ClassName
                    : ClassificationTypeNames.StructName;
J
Joey Robichaud 已提交
218 219 220 221 222
            }
            else if (token.Parent is LocalFunctionStatementSyntax localFunctionStatement && localFunctionStatement.Identifier == token)
            {
                return ClassificationTypeNames.MethodName;
            }
223 224 225 226 227 228
            else if (token.Parent is PropertyDeclarationSyntax propertyDeclaration && propertyDeclaration.Identifier == token)
            {
                return ClassificationTypeNames.PropertyName;
            }
            else if (token.Parent is EnumMemberDeclarationSyntax enumMemberDeclaration && enumMemberDeclaration.Identifier == token)
            {
229
                return ClassificationTypeNames.EnumMemberName;
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
            }
            else if (token.Parent is VariableDeclaratorSyntax variableDeclarator && variableDeclarator.Identifier == token)
            {
                var varDecl = variableDeclarator.Parent as VariableDeclarationSyntax;
                switch (varDecl.Parent)
                {
                    case FieldDeclarationSyntax fieldDeclaration:
                        return fieldDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword) ? ClassificationTypeNames.ConstantName : ClassificationTypeNames.FieldName;
                    case LocalDeclarationStatementSyntax localDeclarationStatement:
                        return localDeclarationStatement.IsConst ? ClassificationTypeNames.ConstantName : ClassificationTypeNames.LocalName;
                    case EventFieldDeclarationSyntax aventFieldDeclarationSyntax:
                        return ClassificationTypeNames.EventName;
                }
                return ClassificationTypeNames.LocalName;
            }
J
Joey Robichaud 已提交
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
            else if (token.Parent is SingleVariableDesignationSyntax singleVariableDesignation && singleVariableDesignation.Identifier == token)
            {
                var parent = singleVariableDesignation.Parent;

                // Handle nested Tuple deconstruction
                while (parent.IsKind(SyntaxKind.ParenthesizedVariableDesignation))
                {
                    parent = parent.Parent;
                }

                // Checking for DeclarationExpression covers the following cases:
                // - Out parameters used within a field initializer or within a method. `int.TryParse("1", out var x)`
                // - Tuple deconstruction. `var (x, _) = (1, 2);`
                //
                // Checking for DeclarationPattern covers the following cases:
                // - Is patterns. `if (foo is Action action)`
                // - Switch patterns. `case int x when x > 0:`
                if (parent.IsKind(SyntaxKind.DeclarationExpression) ||
                    parent.IsKind(SyntaxKind.DeclarationPattern))
                {
                    return ClassificationTypeNames.LocalName;
                }

                return ClassificationTypeNames.Identifier;
            }
270 271 272 273 274 275 276 277 278 279 280 281
            else if (token.Parent is ParameterSyntax parameterSyntax && parameterSyntax.Identifier == token)
            {
                return ClassificationTypeNames.ParameterName;
            }
            else if (token.Parent is ForEachStatementSyntax forEachStatementSyntax && forEachStatementSyntax.Identifier == token)
            {
                return ClassificationTypeNames.LocalName;
            }
            else if (token.Parent is EventDeclarationSyntax eventDeclarationSyntax && eventDeclarationSyntax.Identifier == token)
            {
                return ClassificationTypeNames.EventName;
            }
282
            else if (IsActualContextualKeyword(token))
P
Pilchie 已提交
283 284 285
            {
                return ClassificationTypeNames.Keyword;
            }
J
Joey Robichaud 已提交
286 287 288 289 290 291 292 293 294 295 296 297
            else if (token.Parent is IdentifierNameSyntax identifierNameSyntax && IsNamespaceName(identifierNameSyntax))
            {
                return ClassificationTypeNames.NamespaceName;
            }
            else if (token.Parent is ExternAliasDirectiveSyntax externAliasDirectiveSyntax && externAliasDirectiveSyntax.Identifier == token)
            {
                return ClassificationTypeNames.NamespaceName;
            }
            else if (token.Parent is LabeledStatementSyntax labledStatementSyntax && labledStatementSyntax.Identifier == token)
            {
                return ClassificationTypeNames.LabelName;
            }
P
Pilchie 已提交
298 299 300 301 302 303
            else
            {
                return ClassificationTypeNames.Identifier;
            }
        }

J
Joey Robichaud 已提交
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
        private static bool IsNamespaceName(IdentifierNameSyntax identifierSyntax)
        {
            var parent = identifierSyntax.Parent;

            while (parent is QualifiedNameSyntax)
            {
                parent = parent.Parent;
            }

            return parent is NamespaceDeclarationSyntax;
        }

        public static bool IsStaticallyDeclared(SyntaxToken token)
        {
            var parentNode = token.Parent;

            if (parentNode.IsKind(SyntaxKind.EnumMemberDeclaration))
            {
                // EnumMembers are not classified as static since there is no
                // instance equivalent of the concept and they have their own
                // classification type.
                return false;
            }
            else if (parentNode.IsKind(SyntaxKind.VariableDeclarator))
            {
                // The parent of a VariableDeclarator is a VariableDeclarationSyntax node.
                // It's parent will be the declaration syntax node.
                parentNode = parentNode.Parent.Parent;

                // Check if this is a field constant declaration 
                if (parentNode.GetModifiers().Any(SyntaxKind.ConstKeyword))
                {
                    return true;
                }
            }

            return parentNode.GetModifiers().Any(SyntaxKind.StaticKeyword);
        }

343 344 345 346 347
        private static bool IsExtensionMethod(MethodDeclarationSyntax methodDeclaration)
        {
            return methodDeclaration.ParameterList.Parameters.FirstOrDefault()?.Modifiers.Any(SyntaxKind.ThisKeyword) == true;
        }

P
Pilchie 已提交
348 349
        private static string GetClassificationForTypeDeclarationIdentifier(SyntaxToken identifier)
        {
350
            switch (identifier.Parent.Kind())
P
Pilchie 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
            {
                case SyntaxKind.ClassDeclaration:
                    return ClassificationTypeNames.ClassName;
                case SyntaxKind.EnumDeclaration:
                    return ClassificationTypeNames.EnumName;
                case SyntaxKind.StructDeclaration:
                    return ClassificationTypeNames.StructName;
                case SyntaxKind.InterfaceDeclaration:
                    return ClassificationTypeNames.InterfaceName;
                default:
                    return null;
            }
        }

        private static string GetClassificationForPunctuation(SyntaxToken token)
        {
367
            if (token.Kind().IsOperator())
P
Pilchie 已提交
368 369
            {
                // special cases...
370
                switch (token.Kind())
P
Pilchie 已提交
371 372 373 374 375 376 377
                {
                    case SyntaxKind.LessThanToken:
                    case SyntaxKind.GreaterThanToken:
                        // the < and > tokens of a type parameter list should be classified as
                        // punctuation; otherwise, they're operators.
                        if (token.Parent != null)
                        {
378 379
                            if (token.Parent.Kind() == SyntaxKind.TypeParameterList ||
                                token.Parent.Kind() == SyntaxKind.TypeArgumentList)
P
Pilchie 已提交
380 381 382 383 384 385 386 387 388 389 390
                            {
                                return ClassificationTypeNames.Punctuation;
                            }
                        }

                        break;
                    case SyntaxKind.ColonToken:
                        // the : for inheritance/implements or labels should be classified as
                        // punctuation; otherwise, it's from a conditional operator.
                        if (token.Parent != null)
                        {
391
                            if (token.Parent.Kind() != SyntaxKind.ConditionalExpression)
P
Pilchie 已提交
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
                            {
                                return ClassificationTypeNames.Punctuation;
                            }
                        }

                        break;
                }

                return ClassificationTypeNames.Operator;
            }
            else
            {
                return ClassificationTypeNames.Punctuation;
            }
        }

        private static bool IsOperator(this SyntaxKind kind)
        {
            switch (kind)
            {
                case SyntaxKind.TildeToken:
                case SyntaxKind.ExclamationToken:
                case SyntaxKind.PercentToken:
                case SyntaxKind.CaretToken:
                case SyntaxKind.AmpersandToken:
                case SyntaxKind.AsteriskToken:
                case SyntaxKind.MinusToken:
                case SyntaxKind.PlusToken:
                case SyntaxKind.EqualsToken:
                case SyntaxKind.BarToken:
                case SyntaxKind.ColonToken:
                case SyntaxKind.LessThanToken:
                case SyntaxKind.GreaterThanToken:
                case SyntaxKind.DotToken:
                case SyntaxKind.QuestionToken:
                case SyntaxKind.SlashToken:
                case SyntaxKind.BarBarToken:
                case SyntaxKind.AmpersandAmpersandToken:
                case SyntaxKind.MinusMinusToken:
                case SyntaxKind.PlusPlusToken:
                case SyntaxKind.ColonColonToken:
                case SyntaxKind.QuestionQuestionToken:
                case SyntaxKind.MinusGreaterThanToken:
                case SyntaxKind.ExclamationEqualsToken:
                case SyntaxKind.EqualsEqualsToken:
                case SyntaxKind.EqualsGreaterThanToken:
                case SyntaxKind.LessThanEqualsToken:
                case SyntaxKind.LessThanLessThanToken:
                case SyntaxKind.LessThanLessThanEqualsToken:
                case SyntaxKind.GreaterThanEqualsToken:
                case SyntaxKind.GreaterThanGreaterThanToken:
                case SyntaxKind.GreaterThanGreaterThanEqualsToken:
                case SyntaxKind.SlashEqualsToken:
                case SyntaxKind.AsteriskEqualsToken:
                case SyntaxKind.BarEqualsToken:
                case SyntaxKind.AmpersandEqualsToken:
                case SyntaxKind.PlusEqualsToken:
                case SyntaxKind.MinusEqualsToken:
                case SyntaxKind.CaretEqualsToken:
                case SyntaxKind.PercentEqualsToken:
                    return true;

                default:
                    return false;
            }
        }

        private static bool IsActualContextualKeyword(SyntaxToken token)
        {
J
jasonmalinowski 已提交
461
            if (token.Parent.IsKind(SyntaxKind.LabeledStatement))
P
Pilchie 已提交
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
            {
                var statement = (LabeledStatementSyntax)token.Parent;
                if (statement.Identifier == token)
                {
                    return false;
                }
            }

            // Ensure that the text and value text are the same. Otherwise, the identifier might
            // be escaped. I.e. "var", but not "@var"
            if (token.ToString() != token.ValueText)
            {
                return false;
            }

            // Standard cases.  We can just check the parent and see if we're
            // in the right position to be considered a contextual keyword
            if (token.Parent != null)
            {
                switch (token.ValueText)
                {
483 484 485
                    case AwaitKeyword:
                        return token.GetNextToken(includeZeroWidth: true).IsMissing;

P
Pilchie 已提交
486 487 488 489 490
                    case FromKeyword:
                        var fromClause = token.Parent.FirstAncestorOrSelf<FromClauseSyntax>();
                        return fromClause != null && fromClause.FromKeyword == token;

                    case VarKeyword:
491 492 493 494 495 496
                        // var
                        if (token.Parent is IdentifierNameSyntax && token.Parent?.Parent is ExpressionStatementSyntax)
                        {
                            return true;
                        }

P
Pilchie 已提交
497 498 499 500
                        // we allow var any time it looks like a variable declaration, and is not in a
                        // field or event field.
                        return
                            token.Parent is IdentifierNameSyntax &&
501
                            token.Parent.Parent is VariableDeclarationSyntax &&
P
Pilchie 已提交
502 503
                            !(token.Parent.Parent.Parent is FieldDeclarationSyntax) &&
                            !(token.Parent.Parent.Parent is EventFieldDeclarationSyntax);
504 505 506 507 508

                    case UnmanagedKeyword:
                        return token.Parent is IdentifierNameSyntax
                            && token.Parent.Parent is TypeConstraintSyntax
                            && token.Parent.Parent.Parent is TypeParameterConstraintClauseSyntax;
P
Pilchie 已提交
509 510 511 512 513 514
                }
            }

            return false;
        }

515
        internal static void AddLexicalClassifications(SourceText text, TextSpan textSpan, ArrayBuilder<ClassifiedSpan> result, CancellationToken cancellationToken)
P
Pilchie 已提交
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
        {
            var text2 = text.ToString(textSpan);
            var tokens = SyntaxFactory.ParseTokens(text2, initialTokenPosition: textSpan.Start);

            Worker.CollectClassifiedSpans(tokens, textSpan, result, cancellationToken);
        }

        internal static ClassifiedSpan AdjustStaleClassification(SourceText rawText, ClassifiedSpan classifiedSpan)
        {
            // If we marked this as an identifier and it should now be a keyword
            // (or vice versa), then fix this up and return it. 
            var classificationType = classifiedSpan.ClassificationType;

            // Check if the token's type has changed.  Note: we don't check for "wasPPKeyword &&
            // !isPPKeyword" here.  That's because for fault tolerance any identifier will end up
            // being parsed as a PP keyword eventually, and if we have the check here, the text
            // flickers between blue and black while typing.  See
            // http://vstfdevdiv:8080/web/wi.aspx?id=3521 for details.
            var wasKeyword = classificationType == ClassificationTypeNames.Keyword;
            var wasIdentifier = classificationType == ClassificationTypeNames.Identifier;

            // We only do this for identifiers/keywords.
            if (wasKeyword || wasIdentifier)
            {
                // Get the current text under the tag.
                var span = classifiedSpan.TextSpan;
                var text = rawText.ToString(span);

                // Now, try to find the token that corresponds to that text.  If
                // we get 0 or 2+ tokens, then we can't do anything with this.  
                // Also, if that text includes trivia, then we can't do anything.
                var token = SyntaxFactory.ParseToken(text);
                if (token.Span.Length == span.Length)
                {
550
                    // var, dynamic, and unmanaged are not contextual keywords.  They are always identifiers
P
Pilchie 已提交
551 552 553
                    // (that we classify as keywords).  Because we are just parsing a token we don't
                    // know if we're in the right context for them to be identifiers or keywords.
                    // So, we base on decision on what they were before.  i.e. if we had a keyword
554 555
                    // before, then assume it stays a keyword if we see 'var', 'dynamic', or 'unmanaged'.
                    var tokenString = token.ToString();
556
                    var isKeyword = SyntaxFacts.IsKeywordKind(token.Kind())
P
Pilchie 已提交
557
                        || (wasKeyword && SyntaxFacts.GetContextualKeywordKind(text) != SyntaxKind.None)
558
                        || (wasKeyword && (tokenString == VarKeyword || tokenString == DynamicKeyword || tokenString == UnmanagedKeyword));
P
Pilchie 已提交
559

560
                    var isIdentifier = token.Kind() == SyntaxKind.IdentifierToken;
P
Pilchie 已提交
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579

                    // We only do this for identifiers/keywords.
                    if (isKeyword || isIdentifier)
                    {
                        if ((wasKeyword && !isKeyword) ||
                            (wasIdentifier && !isIdentifier))
                        {
                            // It changed!  Return the new type of tagspan.
                            return new ClassifiedSpan(
                                isKeyword ? ClassificationTypeNames.Keyword : ClassificationTypeNames.Identifier, span);
                        }
                    }
                }
            }

            // didn't need to do anything to this one.
            return classifiedSpan;
        }
    }
580
}