Skip to content

Commit

Permalink
Notify XAML of renames caused by Naming Rule fixes
Browse files Browse the repository at this point in the history
Fixes #16562
  • Loading branch information
David Poeschl committed Apr 17, 2017
1 parent 11eb695 commit 7b2ecf1
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@
<Compile Include="Workspaces\TestHostDocument.cs" />
<Compile Include="Workspaces\TestHostProject.cs" />
<Compile Include="Workspaces\TestHostSolution.cs" />
<Compile Include="Workspaces\TestSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs" />
<Compile Include="Workspaces\TestWorkspace.cs" />
<Compile Include="Workspaces\TestWorkspaceFixture.cs" />
<Compile Include="Workspaces\TestWorkspace_Create.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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 Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeActions.WorkspaceServices;
using Microsoft.CodeAnalysis.Host.Mef;

namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
{
[ExportWorkspaceService(typeof(ISymbolRenamedCodeActionOperationFactoryWorkspaceService), TestWorkspace.WorkspaceName), Shared]
public class TestSymbolRenamedCodeActionOperationFactoryWorkspaceService : ISymbolRenamedCodeActionOperationFactoryWorkspaceService
{
public CodeActionOperation CreateSymbolRenamedOperation(ISymbol symbol, string newName, Solution startingSolution, Solution updatedSolution)
{
return new Operation(symbol, newName, startingSolution, updatedSolution);
}

public class Operation : CodeActionOperation
{
private ISymbol _symbol;
private string _newName;
private Solution _startingSolution;
private Solution _updatedSolution;

public Operation(ISymbol symbol, string newName, Solution startingSolution, Solution updatedSolution)
{
_symbol = symbol;
_newName = newName;
_startingSolution = startingSolution;
_updatedSolution = updatedSolution;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// 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.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeActions.WorkspaceServices;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.NamingStyles;
using Microsoft.CodeAnalysis.Rename;
Expand Down Expand Up @@ -52,9 +54,12 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
var solution = context.Document.Project.Solution;
context.RegisterCodeFix(
new FixNameCodeAction(
solution,
symbol,
fixedName,
string.Format(FeaturesResources.Fix_Name_Violation_colon_0, fixedName),
c => FixAsync(document, symbol, fixedName, c),
nameof(NamingStyleCodeFixProvider)),
equivalenceKey: nameof(NamingStyleCodeFixProvider)),
diagnostic);
}
}
Expand All @@ -68,12 +73,51 @@ await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false),
cancellationToken).ConfigureAwait(false);
}

