提交 950bb0ae 编写于 作者: C CyrusNajmabadi

Reenable 'Move declaration near reference' refactoring.

上级 b756b839
...@@ -163,7 +163,6 @@ ...@@ -163,7 +163,6 @@
<Compile Include="CodeRefactorings\LambdaSimplifier\LambdaSimplifierCodeRefactoringProvider.cs" /> <Compile Include="CodeRefactorings\LambdaSimplifier\LambdaSimplifierCodeRefactoringProvider.cs" />
<Compile Include="CodeRefactorings\LambdaSimplifier\LambdaSimplifierCodeRefactoringProvider.Rewriter.cs" /> <Compile Include="CodeRefactorings\LambdaSimplifier\LambdaSimplifierCodeRefactoringProvider.Rewriter.cs" />
<Compile Include="CodeRefactorings\MoveDeclarationNearReference\MoveDeclarationNearReferenceCodeRefactoringProvider.cs" /> <Compile Include="CodeRefactorings\MoveDeclarationNearReference\MoveDeclarationNearReferenceCodeRefactoringProvider.cs" />
<Compile Include="CodeRefactorings\MoveDeclarationNearReference\MoveDeclarationNearReferenceCodeRefactoringProvider.Rewriter.cs" />
<Compile Include="CodeRefactorings\MoveDeclarationNearReference\MoveDeclarationNearReferenceCodeRefactoringProvider.State.cs" /> <Compile Include="CodeRefactorings\MoveDeclarationNearReference\MoveDeclarationNearReferenceCodeRefactoringProvider.State.cs" />
<Compile Include="CodeRefactorings\MoveType\CSharpMoveTypeService.cs" /> <Compile Include="CodeRefactorings\MoveType\CSharpMoveTypeService.cs" />
<Compile Include="Completion\CompletionProviders\AttributeNamedParameterCompletionProvider.cs" /> <Compile Include="Completion\CompletionProviders\AttributeNamedParameterCompletionProvider.cs" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.MoveDeclarationNearReference
{
internal partial class MoveDeclarationNearReferenceCodeRefactoringProvider
{
private class Rewriter : CSharpSyntaxRewriter
{
private readonly BlockSyntax _oldInnermostBlock;
private readonly BlockSyntax _newInnermostBlock;
private readonly BlockSyntax _oldOutermostBlock;
private readonly LocalDeclarationStatementSyntax _declarationStatement;
public Rewriter(
BlockSyntax oldInnermostBlock,
BlockSyntax newInnermostBlock,
BlockSyntax oldOutermostBlock,
LocalDeclarationStatementSyntax declarationStatement)
{
_oldInnermostBlock = oldInnermostBlock;
_newInnermostBlock = newInnermostBlock;
_oldOutermostBlock = oldOutermostBlock;
_declarationStatement = declarationStatement;
}
public override SyntaxNode VisitBlock(BlockSyntax oldBlock)
{
if (oldBlock == _oldInnermostBlock)
{
return _newInnermostBlock;
}
if (oldBlock == _oldOutermostBlock)
{
var statements = SyntaxFactory.List(oldBlock.Statements.Where(s => s != _declarationStatement).Select(this.Visit));
return oldBlock.WithStatements(statements).WithAdditionalAnnotations(Formatter.Annotation);
}
return base.VisitBlock(oldBlock);
}
}
}
}
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.MoveDeclarationNearReference namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.MoveDeclarationNearReference
{ {
...@@ -25,7 +26,7 @@ private class State ...@@ -25,7 +26,7 @@ private class State
public StatementSyntax FirstStatementAffectedInInnermostBlock { get; private set; } public StatementSyntax FirstStatementAffectedInInnermostBlock { get; private set; }
internal static async Task<State> GenerateAsync( internal static async Task<State> GenerateAsync(
SemanticDocument document, Document document,
LocalDeclarationStatementSyntax statement, LocalDeclarationStatementSyntax statement,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
...@@ -39,7 +40,7 @@ private class State ...@@ -39,7 +40,7 @@ private class State
} }
private async Task<bool> TryInitializeAsync( private async Task<bool> TryInitializeAsync(
SemanticDocument document, Document document,
LocalDeclarationStatementSyntax node, LocalDeclarationStatementSyntax node,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
...@@ -49,16 +50,21 @@ private class State ...@@ -49,16 +50,21 @@ private class State
} }
this.DeclarationStatement = node; this.DeclarationStatement = node;
if (!(this.DeclarationStatement.IsParentKind(SyntaxKind.Block) && if (!this.DeclarationStatement.IsParentKind(SyntaxKind.Block))
this.DeclarationStatement.Declaration.Variables.Count == 1))
{ {
return false; return false;
} }
this.VariableDeclaration = this.DeclarationStatement.Declaration; this.VariableDeclaration = this.DeclarationStatement.Declaration;
this.VariableDeclarator = this.VariableDeclaration.Variables[0]; this.VariableDeclarator = this.VariableDeclaration.Variables[0];
this.OutermostBlock = (BlockSyntax)this.DeclarationStatement.Parent; this.OutermostBlock = this.DeclarationStatement.Parent as BlockSyntax;
this.LocalSymbol = (ILocalSymbol)document.SemanticModel.GetDeclaredSymbol(this.VariableDeclarator, cancellationToken); if (OutermostBlock == null)
{
return false;
}
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
this.LocalSymbol = (ILocalSymbol)semanticModel.GetDeclaredSymbol(this.VariableDeclarator, cancellationToken);
if (this.LocalSymbol == null) if (this.LocalSymbol == null)
{ {
// This can happen in broken code, for example: "{ object x; object }" // This can happen in broken code, for example: "{ object x; object }"
...@@ -78,13 +84,13 @@ private class State ...@@ -78,13 +84,13 @@ private class State
return false; return false;
} }
var syntaxTree = document.SyntaxTree; var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var referencingStatements = var referencingStatements =
(from r in references (from r in references
let token = document.Root.FindToken(r.Location.SourceSpan.Start) let token = syntaxRoot.FindToken(r.Location.SourceSpan.Start)
let statement = token.GetAncestor<StatementSyntax>() let statement = token.GetAncestor<StatementSyntax>()
where statement != null where statement != null
select statement).ToList(); select statement).ToSet();
if (referencingStatements.Count == 0) if (referencingStatements.Count == 0)
{ {
...@@ -133,7 +139,7 @@ private class State ...@@ -133,7 +139,7 @@ private class State
? this.InnermostBlock.OpenBraceToken ? this.InnermostBlock.OpenBraceToken
: (SyntaxNodeOrToken)this.InnermostBlock.Statements[firstStatementIndexAffectedInBlock - 1]; : (SyntaxNodeOrToken)this.InnermostBlock.Statements[firstStatementIndexAffectedInBlock - 1];
var affectedSpan = TextSpan.FromBounds(previousNodeOrToken.SpanStart, FirstStatementAffectedInInnermostBlock.Span.End); var affectedSpan = TextSpan.FromBounds(previousNodeOrToken.SpanStart, FirstStatementAffectedInInnermostBlock.Span.End);
if (syntaxTree.OverlapsHiddenPosition(affectedSpan, cancellationToken)) if (semanticModel.SyntaxTree.OverlapsHiddenPosition(affectedSpan, cancellationToken))
{ {
return false; return false;
} }
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Composition;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Simplification;
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.MoveDeclarationNearReference namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.MoveDeclarationNearReference
{ {
// [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.MoveDeclarationNearReference)] [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.MoveDeclarationNearReference), Shared]
[ExtensionOrder(After = PredefinedCodeFixProviderNames.AddImport)]
internal partial class MoveDeclarationNearReferenceCodeRefactoringProvider : CodeRefactoringProvider internal partial class MoveDeclarationNearReferenceCodeRefactoringProvider : CodeRefactoringProvider
{ {
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
...@@ -37,37 +41,51 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte ...@@ -37,37 +41,51 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
return; return;
} }
var position = textSpan.Start;
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var statement = root.FindToken(textSpan.Start).GetAncestor<LocalDeclarationStatementSyntax>(); var statement = root.FindToken(position).GetAncestor<LocalDeclarationStatementSyntax>();
if (statement == null || !statement.Span.IntersectsWith(textSpan.Start)) if (statement == null)
{ {
return; return;
} }
var semanticDocument = await SemanticDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); if (statement.Declaration.Variables.Count != 1)
var state = await State.GenerateAsync(semanticDocument, statement, cancellationToken).ConfigureAwait(false); {
return;
}
// Only offer the refactoring when somewhere in the type+name of the local variable.
if (position < statement.SpanStart || position > statement.Declaration.Variables[0].Identifier.Span.End)
{
return;
}
var state = await State.GenerateAsync(document, statement, cancellationToken).ConfigureAwait(false);
if (state == null) if (state == null)
{ {
return; return;
} }
context.RegisterRefactoring( context.RegisterRefactoring(
new MyCodeAction( new MyCodeAction(c => MoveDeclarationNearReferenceAsync(document, state, root, c)));
CSharpFeaturesResources.Move_declaration_near_reference,
(c) => MoveDeclarationNearReferenceAsync(document, state, c)));
} }
private async Task<Document> MoveDeclarationNearReferenceAsync(Document document, State state, CancellationToken cancellationToken) private async Task<Document> MoveDeclarationNearReferenceAsync(
Document document, State state, SyntaxNode root, CancellationToken cancellationToken)
{ {
var innermostStatements = var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);
state.InnermostBlock.Statements.Where(s => s != state.DeclarationStatement).ToList();
var innermostAffectedIndex = innermostStatements.IndexOf(state.FirstStatementAffectedInInnermostBlock); //var innermostStatements =
// state.InnermostBlock.Statements.Where(s => s != state.DeclarationStatement).ToList();
//var innermostAffectedIndex = innermostStatements.IndexOf(state.FirstStatementAffectedInInnermostBlock);
var crossesMeaningfulBlock = CrossesMeaningfulBlock(state); var crossesMeaningfulBlock = CrossesMeaningfulBlock(state);
var warningAnnotation = crossesMeaningfulBlock var warningAnnotation = crossesMeaningfulBlock
? WarningAnnotation.Create(CSharpFeaturesResources.Warning_colon_Declaration_changes_scope_and_may_change_meaning) ? WarningAnnotation.Create(CSharpFeaturesResources.Warning_colon_Declaration_changes_scope_and_may_change_meaning)
: null; : null;
editor.RemoveNode(state.DeclarationStatement);
var canMergeDeclarationAndAssignment = await CanMergeDeclarationAndAssignmentAsync(document, state, cancellationToken).ConfigureAwait(false); var canMergeDeclarationAndAssignment = await CanMergeDeclarationAndAssignmentAsync(document, state, cancellationToken).ConfigureAwait(false);
if (canMergeDeclarationAndAssignment) if (canMergeDeclarationAndAssignment)
{ {
...@@ -77,29 +95,28 @@ private async Task<Document> MoveDeclarationNearReferenceAsync(Document document ...@@ -77,29 +95,28 @@ private async Task<Document> MoveDeclarationNearReferenceAsync(Document document
? declarationStatement ? declarationStatement
: declarationStatement.WithAdditionalAnnotations(warningAnnotation); : declarationStatement.WithAdditionalAnnotations(warningAnnotation);
innermostStatements[innermostAffectedIndex] = declarationStatement.WithAdditionalAnnotations(Formatter.Annotation); editor.ReplaceNode(
state.FirstStatementAffectedInInnermostBlock,
declarationStatement.WithAdditionalAnnotations(Formatter.Annotation));
} }
else else
{ {
// If we're not merging with an existing declaration, make the declaration semantically // If we're not merging with an existing declaration, make the declaration semantically
// explicit to improve the chances that it won't break code. // explicit to improve the chances that it won't break code.
var explicitDeclarationStatement = await Simplifier.ExpandAsync(state.DeclarationStatement, document, cancellationToken: cancellationToken).ConfigureAwait(false); var explicitDeclarationStatement = await Simplifier.ExpandAsync(
state.DeclarationStatement, document, cancellationToken: cancellationToken).ConfigureAwait(false);
// place the declaration above the first statement that references it. // place the declaration above the first statement that references it.
var declarationStatement = warningAnnotation == null var declarationStatement = warningAnnotation == null
? explicitDeclarationStatement ? explicitDeclarationStatement
: explicitDeclarationStatement.WithAdditionalAnnotations(warningAnnotation); : explicitDeclarationStatement.WithAdditionalAnnotations(warningAnnotation);
innermostStatements.Insert(innermostAffectedIndex, declarationStatement.WithAdditionalAnnotations(Formatter.Annotation)); editor.InsertBefore(
state.FirstStatementAffectedInInnermostBlock,
declarationStatement.WithAdditionalAnnotations(Formatter.Annotation));
} }
var newInnermostBlock = state.InnermostBlock.WithStatements( var newRoot = editor.GetChangedRoot();
SyntaxFactory.List<StatementSyntax>(innermostStatements)).WithAdditionalAnnotations(Formatter.Annotation);
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
var rewriter = new Rewriter(state.InnermostBlock, newInnermostBlock, state.OutermostBlock, state.DeclarationStatement);
var newRoot = rewriter.Visit(tree.GetRoot(cancellationToken));
return document.WithSyntaxRoot(newRoot); return document.WithSyntaxRoot(newRoot);
} }
...@@ -185,10 +202,12 @@ private StatementSyntax CreateMergedDeclarationStatement(State state, StatementS ...@@ -185,10 +202,12 @@ private StatementSyntax CreateMergedDeclarationStatement(State state, StatementS
private class MyCodeAction : CodeAction.DocumentChangeAction private class MyCodeAction : CodeAction.DocumentChangeAction
{ {
public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument) : public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument) :
base(title, createChangedDocument) base(CSharpFeaturesResources.Move_declaration_near_reference, createChangedDocument)
{ {
} }
internal override CodeActionPriority Priority => CodeActionPriority.Low;
} }
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册