diff --git a/src/Features/CSharp/Portable/MoveDeclarationNearReference/CSharpMoveDeclarationNearReferenceCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/MoveDeclarationNearReference/CSharpMoveDeclarationNearReferenceService.cs similarity index 82% rename from src/Features/CSharp/Portable/MoveDeclarationNearReference/CSharpMoveDeclarationNearReferenceCodeRefactoringProvider.cs rename to src/Features/CSharp/Portable/MoveDeclarationNearReference/CSharpMoveDeclarationNearReferenceService.cs index 9138d6e2afd424107477944927d56f28e23ab2dc..6e2ba6a230195b2d7fbb31afc32479e79a9237ef 100644 --- a/src/Features/CSharp/Portable/MoveDeclarationNearReference/CSharpMoveDeclarationNearReferenceCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/MoveDeclarationNearReference/CSharpMoveDeclarationNearReferenceService.cs @@ -1,21 +1,19 @@ // 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> diff --git a/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.State.cs b/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.State.cs index 8bb34a99fc6cb2c3405c8fc9d5b47f49f87534c3..50cb0654739209d5c3e67d8c5b213abb73c83f8e 100644 --- a/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.State.cs +++ b/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.State.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.MoveDeclarationNearReference { - internal partial class AbstractMoveDeclarationNearReferenceCodeRefactoringProvider< + internal partial class AbstractMoveDeclarationNearReferenceService< TService, TStatementSyntax, TLocalDeclarationStatementSyntax, diff --git a/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs b/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs index 032ef85f33f065faf729f1e3baca41f41d90a305..278ddd89c06545de24bf8a9a3a5f00731b5fd731 100644 --- a/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs @@ -1,28 +1,25 @@ // 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 + TVariableDeclaratorSyntax> : IMoveDeclarationNearReferenceService + where TService : AbstractMoveDeclarationNearReferenceService 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 TypesAreCompatibleAsync(Document document, ILocalSymbol localSymbol, TLocalDeclarationStatementSyntax declarationStatement, SyntaxNode right, CancellationToken cancellationToken); - public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) + public async Task 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(); - 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; - } - - // Don't offer the refactoring inside the initializer for the variable. - var syntaxFacts = document.GetLanguageService(); - var initializer = syntaxFacts.GetInitializerOfVariableDeclarator(state.VariableDeclarator); - var applicableSpan = initializer == null - ? statement.Span - : TextSpan.FromBounds(statement.SpanStart, initializer.SpanStart); - - if (!applicableSpan.IntersectsWith(position)) - { - return; + return false; } - context.RegisterRefactoring( - new MyCodeAction(c => MoveDeclarationNearReferenceAsync(document, state, root, c))); + return true; } - private async Task MoveDeclarationNearReferenceAsync( - Document document, State state, SyntaxNode root, CancellationToken cancellationToken) + public async Task MoveDeclarationNearReferenceAsync( + Document document, SyntaxNode localDeclarationStatement, CancellationToken cancellationToken) { + Debug.Assert(await CanMoveDeclarationNearReferenceAsync(document, localDeclarationStatement, cancellationToken)); + + var statement = (TLocalDeclarationStatementSyntax)localDeclarationStatement; + var root = await document.GetSyntaxRootAsync(cancellationToken); + var state = await State.GenerateAsync((TService)this, document, statement, cancellationToken).ConfigureAwait(false); + var editor = new SyntaxEditor(root, document.Project.Solution.Workspace); var crossesMeaningfulBlock = CrossesMeaningfulBlock(state); diff --git a/src/Features/Core/Portable/MoveDeclarationNearReference/IMoveDeclarationNearReferenceService.cs b/src/Features/Core/Portable/MoveDeclarationNearReference/IMoveDeclarationNearReferenceService.cs new file mode 100644 index 0000000000000000000000000000000000000000..404f8ec5eef424307a2cbe3d6291d28de66bf13d --- /dev/null +++ b/src/Features/Core/Portable/MoveDeclarationNearReference/IMoveDeclarationNearReferenceService.cs @@ -0,0 +1,24 @@ +// 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 + { + /// + /// Returns true if is local declaration statement + /// that can be moved forward to be closer to its first reference. + /// + Task CanMoveDeclarationNearReferenceAsync(Document document, SyntaxNode localDeclarationStatement, CancellationToken cancellationToken); + + /// + /// Moves true if closer to its first + /// reference. Only applicable if + /// returned true. + /// + Task MoveDeclarationNearReferenceAsync(Document document, SyntaxNode localDeclarationStatement, CancellationToken cancellationToken); + } +} diff --git a/src/Features/Core/Portable/MoveDeclarationNearReference/MoveDeclarationNearReferenceCodeRefactoringProvider.cs b/src/Features/Core/Portable/MoveDeclarationNearReference/MoveDeclarationNearReferenceCodeRefactoringProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..2f3e99bed9bae24796cf496c756295035613570d --- /dev/null +++ b/src/Features/Core/Portable/MoveDeclarationNearReference/MoveDeclarationNearReferenceCodeRefactoringProvider.cs @@ -0,0 +1,96 @@ +// 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(); + 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(); + if (!await service.CanMoveDeclarationNearReferenceAsync( + document, statement, cancellationToken)) + { + return; + } + + context.RegisterRefactoring( + new MyCodeAction(c => MoveDeclarationNearReferenceAsync(document, textSpan, c))); + } + + private async Task GetLocalDeclarationStatementAsync( + Document document, TextSpan textSpan, CancellationToken cancellationToken) + { + var syntaxFacts = document.GetLanguageService(); + + 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 MoveDeclarationNearReferenceAsync( + Document document, TextSpan span, CancellationToken cancellationToken) + { + var statement = await GetLocalDeclarationStatementAsync(document, span, cancellationToken).ConfigureAwait(false); + var service = document.GetLanguageService(); + + return await service.MoveDeclarationNearReferenceAsync(document, statement, cancellationToken); + } + + private class MyCodeAction : CodeAction.DocumentChangeAction + { + public MyCodeAction(Func> createChangedDocument) + : base(FeaturesResources.Move_declaration_near_reference, createChangedDocument) + { + } + + internal override CodeActionPriority Priority => CodeActionPriority.Low; + } + } +} diff --git a/src/Features/VisualBasic/Portable/MoveDeclarationNearReference/VisualBasicMoveDeclarationNearReferenceCodeRefactoringProvider.vb b/src/Features/VisualBasic/Portable/MoveDeclarationNearReference/VisualBasicMoveDeclarationNearReferenceService.vb similarity index 80% rename from src/Features/VisualBasic/Portable/MoveDeclarationNearReference/VisualBasicMoveDeclarationNearReferenceCodeRefactoringProvider.vb rename to src/Features/VisualBasic/Portable/MoveDeclarationNearReference/VisualBasicMoveDeclarationNearReferenceService.vb index 1e299829e7f91bb77893dc426e74583c49329b63..f595c3289e7e3068b7d8e634e9353615d03fbe35 100644 --- a/src/Features/VisualBasic/Portable/MoveDeclarationNearReference/VisualBasicMoveDeclarationNearReferenceCodeRefactoringProvider.vb +++ b/src/Features/VisualBasic/Portable/MoveDeclarationNearReference/VisualBasicMoveDeclarationNearReferenceService.vb @@ -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 - - - Friend Class VisualBasicMoveDeclarationNearReferenceCodeRefactoringProvider - Inherits AbstractMoveDeclarationNearReferenceCodeRefactoringProvider(Of - VisualBasicMoveDeclarationNearReferenceCodeRefactoringProvider, + + Friend Class VisualBasicMoveDeclarationNearReferenceService + Inherits AbstractMoveDeclarationNearReferenceService(Of + VisualBasicMoveDeclarationNearReferenceService, StatementSyntax, LocalDeclarationStatementSyntax, VariableDeclaratorSyntax)