File: CodeCleanup\Providers\AbstractTokensCodeCleanupProvider.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 System.Collections.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.Host
Imports Microsoft.CodeAnalysis.Shared.Collections
Imports Microsoft.CodeAnalysis.Text
 
Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers
    Friend MustInherit Class AbstractTokensCodeCleanupProvider
        Implements ICodeCleanupProvider
 
        Public MustOverride ReadOnly Property Name As String Implements ICodeCleanupProvider.Name
 
        Protected MustOverride Function GetRewriterAsync(
            document As Document, root As SyntaxNode, spans As ImmutableArray(Of TextSpan), cancellationToken As CancellationToken) As Task(Of Rewriter)
 
        Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As CodeCleanupOptions, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync
            Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
            Dim rewriter As Rewriter = Await GetRewriterAsync(document, root, spans, cancellationToken).ConfigureAwait(False)
            Dim newRoot = rewriter.Visit(root)
 
            Return If(root Is newRoot, document, document.WithSyntaxRoot(newRoot))
        End Function
 
        Public Async Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As SyntaxFormattingOptions, services As SolutionServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync
            Dim rewriter As Rewriter = Await GetRewriterAsync(Nothing, root, spans, cancellationToken).ConfigureAwait(False)
            Return rewriter.Visit(root)
        End Function
 
        Protected MustInherit Class Rewriter
            Inherits VisualBasicSyntaxRewriter
 
            Protected ReadOnly _spans As TextSpanMutableIntervalTree
            Protected ReadOnly _cancellationToken As CancellationToken
 
            ' a global state indicating whether the visitor is visiting structured trivia or not
            Protected _underStructuredTrivia As Boolean
 
            Public Sub New(spans As ImmutableArray(Of TextSpan), cancellationToken As CancellationToken)
                ' need to visit structured trivia for cases such as "Region"
                MyBase.New(visitIntoStructuredTrivia:=True)
 
                _cancellationToken = cancellationToken
                _spans = New TextSpanMutableIntervalTree(spans)
                _underStructuredTrivia = False
            End Sub
 
            Protected Function ShouldRewrite(node As SyntaxNode) As Boolean
                ' if there is no overlapping spans, no need to walk down this node
                ' use full span to include structured trivia
                Return node IsNot Nothing AndAlso
                    _spans.GetIntervalsThatOverlapWith(node.FullSpan.Start, node.FullSpan.Length).Any()
            End Function
 
            Public Overrides Function Visit(node As SyntaxNode) As SyntaxNode
                _cancellationToken.ThrowIfCancellationRequested()
 
                If Not ShouldRewrite(node) Then
                    Return node
                End If
 
                ' set structured trivia state before walking down tree
                Dim oldState = _underStructuredTrivia
                Try
                    _underStructuredTrivia = node.IsStructuredTrivia() OrElse oldState
 
                    Return MyBase.Visit(node)
                Finally
                    _underStructuredTrivia = oldState
                End Try
            End Function
 
            Protected Shared Function CreateToken(token As SyntaxToken, kind As SyntaxKind) As SyntaxToken
                ' create a new token with valid token text and carries over annotations attached to original token to be a good citizen 
                ' it might be replacing a token that has annotation injected by other code cleanups
                Dim leading = If(token.LeadingTrivia.Count > 0, token.LeadingTrivia, SyntaxTriviaList.Create(SyntaxFactory.ElasticMarker))
                Dim trailing = If(token.TrailingTrivia.Count > 0, token.TrailingTrivia, SyntaxTriviaList.Create(SyntaxFactory.ElasticMarker))
 
                Return token.CopyAnnotationsTo(SyntaxFactory.Token(leading, kind, trailing))
            End Function
 
            Protected Shared Function CreateIdentifierToken(token As SyntaxToken, newValueText As String) As SyntaxToken
                Debug.Assert(token.Kind = SyntaxKind.IdentifierToken)
 
                ' create a new token with valid token text and carries over annotations attached to original token to be a good citizen 
                ' it might be replacing a token that has annotation injected by other code cleanups
                Dim leading = If(token.LeadingTrivia.Count > 0, token.LeadingTrivia, SyntaxTriviaList.Create(SyntaxFactory.ElasticMarker))
                Dim trailing = If(token.TrailingTrivia.Count > 0, token.TrailingTrivia, SyntaxTriviaList.Create(SyntaxFactory.ElasticMarker))
 
                Return token.CopyAnnotationsTo(SyntaxFactory.Identifier(leading, newValueText, trailing))
            End Function
        End Class
    End Class
End Namespace