/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/EditorFeatures/VisualBasic/LineCommit/ContainingStatementInfo.vb
189 строк
9 KB
Cyrus Najmabadi
Switch to more modern vb construc
04 янв 2025, 00:11
04 янв 2025, 00:11
03adb27
Код
Авторство
О чём код?
' 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.Threading Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.VisualStudio.Text Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit Partial Friend Class ContainingStatementInfo Public ReadOnly IsIncomplete As Boolean Public ReadOnly TextSpan As TextSpan Public ReadOnly MatchingBlockConstruct As StatementSyntax Public Sub New(node As SyntaxNode) Me.New(node, node.Span) End Sub Public Sub New(node As SyntaxNode, span As TextSpan) TextSpan = span IsIncomplete = node.GetLastToken(includeZeroWidth:=True).IsMissing ' We'll only do expansion if there were no errors Dim statement = TryCast(node, StatementSyntax) If Not IsIncomplete AndAlso statement IsNot Nothing Then MatchingBlockConstruct = FindExpansionStatement(statement) End If End Sub Public Sub New(trivia As SyntaxTrivia) TextSpan = trivia.Span IsIncomplete = trivia.ContainsDiagnostics End Sub ''' <summary> ''' This function returns the "logical" statement that a given point is in. "Logical" in ''' this case means "the smallest unit the user probably thinks as a statement", or "the ''' thing we should format when you leave it." ''' </summary> Public Shared Function GetInfo(point As SnapshotPoint, syntaxTree As SyntaxTree, cancellationToken As CancellationToken) As ContainingStatementInfo Dim snapshot = point.Snapshot Dim pointLineNumber = snapshot.GetLineNumberFromPosition(point) ' Let's see if we're following a query which we are continuing Dim previousToken = syntaxTree.FindTokenOnLeftOfPosition(point, cancellationToken) If previousToken.IsLastTokenOfStatement() Then Dim previousRealTokenLineNumber = snapshot.GetLineNumberFromPosition(previousToken.SpanStart) If pointLineNumber = previousRealTokenLineNumber + 1 AndAlso previousToken.GetAncestor(Of QueryClauseSyntax)() IsNot Nothing Then Return New ContainingStatementInfo(previousToken.GetAncestor(Of StatementSyntax)()) End If End If Dim trivia = syntaxTree.GetRoot(cancellationToken).FindTrivia(point) ' If we're at the newline, we'll want to look to the left instead If trivia.Kind = SyntaxKind.None Or trivia.Kind = SyntaxKind.EndOfLineTrivia Then trivia = syntaxTree.FindTriviaToLeft(point, cancellationToken) End If If trivia.Kind = SyntaxKind.CommentTrivia OrElse trivia.Kind = SyntaxKind.DocumentationCommentTrivia Then Return GetContainingStatementInfoForTrivia(trivia, snapshot, pointLineNumber) End If ' We'll keep going to the left to see if we find a LineContinuation trivia before we see more than one newline Dim alreadySawNewLine = False Do While trivia.Kind <> SyntaxKind.None If trivia.Kind = SyntaxKind.LineContinuationTrivia Then Dim lineNumberOfContinuation = snapshot.GetLineNumberFromPosition(trivia.SpanStart) ' We can be either on the line, or the line immediately following it If pointLineNumber = lineNumberOfContinuation OrElse pointLineNumber = lineNumberOfContinuation + 1 AndAlso trivia.Token.GetAncestor(Of StatementSyntax)() IsNot Nothing Then Return New ContainingStatementInfo(trivia.Token.GetAncestor(Of StatementSyntax)()) Else Exit Do End If End If trivia = syntaxTree.FindTriviaToLeft(trivia.SpanStart, cancellationToken) If trivia.Kind = SyntaxKind.EndOfLineTrivia Then If alreadySawNewLine Then Exit Do Else alreadySawNewLine = True End If End If Loop Dim token = syntaxTree.GetRoot(cancellationToken).FindToken(point, findInsideTrivia:=True) ' If the first token is on the next line, then we're blank and so we have no statement If pointLineNumber <> snapshot.GetLineNumberFromPosition(token.SpanStart) Then Return Nothing End If Dim containingDirective = token.GetAncestor(Of DirectiveTriviaSyntax)() If containingDirective IsNot Nothing Then Return New ContainingStatementInfo(containingDirective) End If Dim containingStatement = token.GetAncestors(Of StatementSyntax) _ .Where(Function(a) TypeOf a IsNot LambdaHeaderSyntax) _ .FirstOrDefault() Dim containingTypeStatement = TryCast(containingStatement, TypeStatementSyntax) If containingTypeStatement IsNot Nothing Then Return GetContainingStatementInfoForAttributedStatement(containingTypeStatement, containingTypeStatement.AttributeLists, point) End If Dim containingMethodStatement = TryCast(containingStatement, MethodBaseSyntax) If containingMethodStatement IsNot Nothing Then Return GetContainingStatementInfoForAttributedStatement(containingMethodStatement, containingMethodStatement.AttributeLists, point) End If If containingStatement IsNot Nothing Then Return New ContainingStatementInfo(containingStatement) End If Return Nothing End Function Private Shared Function GetContainingStatementInfoForTrivia(trivia As SyntaxTrivia, snapshot As ITextSnapshot, pointLineNumber As Integer) As ContainingStatementInfo Dim triviaStatement = trivia.Token.GetAncestor(Of StatementSyntax)() ' If the trivia is on a different line then the actual token, we consider this ' comment to be on it's own statement entirely If triviaStatement Is Nothing OrElse snapshot.GetLineNumberFromPosition(trivia.Token.SpanStart) <> pointLineNumber Then Return New ContainingStatementInfo(trivia) Else ' It's on the same line as the statement, so just do the statement Return New ContainingStatementInfo(triviaStatement) End If End Function Private Shared Function FindExpansionStatement(node As StatementSyntax) As StatementSyntax For Each ancestor In node.Ancestors() Dim matchingStatements = MatchingStatementsVisitor.Instance.Visit(ancestor) If matchingStatements Is Nothing Then Continue For End If Dim indexOfNode = matchingStatements.IndexOf(node) ' If we wrote the opening statement, then expand to the end Dim possibleExpansion As StatementSyntax = Nothing If indexOfNode = 0 Then possibleExpansion = matchingStatements.Last() ElseIf indexOfNode > 0 Then ' Somewhere in the middle or end, so expand to the beginning possibleExpansion = matchingStatements.First() End If If possibleExpansion IsNot Nothing AndAlso Not possibleExpansion.IsMissing Then Return possibleExpansion End If Next Return Nothing End Function Private Shared Function GetContainingStatementInfoForAttributedStatement(node As StatementSyntax, attributes As SyntaxList(Of AttributeListSyntax), position As Integer) As ContainingStatementInfo If Not attributes.Any Then Return New ContainingStatementInfo(node) End If ' If we're inside a attribute of the statement, then we should count that as it's own ' statement Dim containingAttribute = attributes.FirstOrDefault(Function(a) a.Span.Contains(position) OrElse a.Span.End = position) If containingAttribute IsNot Nothing Then Return New ContainingStatementInfo(containingAttribute) End If ' We're outside all the attributes, so we'll return just the span that ignores the ' attributes Return New ContainingStatementInfo(node, TextSpan.FromBounds(attributes.Last.Span.End, node.Span.End)) End Function End Class End Namespace