-
Notifications
You must be signed in to change notification settings - Fork 4k
/
TextChangeRange.cs
131 lines (112 loc) · 4.04 KB
/
TextChangeRange.cs
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
// 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 enable
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Text
{
/// <summary>
/// Represents the change to a span of text.
/// </summary>
public readonly struct TextChangeRange : IEquatable<TextChangeRange>
{
/// <summary>
/// The span of text before the edit which is being changed
/// </summary>
public TextSpan Span { get; }
/// <summary>
/// Width of the span after the edit. A 0 here would represent a delete
/// </summary>
public int NewLength { get; }
/// <summary>
/// Initializes a new instance of <see cref="TextChangeRange"/>.
/// </summary>
/// <param name="span"></param>
/// <param name="newLength"></param>
public TextChangeRange(TextSpan span, int newLength)
: this()
{
if (newLength < 0)
{
throw new ArgumentOutOfRangeException(nameof(newLength));
}
this.Span = span;
this.NewLength = newLength;
}
/// <summary>
/// Compares current instance of <see cref="TextChangeRange"/> to another.
/// </summary>
public bool Equals(TextChangeRange other)
{
return
other.Span == this.Span &&
other.NewLength == this.NewLength;
}
/// <summary>
/// Compares current instance of <see cref="TextChangeRange"/> to another.
/// </summary>
public override bool Equals(object? obj)
{
return obj is TextChangeRange && Equals((TextChangeRange)obj);
}
/// <summary>
/// Provides hash code for current instance of <see cref="TextChangeRange"/>.
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return Hash.Combine(this.NewLength, this.Span.GetHashCode());
}
/// <summary>
/// Determines if two instances of <see cref="TextChangeRange"/> are same.
/// </summary>
public static bool operator ==(TextChangeRange left, TextChangeRange right)
{
return left.Equals(right);
}
/// <summary>
/// Determines if two instances of <see cref="TextChangeRange"/> are different.
/// </summary>
public static bool operator !=(TextChangeRange left, TextChangeRange right)
{
return !(left == right);
}
/// <summary>
/// An empty set of changes.
/// </summary>
public static IReadOnlyList<TextChangeRange> NoChanges => SpecializedCollections.EmptyReadOnlyList<TextChangeRange>();
/// <summary>
/// Collapse a set of <see cref="TextChangeRange"/>s into a single encompassing range. If
/// the set of ranges provided is empty, an empty range is returned.
/// </summary>
public static TextChangeRange Collapse(IEnumerable<TextChangeRange> changes)
{
var diff = 0;
var start = int.MaxValue;
var end = 0;
foreach (var change in changes)
{
diff += change.NewLength - change.Span.Length;
if (change.Span.Start < start)
{
start = change.Span.Start;
}
if (change.Span.End > end)
{
end = change.Span.End;
}
}
if (start > end)
{
// there were no changes.
return default(TextChangeRange);
}
var combined = TextSpan.FromBounds(start, end);
var newLen = combined.Length + diff;
return new TextChangeRange(combined, newLen);
}
}
}