private static AnalysisResult?Analyze( Document document, IReadOnlyCollection <string> newFolders ) { // https://github.com/dotnet/roslyn/issues/41841 // VB implementation is incomplete for sync namespace if (document.Project.Language == LanguageNames.CSharp) { var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>(); var targetNamespace = PathMetadataUtilities.TryBuildNamespaceFromFolders( newFolders, syntaxFacts, document.Project.DefaultNamespace ); if (targetNamespace is null) { return(null); } return(new AnalysisResult(targetNamespace)); } else { return(null); } }
public static async Task <State> CreateAsync( AbstractSyncNamespaceCodeRefactoringProvider <TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax> provider, Document document, TextSpan textSpan, CancellationToken cancellationToken) { // User must put cursor on one of the nodes described below to trigger the refactoring. // For each scenario, all requirements must be met. Some of them are checked by `TryGetApplicableInvocationNodeAsync`, // rest by `IChangeNamespaceService.CanChangeNamespaceAsync`. // // - A namespace declaration node that is the only namespace declaration in the document and all types are declared in it: // 1. No nested namespace declarations (even it's empty). // 2. The cursor is on the name of the namespace declaration. // 3. The name of the namespace is valid (i.e. no errors). // 4. No partial type declared in the namespace. Otherwise its multiple declaration will // end up in different namespace. // // - A compilation unit node that contains no namespace declaration: // 1. The cursor is on the name of first declared type. // 2. No partial type declared in the document. Otherwise its multiple declaration will // end up in different namespace. var applicableNode = await provider.TryGetApplicableInvocationNodeAsync(document, textSpan, cancellationToken).ConfigureAwait(false); if (applicableNode == null) { return(null); } var changeNamespaceService = document.GetLanguageService <IChangeNamespaceService>(); var canChange = await changeNamespaceService.CanChangeNamespaceAsync(document, applicableNode, cancellationToken).ConfigureAwait(false); if (!canChange || !IsDocumentPathRootedInProjectFolder(document)) { return(null); } var syntaxFacts = document.GetLanguageService <ISyntaxFactsService>(); // We can't determine what the expected namespace would be without knowing the default namespace. var defaultNamespace = GetDefaultNamespace(document, syntaxFacts); if (defaultNamespace == null) { return(null); } string declaredNamespace; if (applicableNode is TCompilationUnitSyntax) { declaredNamespace = string.Empty; } else if (applicableNode is TNamespaceDeclarationSyntax) { var syntaxGenerator = SyntaxGenerator.GetGenerator(document); declaredNamespace = syntaxGenerator.GetName(applicableNode); } else { throw ExceptionUtilities.Unreachable; } // Namespace can't be changed if we can't construct a valid qualified identifier from folder names. // In this case, we might still be able to provide refactoring to move file to new location. var namespaceFromFolders = PathMetadataUtilities.TryBuildNamespaceFromFolders(document.Folders, syntaxFacts); var targetNamespace = namespaceFromFolders == null ? null : ConcatNamespace(defaultNamespace, namespaceFromFolders); // No action required if namespace already matches folders. if (syntaxFacts.StringComparer.Equals(targetNamespace, declaredNamespace)) { return(null); } // Only provide "move file" action if default namespace contains declared namespace. // For example, if the default namespace is `Microsoft.CodeAnalysis`, and declared // namespace is `System.Diagnostics`, it's very likely this document is an outlier // in the project and user probably has some special rule for it. var relativeNamespace = GetRelativeNamespace(defaultNamespace, declaredNamespace, syntaxFacts); return(new State(document, applicableNode, targetNamespace, relativeNamespace)); }