forked from aspnet/AspNetWebStack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSpanEditHandler.cs
More file actions
184 lines (159 loc) · 7.76 KB
/
Copy pathSpanEditHandler.cs
File metadata and controls
184 lines (159 loc) · 7.76 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
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Text;
using System.Web.Razor.Tokenizer.Symbols;
using Microsoft.Internal.Web.Utils;
namespace System.Web.Razor.Editor
{
// Manages edits to a span
public class SpanEditHandler
{
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public SpanEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer)
: this(tokenizer, AcceptedCharacters.Any)
{
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public SpanEditHandler(Func<string, IEnumerable<ISymbol>> tokenizer, AcceptedCharacters accepted)
{
AcceptedCharacters = accepted;
Tokenizer = tokenizer;
}
public AcceptedCharacters AcceptedCharacters { get; set; }
/// <summary>
/// Provides a set of hints to editors which may be manipulating the document in which this span is located.
/// </summary>
public EditorHints EditorHints { get; set; }
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public Func<string, IEnumerable<ISymbol>> Tokenizer { get; set; }
public static SpanEditHandler CreateDefault()
{
return CreateDefault(s => Enumerable.Empty<ISymbol>());
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Func<T> is the recommended delegate type and requires this level of nesting.")]
public static SpanEditHandler CreateDefault(Func<string, IEnumerable<ISymbol>> tokenizer)
{
return new SpanEditHandler(tokenizer);
}
public virtual EditResult ApplyChange(Span target, TextChange change)
{
return ApplyChange(target, change, force: false);
}
public virtual EditResult ApplyChange(Span target, TextChange change, bool force)
{
PartialParseResult result = PartialParseResult.Accepted;
TextChange normalized = change.Normalize();
if (!force)
{
result = CanAcceptChange(target, normalized);
}
// If the change is accepted then apply the change
if (result.HasFlag(PartialParseResult.Accepted))
{
return new EditResult(result, UpdateSpan(target, normalized));
}
return new EditResult(result, new SpanBuilder(target));
}
public virtual bool OwnsChange(Span target, TextChange change)
{
int end = target.Start.AbsoluteIndex + target.Length;
int changeOldEnd = change.OldPosition + change.OldLength;
return change.OldPosition >= target.Start.AbsoluteIndex &&
(changeOldEnd < end || (changeOldEnd == end && AcceptedCharacters != AcceptedCharacters.None));
}
protected virtual PartialParseResult CanAcceptChange(Span target, TextChange normalizedChange)
{
return PartialParseResult.Rejected;
}
protected virtual SpanBuilder UpdateSpan(Span target, TextChange normalizedChange)
{
string newContent = normalizedChange.ApplyChange(target);
SpanBuilder newSpan = new SpanBuilder(target);
newSpan.ClearSymbols();
foreach (ISymbol sym in Tokenizer(newContent))
{
sym.OffsetStart(target.Start);
newSpan.Accept(sym);
}
if (target.Next != null)
{
SourceLocation newEnd = SourceLocationTracker.CalculateNewLocation(target.Start, newContent);
target.Next.ChangeStart(newEnd);
}
return newSpan;
}
protected internal static bool IsAtEndOfFirstLine(Span target, TextChange change)
{
int endOfFirstLine = target.Content.IndexOfAny(new char[] { (char)0x000d, (char)0x000a, (char)0x2028, (char)0x2029 });
return (endOfFirstLine == -1 || (change.OldPosition - target.Start.AbsoluteIndex) <= endOfFirstLine);
}
/// <summary>
/// Returns true if the specified change is an insertion of text at the end of this span.
/// </summary>
protected internal static bool IsEndInsertion(Span target, TextChange change)
{
return change.IsInsert && IsAtEndOfSpan(target, change);
}
/// <summary>
/// Returns true if the specified change is an insertion of text at the end of this span.
/// </summary>
protected internal static bool IsEndDeletion(Span target, TextChange change)
{
return change.IsDelete && IsAtEndOfSpan(target, change);
}
/// <summary>
/// Returns true if the specified change is a replacement of text at the end of this span.
/// </summary>
protected internal static bool IsEndReplace(Span target, TextChange change)
{
return change.IsReplace && IsAtEndOfSpan(target, change);
}
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "This method should only be used on Spans")]
protected internal static bool IsAtEndOfSpan(Span target, TextChange change)
{
return (change.OldPosition + change.OldLength) == (target.Start.AbsoluteIndex + target.Length);
}
/// <summary>
/// Returns the old text referenced by the change.
/// </summary>
/// <remarks>
/// If the content has already been updated by applying the change, this data will be _invalid_
/// </remarks>
protected internal static string GetOldText(Span target, TextChange change)
{
return target.Content.Substring(change.OldPosition - target.Start.AbsoluteIndex, change.OldLength);
}
// Is the specified span to the right of this span and immediately adjacent?
internal static bool IsAdjacentOnRight(Span target, Span other)
{
return target.Start.AbsoluteIndex < other.Start.AbsoluteIndex && target.Start.AbsoluteIndex + target.Length == other.Start.AbsoluteIndex;
}
// Is the specified span to the left of this span and immediately adjacent?
internal static bool IsAdjacentOnLeft(Span target, Span other)
{
return other.Start.AbsoluteIndex < target.Start.AbsoluteIndex && other.Start.AbsoluteIndex + other.Length == target.Start.AbsoluteIndex;
}
public override string ToString()
{
return GetType().Name + ";Accepts:" + AcceptedCharacters + ((EditorHints == EditorHints.None) ? String.Empty : (";Hints: " + EditorHints.ToString()));
}
public override bool Equals(object obj)
{
SpanEditHandler other = obj as SpanEditHandler;
return other != null &&
AcceptedCharacters == other.AcceptedCharacters &&
EditorHints == other.EditorHints;
}
public override int GetHashCode()
{
return HashCodeCombiner.Start()
.Add(AcceptedCharacters)
.Add(EditorHints)
.CombinedHash;
}
}
}