-
Notifications
You must be signed in to change notification settings - Fork 509
Expand file tree
/
Copy pathUsingCodeFixProvider.TreeTextSpan.cs
More file actions
253 lines (219 loc) · 9.26 KB
/
UsingCodeFixProvider.TreeTextSpan.cs
File metadata and controls
253 lines (219 loc) · 9.26 KB
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
#nullable disable
namespace StyleCop.Analyzers.OrderingRules
{
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Text;
/// <summary>
/// Implements a code fix for all misaligned using statements.
/// </summary>
internal sealed partial class UsingCodeFixProvider
{
/// <summary>
/// Immutable class representing a text span with a collection of children.
/// </summary>
private class TreeTextSpan : IEquatable<TreeTextSpan>, IComparable<TreeTextSpan>
{
/// <summary>
/// Initializes a new instance of the <see cref="TreeTextSpan"/> class.
/// </summary>
/// <param name="start">The start position for the span.</param>
/// <param name="end">The end position for the span.</param>
/// <param name="children">The children of the span.</param>
internal TreeTextSpan(int start, int end, ImmutableArray<TreeTextSpan> children)
{
this.Start = start;
this.End = end;
this.Children = children;
}
internal static TreeTextSpan Empty { get; } = new TreeTextSpan(0, 0, ImmutableArray<TreeTextSpan>.Empty);
/// <summary>
/// Gets the start position of the span.
/// </summary>
/// <value>The start position within the source code.</value>
internal int Start { get; }
/// <summary>
/// Gets the end position of the span.
/// </summary>
/// <value>The end position within the source code.</value>
internal int End { get; }
/// <summary>
/// Gets the children of this span.
/// </summary>
/// <value>A read-only list containing the children.</value>
internal ImmutableArray<TreeTextSpan> Children { get; }
/// <summary>
/// Determines if two instances of <see cref="TreeTextSpan"/> are the same.
/// </summary>
/// <param name="left">The first instance to compare.</param>
/// <param name="right">The second instance to compare.</param>
/// <returns>True if the instances are the same.</returns>
public static bool operator ==(TreeTextSpan left, TreeTextSpan right)
{
return left.Equals(right);
}
/// <summary>
/// Determines if two instances of <see cref="TreeTextSpan"/> are the different.
/// </summary>
/// <param name="left">The first instance to compare.</param>
/// <param name="right">The second instance to compare.</param>
/// <returns>True if the instances are different.</returns>
public static bool operator !=(TreeTextSpan left, TreeTextSpan right)
{
return !left.Equals(right);
}
/// <inheritdoc/>
public bool Equals(TreeTextSpan other)
{
return (this.Start == other.Start) && (this.End == other.End);
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return (obj is TreeTextSpan span) && this.Equals(span);
}
/// <inheritdoc/>
public override int GetHashCode()
{
unchecked
{
return this.Start + (this.End << 16);
}
}
/// <inheritdoc/>
public int CompareTo(TreeTextSpan other)
{
var diff = this.Start - other.Start;
if (diff == 0)
{
diff = this.End - other.End;
}
return diff;
}
/// <summary>
/// Creates a new builder for a <see cref="TreeTextSpan"/>.
/// </summary>
/// <param name="start">The start of the span.</param>
/// <returns>The created builder.</returns>
internal static Builder CreateBuilder(int start)
{
return new Builder(start);
}
/// <summary>
/// Checks if the given <paramref name="span"/> is contained within this span.
/// </summary>
/// <param name="span">The <see cref="TreeTextSpan"/> to check.</param>
/// <returns>True if the given <paramref name="span"/> is contained.</returns>
internal bool Contains(TreeTextSpan span)
{
return (span.Start >= this.Start) && (span.End <= this.End);
}
/// <summary>
/// Gets smallest (child) span that contains the given <paramref name="textSpan"/>.
/// This assumes non-overlapping children.
/// </summary>
/// <param name="textSpan">The span to check.</param>
/// <returns>The <see cref="TreeTextSpan"/> that is the best match, or null if there is no match.</returns>
internal TreeTextSpan GetContainingSpan(TextSpan textSpan)
{
if ((textSpan.Start < this.Start) || (textSpan.End > this.End))
{
return Empty;
}
foreach (var span in this.Children)
{
var childSpan = span.GetContainingSpan(textSpan);
if (childSpan != Empty)
{
return childSpan;
}
}
return this;
}
/// <summary>
/// Helper class that can be used to construct a tree of <see cref="TreeTextSpan"/> objects.
/// </summary>
internal class Builder
{
private readonly List<Builder> children = new List<Builder>();
private readonly int start;
private int end = int.MaxValue;
/// <summary>
/// Initializes a new instance of the <see cref="Builder"/> class.
/// </summary>
/// <param name="start">The start of the span.</param>
internal Builder(int start)
{
this.start = start;
}
private Builder(int start, int end)
{
this.start = start;
this.end = end;
}
/// <summary>
/// Sets the end of the span.
/// </summary>
/// <param name="end">The end of the span.</param>
internal void SetEnd(int end)
{
this.end = end;
}
/// <summary>
/// Add a new child to the span.
/// </summary>
/// <param name="start">The start of the child span.</param>
/// <returns>The <see cref="Builder"/> for the child.</returns>
internal Builder AddChild(int start)
{
var childBuilder = new Builder(start);
this.children.Add(childBuilder);
return childBuilder;
}
/// <summary>
/// Makes sure that the gaps between children are filled.
/// These extra spans are created to make sure that using statements will not be moved over directive boundaries.
/// </summary>
internal void FillGaps()
{
Builder newFiller;
if (this.children.Count == 0)
{
return;
}
var previousEnd = int.MaxValue;
for (var i = 0; i < this.children.Count; i++)
{
var child = this.children[i];
if (child.start > previousEnd)
{
newFiller = new Builder(previousEnd, child.start);
this.children.Insert(i, newFiller);
i++;
}
child.FillGaps();
previousEnd = child.end;
}
if (previousEnd < this.end)
{
newFiller = new Builder(previousEnd, this.end);
this.children.Add(newFiller);
}
}
/// <summary>
/// Converts the builder (and its children) to a <see cref="TreeTextSpan"/> object.
/// </summary>
/// <returns>The created <see cref="TreeTextSpan"/> object.</returns>
internal TreeTextSpan ToSpan()
{
var children = this.children.Select(x => x.ToSpan()).ToImmutableArray();
return new TreeTextSpan(this.start, this.end, children);
}
}
}
}
}