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