提交 4425eb6a 编写于 作者: C Cyrus Najmabadi

Split the 'move declaration near reference' feature into a feature and...

Split the 'move declaration near reference' feature into a feature and *service*.  That way other features can leverage the service portion.
上级 a7cee9ff
// 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.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.MoveDeclarationNearReference;
namespace Microsoft.CodeAnalysis.CSharp.MoveDeclarationNearReference
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.MoveDeclarationNearReference), Shared]
[ExtensionOrder(After = PredefinedCodeRefactoringProviderNames.InlineTemporary)]
internal partial class CSharpMoveDeclarationNearReferenceCodeRefactoringProvider :
AbstractMoveDeclarationNearReferenceCodeRefactoringProvider<
CSharpMoveDeclarationNearReferenceCodeRefactoringProvider,
[ExportLanguageService(typeof(IMoveDeclarationNearReferenceService), LanguageNames.CSharp), Shared]
internal partial class CSharpMoveDeclarationNearReferenceService :
AbstractMoveDeclarationNearReferenceService<
CSharpMoveDeclarationNearReferenceService,
StatementSyntax,
LocalDeclarationStatementSyntax,
VariableDeclaratorSyntax>
......
......@@ -12,7 +12,7 @@
namespace Microsoft.CodeAnalysis.MoveDeclarationNearReference
{
internal partial class AbstractMoveDeclarationNearReferenceCodeRefactoringProvider<
internal partial class AbstractMoveDeclarationNearReferenceService<
TService,
TStatementSyntax,
TLocalDeclarationStatementSyntax,
......
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.MoveDeclarationNearReference
{
internal abstract partial class AbstractMoveDeclarationNearReferenceCodeRefactoringProvider<
internal abstract partial class AbstractMoveDeclarationNearReferenceService<
TService,
TStatementSyntax,
TLocalDeclarationStatementSyntax,
TVariableDeclaratorSyntax> : CodeRefactoringProvider
where TService : AbstractMoveDeclarationNearReferenceCodeRefactoringProvider<TService, TStatementSyntax, TLocalDeclarationStatementSyntax, TVariableDeclaratorSyntax>
TVariableDeclaratorSyntax> : IMoveDeclarationNearReferenceService
where TService : AbstractMoveDeclarationNearReferenceService<TService, TStatementSyntax, TLocalDeclarationStatementSyntax, TVariableDeclaratorSyntax>
where TStatementSyntax : SyntaxNode
where TLocalDeclarationStatementSyntax : TStatementSyntax
where TVariableDeclaratorSyntax : SyntaxNode
......@@ -34,55 +31,37 @@ internal abstract partial class AbstractMoveDeclarationNearReferenceCodeRefactor
protected abstract SyntaxToken GetIdentifierOfVariableDeclarator(TVariableDeclaratorSyntax variableDeclarator);
protected abstract Task<bool> TypesAreCompatibleAsync(Document document, ILocalSymbol localSymbol, TLocalDeclarationStatementSyntax declarationStatement, SyntaxNode right, CancellationToken cancellationToken);
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
public async Task<bool> CanMoveDeclarationNearReferenceAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
{
var document = context.Document;
var textSpan = context.Span;
var cancellationToken = context.CancellationToken;
if (!textSpan.IsEmpty)
if (!(node is TLocalDeclarationStatementSyntax statement))
{
return;
return false;
}
var position = textSpan.Start;
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var statement = root.FindToken(position).GetAncestor<TLocalDeclarationStatementSyntax>();
if (statement == null)
{
return;
}
var state = await State.GenerateAsync((TService)this, document, statement, cancellationToken).ConfigureAwait(false);
if (state == null)
{
return;
return false;
}
if (!CanMoveToBlock(state.LocalSymbol, state.OutermostBlock, state.InnermostBlock))
{
return;
return false;
}
// Don't offer the refactoring inside the initializer for the variable.
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var initializer = syntaxFacts.GetInitializerOfVariableDeclarator(state.VariableDeclarator);
var applicableSpan = initializer == null
? statement.Span
: TextSpan.FromBounds(statement.SpanStart, initializer.SpanStart);
return true;
}
if (!applicableSpan.IntersectsWith(position))
public async Task<Document> MoveDeclarationNearReferenceAsync(
Document document, SyntaxNode localDeclarationStatement, CancellationToken cancellationToken)
{
return;
}
Debug.Assert(await CanMoveDeclarationNearReferenceAsync(document, localDeclarationStatement, cancellationToken));
context.RegisterRefactoring(
new MyCodeAction(c => MoveDeclarationNearReferenceAsync(document, state, root, c)));
}
var statement = (TLocalDeclarationStatementSyntax)localDeclarationStatement;
var root = await document.GetSyntaxRootAsync(cancellationToken);
var state = await State.GenerateAsync((TService)this, document, statement, cancellationToken).ConfigureAwait(false);
private async Task<Document> MoveDeclarationNearReferenceAsync(
Document document, State state, SyntaxNode root, CancellationToken cancellationToken)
{
var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);
var crossesMeaningfulBlock = CrossesMeaningfulBlock(state);
......
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.MoveDeclarationNearReference
{
internal interface IMoveDeclarationNearReferenceService : ILanguageService
{
/// <summary>
/// Returns true if <paramref name="localDeclarationStatement"/> is local declaration statement
/// that can be moved forward to be closer to its first reference.
/// </summary>
Task<bool> CanMoveDeclarationNearReferenceAsync(Document document, SyntaxNode localDeclarationStatement, CancellationToken cancellationToken);
/// <summary>
/// Moves true if <paramref name="localDeclarationStatement"/> closer to its first
/// reference. Only applicable if <see cref="CanMoveDeclarationNearReferenceAsync"/>
/// returned <code>true</code>.
/// </summary>
Task<Document> MoveDeclarationNearReferenceAsync(Document document, SyntaxNode localDeclarationStatement, CancellationToken cancellationToken);
}
}
// 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.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.MoveDeclarationNearReference
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = PredefinedCodeRefactoringProviderNames.MoveDeclarationNearReference), Shared]
[ExtensionOrder(After = PredefinedCodeRefactoringProviderNames.InlineTemporary)]
internal sealed class MoveDeclarationNearReferenceCodeRefactoringProvider : CodeRefactoringProvider
{
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var document = context.Document;
var textSpan = context.Span;
var cancellationToken = context.CancellationToken;
if (!textSpan.IsEmpty)
{
return;
}
var statement = await GetLocalDeclarationStatementAsync(document, textSpan, cancellationToken).ConfigureAwait(false);
if (statement == null)
{
return;
}
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var variables = syntaxFacts.GetVariablesOfLocalDeclarationStatement(statement);
if (variables.Count != 1)
{
return;
}
// Don't offer the refactoring inside the initializer for the variable.
var initializer = syntaxFacts.GetInitializerOfVariableDeclarator(variables[0]);
var applicableSpan = initializer == null
? statement.Span
: TextSpan.FromBounds(statement.SpanStart, initializer.SpanStart);
if (!applicableSpan.IntersectsWith(textSpan.Start))
{
return;
}
var service = document.GetLanguageService<IMoveDeclarationNearReferenceService>();
if (!await service.CanMoveDeclarationNearReferenceAsync(
document, statement, cancellationToken))
{
return;
}
context.RegisterRefactoring(
new MyCodeAction(c => MoveDeclarationNearReferenceAsync(document, textSpan, c)));
}
private async Task<SyntaxNode> GetLocalDeclarationStatementAsync(
Document document, TextSpan textSpan, CancellationToken cancellationToken)
{
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var position = textSpan.Start;
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var statement = root.FindToken(position).Parent.Ancestors().FirstOrDefault(n => syntaxFacts.IsLocalDeclarationStatement(n));
return statement;
}
private async Task<Document> MoveDeclarationNearReferenceAsync(
Document document, TextSpan span, CancellationToken cancellationToken)
{
var statement = await GetLocalDeclarationStatementAsync(document, span, cancellationToken).ConfigureAwait(false);
var service = document.GetLanguageService<IMoveDeclarationNearReferenceService>();
return await service.MoveDeclarationNearReferenceAsync(document, statement, cancellationToken);
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument)
: base(FeaturesResources.Move_declaration_near_reference, createChangedDocument)
{
}
internal override CodeActionPriority Priority => CodeActionPriority.Low;
}
}
}
......@@ -2,16 +2,15 @@
Imports System.Composition
Imports System.Threading
Imports Microsoft.CodeAnalysis.CodeRefactorings
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.MoveDeclarationNearReference
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.MoveDeclarationNearReference
<ExportCodeRefactoringProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeRefactoringProviderNames.MoveDeclarationNearReference), [Shared]>
<ExtensionOrder(After:=PredefinedCodeRefactoringProviderNames.InlineTemporary)>
Friend Class VisualBasicMoveDeclarationNearReferenceCodeRefactoringProvider
Inherits AbstractMoveDeclarationNearReferenceCodeRefactoringProvider(Of
VisualBasicMoveDeclarationNearReferenceCodeRefactoringProvider,
<ExportLanguageService(GetType(IMoveDeclarationNearReferenceService), LanguageNames.VisualBasic), [Shared]>
Friend Class VisualBasicMoveDeclarationNearReferenceService
Inherits AbstractMoveDeclarationNearReferenceService(Of
VisualBasicMoveDeclarationNearReferenceService,
StatementSyntax,
LocalDeclarationStatementSyntax,
VariableDeclaratorSyntax)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册