提交 949d1137 编写于 作者: B Balaji Krishnan

Minor code cleanups and added..

.. checks identifier checks for rename type.
上级 aacc93a8
......@@ -17,8 +17,8 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.MoveType
{
public abstract class AbstractMoveTypeTest : AbstractCodeActionTest
{
private string RenameFileCodeActionTitle = FeaturesResources.Renamefileto_0;
private string RenameTypeCodeActionTitle = FeaturesResources.Renametypeto_0;
private string RenameFileCodeActionTitle = FeaturesResources.Rename_file_to_0;
private string RenameTypeCodeActionTitle = FeaturesResources.Rename_type_to_0;
protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace)
{
......
......@@ -34,11 +34,11 @@ private string CreateDisplayText()
switch (_operationKind)
{
case OperationKind.MoveType:
return string.Format(FeaturesResources.Movetypeto_0, _state.TargetFileNameCandidate);
return string.Format(FeaturesResources.Move_type_to_0, _state.TargetFileNameCandidate);
case OperationKind.RenameType:
return string.Format(FeaturesResources.Renametypeto_0, _state.DocumentName);
return string.Format(FeaturesResources.Rename_type_to_0, _state.DocumentName);
case OperationKind.RenameFile:
return string.Format(FeaturesResources.Renamefileto_0, _state.TargetFileNameCandidate);
return string.Format(FeaturesResources.Rename_file_to_0, _state.TargetFileNameCandidate);
}
throw ExceptionUtilities.Unreachable;
......
......@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
......@@ -41,8 +42,8 @@ internal override async Task<IEnumerable<CodeActionOperation>> GetOperationsAsyn
/// <remarks>
/// The algorithm for this, is as follows:
/// 1. Fork the original document that contains the type to be moved.
/// 2. Keep the type and required namespace containers, using statements
/// and remove everything else from the forked document.
/// 2. Keep the type, required namespace containers and using statements.
/// remove everything else from the forked document.
/// 3. Add this forked document to the solution.
/// 4. Finally, update the original document and remove the type from it.
/// </remarks>
......@@ -52,7 +53,7 @@ private async Task<IEnumerable<CodeActionOperation>> MoveTypeToNewFileAsync(stri
var projectToBeUpdated = SemanticDocument.Document.Project;
var newDocumentId = DocumentId.CreateNewId(projectToBeUpdated.Id, documentName);
var solutionWithNewDocument = await AddNewDocumentWithTypeDeclarationAsync(
var solutionWithNewDocument = await AddNewDocumentWithSingleTypeDeclarationAndImportsAsync(
SemanticDocument, documentName, newDocumentId, State.TypeNode, CancellationToken).ConfigureAwait(false);
// Get the original source document again, from the latest forked solution.
......@@ -60,10 +61,10 @@ private async Task<IEnumerable<CodeActionOperation>> MoveTypeToNewFileAsync(stri
// update source document to add partial modifiers to type chain
// and/or remove type declaration from original source document.
var solutionWithBothDocumentsUpdated = await UpdateSourceDocumentAsync(
var solutionWithBothDocumentsUpdated = await RemoveTypeFromSourceDocumentAsync(
sourceDocument, State.TypeNode, CancellationToken).ConfigureAwait(false);
return new CodeActionOperation[] { new ApplyChangesOperation(solutionWithBothDocumentsUpdated) };
return SpecializedCollections.SingletonEnumerable(new ApplyChangesOperation(solutionWithBothDocumentsUpdated));
}
/// <summary>
......@@ -76,13 +77,16 @@ private async Task<IEnumerable<CodeActionOperation>> MoveTypeToNewFileAsync(stri
/// <param name="typeNode">type to move from original document to new document</param>
/// <param name="cancellationToken">a cancellation token</param>
/// <returns>the new solution which contains a new document with the type being moved</returns>
private async Task<Solution> AddNewDocumentWithTypeDeclarationAsync(
private async Task<Solution> AddNewDocumentWithSingleTypeDeclarationAndImportsAsync(
SemanticDocument sourceDocument,
string newDocumentName,
DocumentId newDocumentId,
TTypeDeclarationSyntax typeNode,
CancellationToken cancellationToken)
{
Debug.Assert(sourceDocument.Document.Name != newDocumentName,
$"New document name is same as old document name:{newDocumentName}");
var root = sourceDocument.Root;
var projectToBeUpdated = sourceDocument.Document.Project;
var documentEditor = await DocumentEditor.CreateAsync(sourceDocument.Document, cancellationToken).ConfigureAwait(false);
......@@ -106,7 +110,9 @@ private async Task<IEnumerable<CodeActionOperation>> MoveTypeToNewFileAsync(stri
// get the updated document, perform clean up like remove unused usings.
var newDocument = solutionWithNewDocument.GetDocument(newDocumentId);
return await CleanUpDocumentAsync(newDocument, cancellationToken).ConfigureAwait(false);
newDocument = await CleanUpDocumentAsync(newDocument, cancellationToken).ConfigureAwait(false);
return newDocument.Project.Solution;
}
/// <summary>
......@@ -117,7 +123,7 @@ private async Task<IEnumerable<CodeActionOperation>> MoveTypeToNewFileAsync(stri
/// <param name="typeNode">type that was moved to new document</param>
/// <param name="cancellationToken">a cancellation token</param>
/// <returns>an updated solution with the original document fixed up as appropriate.</returns>
private async Task<Solution> UpdateSourceDocumentAsync(
private async Task<Solution> RemoveTypeFromSourceDocumentAsync(
Document sourceDocument, TTypeDeclarationSyntax typeNode, CancellationToken cancellationToken)
{
var documentEditor = await DocumentEditor.CreateAsync(sourceDocument, cancellationToken).ConfigureAwait(false);
......@@ -126,7 +132,10 @@ private async Task<IEnumerable<CodeActionOperation>> MoveTypeToNewFileAsync(stri
documentEditor.RemoveNode(typeNode, SyntaxRemoveOptions.KeepNoTrivia);
var updatedDocument = documentEditor.GetChangedDocument();
return await CleanUpDocumentAsync(updatedDocument, cancellationToken).ConfigureAwait(false);
updatedDocument = await CleanUpDocumentAsync(updatedDocument, cancellationToken).ConfigureAwait(false);
return updatedDocument.Project.Solution;
}
/// <summary>
......@@ -187,18 +196,14 @@ private static bool FilterToTopLevelMembers(SyntaxNode node, SyntaxNode typeNode
private void AddPartialModifiersToTypeChain(DocumentEditor documentEditor, TTypeDeclarationSyntax typeNode)
{
var semanticFacts = State.SemanticDocument.Document.GetLanguageService<ISemanticFactsService>();
var typeChain = typeNode.Ancestors().OfType<TTypeDeclarationSyntax>();
if (typeNode.Parent is TTypeDeclarationSyntax)
foreach (var node in typeChain)
{
var typeChain = typeNode.Ancestors().OfType<TTypeDeclarationSyntax>();
foreach (var node in typeChain)
var symbol = (ITypeSymbol)State.SemanticDocument.SemanticModel.GetDeclaredSymbol(node, CancellationToken);
if (!semanticFacts.IsPartial(symbol))
{
var symbol = (ITypeSymbol)State.SemanticDocument.SemanticModel.GetDeclaredSymbol(node, CancellationToken);
if (!semanticFacts.IsPartial(symbol))
{
documentEditor.SetModifiers(node, DeclarationModifiers.Partial);
}
documentEditor.SetModifiers(node, DeclarationModifiers.Partial);
}
}
}
......@@ -206,7 +211,7 @@ private void AddPartialModifiersToTypeChain(DocumentEditor documentEditor, TType
/// <summary>
/// Perform clean ups on a given document.
/// </summary>
private async Task<Solution> CleanUpDocumentAsync(Document document, CancellationToken cancellationToken)
private async Task<Document> CleanUpDocumentAsync(Document document, CancellationToken cancellationToken)
{
document = await document
.GetLanguageService<IRemoveUnnecessaryImportsService>()
......@@ -214,7 +219,7 @@ private async Task<Solution> CleanUpDocumentAsync(Document document, Cancellatio
.ConfigureAwait(false);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
return document.Project.Solution.WithDocumentSyntaxRoot(document.Id, root, PreservationMode.PreserveIdentity);
return document.WithSyntaxRoot(root);
}
}
}
......
......@@ -5,6 +5,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Rename;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeRefactorings.MoveType
{
......@@ -33,7 +34,7 @@ private async Task<IEnumerable<CodeActionOperation>> RenameTypeToMatchFileAsync(
// if no such conflicts exist, proceed with RenameSymbolAsync.
var symbol = State.SemanticDocument.SemanticModel.GetDeclaredSymbol(State.TypeNode, CancellationToken);
var newSolution = await Renamer.RenameSymbolAsync(solution, symbol, State.DocumentName, SemanticDocument.Document.Options, CancellationToken).ConfigureAwait(false);
return new CodeActionOperation[] { new ApplyChangesOperation(newSolution) };
return SpecializedCollections.SingletonEnumerable(new ApplyChangesOperation(newSolution));
}
}
}
......
......@@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings.MoveType
{
internal abstract partial class AbstractMoveTypeService<TService, TTypeDeclarationSyntax, TNamespaceDeclarationSyntax, TMemberDeclarationSyntax, TCompilationUnitSyntax>
{
protected class State
private class State
{
private readonly TService _service;
public SemanticDocument SemanticDocument { get; }
......@@ -20,8 +20,9 @@ protected class State
public string TypeName { get; set; }
public string DocumentName { get; set; }
public string TargetFileNameCandidate { get; set; }
public bool IsDocumentNameAValidIdentifier { get; set; }
private State(TService service,SemanticDocument document)
private State(TService service, SemanticDocument document)
{
this._service = service;
this.SemanticDocument = document;
......@@ -51,7 +52,7 @@ internal static State Generate(TService service, SemanticDocument document, Text
var root = this.SemanticDocument.Root;
var syntaxFacts = this.SemanticDocument.Project.LanguageServices.GetService<ISyntaxFactsService>();
var typeDeclaration = _service.GetNodetoAnalyze(root, textSpan) as TTypeDeclarationSyntax;
var typeDeclaration = _service.GetNodeToAnalyze(root, textSpan) as TTypeDeclarationSyntax;
if (typeDeclaration == null)
{
return false;
......@@ -71,15 +72,16 @@ internal static State Generate(TService service, SemanticDocument document, Text
TypeNode = typeDeclaration;
TypeName = typeSymbol.Name;
DocumentName = Path.GetFileNameWithoutExtension(this.SemanticDocument.Document.Name);
IsDocumentNameAValidIdentifier = syntaxFacts.IsValidIdentifier(DocumentName);
if (string.Equals(DocumentName, TypeName, StringComparison.CurrentCultureIgnoreCase))
// TODO: Make this check better, it won't detect Outer.Inner.cs cases.
if (string.Equals(DocumentName, TypeName, StringComparison.CurrentCulture))
{
// if type name matches document name, we have nothing more to do.
// if type name matches document name in a case sensitive manner, we have nothing more to do.
return false;
}
TargetFileNameCandidate =
typeSymbol.Name + (SemanticDocument.Document.Project.Language == LanguageNames.CSharp ? ".cs" : ".vb");
TargetFileNameCandidate = Path.Combine(typeSymbol.Name + Path.GetExtension(this.SemanticDocument.Document.Name));
return true;
}
......
......@@ -27,10 +27,10 @@ internal enum OperationKind
public bool ShouldAnalyze(SyntaxNode root, TextSpan span)
{
return GetNodetoAnalyze(root, span) is TTypeDeclarationSyntax;
return GetNodeToAnalyze(root, span) is TTypeDeclarationSyntax;
}
protected virtual SyntaxNode GetNodetoAnalyze(SyntaxNode root, TextSpan span)
protected virtual SyntaxNode GetNodeToAnalyze(SyntaxNode root, TextSpan span)
{
return root.FindNode(span);
}
......@@ -74,7 +74,13 @@ private List<CodeAction> CreateActions(State state, CancellationToken cancellati
// one type declaration in current document. No moving around required, just sync
// document name and type name by offering rename in both directions between type and document.
actions.Add(GetCodeAction(state, operationKind: OperationKind.RenameFile));
actions.Add(GetCodeAction(state, operationKind: OperationKind.RenameType));
// only if the document name can be legal identifier in the language,
// offer to rename type with document name
if (state.IsDocumentNameAValidIdentifier)
{
actions.Add(GetCodeAction(state, operationKind: OperationKind.RenameType));
}
}
else
{
......
......@@ -1035,13 +1035,13 @@ This version used in: {2}</value>
<data name="Convert_to_interpolated_string" xml:space="preserve">
<value>Convert to interpolated string</value>
</data>
<data name="Movetypeto_0" xml:space="preserve">
<data name="Move_type_to_0" xml:space="preserve">
<value>Move type to {0}</value>
</data>
<data name="Renamefileto_0" xml:space="preserve">
<data name="Rename_file_to_0" xml:space="preserve">
<value>Rename file to {0}</value>
</data>
<data name="Renametypeto_0" xml:space="preserve">
<data name="Rename_type_to_0" xml:space="preserve">
<value>Rename type to {0}</value>
</data>
</root>
\ No newline at end of file
......@@ -13,8 +13,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.MoveType
''' <summary>
''' Gets the TypeBlock node to analyze
''' </summary>
Protected Overrides Function GetNodetoAnalyze(root As SyntaxNode, span As TextSpan) As SyntaxNode
Dim node = MyBase.GetNodetoAnalyze(root, span)
Protected Overrides Function GetNodeToAnalyze(root As SyntaxNode, span As TextSpan) As SyntaxNode
Dim node = MyBase.GetNodeToAnalyze(root, span)
If node.IsKind(SyntaxKind.ModuleStatement,
SyntaxKind.ClassStatement,
SyntaxKind.StructureStatement,
......
......@@ -266,8 +266,7 @@ public bool IsNameOfContext(SemanticModel semanticModel, int position, Cancellat
public bool IsPartial(ITypeSymbol typeSymbol)
{
var syntaxRefs = typeSymbol.DeclaringSyntaxReferences;
return syntaxRefs.Length > 1
|| ((BaseTypeDeclarationSyntax)syntaxRefs.Single().GetSyntax()).Modifiers.Any(SyntaxKind.PartialKeyword);
return syntaxRefs.Any(n => ((BaseTypeDeclarationSyntax)n.GetSyntax()).Modifiers.Any(SyntaxKind.PartialKeyword));
}
}
}
......@@ -250,8 +250,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Public Function IsPartial(typeSymbol As ITypeSymbol) As Boolean Implements ISemanticFactsService.IsPartial
Dim syntaxRefs = typeSymbol.DeclaringSyntaxReferences
Return syntaxRefs.Length > 1 OrElse
DirectCast(syntaxRefs.Single().GetSyntax(), TypeStatementSyntax).Modifiers.Any(SyntaxKind.PartialKeyword)
Return syntaxRefs.Any(
Function(n As SyntaxReference)
Return DirectCast(n.GetSyntax(), TypeStatementSyntax).Modifiers.Any(SyntaxKind.PartialKeyword)
End Function)
End Function
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册