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
}
}
|