private static async Task <AnalysisResult?> AnalyzeAsync(Document document, string newDocumentName, CancellationToken cancellationToken) { // TODO: Detect naming conflicts ahead of time var documentWithNewName = document.WithName(newDocumentName); var originalSymbolName = WorkspacePathUtilities.GetTypeNameFromDocumentName(document); var newTypeName = WorkspacePathUtilities.GetTypeNameFromDocumentName(documentWithNewName); if (originalSymbolName is null || newTypeName is null) { return(null); } var matchingDeclaration = await GetMatchingTypeDeclarationAsync(document, originalSymbolName, cancellationToken).ConfigureAwait(false); if (matchingDeclaration is null) { return(null); } var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var symbol = semanticModel.GetDeclaredSymbol(matchingDeclaration, cancellationToken); if (symbol is null || WorkspacePathUtilities.TypeNameMatchesDocumentName(documentWithNewName, symbol.Name)) { return(null); } return(new AnalysisResult( document, newDocumentName, newTypeName, symbol.Name)); }
private static async Task <SyntaxNode?> GetMatchingTypeDeclarationAsync(Document document, string name, CancellationToken cancellationToken) { var syntaxRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>(); var typeDeclarations = syntaxRoot.DescendantNodesAndSelf(n => !syntaxFacts.IsMethodBody(n)).Where(syntaxFacts.IsTypeDeclaration); return(typeDeclarations.FirstOrDefault(d => WorkspacePathUtilities.TypeNameMatchesDocumentName(document, d, syntaxFacts))); }
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 = WorkspacePathUtilities.TryBuildNamespaceFromFolders(newFolders, syntaxFacts); if (targetNamespace is null) { return(null); } return(new AnalysisResult(targetNamespace)); } else { return(null); } }
public InlineRenameFileRenameInfo GetFileRenameInfo() { if (RenameSymbol.Kind == SymbolKind.NamedType && _document.Project.Solution.Workspace.CanApplyChange(ApplyChangesKind.ChangeDocumentInfo)) { if (RenameSymbol.Locations.Length > 1) { return(InlineRenameFileRenameInfo.TypeWithMultipleLocations); } // Get the document that the symbol is defined in to compare // the name with the symbol name. If they match allow // rename file rename as part of the symbol rename var symbolSourceDocument = _document.Project.Solution.GetDocument(RenameSymbol.Locations.Single().SourceTree); if (symbolSourceDocument != null && WorkspacePathUtilities.TypeNameMatchesDocumentName(symbolSourceDocument, RenameSymbol.Name)) { return(InlineRenameFileRenameInfo.Allowed); } return(InlineRenameFileRenameInfo.TypeDoesNotMatchFileName); } return(InlineRenameFileRenameInfo.NotAllowed); }
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 = WorkspacePathUtilities.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)); }