File: InlineRename\UndoManagerServiceFactory.cs
Web Access
Project: ..\..\..\src\EditorFeatures\Core\Microsoft.CodeAnalysis.EditorFeatures.csproj (Microsoft.CodeAnalysis.EditorFeatures)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Operations;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename
{
    [ExportWorkspaceServiceFactory(typeof(IInlineRenameUndoManager), ServiceLayer.Default), Shared]
    internal class UndoManagerServiceFactory : IWorkspaceServiceFactory
    {
        private readonly InlineRenameService _inlineRenameService;
        private readonly IGlobalOptionService _globalOptionService;
 
        [ImportingConstructor]
        [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
        public UndoManagerServiceFactory(InlineRenameService inlineRenameService, IGlobalOptionService globalOptionService)
        {
            _inlineRenameService = inlineRenameService;
            _globalOptionService = globalOptionService;
        }
 
        public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
            => new InlineRenameUndoManager(_inlineRenameService, _globalOptionService);
 
        internal class InlineRenameUndoManager : AbstractInlineRenameUndoManager<InlineRenameUndoManager.BufferUndoState>, IInlineRenameUndoManager
        {
            internal class BufferUndoState
            {
                public ITextUndoHistory TextUndoHistory { get; set; }
                public ITextUndoTransaction StartRenameSessionUndoTransaction { get; set; }
                public ITextUndoTransaction ConflictResolutionUndoTransaction { get; set; }
            }
 
            public InlineRenameUndoManager(InlineRenameService inlineRenameService, IGlobalOptionService globalOptionService) : base(inlineRenameService, globalOptionService)
            {
            }
 
            public void CreateStartRenameUndoTransaction(Workspace workspace, ITextBuffer subjectBuffer, IInlineRenameSession inlineRenameSession)
            {
                var textUndoHistoryService = workspace.Services.GetService<ITextUndoHistoryWorkspaceService>();
                Contract.ThrowIfFalse(textUndoHistoryService.TryGetTextUndoHistory(workspace, subjectBuffer, out var undoHistory));
                UndoManagers[subjectBuffer] = new BufferUndoState() { TextUndoHistory = undoHistory };
                CreateStartRenameUndoTransaction(subjectBuffer);
            }
 
            public void CreateStartRenameUndoTransaction(ITextBuffer subjectBuffer)
            {
                var undoHistory = this.UndoManagers[subjectBuffer].TextUndoHistory;
 
                // Create an undo transaction to mark the starting point of the rename session in this buffer
                using var undoTransaction = undoHistory.CreateTransaction(EditorFeaturesResources.Start_Rename);
 
                undoTransaction.Complete();
                this.UndoManagers[subjectBuffer].StartRenameSessionUndoTransaction = undoTransaction;
                this.UndoManagers[subjectBuffer].ConflictResolutionUndoTransaction = null;
            }
 
            public void CreateConflictResolutionUndoTransaction(ITextBuffer subjectBuffer, Action applyEdit)
            {
                var undoHistory = this.UndoManagers[subjectBuffer].TextUndoHistory;
                while (true)
                {
                    if (undoHistory.UndoStack.First() == this.UndoManagers[subjectBuffer].StartRenameSessionUndoTransaction)
                    {
                        undoHistory.Undo(1);
                        break;
                    }
 
                    undoHistory.Undo(1);
                }
 
                using var undoTransaction = undoHistory.CreateTransaction(EditorFeaturesResources.Start_Rename);
 
                applyEdit();
                undoTransaction.Complete();
                UndoManagers[subjectBuffer].ConflictResolutionUndoTransaction = undoTransaction;
            }
 
            public void UndoTemporaryEdits(ITextBuffer subjectBuffer, bool disconnect)
                => UndoTemporaryEdits(subjectBuffer, disconnect, true);
 
            protected override void UndoTemporaryEdits(ITextBuffer subjectBuffer, bool disconnect, bool undoConflictResolution)
            {
                if (!this.UndoManagers.TryGetValue(subjectBuffer, out var bufferUndoState))
                {
                    return;
                }
 
                var undoHistory = bufferUndoState.TextUndoHistory;
                var targetTransaction = this.UndoManagers[subjectBuffer].ConflictResolutionUndoTransaction ?? this.UndoManagers[subjectBuffer].StartRenameSessionUndoTransaction;
                while (undoHistory.UndoStack.First() != targetTransaction)
                {
                    undoHistory.Undo(1);
                }
 
                if (undoConflictResolution)
                {
                    undoHistory.Undo(1);
                    if (disconnect)
                    {
                        return;
                    }
 
                    CreateStartRenameUndoTransaction(subjectBuffer);
                }
            }
 
            public void ApplyCurrentState(ITextBuffer subjectBuffer, object propagateSpansEditTag, IEnumerable<ITrackingSpan> spans)
            {
                ApplyReplacementText(subjectBuffer, this.UndoManagers[subjectBuffer].TextUndoHistory, propagateSpansEditTag, spans, this.currentState.ReplacementText);
 
                // Here we create the descriptions for the redo list dropdown.
                var undoHistory = this.UndoManagers[subjectBuffer].TextUndoHistory;
                foreach (var state in this.RedoStack.Reverse())
                {
                    using var transaction = undoHistory.CreateTransaction(GetUndoTransactionDescription(state.ReplacementText));
                    transaction.Complete();
                }
 
                if (this.RedoStack.Any())
                {
                    undoHistory.Undo(this.RedoStack.Count);
                }
            }
        }
    }
}