|
' 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.Composition
Imports System.Diagnostics.CodeAnalysis
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.Formatting.Rules
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.Indentation
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Indentation
<ExportLanguageService(GetType(IIndentationService), LanguageNames.VisualBasic), [Shared]>
Partial Friend NotInheritable Class VisualBasicIndentationService
Inherits AbstractIndentationService(Of CompilationUnitSyntax)
Public Shared ReadOnly WithoutParameterAlignmentInstance As New VisualBasicIndentationService(NoOpFormattingRule.Instance)
Private ReadOnly _specializedIndentationRule As AbstractFormattingRule
<ImportingConstructor>
<Obsolete(MefConstruction.ImportingConstructorMessage, True)>
Public Sub New()
Me.New(Nothing)
End Sub
<SuppressMessage("RoslynDiagnosticsReliability", "RS0034:Exported parts should have [ImportingConstructor]", Justification:="Intentionally used for creating multiple instances")>
Private Sub New(specializedIndentationRule As AbstractFormattingRule)
_specializedIndentationRule = specializedIndentationRule
End Sub
Protected Overrides Function GetSpecializedIndentationFormattingRule(indentStyle As FormattingOptions2.IndentStyle) As AbstractFormattingRule
Return If(_specializedIndentationRule, New SpecialFormattingRule(indentStyle))
End Function
Public Overloads Shared Function ShouldUseSmartTokenFormatterInsteadOfIndenter(
formattingRules As IEnumerable(Of AbstractFormattingRule),
root As CompilationUnitSyntax,
line As TextLine,
options As SyntaxFormattingOptions,
ByRef token As SyntaxToken,
Optional neverUseWhenHavingMissingToken As Boolean = True) As Boolean
' find first text on line
Dim firstNonWhitespacePosition = line.GetFirstNonWhitespacePosition()
If Not firstNonWhitespacePosition.HasValue Then
Return False
End If
' enter on token only works when first token on line is first text on line
token = root.FindToken(firstNonWhitespacePosition.Value)
If IsInvalidToken(token) Then
Return False
End If
If token.Kind = SyntaxKind.None OrElse token.SpanStart <> firstNonWhitespacePosition Then
Return False
End If
' now try to gather various token information to see whether we are at an applicable position.
' all these are heuristic based
'
' we need at least current and previous tokens to ask about existing line break formatting rules
Dim previousToken = token.GetPreviousToken(includeZeroWidth:=True)
' only use smart token formatter when we have at least two visible tokens.
If previousToken.Kind = SyntaxKind.None Then
Return False
End If
' check special case
' if previous token (or one before previous token if the previous token is statement terminator token) is missing, make sure
' we are a first token of a statement
If previousToken.IsMissing AndAlso neverUseWhenHavingMissingToken Then
Return False
ElseIf previousToken.IsMissing Then
Dim statement = token.GetAncestor(Of StatementSyntax)()
If statement Is Nothing Then
Return False
End If
' check whether current token is first token of a statement
Return statement.GetFirstToken() = token
End If
' now, regular case. ask formatting rule to see whether we should use token formatter or not
Dim lineOperation = FormattingOperations.GetAdjustNewLinesOperation(formattingRules, previousToken, token, options)
If lineOperation IsNot Nothing AndAlso lineOperation.Option <> AdjustNewLinesOption.ForceLinesIfOnSingleLine Then
Return True
End If
' check whether there is an alignment operation
Dim startNode = token.Parent
Dim currentNode = startNode
Dim localToken = token
Do While currentNode IsNot Nothing
Dim operations = FormattingOperations.GetAlignTokensOperations(
formattingRules, currentNode, options)
If Not operations.Any() Then
currentNode = currentNode.Parent
Continue Do
End If
' make sure we have the given token as one of tokens to be aligned to the base token
Dim match = operations.FirstOrDefault(Function(o) o.Tokens.Contains(localToken))
If match IsNot Nothing Then
Return True
End If
currentNode = currentNode.Parent
Loop
' no indentation operation, nothing to do for smart token formatter
Return False
End Function
Private Shared Function IsInvalidToken(token As SyntaxToken) As Boolean
' invalid token to be formatted
Return token.Kind = SyntaxKind.None OrElse
token.Kind = SyntaxKind.EndOfFileToken
End Function
End Class
End Namespace
|