Welcome to mirror list, hosted at ThFree Co, Russian Federation.

TextTransactionMergePolicy.cs « EditorOperations « Impl « Text « src - github.com/microsoft/vs-editor-api.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 9c8086a0b0dad0a2d18edf79707a0f2285d80ac9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//
//  Copyright (c) Microsoft Corporation. All rights reserved.
//  Licensed under the MIT License. See License.txt in the project root for license information.
//
// This file contain implementations details that are subject to change without notice.
// Use at your own risk.
//
namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
    using System;
    using System.Linq;
    using System.Text;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Text;
    using System.Diagnostics;

    [Flags]
    internal enum TextTransactionMergeDirections
    {
        Forward     = 0x0001,
        Backward    = 0x0002
    }

    /// <summary>
    /// This is the merge policy used for determining whether text's undo transactions can be merged.
    /// </summary>
    internal class TextTransactionMergePolicy : IMergeTextUndoTransactionPolicy
    {
        #region Private members
        TextTransactionMergeDirections _allowableMergeDirections;
        #endregion

        #region Constructors
        public TextTransactionMergePolicy() : this (TextTransactionMergeDirections.Forward | TextTransactionMergeDirections.Backward)
        {
        }

        public TextTransactionMergePolicy(TextTransactionMergeDirections allowableMergeDirections)
        {
            _allowableMergeDirections = allowableMergeDirections;
        }
        #endregion

        #region IMergeTextUndoTransactionPolicy Members

        public bool CanMerge(ITextUndoTransaction newTransaction, ITextUndoTransaction oldTransaction)
        {
            // Validate
            if (newTransaction == null)
            {
                throw new ArgumentNullException(nameof(newTransaction));
            }

            if (oldTransaction == null)
            {
                throw new ArgumentNullException(nameof(oldTransaction));
            }

            TextTransactionMergePolicy oldPolicy = oldTransaction.MergePolicy as TextTransactionMergePolicy;
            TextTransactionMergePolicy newPolicy = newTransaction.MergePolicy as TextTransactionMergePolicy;
            if (oldPolicy == null || newPolicy == null)
            {
                throw new InvalidOperationException("The MergePolicy for both transactions should be a TextTransactionMergePolicy.");
            }

            // Make sure the merge policy directions permit merging these two transactions.
            if ((oldPolicy._allowableMergeDirections & TextTransactionMergeDirections.Forward) == 0 ||
                (newPolicy._allowableMergeDirections & TextTransactionMergeDirections.Backward) == 0)
            {
                return false;
            }

            // Only merge text transactions that have the same description
            if (!string.Equals(newTransaction.Description, oldTransaction.Description, StringComparison.Ordinal))
            {
                return false;
            }

            // If one of the transactions is empty, than it is safe to merge
            if ((newTransaction.UndoPrimitives.Count == 0) || (oldTransaction.UndoPrimitives.Count == 0))
            {
                return true;
            }

            // Make sure that we only merge consecutive edits
            ITextUndoPrimitive newerBeforeTextChangePrimitive = newTransaction.UndoPrimitives[0];
            ITextUndoPrimitive olderAfterTextChangePrimitive = oldTransaction.UndoPrimitives[oldTransaction.UndoPrimitives.Count - 1];

            return newerBeforeTextChangePrimitive.CanMerge(olderAfterTextChangePrimitive);
        }

        public void PerformTransactionMerge(ITextUndoTransaction existingTransaction, ITextUndoTransaction newTransaction)
        {
            if (existingTransaction == null)
                throw new ArgumentNullException(nameof(existingTransaction));
            if (newTransaction == null)
                throw new ArgumentNullException(nameof(newTransaction));

            // Remove trailing AfterTextBufferChangeUndoPrimitive from previous transaction and skip copying
            // initial BeforeTextBufferChangeUndoPrimitive from newTransaction, as they are unnecessary.
            int copyStartIndex = 0;
            int existingCount = existingTransaction.UndoPrimitives.Count;
            int newCount = newTransaction.UndoPrimitives.Count;
            if (existingCount > 0 && 
                newCount > 0 && 
                existingTransaction.UndoPrimitives[existingCount - 1] is AfterTextBufferChangeUndoPrimitive &&
                newTransaction.UndoPrimitives[0] is BeforeTextBufferChangeUndoPrimitive)
            {
                existingTransaction.UndoPrimitives.RemoveAt(existingCount - 1);
                copyStartIndex = 1;
            }
            else
            {
                // Unless undo is disabled (in which case both transactions will be empty), this is unexpected.
                Debug.Assert(existingCount == 0 && newCount == 0,
                    "Expected previous transaction to end with AfterTextBufferChangeUndoPrimitive and "
                    + "new transaction to start with BeforeTextBufferChangeUndoPrimitive");
            }

            // Copy items from newTransaction into existingTransaction.
            for (int i = copyStartIndex; i < newTransaction.UndoPrimitives.Count; i++)
            {
                existingTransaction.UndoPrimitives.Add(newTransaction.UndoPrimitives[i]);
            }
        }

        public bool TestCompatiblePolicy(IMergeTextUndoTransactionPolicy other)
        {
            if (other == null)
            {
                throw new ArgumentNullException(nameof(other));
            }

            // Only merge transaction if they are both a text transaction
            return this.GetType() == other.GetType();
        }

        #endregion
    }
}