提交 fa482ddc 编写于 作者: C CyrusNajmabadi

Move to more data based (i.e. no logic) code actions

上级 40bf6a70
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.AddPackage;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CodeFixes.AddImport
{
internal abstract partial class AbstractAddImportCodeFixProvider<TSimpleNameSyntax>
{
private class InstallPackageAndAddImportCodeAction : CodeAction
{
/// <summary>
/// The document before we added the import. Used so we can roll back if installing
/// the package failed.
/// </summary>
private readonly Document _oldDocument;
/// <summary>
/// The changes to make to <see cref="_oldDocument"/> to add the import.
/// </summary>
private readonly ImmutableArray<TextChange> _textChanges;
/// <summary>
/// The operation that will actually install the nuget package.
/// </summary>
private readonly InstallPackageDirectlyCodeActionOperation _installOperation;
public override string Title { get; }
public override string EquivalenceKey => Title;
internal override CodeActionPriority Priority { get; }
public InstallPackageAndAddImportCodeAction(
string title,
CodeActionPriority priority,
Document oldDocument,
ImmutableArray<TextChange> textChanges,
InstallPackageDirectlyCodeActionOperation installOperation)
{
Title = title;
Priority = priority;
_oldDocument = oldDocument;
_textChanges = textChanges;
_installOperation = installOperation;
}
/// <summary>
/// For preview purposes we return all the operations in a list. This way the
/// preview system stiches things together in the UI to make a suitable display.
/// i.e. if we have a SolutionChangedOperation and some other operation with a
/// Title, then the UI will show that nicely to the user.
/// </summary>
protected override async Task<IEnumerable<CodeActionOperation>> ComputePreviewOperationsAsync(CancellationToken cancellationToken)
{
// Make a SolutionChangeAction. This way we can let it generate the diff
// preview appropriately.
var solutionChangeAction = new SolutionChangeAction(
"", c => GetUpdatedSolutionAsync(c));
var result = ArrayBuilder<CodeActionOperation>.GetInstance();
result.AddRange(await solutionChangeAction.GetPreviewOperationsAsync(cancellationToken).ConfigureAwait(false));
result.Add(_installOperation);
return result.ToImmutableAndFree();
}
private async Task<Solution> GetUpdatedSolutionAsync(CancellationToken cancellationToken)
{
var oldText = await _oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
var newText = oldText.WithChanges(_textChanges);
var newDocument = _oldDocument.WithText(newText);
var newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
// Suppress diagnostics on the import we create. Because we only get here when we are
// adding a nuget package, it is certainly the case that in the preview this will not
// bind properly. It will look silly to show such an error, so we just suppress things.
var updatedRoot = newRoot.WithAdditionalAnnotations(SuppressDiagnosticsAnnotation.Create());
var updatedDocument = newDocument.WithSyntaxRoot(updatedRoot);
return updatedDocument.Project.Solution;
}
/// <summary>
/// However, for application purposes, we end up returning a single operation
/// that will then apply all our sub actions in order, stopping the moment
/// one of them fails.
/// </summary>
protected override async Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(
CancellationToken cancellationToken)
{
var oldText = await _oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
var newText = oldText.WithChanges(_textChanges);
return ImmutableArray.Create<CodeActionOperation>(
new InstallPackageAndAddImportOperation(
_oldDocument.Id, oldText, newText, _installOperation));
}
}
private class InstallPackageAndAddImportOperation : CodeActionOperation
{
private readonly DocumentId _changedDocumentId;
private readonly SourceText _oldText;
private readonly SourceText _newText;
private readonly InstallPackageDirectlyCodeActionOperation _installPackageOperation;
public InstallPackageAndAddImportOperation(
DocumentId changedDocumentId,
SourceText oldText,
SourceText newText,
InstallPackageDirectlyCodeActionOperation item2)
{
_changedDocumentId = changedDocumentId;
_oldText = oldText;
_newText = newText;
_installPackageOperation = item2;
}
internal override bool ApplyDuringTests => _installPackageOperation.ApplyDuringTests;
public override string Title => _installPackageOperation.Title;
internal override bool TryApply(Workspace workspace, IProgressTracker progressTracker, CancellationToken cancellationToken)
{
var newSolution = workspace.CurrentSolution.WithDocumentText(
_changedDocumentId, _newText);
// First make the changes to add the import to the document.
if (workspace.TryApplyChanges(newSolution, progressTracker))
{
if (_installPackageOperation.TryApply(workspace, progressTracker, cancellationToken))
{
return true;
}
// Installing the nuget package failed. Roll back the workspace.
var rolledBackSolution = workspace.CurrentSolution.WithDocumentText(
_changedDocumentId, _oldText);
workspace.TryApplyChanges(rolledBackSolution, progressTracker);
}
return false;
}
}
}
}
\ No newline at end of file
// 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.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Packaging;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeFixes.AddImport
{
internal abstract partial class AbstractAddImportCodeFixProvider<TSimpleNameSyntax>
{
private class InstallWithPackageManagerCodeAction : CodeAction
{
private readonly IPackageInstallerService _installerService;
private readonly string _packageName;
public InstallWithPackageManagerCodeAction(
IPackageInstallerService installerService,
string packageName)
{
_installerService = installerService;
_packageName = packageName;
}
public override string Title => FeaturesResources.Install_with_package_manager;
protected override Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(CancellationToken cancellationToken)
{
return Task.FromResult(SpecializedCollections.SingletonEnumerable<CodeActionOperation>(
new InstallWithPackageManagerCodeActionOperation(_installerService, _packageName)));
}
private class InstallWithPackageManagerCodeActionOperation : CodeActionOperation
{
private readonly IPackageInstallerService _installerService;
private readonly string _packageName;
public InstallWithPackageManagerCodeActionOperation(
IPackageInstallerService installerService,
string packageName)
{
_installerService = installerService;
_packageName = packageName;
}
public override string Title => FeaturesResources.Install_with_package_manager;
public override void Apply(Workspace workspace, CancellationToken cancellationToken)
=> _installerService.ShowManagePackagesDialog(_packageName);
}
}
}
}
\ No newline at end of file
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.AddPackage;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CodeFixes.AddImport
{
internal abstract partial class AbstractAddImportCodeFixProvider<TSimpleNameSyntax>
{
private partial class PackageReference
{
private class InstallPackageAndAddImportCodeAction : CodeAction
{
public override string Title { get; }
public override string EquivalenceKey => Title;
internal override CodeActionPriority Priority { get; }
/// <summary>
/// The document before we added the import. Used so we can roll back if installing
/// the package failed.
/// </summary>
private readonly Document _oldDocument;
/// <summary>
/// The changes to make to <see cref="_oldDocument"/> to add the import.
/// </summary>
private readonly ImmutableArray<TextChange> _textChanges;
/// <summary>
/// The operation that will actually install the nuget package.
/// </summary>
private readonly InstallPackageDirectlyCodeActionOperation _installOperation;
public InstallPackageAndAddImportCodeAction(
string title,
CodeActionPriority priority,
Document oldDocument,
ImmutableArray<TextChange> textChanges,
InstallPackageDirectlyCodeActionOperation installOperation)
{
Title = title;
Priority = priority;
_oldDocument = oldDocument;
_textChanges = textChanges;
_installOperation = installOperation;
}
/// <summary>
/// For preview purposes we return all the operations in a list. This way the
/// preview system stiches things together in the UI to make a suitable display.
/// i.e. if we have a SolutionChangedOperation and some other operation with a
/// Title, then the UI will show that nicely to the user.
/// </summary>
protected override async Task<IEnumerable<CodeActionOperation>> ComputePreviewOperationsAsync(CancellationToken cancellationToken)
{
// Make a SolutionChangeAction. This way we can let it generate the diff
// preview appropriately.
var solutionChangeAction = new SolutionChangeAction(
"", c => GetUpdatedSolutionAsync(c));
var result = ArrayBuilder<CodeActionOperation>.GetInstance();
result.AddRange(await solutionChangeAction.GetPreviewOperationsAsync(cancellationToken).ConfigureAwait(false));
result.Add(_installOperation);
return result.ToImmutableAndFree();
}
private async Task<Solution> GetUpdatedSolutionAsync(CancellationToken cancellationToken)
{
var oldText = await _oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
var newText = oldText.WithChanges(_textChanges);
var newDocument = _oldDocument.WithText(newText);
var newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
// Suppress diagnostics on the import we create. Because we only get here when we are
// adding a nuget package, it is certainly the case that in the preview this will not
// bind properly. It will look silly to show such an error, so we just suppress things.
var updatedRoot = newRoot.WithAdditionalAnnotations(SuppressDiagnosticsAnnotation.Create());
var updatedDocument = newDocument.WithSyntaxRoot(updatedRoot);
return updatedDocument.Project.Solution;
}
/// <summary>
/// However, for application purposes, we end up returning a single operation
/// that will then apply all our sub actions in order, stopping the moment
/// one of them fails.
/// </summary>
protected override async Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(
CancellationToken cancellationToken)
{
var oldText = await _oldDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);
var newText = oldText.WithChanges(_textChanges);
return ImmutableArray.Create<CodeActionOperation>(
new InstallPackageAndAddImportOperation(
_oldDocument.Id, oldText, newText, _installOperation));
}
}
private class InstallPackageAndAddImportOperation : CodeActionOperation
{
private readonly DocumentId _changedDocumentId;
private readonly SourceText _oldText;
private readonly SourceText _newText;
private readonly InstallPackageDirectlyCodeActionOperation _installPackageOperation;
public InstallPackageAndAddImportOperation(
DocumentId changedDocumentId,
SourceText oldText,
SourceText newText,
InstallPackageDirectlyCodeActionOperation item2)
{
_changedDocumentId = changedDocumentId;
_oldText = oldText;
_newText = newText;
_installPackageOperation = item2;
}
internal override bool ApplyDuringTests => _installPackageOperation.ApplyDuringTests;
public override string Title => _installPackageOperation.Title;
internal override bool TryApply(Workspace workspace, IProgressTracker progressTracker, CancellationToken cancellationToken)
{
var newSolution = workspace.CurrentSolution.WithDocumentText(
_changedDocumentId, _newText);
// First make the changes to add the import to the document.
if (workspace.TryApplyChanges(newSolution, progressTracker))
{
if (_installPackageOperation.TryApply(workspace, progressTracker, cancellationToken))
{
return true;
}
// Installing the nuget package failed. Roll back the workspace.
var rolledBackSolution = workspace.CurrentSolution.WithDocumentText(
_changedDocumentId, _oldText);
workspace.TryApplyChanges(rolledBackSolution, progressTracker);
}
return false;
}
}
}
}
}
\ No newline at end of file
// 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.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeFixes.AddImport
{
internal abstract partial class AbstractAddImportCodeFixProvider<TSimpleNameSyntax>
{
private partial class PackageReference : Reference
{
private class InstallWithPackageManagerCodeAction : CodeAction
{
private readonly PackageReference reference;
public InstallWithPackageManagerCodeAction(PackageReference reference)
{
this.reference = reference;
}
public override string Title => FeaturesResources.Install_with_package_manager;
protected override Task<IEnumerable<CodeActionOperation>> ComputeOperationsAsync(CancellationToken cancellationToken)
{
return Task.FromResult(SpecializedCollections.SingletonEnumerable<CodeActionOperation>(
new InstallWithPackageManagerCodeActionOperation(reference)));
}
private class InstallWithPackageManagerCodeActionOperation : CodeActionOperation
{
private readonly PackageReference reference;
public InstallWithPackageManagerCodeActionOperation(PackageReference reference)
{
this.reference = reference;
}
public override string Title => FeaturesResources.Install_with_package_manager;
public override void Apply(Workspace workspace, CancellationToken cancellationToken)
{
reference._installerService.ShowManagePackagesDialog(reference._packageName);
}
}
}
}
}
}
// 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.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.AddPackage;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Tags;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeFixes.AddImport
{
internal abstract partial class AbstractAddImportCodeFixProvider<TSimpleNameSyntax>
{
private partial class PackageReference : Reference
{
/// <summary>
/// This is the top level 'Install Nuget Package' code action we show in
/// the lightbulb. It will have children to 'Install Latest',
/// 'Install Version 'X' ..., and 'Install with package manager'.
/// </summary>
private class ParentCodeAction : CodeAction.CodeActionWithNestedActions
{
private readonly PackageReference _reference;
public override ImmutableArray<string> Tags => WellKnownTagArrays.NuGet;
// Adding a nuget reference is lower priority than other fixes..
internal override CodeActionPriority Priority => CodeActionPriority.Low;
/// <summary>
/// Even though we have child actions, we mark ourselves as explicitly non-inlinable.
/// We want to the experience of having the top level item the user has to see and
/// navigate through, and we don't want our child items confusingly being added to the
/// top level light-bulb where it's not clear what effect they would have if invoked.
/// </summary>
public ParentCodeAction(
PackageReference reference,
Document document,
ImmutableArray<TextChange> textChanges)
: base(string.Format(FeaturesResources.Install_package_0, reference._packageName),
CreateNestedActions(reference, document, textChanges),
isInlinable: false)
{
_reference = reference;
}
private static ImmutableArray<CodeAction> CreateNestedActions(
PackageReference reference,
Document document,
ImmutableArray<TextChange> textChanges)
{
// Determine what versions of this package are already installed in some project
// in this solution. We'll offer to add those specific versions to this project,
// followed by an option to "Find and install latest version."
var installedVersions = reference._installerService.GetInstalledVersions(reference._packageName).NullToEmpty();
var codeActions = ArrayBuilder<CodeAction>.GetInstance();
// First add the actions to install a specific version.
codeActions.AddRange(installedVersions.Select(
v => CreateCodeAction(reference, document, textChanges, versionOpt: v, isLocal: true)));
// Now add the action to install the specific version.
var preferredVersion = reference._versionOpt;
if (preferredVersion == null || !installedVersions.Contains(preferredVersion))
{
codeActions.Add(CreateCodeAction(reference, document, textChanges,
versionOpt: reference._versionOpt, isLocal: false));
}
// And finally the action to show the package manager dialog.
codeActions.Add(new InstallWithPackageManagerCodeAction(reference));
return codeActions.ToImmutableAndFree();
}
private static CodeAction CreateCodeAction(
PackageReference reference,
Document document,
ImmutableArray<TextChange> textChanges,
string versionOpt,
bool isLocal)
{
var title = versionOpt == null
? FeaturesResources.Find_and_install_latest_version
: isLocal
? string.Format(FeaturesResources.Use_local_version_0, versionOpt)
: string.Format(FeaturesResources.Install_version_0, versionOpt);
var installOperation = new InstallPackageDirectlyCodeActionOperation(
reference._installerService, document, reference._source,
reference._packageName, versionOpt,
includePrerelease: false, isLocal: isLocal);
// Nuget hits should always come after other results.
return new InstallPackageAndAddImportCodeAction(
title, CodeActionPriority.Low,
document, textChanges, installOperation);
}
}
}
}
}
\ No newline at end of file
// 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.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.AddPackage;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.Tags;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeFixes.AddImport
{
internal abstract partial class AbstractAddImportCodeFixProvider<TSimpleNameSyntax>
{
/// <summary>
/// This is the top level 'Install Nuget Package' code action we show in
/// the lightbulb. It will have children to 'Install Latest',
/// 'Install Version 'X' ..., and 'Install with package manager'.
/// </summary>
private class ParentInstallPackageCodeAction : CodeAction.CodeActionWithNestedActions
{
public override ImmutableArray<string> Tags => WellKnownTagArrays.NuGet;
// Adding a nuget reference is lower priority than other fixes..
internal override CodeActionPriority Priority => CodeActionPriority.Low;
/// <summary>
/// Even though we have child actions, we mark ourselves as explicitly non-inlinable.
/// We want to the experience of having the top level item the user has to see and
/// navigate through, and we don't want our child items confusingly being added to the
/// top level light-bulb where it's not clear what effect they would have if invoked.
/// </summary>
public ParentInstallPackageCodeAction(
IPackageInstallerService installerService,
string source,
string packageName,
string versionOpt,
Document document,
ImmutableArray<TextChange> textChanges)
: base(string.Format(FeaturesResources.Install_package_0, packageName),
CreateNestedActions(installerService, source, packageName, versionOpt, document, textChanges),
isInlinable: false)
{
}
private static ImmutableArray<CodeAction> CreateNestedActions(
IPackageInstallerService installerService,
string source,
string packageName,
string versionOpt,
Document document,
ImmutableArray<TextChange> textChanges)
{
// Determine what versions of this package are already installed in some project
// in this solution. We'll offer to add those specific versions to this project,
// followed by an option to "Find and install latest version."
var installedVersions = installerService.GetInstalledVersions(packageName).NullToEmpty();
var codeActions = ArrayBuilder<CodeAction>.GetInstance();
// First add the actions to install a specific version.
codeActions.AddRange(installedVersions.Select(
v => CreateCodeAction(
installerService, source, packageName, v,
document, textChanges, isLocal: true)));
// Now add the action to install the specific version.
var preferredVersion = versionOpt;
if (preferredVersion == null || !installedVersions.Contains(preferredVersion))
{
codeActions.Add(CreateCodeAction(
installerService, source, packageName, versionOpt,
document, textChanges, isLocal: false));
}
// And finally the action to show the package manager dialog.
codeActions.Add(new InstallWithPackageManagerCodeAction(installerService, packageName));
return codeActions.ToImmutableAndFree();
}
private static CodeAction CreateCodeAction(
IPackageInstallerService installerService,
string source,
string packageName,
string versionOpt,
Document document,
ImmutableArray<TextChange> textChanges,
bool isLocal)
{
var title = versionOpt == null
? FeaturesResources.Find_and_install_latest_version
: isLocal
? string.Format(FeaturesResources.Use_local_version_0, versionOpt)
: string.Format(FeaturesResources.Install_version_0, versionOpt);
var installOperation = new InstallPackageDirectlyCodeActionOperation(
installerService, document, source, packageName, versionOpt,
includePrerelease: false, isLocal: isLocal);
// Nuget hits should always come after other results.
return new InstallPackageAndAddImportCodeAction(
title, CodeActionPriority.Low,
document, textChanges, installOperation);
}
}
}
}
\ No newline at end of file
......@@ -39,7 +39,8 @@ private partial class PackageReference : Reference
var textChanges = await GetTextChangesAsync(
document, node, placeSystemNamespaceFirst, cancellationToken).ConfigureAwait(false);
return new ParentCodeAction(this, document, textChanges);
return new ParentInstallPackageCodeAction(
_installerService, _source, _packageName, _versionOpt, document, textChanges);
}
public override bool Equals(object obj)
......
......@@ -118,8 +118,8 @@
<Compile Include="ConvertNumericLiteral\AbstractConvertNumericLiteralCodeRefactoringProvider.cs" />
<Compile Include="CodeRefactorings\UseNamedArguments\AbstractUseNamedArgumentsCodeRefactoringProvider.cs" />
<Compile Include="CodeStyle\AbstractCodeStyleDiagnosticAnalyzer.cs" />
<Compile Include="AddImport\CodeActions\PackageReference.InstallPackageAndAddImportCodeAction.cs" />
<Compile Include="AddImport\CodeActions\PackageReference.InstallWithPackageManagerCodeAction.cs" />
<Compile Include="AddImport\CodeActions\InstallPackageAndAddImportCodeAction.cs" />
<Compile Include="AddImport\CodeActions\InstallWithPackageManagerCodeAction.cs" />
<Compile Include="ConflictMarkerResolution\AbstractConflictMarkerCodeFixProvider.cs" />
<Compile Include="Completion\Providers\AbstractCrefCompletionProvider.cs" />
<Compile Include="ConvertToInterpolatedString\AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs" />
......@@ -184,7 +184,7 @@
<Compile Include="Navigation\NavigableItemFactory.cs" />
<Compile Include="Navigation\NavigableItemFactory.DeclaredSymbolNavigableItem.cs" />
<Compile Include="Navigation\NavigableItemFactory.SymbolLocationNavigableItem.cs" />
<Compile Include="AddImport\CodeActions\PackageReference.ParentCodeAction.cs" />
<Compile Include="AddImport\CodeActions\ParentInstallPackageCodeAction.cs" />
<Compile Include="Remote\RemoteArguments.cs" />
<Compile Include="RQName\Nodes\ResolvedRQNode.cs" />
<Compile Include="RQName\Nodes\RQArrayOrPointerType.cs" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册