Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notify XAML of renames caused by Naming Rule fixes #18693

Merged
merged 3 commits into from
May 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// 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.CodeFixes;
using Microsoft.CodeAnalysis.CodeFixes.NamingStyles;
using Microsoft.CodeAnalysis.CSharp.Diagnostics.NamingStyles;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Roslyn.Test.Utilities;
using Xunit;

Expand Down Expand Up @@ -306,5 +308,28 @@ class D : C
internal override void [|m|]() { }
}", new TestParameters(options: MethodNamesArePascalCase));
}

[Fact, Trait(Traits.Feature, Traits.Features.NamingStyle)]
[WorkItem(16562, "https://github.com/dotnet/roslyn/issues/16562")]
public async Task TestRefactorNotify()
{
var markup = @"public class [|c|] { }";
var testParameters = new TestParameters(options: ClassNamesArePascalCase);

using (var workspace = CreateWorkspaceFromOptions(markup, testParameters))
{
var actions = await GetCodeActionsAsync(workspace, testParameters);

var previewOperations = await actions[0].GetPreviewOperationsAsync(CancellationToken.None);
Assert.Empty(previewOperations.OfType<TestSymbolRenamedCodeActionOperationFactoryWorkspaceService.Operation>());

var commitOperations = await actions[0].GetOperationsAsync(CancellationToken.None);
Assert.Equal(2, commitOperations.Length);

var symbolRenamedOperation = (TestSymbolRenamedCodeActionOperationFactoryWorkspaceService.Operation)commitOperations[1];
Assert.Equal("c", symbolRenamedOperation._symbol.Name);
Assert.Equal("C", symbolRenamedOperation._newName);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,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
{
public ISymbol _symbol;
public string _newName;
public Solution _startingSolution;
public 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="Completion\FileSystemCompletionHelper.cs" />
<Compile Include="ConvertIfToSwitch\AbstractConvertIfToSwitchCodeRefactoringProvider.cs" />
<Compile Include="ConvertNumericLiteral\AbstractConvertNumericLiteralCodeRefactoringProvider.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 @@ -740,6 +740,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