1 | // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team |
---|
2 | // |
---|
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this |
---|
4 | // software and associated documentation files (the "Software"), to deal in the Software |
---|
5 | // without restriction, including without limitation the rights to use, copy, modify, merge, |
---|
6 | // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
---|
7 | // to whom the Software is furnished to do so, subject to the following conditions: |
---|
8 | // |
---|
9 | // The above copyright notice and this permission notice shall be included in all copies or |
---|
10 | // substantial portions of the Software. |
---|
11 | // |
---|
12 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
---|
13 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
---|
14 | // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
---|
15 | // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
---|
16 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
---|
17 | // DEALINGS IN THE SOFTWARE. |
---|
18 | |
---|
19 | using System; |
---|
20 | using System.Diagnostics; |
---|
21 | using System.Text; |
---|
22 | |
---|
23 | using ICSharpCode.AvalonEdit.Utils; |
---|
24 | |
---|
25 | namespace ICSharpCode.AvalonEdit.Document |
---|
26 | { |
---|
27 | /* |
---|
28 | /// <summary> |
---|
29 | /// Implementation of a gap text buffer. |
---|
30 | /// </summary> |
---|
31 | sealed class GapTextBuffer |
---|
32 | { |
---|
33 | char[] buffer = Empty<char>.Array; |
---|
34 | |
---|
35 | /// <summary> |
---|
36 | /// The current text content. |
---|
37 | /// Is set to null whenever the buffer changes, and gets a value only when the |
---|
38 | /// full text content is requested. |
---|
39 | /// </summary> |
---|
40 | string textContent; |
---|
41 | |
---|
42 | /// <summary> |
---|
43 | /// last GetText result |
---|
44 | /// </summary> |
---|
45 | string lastGetTextResult; |
---|
46 | int lastGetTextRequestOffset; |
---|
47 | |
---|
48 | int gapBeginOffset; |
---|
49 | int gapEndOffset; |
---|
50 | int gapLength; // gapLength == gapEndOffset - gapBeginOffset |
---|
51 | |
---|
52 | /// <summary> |
---|
53 | /// when gap is too small for inserted text or gap is too large (exceeds maxGapLength), |
---|
54 | /// a new buffer is reallocated with a new gap of at least this size. |
---|
55 | /// </summary> |
---|
56 | const int minGapLength = 128; |
---|
57 | |
---|
58 | /// <summary> |
---|
59 | /// when the gap exceeds this size, reallocate a smaller buffer |
---|
60 | /// </summary> |
---|
61 | const int maxGapLength = 4096; |
---|
62 | |
---|
63 | public int Length { |
---|
64 | get { |
---|
65 | return buffer.Length - gapLength; |
---|
66 | } |
---|
67 | } |
---|
68 | |
---|
69 | /// <summary> |
---|
70 | /// Gets the buffer content. |
---|
71 | /// </summary> |
---|
72 | public string Text { |
---|
73 | get { |
---|
74 | if (textContent == null) |
---|
75 | textContent = GetText(0, Length); |
---|
76 | return textContent; |
---|
77 | } |
---|
78 | set { |
---|
79 | Debug.Assert(value != null); |
---|
80 | textContent = value; lastGetTextResult = null; |
---|
81 | buffer = new char[value.Length + minGapLength]; |
---|
82 | value.CopyTo(0, buffer, 0, value.Length); |
---|
83 | gapBeginOffset = value.Length; |
---|
84 | gapEndOffset = buffer.Length; |
---|
85 | gapLength = gapEndOffset - gapBeginOffset; |
---|
86 | } |
---|
87 | } |
---|
88 | |
---|
89 | public char GetCharAt(int offset) |
---|
90 | { |
---|
91 | return offset < gapBeginOffset ? buffer[offset] : buffer[offset + gapLength]; |
---|
92 | } |
---|
93 | |
---|
94 | public string GetText(int offset, int length) |
---|
95 | { |
---|
96 | if (length == 0) |
---|
97 | return string.Empty; |
---|
98 | if (lastGetTextRequestOffset == offset && lastGetTextResult != null && length == lastGetTextResult.Length) |
---|
99 | return lastGetTextResult; |
---|
100 | |
---|
101 | int end = offset + length; |
---|
102 | string result; |
---|
103 | if (end < gapBeginOffset) { |
---|
104 | result = new string(buffer, offset, length); |
---|
105 | } else if (offset > gapBeginOffset) { |
---|
106 | result = new string(buffer, offset + gapLength, length); |
---|
107 | } else { |
---|
108 | int block1Size = gapBeginOffset - offset; |
---|
109 | int block2Size = end - gapBeginOffset; |
---|
110 | |
---|
111 | StringBuilder buf = new StringBuilder(block1Size + block2Size); |
---|
112 | buf.Append(buffer, offset, block1Size); |
---|
113 | buf.Append(buffer, gapEndOffset, block2Size); |
---|
114 | result = buf.ToString(); |
---|
115 | } |
---|
116 | lastGetTextRequestOffset = offset; |
---|
117 | lastGetTextResult = result; |
---|
118 | return result; |
---|
119 | } |
---|
120 | |
---|
121 | /// <summary> |
---|
122 | /// Inserts text at the specified offset. |
---|
123 | /// </summary> |
---|
124 | public void Insert(int offset, string text) |
---|
125 | { |
---|
126 | Debug.Assert(offset >= 0 && offset <= Length); |
---|
127 | |
---|
128 | if (text.Length == 0) |
---|
129 | return; |
---|
130 | |
---|
131 | textContent = null; lastGetTextResult = null; |
---|
132 | PlaceGap(offset, text.Length); |
---|
133 | text.CopyTo(0, buffer, gapBeginOffset, text.Length); |
---|
134 | gapBeginOffset += text.Length; |
---|
135 | gapLength = gapEndOffset - gapBeginOffset; |
---|
136 | } |
---|
137 | |
---|
138 | /// <summary> |
---|
139 | /// Remove <paramref name="length"/> characters at <paramref name="offset"/>. |
---|
140 | /// Leave a gap of at least <paramref name="reserveGapSize"/>. |
---|
141 | /// </summary> |
---|
142 | public void Remove(int offset, int length, int reserveGapSize) |
---|
143 | { |
---|
144 | Debug.Assert(offset >= 0 && offset <= Length); |
---|
145 | Debug.Assert(length >= 0 && offset + length <= Length); |
---|
146 | Debug.Assert(reserveGapSize >= 0); |
---|
147 | |
---|
148 | if (length == 0) |
---|
149 | return; |
---|
150 | |
---|
151 | textContent = null; lastGetTextResult = null; |
---|
152 | PlaceGap(offset, reserveGapSize - length); |
---|
153 | gapEndOffset += length; // delete removed text |
---|
154 | gapLength = gapEndOffset - gapBeginOffset; |
---|
155 | if (gapLength - reserveGapSize > maxGapLength && gapLength - reserveGapSize > buffer.Length / 4) { |
---|
156 | // shrink gap |
---|
157 | MakeNewBuffer(gapBeginOffset, reserveGapSize + minGapLength); |
---|
158 | } |
---|
159 | } |
---|
160 | |
---|
161 | void PlaceGap(int newGapOffset, int minRequiredGapLength) |
---|
162 | { |
---|
163 | if (gapLength < minRequiredGapLength) { |
---|
164 | // enlarge gap |
---|
165 | MakeNewBuffer(newGapOffset, minRequiredGapLength + Math.Max(minGapLength, buffer.Length / 8)); |
---|
166 | } else { |
---|
167 | while (newGapOffset < gapBeginOffset) { |
---|
168 | buffer[--gapEndOffset] = buffer[--gapBeginOffset]; |
---|
169 | } |
---|
170 | while (newGapOffset > gapBeginOffset) { |
---|
171 | buffer[gapBeginOffset++] = buffer[gapEndOffset++]; |
---|
172 | } |
---|
173 | } |
---|
174 | } |
---|
175 | |
---|
176 | void MakeNewBuffer(int newGapOffset, int newGapLength) |
---|
177 | { |
---|
178 | char[] newBuffer = new char[Length + newGapLength]; |
---|
179 | Debug.WriteLine("GapTextBuffer was reallocated, new size=" + newBuffer.Length); |
---|
180 | if (newGapOffset < gapBeginOffset) { |
---|
181 | // gap is moving backwards |
---|
182 | |
---|
183 | // first part: |
---|
184 | Array.Copy(buffer, 0, newBuffer, 0, newGapOffset); |
---|
185 | // moving middle part: |
---|
186 | Array.Copy(buffer, newGapOffset, newBuffer, newGapOffset + newGapLength, gapBeginOffset - newGapOffset); |
---|
187 | // last part: |
---|
188 | Array.Copy(buffer, gapEndOffset, newBuffer, newBuffer.Length - (buffer.Length - gapEndOffset), buffer.Length - gapEndOffset); |
---|
189 | } else { |
---|
190 | // gap is moving forwards |
---|
191 | // first part: |
---|
192 | Array.Copy(buffer, 0, newBuffer, 0, gapBeginOffset); |
---|
193 | // moving middle part: |
---|
194 | Array.Copy(buffer, gapEndOffset, newBuffer, gapBeginOffset, newGapOffset - gapBeginOffset); |
---|
195 | // last part: |
---|
196 | int lastPartLength = newBuffer.Length - (newGapOffset + newGapLength); |
---|
197 | Array.Copy(buffer, buffer.Length - lastPartLength, newBuffer, newGapOffset + newGapLength, lastPartLength); |
---|
198 | } |
---|
199 | |
---|
200 | gapBeginOffset = newGapOffset; |
---|
201 | gapEndOffset = newGapOffset + newGapLength; |
---|
202 | gapLength = newGapLength; |
---|
203 | buffer = newBuffer; |
---|
204 | } |
---|
205 | } |
---|
206 | */ |
---|
207 | } |
---|