提交 515e4482 编写于 作者: J jmarolf

Fix for 739773.

     We now look for directives in the using statements and attempt to place the new using outside if possible.
***NO_CI***
 (changeset 1389840)
上级 cdca0653
......@@ -114,19 +114,16 @@ private static TextSpan GetUsingsSpan(CompilationUnitSyntax root, NamespaceDecla
bool placeSystemNamespaceFirst,
params SyntaxAnnotation[] annotations)
{
if (!usingDirectives.Any())
if (usingDirectives.Count == 0)
{
return root;
}
var specialCaseSystem = placeSystemNamespaceFirst;
var comparer = specialCaseSystem
var comparer = placeSystemNamespaceFirst
? UsingsAndExternAliasesDirectiveComparer.SystemFirstInstance
: UsingsAndExternAliasesDirectiveComparer.NormalInstance;
var usings = new List<UsingDirectiveSyntax>();
usings.AddRange(root.Usings);
usings.AddRange(usingDirectives);
var usings = AddUsingDirectives(root, usingDirectives);
// If the user likes to have their Usings statements unsorted, allow them to
if (root.Usings.IsSorted(comparer))
......@@ -163,5 +160,41 @@ private static TextSpan GetUsingsSpan(CompilationUnitSyntax root, NamespaceDecla
usings = usings.Select(u => u.WithAdditionalAnnotations(annotations)).ToList();
return root.WithUsings(usings.ToSyntaxList());
}
private static List<UsingDirectiveSyntax> AddUsingDirectives(CompilationUnitSyntax root, IList<UsingDirectiveSyntax> usingDirectives)
{
// We need to try and not place the using inside of a directive if possible.
var usings = new List<UsingDirectiveSyntax>();
var endOfList = root.Usings.Count - 1;
int startOfLastDirective = -1;
int endOfLastDirective = -1;
for (int i = 0; i < root.Usings.Count; i++)
{
if (root.Usings[i].GetLeadingTrivia().Any(trivia => trivia.IsKind(SyntaxKind.IfDirectiveTrivia)))
{
startOfLastDirective = i;
}
if (root.Usings[i].GetLeadingTrivia().Any(trivia => trivia.IsKind(SyntaxKind.EndIfDirectiveTrivia)))
{
endOfLastDirective = i;
}
}
// if the entire using is in a directive or there is a using list at the end outside of the directive add the using at the end,
// else place it before the last directive.
usings.AddRange(root.Usings);
if ((startOfLastDirective == 0 && (endOfLastDirective == endOfList || endOfLastDirective == -1)) ||
(startOfLastDirective == -1 && endOfLastDirective == -1) ||
(endOfLastDirective != endOfList && endOfLastDirective != -1))
{
usings.AddRange(usingDirectives);
}
else
{
usings.InsertRange(startOfLastDirective, usingDirectives);
}
return usings;
}
}
}
\ No newline at end of file
......@@ -12,6 +12,13 @@ namespace Microsoft.CodeAnalysis.CSharp.Extensions
{
internal static class SyntaxListExtensions
{
public static SyntaxList<T> RemoveRange<T>(this SyntaxList<T> syntaxList, int index, int count) where T : SyntaxNode
{
var result = new List<T>(syntaxList);
result.RemoveRange(index, count);
return SyntaxFactory.List(result);
}
public static SyntaxList<T> ToSyntaxList<T>(this IEnumerable<T> sequence) where T : SyntaxNode
{
return SyntaxFactory.List(sequence);
......
......@@ -51,50 +51,69 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
ImportsStatementComparer.SystemFirstInstance,
ImportsStatementComparer.NormalInstance)
Dim usings As SyntaxList(Of ImportsStatementSyntax)
Dim [imports] = AddImportsStatements(root, importsStatements)
' If the user likes to have their Imports statements unsorted, allow them to
If root.Imports.IsSorted(comparer) Then
' already sorted. find where it should go into the list.
usings = root.Imports
[imports].Sort(comparer)
End If
For Each newImport In importsStatements
Dim list = New List(Of ImportsStatementSyntax)(usings)
Dim index = list.BinarySearch(newImport, comparer)
index = If(index < 0, Not index, index)
' If any using we added was moved to the first location, then take the trivia from
' the start of the first token And add it to the using we added. This way things
' Like #define's and #r's will stay above the using.
If root.Imports.Count = 0 OrElse [imports](0) IsNot root.Imports(0) Then
Dim firstToken = root.GetFirstToken
usings = Insert(usings, index, newImport)
Next
Else
usings = root.Imports.InsertRange(root.Imports.Count, importsStatements.ToArray())
End If
' Move the leading directives from the first directive to the New using.
Dim firstImport = [imports](0).WithLeadingTrivia(firstToken.LeadingTrivia.Where(Function(t) Not t.IsKind(SyntaxKind.DocumentationCommentTrivia) AndAlso Not t.IsElastic))
Dim newRoot = root.WithImports(usings)
' Remove the leading directives from the first token.
Dim newFirstToken = firstToken.WithLeadingTrivia(firstToken.LeadingTrivia.Where(Function(t) t.IsKind(SyntaxKind.DocumentationCommentTrivia) OrElse t.IsElastic))
Return newRoot.WithAdditionalAnnotations(annotations)
' Remove the leading trivia from the first token from the tree.
root = root.ReplaceToken(firstToken, newFirstToken)
' Create the New list of imports
Dim finalImports = New List(Of ImportsStatementSyntax)
finalImports.Add(firstImport)
finalImports.AddRange(root.Imports)
finalImports.AddRange(importsStatements.Except({[imports](0)}))
finalImports.Sort(comparer)
[imports] = finalImports
End If
Return root.WithImports([imports].ToSyntaxList).WithAdditionalAnnotations(annotations)
End Function
Private Function Insert(usings As SyntaxList(Of ImportsStatementSyntax), index As Integer, newImport As ImportsStatementSyntax) As SyntaxList(Of ImportsStatementSyntax)
If index = 0 AndAlso usings.Count > 0 Then
' take any leading trivia from the existing first using and add it to the new using
' that is being added.
Dim firstImport = usings.First()
Dim firstToken = firstImport.GetFirstToken()
Dim leadingTrivia = firstToken.LeadingTrivia
firstImport = firstImport.ReplaceToken(
firstToken,
firstToken.WithLeadingTrivia())
Dim statements = New List(Of ImportsStatementSyntax)
statements.Add(firstImport)
statements.AddRange(usings.Skip(1))
usings = SyntaxFactory.List(statements)
newImport = newImport.ReplaceToken(
newImport.GetFirstToken(),
newImport.GetFirstToken().WithLeadingTrivia(leadingTrivia))
Private Function AddImportsStatements(root As CompilationUnitSyntax, importsStatements As IList(Of ImportsStatementSyntax)) As List(Of ImportsStatementSyntax)
' We need to try and not place the using inside of a directive if possible.
Dim [imports] = New List(Of ImportsStatementSyntax)
Dim importsLength = root.Imports.Count
Dim endOfList = importsLength - 1
Dim startOfLastDirective = -1
Dim endOfLastDirective = -1
For index = 0 To endOfList
If root.Imports(index).GetLeadingTrivia().Any(Function(trivia) trivia.IsKind(SyntaxKind.IfDirectiveTrivia, SyntaxKind.ElseIfDirectiveTrivia, SyntaxKind.ElseDirectiveTrivia)) Then
startOfLastDirective = index
End If
If root.Imports(index).GetLeadingTrivia().Any(Function(trivia) trivia.IsKind(SyntaxKind.EndIfDirectiveTrivia)) Then
endOfLastDirective = index
End If
Next
' if the entire using Is in a directive Or there Is a using list at the end outside of the directive add the using at the end,
' else place it before the last directive.
[imports].AddRange(root.Imports)
If (startOfLastDirective = 0 AndAlso (endOfLastDirective = endOfList OrElse endOfLastDirective = -1)) OrElse
(startOfLastDirective = -1 AndAlso endOfLastDirective = -1) OrElse
(endOfLastDirective <> endOfList AndAlso endOfLastDirective <> -1) Then
[imports].AddRange(importsStatements)
Else
[imports].InsertRange(startOfLastDirective, importsStatements)
End If
Return usings.Insert(index, newImport)
Return [imports]
End Function
End Module
End Namespace
\ No newline at end of file
......@@ -11,5 +11,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
result.RemoveRange(index, count)
Return SyntaxFactory.List(result)
End Function
<Extension()>
Public Function ToSyntaxList(Of T As SyntaxNode)(sequence As IEnumerable(Of T)) As SyntaxList(Of T)
Return SyntaxFactory.List(sequence)
End Function
<Extension()>
Public Function Insert(Of T As SyntaxNode)(syntaxList As SyntaxList(Of T), index As Integer, item As T) As SyntaxList(Of T)
Return syntaxList.Take(index).Concat(item).Concat(syntaxList.Skip(index)).ToSyntaxList
End Function
End Module
End Namespace
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册