private class FixNameCodeAction : CodeAction.SolutionChangeAction
private class FixNameCodeAction : CodeAction
{
public FixNameCodeAction(string title, Func<CancellationToken, Task<Solution>> createChangedSolution, string equivalenceKey)
: base(title, createChangedSolution, equivalenceKey)
private readonly Solution _startingSolution;
private readonly ISymbol _symbol;
private readonly string _newName;
private readonly string _title;
private readonly Func<CancellationToken, Task<Solution>> _createChangedSolutionAsync;
private readonly string _equivalenceKey;

public FixNameCodeAction(
Solution startingSolution,
ISymbol symbol,
string newName,
string title,
Func<CancellationToken, Task<Solution>> createChangedSolutionAsync,
string equivalenceKey)
{
_startingSolution = startingSolution;
_symbol = symbol;
_newName = newName;
_title = title;
_createChangedSolutionAsync = createChangedSolutionAsync;
_equivalenceKey = equivalenceKey;
}

protected override async Task<IEnumerable<CodeActionOperation>> ComputePreviewOperationsAsync(CancellationToken cancellationToken)
{
return SpecializedCollections.SingletonEnumerable(
new ApplyChangesOperation(await _createChangedSolutionAsync(cancellationToken).ConfigureAwait(false)));
}

protected override async Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(CancellationToken cancellationToken)
{
var factory =_startingSolution.Workspace.Services.GetService<ISymbolRenamedCodeActionOperationFactoryWorkspaceService>();
var newSolution = await _createChangedSolutionAsync(cancellationToken).ConfigureAwait(false);
return new CodeActionOperation[]
{
new ApplyChangesOperation(newSolution),
factory.CreateSymbolRenamedOperation(_symbol, _newName, _startingSolution, newSolution)
}.AsEnumerable();
}

public override string Title => _title;

public override string EquivalenceKey => _equivalenceKey;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// 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 Microsoft.CodeAnalysis.Host;

namespace Microsoft.CodeAnalysis.CodeActions.WorkspaceServices
{
internal interface ISymbolRenamedCodeActionOperationFactoryWorkspaceService : IWorkspaceService
{
CodeActionOperation CreateSymbolRenamedOperation(ISymbol symbol, string newName, Solution startingSolution, Solution updatedSolution);
}
}
1 change: 1 addition & 0 deletions src/Features/Core/Portable/Features.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
<Compile Include="AddPackage\InstallPackageParentCodeAction.cs" />
<Compile Include="AddParameter\AbstractAddParameterCodeFixProvider.cs" />
<Compile Include="CodeFixes\RemoveUnusedVariable\AbstractRemoveUnusedVariableCodeFixProvider.cs" />
<Compile Include="CodeRefactorings\WorkspaceServices\ISymbolRenamedCodeActionOperationFactoryWorkspaceService.cs" />
<Compile Include="ConvertIfToSwitch\AbstractConvertIfToSwitchCodeRefactoringProvider.cs" />
<Compile Include="ConvertNumericLiteral\AbstractConvertNumericLiteralCodeRefactoringProvider.cs" />
<Compile Include="CodeRefactorings\UseNamedArguments\AbstractUseNamedArgumentsCodeRefactoringProvider.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 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.Composition;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeActions.WorkspaceServices;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Host.Mef;

namespace Microsoft.VisualStudio.LanguageServices.Implementation
{
[ExportWorkspaceService(typeof(ISymbolRenamedCodeActionOperationFactoryWorkspaceService), ServiceLayer.Host), Shared]
internal sealed class VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService : ISymbolRenamedCodeActionOperationFactoryWorkspaceService
{
private readonly IEnumerable<IRefactorNotifyService> _refactorNotifyServices;

[ImportingConstructor]
public VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService(
[ImportMany] IEnumerable<IRefactorNotifyService> refactorNotifyServices)
{
_refactorNotifyServices = refactorNotifyServices;
}

public CodeActionOperation CreateSymbolRenamedOperation(ISymbol symbol, string newName, Solution startingSolution, Solution updatedSolution)
{
return new RenameSymbolOperation(
_refactorNotifyServices,
symbol ?? throw new ArgumentNullException(nameof(symbol)),
newName ?? throw new ArgumentNullException(nameof(newName)),
startingSolution ?? throw new ArgumentNullException(nameof(startingSolution)),
updatedSolution ?? throw new ArgumentNullException(nameof(updatedSolution)));
}

private class RenameSymbolOperation : CodeActionOperation
{
private readonly IEnumerable<IRefactorNotifyService> _refactorNotifyServices;
private readonly ISymbol _symbol;
private readonly string _newName;
private readonly Solution _startingSolution;
private readonly Solution _updatedSolution;

public RenameSymbolOperation(
IEnumerable<IRefactorNotifyService> refactorNotifyServices,
ISymbol symbol,
string newName,
Solution startingSolution,
Solution updatedSolution)
{
_refactorNotifyServices = refactorNotifyServices;
_symbol = symbol;
_newName = newName;
_startingSolution = startingSolution;
_updatedSolution = updatedSolution;
}

public override void Apply(Workspace workspace, CancellationToken cancellationToken = default(CancellationToken))
{
var updatedDocumentIds = _updatedSolution.GetChanges(_startingSolution).GetProjectChanges().SelectMany(p => p.GetChangedDocuments());

foreach (var refactorNotifyService in _refactorNotifyServices)
{
// If something goes wrong and some language service rejects the rename, we
// can't really do anything about it because we're potentially in the middle of
// some unknown set of CodeActionOperations. This is a best effort approach.

if (refactorNotifyService.TryOnBeforeGlobalSymbolRenamed(workspace, updatedDocumentIds, _symbol, _newName, throwOnFailure: false))
{
refactorNotifyService.TryOnAfterGlobalSymbolRenamed(workspace, updatedDocumentIds, _symbol, _newName, throwOnFailure: false);
}
}
}

public override string Title => string.Format(EditorFeaturesResources.Rename_0_to_1, _symbol.Name, _newName);
}
}
}
1 change: 1 addition & 0 deletions src/VisualStudio/Core/Def/ServicesVisualStudio.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,7 @@
<Compile Include="Implementation\Workspace\GlobalUndoServiceFactory.NoOpUndoPrimitive.cs" />
<Compile Include="Implementation\Workspace\GlobalUndoServiceFactory.WorkspaceGlobalUndoTransaction.cs" />
<Compile Include="Implementation\Workspace\VisualStudioAddMetadataReferenceCodeActionOperationFactoryWorkspaceService.cs" />
<Compile Include="Implementation\Workspace\VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs" />
<Compile Include="Implementation\Workspace\VisualStudioFormattingRuleFactoryServiceFactory.cs" />
<Compile Include="Implementation\Workspace\VisualStudioDocumentNavigationService.cs" />
<Compile Include="Implementation\Workspace\VisualStudioDocumentNavigationServiceFactory.cs" />
Expand Down

0 comments on commit 7b2ecf1

Please sign in to comment.