未验证 提交 a038cef0 编写于 作者: S Sam Harwell 提交者: GitHub

Merge pull request #35907 from sharwell/retry-lightbulb

Update SuggestedAction to track successful application
...@@ -34,6 +34,8 @@ internal abstract partial class SuggestedAction : ForegroundThreadAffinitizedObj ...@@ -34,6 +34,8 @@ internal abstract partial class SuggestedAction : ForegroundThreadAffinitizedObj
protected readonly object Provider; protected readonly object Provider;
internal readonly CodeAction CodeAction; internal readonly CodeAction CodeAction;
private bool _isApplied;
private ICodeActionEditHandlerService EditHandler => SourceProvider.EditHandler; private ICodeActionEditHandlerService EditHandler => SourceProvider.EditHandler;
internal SuggestedAction( internal SuggestedAction(
...@@ -163,9 +165,11 @@ protected virtual void InnerInvoke(IProgressTracker progressTracker, Cancellatio ...@@ -163,9 +165,11 @@ protected virtual void InnerInvoke(IProgressTracker progressTracker, Cancellatio
FunctionId.CodeFixes_ApplyChanges, KeyValueLogMessage.Create(LogType.UserAction, m => CreateLogProperties(m)), cancellationToken)) FunctionId.CodeFixes_ApplyChanges, KeyValueLogMessage.Create(LogType.UserAction, m => CreateLogProperties(m)), cancellationToken))
{ {
// Note: we want to block the UI thread here so the user cannot modify anything while the codefix applies // Note: we want to block the UI thread here so the user cannot modify anything while the codefix applies
EditHandler.ApplyAsync(Workspace, getFromDocument(), var applicationTask = EditHandler.ApplyAsync(Workspace, getFromDocument(),
operations.ToImmutableArray(), CodeAction.Title, operations.ToImmutableArray(), CodeAction.Title,
progressTracker, cancellationToken).Wait(cancellationToken); progressTracker, cancellationToken);
applicationTask.Wait(cancellationToken);
_isApplied = applicationTask.Result;
} }
} }
} }
...@@ -308,5 +312,18 @@ public override int GetHashCode() ...@@ -308,5 +312,18 @@ public override int GetHashCode()
} }
#endregion #endregion
internal TestAccessor GetTestAccessor()
=> new TestAccessor(this);
internal readonly struct TestAccessor
{
private readonly SuggestedAction _suggestedAction;
public TestAccessor(SuggestedAction suggestedAction)
=> _suggestedAction = suggestedAction;
public ref bool IsApplied => ref _suggestedAction._isApplied;
}
} }
} }
...@@ -94,7 +94,7 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject, ...@@ -94,7 +94,7 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject,
return currentResult; return currentResult;
} }
public async Task ApplyAsync( public async Task<bool> ApplyAsync(
Workspace workspace, Document fromDocument, Workspace workspace, Document fromDocument,
ImmutableArray<CodeActionOperation> operations, ImmutableArray<CodeActionOperation> operations,
string title, IProgressTracker progressTracker, string title, IProgressTracker progressTracker,
...@@ -104,7 +104,7 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject, ...@@ -104,7 +104,7 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject,
if (operations.IsDefaultOrEmpty) if (operations.IsDefaultOrEmpty)
{ {
return; return _renameService.ActiveSession is null;
} }
if (_renameService.ActiveSession != null) if (_renameService.ActiveSession != null)
...@@ -112,7 +112,7 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject, ...@@ -112,7 +112,7 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject,
workspace.Services.GetService<INotificationService>()?.SendNotification( workspace.Services.GetService<INotificationService>()?.SendNotification(
EditorFeaturesResources.Cannot_apply_operation_while_a_rename_session_is_active, EditorFeaturesResources.Cannot_apply_operation_while_a_rename_session_is_active,
severity: NotificationSeverity.Error); severity: NotificationSeverity.Error);
return; return false;
} }
#if DEBUG && false #if DEBUG && false
...@@ -133,6 +133,8 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject, ...@@ -133,6 +133,8 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject,
var oldSolution = workspace.CurrentSolution; var oldSolution = workspace.CurrentSolution;
bool applied;
// Determine if we're making a simple text edit to a single file or not. // Determine if we're making a simple text edit to a single file or not.
// If we're not, then we need to make a linked global undo to wrap the // If we're not, then we need to make a linked global undo to wrap the
// application of these operations. This way we should be able to undo // application of these operations. This way we should be able to undo
...@@ -151,7 +153,7 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject, ...@@ -151,7 +153,7 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject,
using (workspace.Services.GetService<ISourceTextUndoService>().RegisterUndoTransaction(text, title)) using (workspace.Services.GetService<ISourceTextUndoService>().RegisterUndoTransaction(text, title))
{ {
operations.Single().Apply(workspace, cancellationToken); applied = operations.Single().TryApply(workspace, progressTracker, cancellationToken);
} }
} }
else else
...@@ -168,7 +170,7 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject, ...@@ -168,7 +170,7 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject,
transaction.AddDocument(fromDocument.Id); transaction.AddDocument(fromDocument.Id);
} }
ProcessOperations( applied = ProcessOperations(
workspace, operations, progressTracker, workspace, operations, progressTracker,
cancellationToken); cancellationToken);
...@@ -178,6 +180,7 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject, ...@@ -178,6 +180,7 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject,
var updatedSolution = operations.OfType<ApplyChangesOperation>().FirstOrDefault()?.ChangedSolution ?? oldSolution; var updatedSolution = operations.OfType<ApplyChangesOperation>().FirstOrDefault()?.ChangedSolution ?? oldSolution;
TryNavigateToLocationOrStartRenameSession(workspace, oldSolution, updatedSolution, cancellationToken); TryNavigateToLocationOrStartRenameSession(workspace, oldSolution, updatedSolution, cancellationToken);
return applied;
} }
private TextDocument TryGetSingleChangedText( private TextDocument TryGetSingleChangedText(
...@@ -257,10 +260,13 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject, ...@@ -257,10 +260,13 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject,
} }
} }
private static void ProcessOperations( /// <returns><see langword="true"/> if all expected <paramref name="operations"/> are applied successfully;
/// otherwise, <see langword="false"/>.</returns>
private static bool ProcessOperations(
Workspace workspace, ImmutableArray<CodeActionOperation> operations, Workspace workspace, ImmutableArray<CodeActionOperation> operations,
IProgressTracker progressTracker, CancellationToken cancellationToken) IProgressTracker progressTracker, CancellationToken cancellationToken)
{ {
var applied = true;
var seenApplyChanges = false; var seenApplyChanges = false;
foreach (var operation in operations) foreach (var operation in operations)
{ {
...@@ -275,8 +281,10 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject, ...@@ -275,8 +281,10 @@ internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject,
seenApplyChanges = true; seenApplyChanges = true;
} }
operation.TryApply(workspace, progressTracker, cancellationToken); applied &= operation.TryApply(workspace, progressTracker, cancellationToken);
} }
return applied;
} }
private void TryNavigateToLocationOrStartRenameSession(Workspace workspace, Solution oldSolution, Solution newSolution, CancellationToken cancellationToken) private void TryNavigateToLocationOrStartRenameSession(Workspace workspace, Solution oldSolution, Solution newSolution, CancellationToken cancellationToken)
......
...@@ -16,7 +16,7 @@ internal interface ICodeActionEditHandlerService ...@@ -16,7 +16,7 @@ internal interface ICodeActionEditHandlerService
SolutionPreviewResult GetPreviews( SolutionPreviewResult GetPreviews(
Workspace workspace, ImmutableArray<CodeActionOperation> operations, CancellationToken cancellationToken); Workspace workspace, ImmutableArray<CodeActionOperation> operations, CancellationToken cancellationToken);
Task ApplyAsync( Task<bool> ApplyAsync(
Workspace workspace, Document fromDocument, Workspace workspace, Document fromDocument,
ImmutableArray<CodeActionOperation> operations, ImmutableArray<CodeActionOperation> operations,
string title, IProgressTracker progressTracker, string title, IProgressTracker progressTracker,
......
...@@ -329,7 +329,7 @@ private async Task<IEnumerable<ISuggestedAction>> GetLightBulbActionsAsync(ILigh ...@@ -329,7 +329,7 @@ private async Task<IEnumerable<ISuggestedAction>> GetLightBulbActionsAsync(ILigh
return await SelectActionsAsync(actionSets); return await SelectActionsAsync(actionSets);
} }
public void ApplyLightBulbAction(string actionName, FixAllScope? fixAllScope, bool blockUntilComplete) public bool ApplyLightBulbAction(string actionName, FixAllScope? fixAllScope, bool blockUntilComplete)
{ {
var lightBulbAction = GetLightBulbApplicationAction(actionName, fixAllScope, blockUntilComplete); var lightBulbAction = GetLightBulbApplicationAction(actionName, fixAllScope, blockUntilComplete);
var task = ThreadHelper.JoinableTaskFactory.RunAsync(async () => var task = ThreadHelper.JoinableTaskFactory.RunAsync(async () =>
...@@ -337,16 +337,20 @@ public void ApplyLightBulbAction(string actionName, FixAllScope? fixAllScope, bo ...@@ -337,16 +337,20 @@ public void ApplyLightBulbAction(string actionName, FixAllScope? fixAllScope, bo
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
var activeTextView = GetActiveTextView(); var activeTextView = GetActiveTextView();
await lightBulbAction(activeTextView); return await lightBulbAction(activeTextView);
}); });
if (blockUntilComplete) if (blockUntilComplete)
{ {
task.Join(); var result = task.Join();
DismissLightBulbSession();
return result;
} }
return true;
} }
private Func<IWpfTextView, Task> GetLightBulbApplicationAction(string actionName, FixAllScope? fixAllScope, bool willBlockUntilComplete) private Func<IWpfTextView, Task<bool>> GetLightBulbApplicationAction(string actionName, FixAllScope? fixAllScope, bool willBlockUntilComplete)
{ {
return async view => return async view =>
{ {
...@@ -396,7 +400,7 @@ public void ApplyLightBulbAction(string actionName, FixAllScope? fixAllScope, bo ...@@ -396,7 +400,7 @@ public void ApplyLightBulbAction(string actionName, FixAllScope? fixAllScope, bo
if (string.IsNullOrEmpty(actionName)) if (string.IsNullOrEmpty(actionName))
{ {
return; return false;
} }
// Dismiss the lightbulb session as we not invoking the original code fix. // Dismiss the lightbulb session as we not invoking the original code fix.
...@@ -404,6 +408,8 @@ public void ApplyLightBulbAction(string actionName, FixAllScope? fixAllScope, bo ...@@ -404,6 +408,8 @@ public void ApplyLightBulbAction(string actionName, FixAllScope? fixAllScope, bo
} }
action.Invoke(CancellationToken.None); action.Invoke(CancellationToken.None);
return !(action is SuggestedAction suggestedAction)
|| suggestedAction.GetTestAccessor().IsApplied;
}; };
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.TestHooks;
using Xunit; using Xunit;
...@@ -30,12 +31,28 @@ public Verifier(TTextViewWindow textViewWindow, VisualStudioInstance instance) ...@@ -30,12 +31,28 @@ public Verifier(TTextViewWindow textViewWindow, VisualStudioInstance instance)
FixAllScope? fixAllScope = null, FixAllScope? fixAllScope = null,
bool blockUntilComplete = true) bool blockUntilComplete = true)
{ {
using var cancellationTokenSource = new CancellationTokenSource(Helper.HangMitigatingTimeout);
var expectedItems = new[] { expectedItem }; var expectedItems = new[] { expectedItem };
CodeActions(expectedItems, applyFix ? expectedItem : null, verifyNotShowing,
ensureExpectedItemsAreOrdered, fixAllScope, blockUntilComplete); bool? applied;
do
{
cancellationTokenSource.Token.ThrowIfCancellationRequested();
applied = CodeActions(expectedItems, applyFix ? expectedItem : null, verifyNotShowing,
ensureExpectedItemsAreOrdered, fixAllScope, blockUntilComplete);
} while (applied is false);
} }
public void CodeActions( /// <returns>
/// <list type="bullet">
/// <item><description><see langword="true"/> if <paramref name="applyFix"/> is specified and the fix is successfully applied</description></item>
/// <item><description><see langword="false"/> if <paramref name="applyFix"/> is specified but the fix is not successfully applied</description></item>
/// <item><description><see langword="null"/> if <paramref name="applyFix"/> is false, so there is no fix to apply</description></item>
/// </list>
/// </returns>
public bool? CodeActions(
IEnumerable<string> expectedItems, IEnumerable<string> expectedItems,
string applyFix = null, string applyFix = null,
bool verifyNotShowing = false, bool verifyNotShowing = false,
...@@ -49,7 +66,7 @@ public Verifier(TTextViewWindow textViewWindow, VisualStudioInstance instance) ...@@ -49,7 +66,7 @@ public Verifier(TTextViewWindow textViewWindow, VisualStudioInstance instance)
if (verifyNotShowing) if (verifyNotShowing)
{ {
CodeActionsNotShowing(); CodeActionsNotShowing();
return; return null;
} }
var actions = _textViewWindow.GetLightBulbActions(); var actions = _textViewWindow.GetLightBulbActions();
...@@ -72,14 +89,18 @@ public Verifier(TTextViewWindow textViewWindow, VisualStudioInstance instance) ...@@ -72,14 +89,18 @@ public Verifier(TTextViewWindow textViewWindow, VisualStudioInstance instance)
if (!string.IsNullOrEmpty(applyFix) || fixAllScope.HasValue) if (!string.IsNullOrEmpty(applyFix) || fixAllScope.HasValue)
{ {
_textViewWindow.ApplyLightBulbAction(applyFix, fixAllScope, blockUntilComplete); var result = _textViewWindow.ApplyLightBulbAction(applyFix, fixAllScope, blockUntilComplete);
if (blockUntilComplete) if (blockUntilComplete)
{ {
// wait for action to complete // wait for action to complete
_instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.LightBulb); _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.LightBulb);
} }
return result;
} }
return null;
} }
public void CodeActionsNotShowing() public void CodeActionsNotShowing()
......
...@@ -78,7 +78,7 @@ public void DismissLightBulbSession() ...@@ -78,7 +78,7 @@ public void DismissLightBulbSession()
public string[] GetLightBulbActions() public string[] GetLightBulbActions()
=> _textViewWindowInProc.GetLightBulbActions(); => _textViewWindowInProc.GetLightBulbActions();
public void ApplyLightBulbAction(string action, FixAllScope? fixAllScope, bool blockUntilComplete = true) public bool ApplyLightBulbAction(string action, FixAllScope? fixAllScope, bool blockUntilComplete = true)
=> _textViewWindowInProc.ApplyLightBulbAction(action, fixAllScope, blockUntilComplete); => _textViewWindowInProc.ApplyLightBulbAction(action, fixAllScope, blockUntilComplete);
public void InvokeCompletionList() public void InvokeCompletionList()
......
...@@ -41,8 +41,7 @@ public override void Apply(Workspace workspace, CancellationToken cancellationTo ...@@ -41,8 +41,7 @@ public override void Apply(Workspace workspace, CancellationToken cancellationTo
internal override bool TryApply( internal override bool TryApply(
Workspace workspace, IProgressTracker progressTracker, CancellationToken cancellationToken) Workspace workspace, IProgressTracker progressTracker, CancellationToken cancellationToken)
{ {
workspace.TryApplyChanges(ChangedSolution, progressTracker); return workspace.TryApplyChanges(ChangedSolution, progressTracker);
return true;
} }
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册