File: Classification\Worker.XmlClassifier.vb
Web Access
Project: src\src\Workspaces\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj (Microsoft.CodeAnalysis.VisualBasic.Workspaces)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports Microsoft.CodeAnalysis.Classification
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Classification
    Partial Friend Class Worker
        Private Class XmlClassifier
            Private ReadOnly _worker As Worker
 
            Public Sub New(worker As Worker)
                _worker = worker
            End Sub
 
            Private Sub AddTokenClassification(token As SyntaxToken, classificationType As String)
                _worker.AddClassification(token, classificationType)
                _worker.ClassifyTrivia(token)
            End Sub
 
            Private Sub ClassifyToken(token As SyntaxToken)
                _worker.ClassifyToken(token)
            End Sub
 
            Friend Sub ClassifyNode(node As SyntaxNode)
                ' Note: Use FullSpan in case we need to classify trivia around the span of the node.
                If Not _worker._textSpan.OverlapsWith(node.FullSpan) Then
                    Return
                End If
 
                If TypeOf node Is XmlNodeSyntax Then
                    ClassifyXmlNode(DirectCast(node, XmlNodeSyntax))
                Else
                    Select Case node.Kind
                        Case SyntaxKind.XmlDeclaration
                            ClassifyDeclaration(DirectCast(node, XmlDeclarationSyntax))
                        Case SyntaxKind.XmlDeclarationOption
                            ClassifyDeclarationOption(DirectCast(node, XmlDeclarationOptionSyntax))
                        Case SyntaxKind.XmlNamespaceImportsClause
                            ClassifyXmlNamespaceImportsClause(DirectCast(node, XmlNamespaceImportsClauseSyntax))
                        Case SyntaxKind.XmlAttributeAccessExpression,
                             SyntaxKind.XmlDescendantAccessExpression,
                             SyntaxKind.XmlElementAccessExpression
                            ClassifyXmlMemberAccessExpression(DirectCast(node, XmlMemberAccessExpressionSyntax))
                        Case SyntaxKind.GetXmlNamespaceExpression
                            ClassifyGetXmlNamespaceExpression(DirectCast(node, GetXmlNamespaceExpressionSyntax))
                    End Select
                End If
            End Sub
 
            Private Sub ClassifyXmlNode(node As XmlNodeSyntax)
                Select Case node.Kind
                    Case SyntaxKind.XmlDocument
                        ClassifyXmlDocument(DirectCast(node, XmlDocumentSyntax))
                    Case SyntaxKind.XmlElement
                        ClassifyXmlElement(DirectCast(node, XmlElementSyntax))
                    Case SyntaxKind.XmlElementStartTag
                        ClassifyXmlStartElement(DirectCast(node, XmlElementStartTagSyntax))
                    Case SyntaxKind.XmlElementEndTag
                        ClassifyXmlEndElement(DirectCast(node, XmlElementEndTagSyntax))
                    Case SyntaxKind.XmlEmptyElement
                        ClassifyXmlEmptyElement(DirectCast(node, XmlEmptyElementSyntax))
                    Case SyntaxKind.XmlAttribute
                        ClassifyXmlAttribute(DirectCast(node, XmlAttributeSyntax))
                    Case SyntaxKind.XmlString
                        ClassifyXmlString(DirectCast(node, XmlStringSyntax))
                    Case SyntaxKind.XmlProcessingInstruction
                        ClassifyXmlProcessingInstruction(DirectCast(node, XmlProcessingInstructionSyntax))
                    Case SyntaxKind.XmlName
                        ClassifyXmlName(DirectCast(node, XmlNameSyntax))
                    Case SyntaxKind.XmlComment
                        ClassifyXmlComment(DirectCast(node, XmlCommentSyntax))
                    Case SyntaxKind.XmlCDataSection
                        ClassifyXmlCData(DirectCast(node, XmlCDataSectionSyntax))
                    Case SyntaxKind.XmlText
                        ClassifyXmlText(DirectCast(node, XmlTextSyntax))
                    Case SyntaxKind.XmlEmbeddedExpression
                        ClassifyXmlEmbeddedExpression(DirectCast(node, XmlEmbeddedExpressionSyntax))
                End Select
            End Sub
 
            Private Sub ClassifyXmlDocument(node As XmlDocumentSyntax)
                For Each xmlNode In node.PrecedingMisc
                    ClassifyXmlNode(xmlNode)
                Next
 
                ClassifyDeclaration(node.Declaration)
 
                For Each xmlNode In node.FollowingMisc
                    ClassifyXmlNode(xmlNode)
                Next
 
                ClassifyXmlNode(node.Root)
            End Sub
 
            Private Sub ClassifyXmlStartElement(node As XmlElementStartTagSyntax)
                AddTokenClassification(node.GreaterThanToken, ClassificationTypeNames.XmlLiteralDelimiter)
                ClassifyXmlNode(node.Name)
 
                For Each attribute In node.Attributes
                    ClassifyXmlNode(attribute)
                Next
 
                AddTokenClassification(node.LessThanToken, ClassificationTypeNames.XmlLiteralDelimiter)
            End Sub
 
            Private Sub ClassifyXmlEndElement(node As XmlElementEndTagSyntax)
                AddTokenClassification(node.GreaterThanToken, ClassificationTypeNames.XmlLiteralDelimiter)
 
                If node.Name IsNot Nothing Then
                    ClassifyXmlName(node.Name)
                End If
 
                AddTokenClassification(node.LessThanSlashToken, ClassificationTypeNames.XmlLiteralDelimiter)
            End Sub
 
            Private Sub ClassifyXmlElement(node As XmlElementSyntax)
                If node.StartTag IsNot Nothing Then
                    ClassifyXmlNode(node.StartTag)
                End If
 
                For Each content In node.Content
                    ClassifyXmlNode(content)
                Next
 
                If node.EndTag IsNot Nothing Then
                    ClassifyXmlNode(node.EndTag)
                End If
            End Sub
 
            Private Sub ClassifyXmlEmptyElement(node As XmlEmptyElementSyntax)
                AddTokenClassification(node.LessThanToken, ClassificationTypeNames.XmlLiteralDelimiter)
                ClassifyXmlNode(node.Name)
 
                For Each attribute In node.Attributes
                    ClassifyXmlNode(attribute)
                Next
 
                AddTokenClassification(node.SlashGreaterThanToken, ClassificationTypeNames.XmlLiteralDelimiter)
            End Sub
 
            Private Sub ClassifyXmlAttribute(node As XmlAttributeSyntax)
                ClassifyXmlNode(node.Name)
                AddTokenClassification(node.EqualsToken, ClassificationTypeNames.XmlLiteralDelimiter)
                ClassifyXmlNode(node.Value)
            End Sub
 
            Private Sub ClassifyXmlString(node As XmlStringSyntax)
                AddTokenClassification(node.StartQuoteToken, ClassificationTypeNames.XmlLiteralAttributeQuotes)
 
                For Each textToken In node.TextTokens
                    ClassifyToken(textToken)
                Next
 
                AddTokenClassification(node.EndQuoteToken, ClassificationTypeNames.XmlLiteralAttributeQuotes)
            End Sub
            Private Sub ClassifyXmlProcessingInstruction(node As XmlProcessingInstructionSyntax)
                AddTokenClassification(node.LessThanQuestionToken, ClassificationTypeNames.XmlLiteralDelimiter)
                AddTokenClassification(node.Name, ClassificationTypeNames.XmlLiteralName)
 
                For Each textToken In node.TextTokens
                    ClassifyToken(textToken)
                Next
 
                AddTokenClassification(node.QuestionGreaterThanToken, ClassificationTypeNames.XmlLiteralDelimiter)
            End Sub
 
            Private Sub ClassifyXmlEmbeddedExpression(node As XmlEmbeddedExpressionSyntax)
                AddTokenClassification(node.LessThanPercentEqualsToken, ClassificationTypeNames.XmlLiteralEmbeddedExpression)
                _worker.ClassifyNode(node.Expression)
                AddTokenClassification(node.PercentGreaterThanToken, ClassificationTypeNames.XmlLiteralEmbeddedExpression)
            End Sub
 
            Private Sub ClassifyXmlComment(node As XmlCommentSyntax)
                AddTokenClassification(node.LessThanExclamationMinusMinusToken, ClassificationTypeNames.XmlLiteralDelimiter)
 
                For Each textToken In node.TextTokens
                    ClassifyToken(textToken)
                Next
 
                AddTokenClassification(node.MinusMinusGreaterThanToken, ClassificationTypeNames.XmlLiteralDelimiter)
            End Sub
 
            Private Sub ClassifyXmlCData(node As XmlCDataSectionSyntax)
                AddTokenClassification(node.BeginCDataToken, ClassificationTypeNames.XmlLiteralDelimiter)
 
                For Each textToken In node.TextTokens
                    ClassifyToken(textToken)
                Next
 
                AddTokenClassification(node.EndCDataToken, ClassificationTypeNames.XmlLiteralDelimiter)
            End Sub
 
            Private Sub ClassifyXmlText(node As XmlTextSyntax)
                For Each textToken In node.TextTokens
                    ClassifyToken(textToken)
                Next
            End Sub
 
            Private Sub ClassifyDeclaration(node As XmlDeclarationSyntax)
                AddTokenClassification(node.LessThanQuestionToken, ClassificationTypeNames.XmlLiteralDelimiter)
                AddTokenClassification(node.XmlKeyword, ClassificationTypeNames.XmlLiteralName)
 
                If node.Encoding IsNot Nothing Then
                    ClassifyDeclarationOption(node.Encoding)
                End If
 
                If node.Standalone IsNot Nothing Then
                    ClassifyDeclarationOption(node.Standalone)
                End If
 
                ClassifyDeclarationOption(node.Version)
 
                AddTokenClassification(node.QuestionGreaterThanToken, ClassificationTypeNames.XmlLiteralDelimiter)
            End Sub
 
            Private Sub ClassifyDeclarationOption(node As XmlDeclarationOptionSyntax)
                AddTokenClassification(node.Name, ClassificationTypeNames.XmlLiteralAttributeName)
                AddTokenClassification(node.Equals, ClassificationTypeNames.XmlLiteralDelimiter)
                ClassifyXmlNode(node.Value)
            End Sub
 
            Private Sub ClassifyXmlNamespaceImportsClause(node As XmlNamespaceImportsClauseSyntax)
                AddTokenClassification(node.LessThanToken, ClassificationTypeNames.XmlLiteralDelimiter)
                ClassifyXmlNode(node.XmlNamespace)
                AddTokenClassification(node.GreaterThanToken, ClassificationTypeNames.XmlLiteralDelimiter)
            End Sub
 
            Private Sub ClassifyXmlName(element As XmlNameSyntax)
                ' First we need to determine the context of the name so we know the type
                Dim type As String = Nothing
                If TypeOf element.Parent Is XmlAttributeSyntax AndAlso
                    DirectCast(element.Parent, XmlAttributeSyntax).Name Is element Then
                    type = ClassificationTypeNames.XmlLiteralAttributeName
                ElseIf TypeOf element.Parent Is XmlMemberAccessExpressionSyntax AndAlso
                    element.Parent.Kind = SyntaxKind.XmlAttributeAccessExpression AndAlso
                    DirectCast(element.Parent, XmlMemberAccessExpressionSyntax).Name Is element Then
                    type = ClassificationTypeNames.XmlLiteralAttributeName
                ElseIf IsElementName(element) Then
                    type = ClassificationTypeNames.XmlLiteralName
                End If
 
                If type IsNot Nothing Then
                    If element.Prefix IsNot Nothing Then
                        Dim prefix = element.Prefix
                        AddTokenClassification(prefix.ColonToken, type)
                        AddTokenClassification(prefix.Name, type)
                    End If
 
                    If Not element.LocalName.IsMissing Then
                        AddTokenClassification(element.LocalName, type)
                    End If
                End If
            End Sub
 
            Friend Shared Function IsElementName(name As XmlNameSyntax) As Boolean
                Dim parent = name.Parent
                Dim startParent = TryCast(parent, XmlElementStartTagSyntax)
                If startParent IsNot Nothing AndAlso startParent.Name Is name Then
                    Return True
                End If
 
                Dim endParent = TryCast(parent, XmlElementEndTagSyntax)
                If endParent IsNot Nothing AndAlso endParent.Name Is name Then
                    Return True
                End If
 
                Dim emptyParent = TryCast(parent, XmlEmptyElementSyntax)
                If emptyParent IsNot Nothing AndAlso emptyParent.Name Is name Then
                    Return True
                End If
 
                Dim bracketedNameParent = TryCast(parent, XmlBracketedNameSyntax)
                If bracketedNameParent IsNot Nothing AndAlso bracketedNameParent.Name Is name Then
                    Return True
                End If
 
                Return False
            End Function
 
            Private Sub ClassifyXmlBracketedName(bracketedName As XmlBracketedNameSyntax)
                AddTokenClassification(bracketedName.LessThanToken, ClassificationTypeNames.XmlLiteralDelimiter)
                ClassifyXmlName(bracketedName.Name)
                AddTokenClassification(bracketedName.GreaterThanToken, ClassificationTypeNames.XmlLiteralDelimiter)
            End Sub
 
            Private Sub ClassifyXmlMemberAccessExpression(syntax As XmlMemberAccessExpressionSyntax)
                If syntax.Base IsNot Nothing Then
                    _worker.ClassifyNode(syntax.Base)
                End If
 
                ' XML member access operators are split into several tokens:
                '     1. separator -- the initial '.'
                '     2. optional '.' or '@' -- required for descendant access '...' or attribute access '.@'
                '     3. final optional '.' -- required for descendant access '...'
                ' After the operator, is a name -- which might need to be classified as an element or an attribute name
 
                ' Classify initial '.'
                AddTokenClassification(syntax.Token1, ClassificationTypeNames.XmlLiteralDelimiter)
 
                ' Classify access modifier (e.g. '..' or '@')
                If Not syntax.Token2.IsMissing Then
                    AddTokenClassification(syntax.Token2, ClassificationTypeNames.XmlLiteralDelimiter)
                End If
 
                If Not syntax.Token3.IsMissing Then
                    AddTokenClassification(syntax.Token3, ClassificationTypeNames.XmlLiteralDelimiter)
                End If
 
                ' Classify name -- which should be the last child of the expression (e.g.
                ' x.<goo>, x...<goo> or x.@goo). Note that the name can be an XmlName in the
                ' case of an attribute, or an XmlBracketName, in which case, the brackets need
                ' to be classified as well
                Dim childNodesAndTokens = syntax.ChildNodesAndTokens()
                Dim lastChild = If(childNodesAndTokens.IsEmpty, Nothing, childNodesAndTokens(childNodesAndTokens.Count - 1))
                If lastChild.Kind() <> SyntaxKind.None Then
                    Select Case lastChild.Kind()
                        Case SyntaxKind.XmlName
                            ClassifyXmlName(DirectCast(lastChild.AsNode(), XmlNameSyntax))
                        Case SyntaxKind.XmlBracketedName
                            ClassifyXmlBracketedName(DirectCast(lastChild.AsNode(), XmlBracketedNameSyntax))
                    End Select
                End If
            End Sub
 
            Private Sub ClassifyGetXmlNamespaceExpression(node As GetXmlNamespaceExpressionSyntax)
                AddTokenClassification(node.GetXmlNamespaceKeyword, ClassificationTypeNames.Keyword)
                AddTokenClassification(node.OpenParenToken, ClassificationTypeNames.Punctuation)
 
                If node.Name IsNot Nothing Then
                    AddTokenClassification(node.Name.Name, ClassificationTypeNames.XmlLiteralName)
                End If
 
                AddTokenClassification(node.CloseParenToken, ClassificationTypeNames.Punctuation)
            End Sub
        End Class
    End Class
End Namespace