提交 2966c70a 编写于 作者: G Gen Lu

Address review comments

上级 21151ddd
......@@ -6,6 +6,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace;
using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.SyncNamespace;
......@@ -172,6 +173,26 @@ protected async Task TestMoveFileToMatchNamespace(string initialMarkup, List<str
Assert.True(expectedSourceReference == null || changedDocumentIds.Contains(refDocumentId), "reference document was not changed.");
var modifiedOriginalDocument = newSolution.GetDocument(originalDocumentId);
var modifiedOringinalRoot = await modifiedOriginalDocument.GetSyntaxRootAsync();
// One node/token will contain the warning we attached for change namespace action.
Assert.Single(modifiedOringinalRoot.DescendantNodesAndTokensAndSelf().Where(n =>
{
IEnumerable<SyntaxAnnotation> annotations;
if (n.IsNode)
{
annotations = n.AsNode().GetAnnotations(WarningAnnotation.Kind);
}
else
{
annotations = n.AsToken().GetAnnotations(WarningAnnotation.Kind);
}
return annotations.Any(annotation =>
WarningAnnotation.GetDescription(annotation) == FeaturesResources.Warning_colon_changing_namespace_may_produce_invalid_code_and_change_code_meaning);
}));
var actualText = (await modifiedOriginalDocument.GetTextAsync()).ToString();
Assert.Equal(expectedSourceOriginal, actualText);
......
......@@ -1125,18 +1125,6 @@ void M1()
await TestChangeNamespaceAsync(code, expectedSourceOriginal, expectedSourceReference);
}
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsSyncNamespace)]
public async Task ChangeFromGlobalNamespace_SingleDocumentNoRef()
{
......
......@@ -136,9 +136,6 @@ protected override SyntaxList<MemberDeclarationSyntax> GetMemberDeclarationsInCo
// The name will be resolved by adding proper import.
old = @new = nameRef;
return false;
bool IsGlobalNamespace(ImmutableArray<string> parts)
=> parts.Length == 1 && parts[0].Length == 0;
}
protected override string EscapeIdentifier(string identifier)
......@@ -178,12 +175,13 @@ protected override async Task<SyntaxNode> ShouldPositionTriggerRefactoringAsync(
SyntaxNode GetTriggeringNode(CompilationUnitSyntax compUnit, int pos)
{
var namespaceDecls = compilationUnit.DescendantNodes().OfType<NamespaceDeclarationSyntax>().ToImmutableArray();
var namespaceDecls = compilationUnit.DescendantNodes(n => n is CompilationUnitSyntax || n is NamespaceDeclarationSyntax)
.OfType<NamespaceDeclarationSyntax>().ToImmutableArray();
if (namespaceDecls.Length == 1 && compilationUnit.Members.Count == 1)
{
var namespaceDeclaration = namespaceDecls.Single();
Debug.Assert(namespaceDeclaration == compilationUnit.Members.Single());
var namespaceDeclaration = namespaceDecls[0];
Debug.Assert(namespaceDeclaration == compilationUnit.Members[0]);
if (namespaceDeclaration.Name.Span.IntersectsWith(position)
&& namespaceDeclaration.Name.GetDiagnostics().All(diag => diag.DefaultSeverity != DiagnosticSeverity.Error))
......@@ -223,7 +221,7 @@ SyntaxNode GetTriggeringNode(CompilationUnitSyntax compUnit, int pos)
Debug.Assert(!declaredNamespaceParts.IsDefault && !targetNamespaceParts.IsDefault);
// Move everything from global namespace to a namespace declaration
if (declaredNamespaceParts.Length == 1 && declaredNamespaceParts[0].Length == 0)
if (IsGlobalNamespace(declaredNamespaceParts))
{
var targetNamespaceDecl = SyntaxFactory.NamespaceDeclaration(
name: CreateNameSyntax(targetNamespaceParts, aliasQualifier: null, targetNamespaceParts.Length - 1)
......@@ -238,7 +236,7 @@ SyntaxNode GetTriggeringNode(CompilationUnitSyntax compUnit, int pos)
var namespaceDeclaration = compilationUnit.DescendantNodes().OfType<NamespaceDeclarationSyntax>().Single();
// Move everything to global namespace
if (targetNamespaceParts.Length == 1 && targetNamespaceParts[0].Length == 0)
if (IsGlobalNamespace(targetNamespaceParts))
{
var (namespaceOpeningTrivia, namespaceClosingTrivia) =
GetOpeningAndClosingTriviaOfNamespaceDeclaration(namespaceDeclaration);
......@@ -288,9 +286,13 @@ SyntaxNode GetTriggeringNode(CompilationUnitSyntax compUnit, int pos)
return compilationUnit.ReplaceNode(namespaceDeclaration,
namespaceDeclaration.WithName(
CreateNameSyntax(targetNamespaceParts, aliasQualifier: null, targetNamespaceParts.Length - 1)
.WithTriviaFrom(namespaceDeclaration.Name)));
.WithTriviaFrom(namespaceDeclaration.Name)
.WithAdditionalAnnotations(WarningAnnotation)));
}
private static bool IsGlobalNamespace(ImmutableArray<string> parts)
=> parts.Length == 1 && parts[0].Length == 0;
private static string GetAliasQualifierOpt(SyntaxNode name)
{
while (true)
......
......@@ -382,7 +382,7 @@ await FixReferencesAsync(document, addImportService, syncNamespaceService, refLo
{
var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false);
var root = editor.OriginalRoot;
var containers = new HashSet<SyntaxNode>();
var containers = PooledHashSet<SyntaxNode>.GetInstance();
var generator = SyntaxGenerator.GetGenerator(document);
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
......@@ -432,7 +432,10 @@ await FixReferencesAsync(document, addImportService, syncNamespaceService, refLo
var fixedDocument = editor.GetChangedDocument();
root = await fixedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
return (fixedDocument, containers.Select(c => root.GetCurrentNode(c)).ToImmutableArray());
var result = (fixedDocument, containers.SelectAsArray(c => root.GetCurrentNode(c)));
containers.Free();
return result;
}
private async Task<Solution> RemoveUnnecessaryImportsAsync(
......
......@@ -9,6 +9,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
......@@ -159,42 +160,51 @@ private static string GetDefaultNamespace(ImmutableArray<Document> documents, IS
// (See `ShouldPositionTriggerRefactoringAsync`), or we are getting different namespace declarations among
// those documents, then we know we can't make a proper code change. We will return false and the refactoring
// will then bail. We use span of namespace declaration found in each document to decide if they are identical.
var spansForNamespaceDeclaration = new Dictionary<TextSpan, TNamespaceDeclarationSyntax>();
foreach (var document in documents)
{
var compilationUnitOrNamespaceDeclOpt = await service.ShouldPositionTriggerRefactoringAsync(document, textSpan.Start, cancellationToken)
.ConfigureAwait(false);
if (compilationUnitOrNamespaceDeclOpt is TNamespaceDeclarationSyntax namespaceDeclaration)
{
spansForNamespaceDeclaration[namespaceDeclaration.Span] = namespaceDeclaration;
}
else if (compilationUnitOrNamespaceDeclOpt is TCompilationUnitSyntax)
var spansForNamespaceDeclaration = PooledDictionary<TextSpan, TNamespaceDeclarationSyntax>.GetInstance();
try
{
foreach (var document in documents)
{
// In case there's no namespace declaration in the document, we used an empty span as key,
// since a valid namespace declaration node can't have zero length.
spansForNamespaceDeclaration[default] = null;
var compilationUnitOrNamespaceDeclOpt = await service.ShouldPositionTriggerRefactoringAsync(document, textSpan.Start, cancellationToken)
.ConfigureAwait(false);
if (compilationUnitOrNamespaceDeclOpt is TNamespaceDeclarationSyntax namespaceDeclaration)
{
spansForNamespaceDeclaration[namespaceDeclaration.Span] = namespaceDeclaration;
}
else if (compilationUnitOrNamespaceDeclOpt is TCompilationUnitSyntax)
{
// In case there's no namespace declaration in the document, we used an empty span as key,
// since a valid namespace declaration node can't have zero length.
spansForNamespaceDeclaration[default] = null;
}
else
{
return default;
}
}
else
if (spansForNamespaceDeclaration.Count != 1)
{
return default;
}
}
if (spansForNamespaceDeclaration.Count != 1)
var namespaceDecl = spansForNamespaceDeclaration.Values.Single();
var declaredNamespace = namespaceDecl == null
// namespaceDecl == null means the target namespace is global namespace.
? string.Empty
// Since the node in each document has identical type and span,
// they should have same name.
: SyntaxGenerator.GetGenerator(documents.First()).GetName(namespaceDecl);
return (true, declaredNamespace);
}
finally
{
return default;
spansForNamespaceDeclaration.Free();
}
var namespaceDecl = spansForNamespaceDeclaration.Values.Single();
var declaredNamespace = namespaceDecl == null
// namespaceDecl == null means the target namespace is global namespace.
? string.Empty
// Since the node in each document has identical type and span,
// they should have same name.
: SyntaxGenerator.GetGenerator(documents.First()).GetName(namespaceDecl);
return (true, declaredNamespace);
}
public static async Task<State> CreateAsync(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册