提交 2af4fdc4 编写于 作者: C CyrusNajmabadi

Add support for merging preview items across multiple operations.

Also, wrap all code action operations with an undo transaction.
This way we will attempt to undo all changes as one user undo
action.
上级 8b8d7ec5
......@@ -6,6 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Editor.Undo;
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Notification;
......@@ -16,7 +17,7 @@
namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeActions
{
[Export(typeof(ICodeActionEditHandlerService))]
internal class CodeActionEditHandlerService : ICodeActionEditHandlerService
internal class CodeActionEditHandlerService : ForegroundThreadAffinitizedObject, ICodeActionEditHandlerService
{
private readonly IPreviewFactoryService _previewService;
private readonly IInlineRenameService _renameService;
......@@ -38,13 +39,16 @@ public ITextBufferAssociatedViewService AssociatedViewService
get { return _associatedViewService; }
}
public SolutionPreviewResult GetPreviews(Workspace workspace, IEnumerable<CodeActionOperation> operations, CancellationToken cancellationToken)
public SolutionPreviewResult GetPreviews(
Workspace workspace, IEnumerable<CodeActionOperation> operations, CancellationToken cancellationToken)
{
if (operations == null)
{
return null;
}
SolutionPreviewResult currentResult = null;
foreach (var op in operations)
{
cancellationToken.ThrowIfCancellationRequested();
......@@ -59,30 +63,39 @@ public SolutionPreviewResult GetPreviews(Workspace workspace, IEnumerable<CodeAc
if (preview != null && !preview.IsEmpty)
{
return preview;
currentResult = SolutionPreviewResult.Merge(currentResult, preview);
continue;
}
}
var previewOp = op as PreviewOperation;
if (previewOp != null)
{
return new SolutionPreviewResult(new List<SolutionPreviewItem>() { new SolutionPreviewItem(projectId: null, documentId: null,
lazyPreview: c => previewOp.GetPreviewAsync(c)) });
currentResult = SolutionPreviewResult.Merge(currentResult,
new SolutionPreviewResult(new SolutionPreviewItem(
projectId: null, documentId: null,
lazyPreview: c => previewOp.GetPreviewAsync(c))));
continue;
}
var title = op.Title;
if (title != null)
{
return new SolutionPreviewResult(new List<SolutionPreviewItem>() { new SolutionPreviewItem(projectId: null, documentId: null,
lazyPreview: c => Task.FromResult<object>(title)) });
currentResult = SolutionPreviewResult.Merge(currentResult,
new SolutionPreviewResult(new SolutionPreviewItem(
projectId: null, documentId: null, text: title)));
continue;
}
}
return null;
return currentResult;
}
public void Apply(Workspace workspace, Document fromDocument, IEnumerable<CodeActionOperation> operations, string title, CancellationToken cancellationToken)
{
this.AssertIsForeground();
if (_renameService.ActiveSession != null)
{
workspace.Services.GetService<INotificationService>()?.SendNotification(
......@@ -108,7 +121,48 @@ public void Apply(Workspace workspace, Document fromDocument, IEnumerable<CodeAc
var oldSolution = workspace.CurrentSolution;
Solution updatedSolution = oldSolution;
foreach (var operation in operations)
var operationsList = operations.ToList();
if (operationsList.Count > 1)
{
// Make a linked undo to wrap all these operations. This way we should
// be able to undo them all with one user action.
using (var transaction = workspace.OpenGlobalUndoTransaction(title))
{
updatedSolution = ProcessOperations(workspace, fromDocument, title, oldSolution, updatedSolution, operationsList, cancellationToken);
// link current file in the global undo transaction
if (fromDocument != null)
{
transaction.AddDocument(fromDocument.Id);
}
transaction.Commit();
}
}
else
{
updatedSolution = ProcessOperations(workspace, fromDocument, title, oldSolution, updatedSolution, operationsList, cancellationToken);
}
#if DEBUG
foreach (var project in workspace.CurrentSolution.Projects)
{
foreach (var document in project.Documents)
{
if (documentErrorLookup.Contains(document.Id))
{
document.VerifyNoErrorsAsync("CodeAction introduced error in error-free code", cancellationToken).Wait(cancellationToken);
}
}
}
#endif
TryStartRenameSession(workspace, oldSolution, updatedSolution, cancellationToken);
}
private static Solution ProcessOperations(Workspace workspace, Document fromDocument, string title, Solution oldSolution, Solution updatedSolution, List<CodeActionOperation> operationsList, CancellationToken cancellationToken)
{
foreach (var operation in operationsList)
{
var applyChanges = operation as ApplyChangesOperation;
if (applyChanges == null)
......@@ -174,20 +228,7 @@ public void Apply(Workspace workspace, Document fromDocument, IEnumerable<CodeAc
}
}
#if DEBUG
foreach (var project in workspace.CurrentSolution.Projects)
{
foreach (var document in project.Documents)
{
if (documentErrorLookup.Contains(document.Id))
{
document.VerifyNoErrorsAsync("CodeAction introduced error in error-free code", cancellationToken).Wait(cancellationToken);
}
}
}
#endif
TryStartRenameSession(workspace, oldSolution, updatedSolution, cancellationToken);
return updatedSolution;
}
private void TryStartRenameSession(Workspace workspace, Solution oldSolution, Solution newSolution, CancellationToken cancellationToken)
......
......@@ -31,11 +31,9 @@ public SolutionPreviewItem(ProjectId projectId, DocumentId documentId, Func<Canc
}
public SolutionPreviewItem(ProjectId projectId, DocumentId documentId, string text)
: this(projectId, documentId, c => Task.FromResult<object>(text))
{
ProjectId = projectId;
DocumentId = documentId;
Text = text;
LazyPreview = c => Task.FromResult<object>(text);
}
}
}
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.VisualStudio.Text.Differencing;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor
......@@ -18,6 +14,11 @@ internal class SolutionPreviewResult : ForegroundThreadAffinitizedObject
private readonly IList<SolutionPreviewItem> _previews;
public readonly SolutionChangeSummary ChangeSummary;
public SolutionPreviewResult(SolutionPreviewItem preview, SolutionChangeSummary changeSummary = null)
: this(new List<SolutionPreviewItem> { preview }, changeSummary)
{
}
public SolutionPreviewResult(IList<SolutionPreviewItem> previews, SolutionChangeSummary changeSummary = null)
{
_previews = previews ?? SpecializedCollections.EmptyList<SolutionPreviewItem>();
......@@ -71,5 +72,24 @@ public async Task<IReadOnlyList<object>> GetPreviewsAsync(DocumentId preferredDo
return result.Count == 0 ? null : result;
}
/// <summary>Merge two different previews into one final preview result. The final preview will
/// have a concatenation of all the inidivual previews contained within each result.</summary>
internal static SolutionPreviewResult Merge(SolutionPreviewResult result1, SolutionPreviewResult result2)
{
if (result1 == null)
{
return result2;
}
if (result2 == null)
{
return result1;
}
return new SolutionPreviewResult(
result1._previews.Concat(result2._previews).ToList(),
result1.ChangeSummary ?? result2.ChangeSummary);
}
}
}
\ No newline at end of file
......@@ -121,7 +121,7 @@ private FrameworkElement CreatePreviewElement(IReadOnlyList<object> previewItems
}
else if (previewItem is string)
{
previewElement = GetPreviewForString((string)previewItem, createBorder: previewItems.Count == 0);
previewElement = GetPreviewForString((string)previewItem);
}
else if (previewItem is FrameworkElement)
{
......@@ -159,38 +159,15 @@ private void InitializeHyperlinks(Uri helpLink, string helpLinkToolTipText)
LearnMoreHyperlink.ToolTip = helpLinkToolTipText;
}
public static FrameworkElement GetPreviewForString(string previewContent, bool useItalicFontStyle = false, bool centerAlignTextHorizontally = false, bool createBorder = false)
private static FrameworkElement GetPreviewForString(string previewContent)
{
var textBlock = new TextBlock()
return new TextBlock()
{
Margin = new Thickness(5),
VerticalAlignment = VerticalAlignment.Center,
Text = previewContent,
TextWrapping = TextWrapping.Wrap,
};
if (useItalicFontStyle)
{
textBlock.FontStyle = FontStyles.Italic;
}
if (centerAlignTextHorizontally)
{
textBlock.TextAlignment = TextAlignment.Center;
}
FrameworkElement preview = textBlock;
if (createBorder)
{
preview = new Border()
{
Width = DefaultWidth,
MinHeight = 75,
Child = textBlock
};
}
return preview;
}
// This method adjusts the width of the header section to match that of the preview content
......
......@@ -81,7 +81,8 @@ private static Uri GetHelpLink(DiagnosticData diagnostic, string language, strin
return helpLink;
}
object IPreviewPaneService.GetPreviewPane(DiagnosticData diagnostic, string language, string projectType, IReadOnlyList<object> previewContent)
object IPreviewPaneService.GetPreviewPane(
DiagnosticData diagnostic, string language, string projectType, IReadOnlyList<object> previewContent)
{
var title = diagnostic?.Message;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册