internal List <ClassificationSpan> GetClassificationSpans( Workspace workspace, SemanticModel semanticModel, SnapshotSpan span) { var spans = new List <ClassificationSpan>(); var root = semanticModel.SyntaxTree.GetCompilationUnitRoot(); var textSpan = new TextSpan(span.Start.Position, span.Length); foreach (var item in Classifier.GetClassifiedSpans(semanticModel, textSpan, workspace)) { if (!ClassificationHelper.IsSupportedClassification(item.ClassificationType)) { continue; } if (item.ClassificationType == ClassificationTypeNames.Keyword) { var keywordToken = root.FindToken(item.TextSpan.Start, true); switch (keywordToken.ValueText) { case "GoTo": case "Else": case "ElseIf": case "Then": case "Throw": case "Do": case "Loop": case "While": case "Until": case "Continue": case "For": case "Next": case "Each": case "Yield": case "Return": case "Select": case "Case": case "Exit": case "Try": case "Catch": case "Finally": case "Function" when keywordToken.IsExitStatementKeyword(): case "Sub" when keywordToken.IsExitStatementKeyword(): case "Property" when keywordToken.IsExitStatementKeyword(): case "End" when keywordToken.IsEndStatementKeyword(): case "If" when keywordToken.IsIfStatementKeyword() || keywordToken.IsEndStatementKeyword(): AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _controlFlowType); break; } continue; } var node = root.FindNode(item.TextSpan, true).HandleNode(); if (!semanticModel.TryGetSymbolInfo(node, out var symbol, out var reason)) { // NOTE: handle alias in imports directive if (node is ImportAliasClauseSyntax && node.Parent is SimpleImportsClauseSyntax importSyntax) { var aliasNameSymbol = semanticModel.GetSymbolInfo(importSyntax.Name).Symbol; IClassificationType aliasType = null; if (!(aliasNameSymbol is null)) { aliasType = aliasNameSymbol.Kind == SymbolKind.Namespace ? _aliasNamespaceType : aliasNameSymbol.Kind == SymbolKind.NamedType ? GetTypeClassification(aliasNameSymbol as INamedTypeSymbol) : null; } if (!(aliasType is null)) { AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, aliasType); continue; } } Log.Debug("Nothing is found. Span start at {0} and end at {1}", item.TextSpan.Start, item.TextSpan.End); Log.Debug("Candidate Reason is {0}", reason); Log.Debug("Node is {0}", node); continue; } // TODO: add posibility to turn off classification a type characters as part of identifiers switch (symbol.Kind) { case SymbolKind.Field: var fieldSymbol = symbol as IFieldSymbol; var fieldType = fieldSymbol.Type.TypeKind == TypeKind.Enum ? _enumFieldType : fieldSymbol.IsConst ? _constantFieldType : _fieldType; AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, fieldType, node); break; case SymbolKind.RangeVariable: AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _rangeVariableType); break; case SymbolKind.Local: var localSymbol = symbol as ILocalSymbol; var localType = localSymbol.IsStatic ? _staticLocalVariableType : localSymbol.IsFunctionValue ? _functionVariableType : _localVariableType; AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, localType); break; case SymbolKind.Method: var methodSymbol = symbol as IMethodSymbol; var methodType = methodSymbol.IsExtensionMethod ? _extensionMethodType : methodSymbol.IsShared() || methodSymbol.ContainingType?.TypeKind == TypeKind.Module ? _sharedMethodType : methodSymbol.ReturnType.SpecialType == SpecialType.System_Void ? _subType : _functionType; AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, methodType, node); break; case SymbolKind.Parameter: AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _parameterType, node); break; case SymbolKind.Property: var propertySymbol = symbol as IPropertySymbol; var propertyType = propertySymbol.IsWithEvents ? _withEventsPropertyType : _propertyType; AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, propertyType, node); break; case SymbolKind.Namespace: var namespaceType = node.IsAliasNamespace(symbol, semanticModel) ? _aliasNamespaceType : _namespaceType; AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, namespaceType, node); break; case SymbolKind.Event: AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _eventType, node); break; case SymbolKind.TypeParameter: AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _typeParameterType, node); break; case SymbolKind.NamedType: var type = GetTypeClassification(symbol as INamedTypeSymbol); if (!(type is null)) { AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, type, node); } break; default: Log.Debug("Symbol kind={0} was on position [{1}..{2}]", symbol.Kind, item.TextSpan.Start, item.TextSpan.End); Log.Debug("Node is: {0}", node); break; } } return(spans); }
internal List <ClassificationSpan> GetClassificationSpans( Workspace workspace, SemanticModel semanticModel, SnapshotSpan span) { var spans = new List <ClassificationSpan>(); var root = semanticModel.SyntaxTree.GetCompilationUnitRoot(); var textSpan = new TextSpan(span.Start.Position, span.Length); foreach (var item in Classifier.GetClassifiedSpans(semanticModel, textSpan, workspace)) { if (!ClassificationHelper.IsSupportedClassification(item.ClassificationType)) { continue; } if (item.ClassificationType == ClassificationTypeNames.Keyword) { var keywordToken = root.FindToken(item.TextSpan.Start, true); switch (keywordToken.ValueText) { case "if": case "else": case "while": case "do": case "for": case "foreach": case "switch": case "case": case "goto": case "return": case "break": case "continue": case "throw": case "yield": case "try": case "catch": case "finally": case "default" when keywordToken.Parent is DefaultSwitchLabelSyntax: AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _controlFlowType); break; } continue; } var node = root.FindNode(item.TextSpan, true).HandleNode(); if (!semanticModel.TryGetSymbolInfo(node, out var symbol, out var reason)) { // NOTE: handle alias in using directive if ((node.Parent as NameEqualsSyntax)?.Parent is UsingDirectiveSyntax usingSyntax) { var aliasNameSymbol = semanticModel.GetSymbolInfo(usingSyntax.Name).Symbol; IClassificationType aliasType = null; if (!(aliasNameSymbol is null)) { aliasType = aliasNameSymbol.Kind == SymbolKind.Namespace ? _aliasNamespaceType : aliasNameSymbol.Kind == SymbolKind.NamedType ? GetTypeClassification(aliasNameSymbol as INamedTypeSymbol) : null; } if (!(aliasType is null)) { AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, aliasType); continue; } } Log.Debug("Nothing is found. Span start at {0} and end at {1}", item.TextSpan.Start, item.TextSpan.End); Log.Debug("Candidate Reason is {0}", reason); Log.Debug("Node is {0}", node); continue; } switch (symbol.Kind) { case SymbolKind.Label: AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _labelType); break; case SymbolKind.RangeVariable: AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _rangeVariableType); break; case SymbolKind.Field: var fieldSymbol = symbol as IFieldSymbol; var fieldType = fieldSymbol.Type.TypeKind == TypeKind.Enum ? _enumFieldType : fieldSymbol.IsConst ? _constantFieldType : _fieldType; AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, fieldType, node); break; case SymbolKind.Property: AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _propertyType, node); break; case SymbolKind.Event: AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _eventType, node); break; case SymbolKind.Local: AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _localVariableType); break; case SymbolKind.Namespace: var namesapceType = node.IsAliasNamespace(symbol, semanticModel) ? _aliasNamespaceType : _namespaceType; AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, namesapceType, node); break; case SymbolKind.Parameter: AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _parameterType, node); break; case SymbolKind.Method: var methodSymbol = symbol as IMethodSymbol; var methodType = methodSymbol.MethodKind == MethodKind.Constructor ? _constructorType : methodSymbol.MethodKind == MethodKind.Destructor ? _destructorType : methodSymbol.MethodKind == MethodKind.LocalFunction ? _localMethodType : methodSymbol.IsExtensionMethod ? _extensionMethodType : methodSymbol.IsStatic ? _staticMethodType : _methodType; AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, methodType, node); break; case SymbolKind.TypeParameter: AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, _typeParameterType, node); break; case SymbolKind.NamedType: var typeSymbol = symbol as INamedTypeSymbol; var type = GetTypeClassification(typeSymbol); if (!(type is null)) { AppendClassificationSpan(spans, span.Snapshot, item.TextSpan, type, node); } break; default: Log.Debug("Symbol kind={0} was on position [{1}..{2}]", symbol.Kind, item.TextSpan.Start, item.TextSpan.End); Log.Debug("Node is: {0}", node); break; } } return(spans); }
private bool IsSupportedClassification(string classification) => ClassificationHelper.IsSupportedClassification(classification) || classification == ClassificationTypeNames.Keyword;