/AvalonEdit/ICSharpCode.AvalonEdit/Document/GapTextBuffer.cs

http://github.com/icsharpcode/ILSpy · C# · 207 lines · 7 code · 3 blank · 197 comment · 0 complexity · 86226131f1ec0526c6b8fcfe954643aa MD5 · raw file

  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. using System;
  19. using System.Diagnostics;
  20. using System.Text;
  21. using ICSharpCode.AvalonEdit.Utils;
  22. namespace ICSharpCode.AvalonEdit.Document
  23. {
  24. /*
  25. /// <summary>
  26. /// Implementation of a gap text buffer.
  27. /// </summary>
  28. sealed class GapTextBuffer
  29. {
  30. char[] buffer = Empty<char>.Array;
  31. /// <summary>
  32. /// The current text content.
  33. /// Is set to null whenever the buffer changes, and gets a value only when the
  34. /// full text content is requested.
  35. /// </summary>
  36. string textContent;
  37. /// <summary>
  38. /// last GetText result
  39. /// </summary>
  40. string lastGetTextResult;
  41. int lastGetTextRequestOffset;
  42. int gapBeginOffset;
  43. int gapEndOffset;
  44. int gapLength; // gapLength == gapEndOffset - gapBeginOffset
  45. /// <summary>
  46. /// when gap is too small for inserted text or gap is too large (exceeds maxGapLength),
  47. /// a new buffer is reallocated with a new gap of at least this size.
  48. /// </summary>
  49. const int minGapLength = 128;
  50. /// <summary>
  51. /// when the gap exceeds this size, reallocate a smaller buffer
  52. /// </summary>
  53. const int maxGapLength = 4096;
  54. public int Length {
  55. get {
  56. return buffer.Length - gapLength;
  57. }
  58. }
  59. /// <summary>
  60. /// Gets the buffer content.
  61. /// </summary>
  62. public string Text {
  63. get {
  64. if (textContent == null)
  65. textContent = GetText(0, Length);
  66. return textContent;
  67. }
  68. set {
  69. Debug.Assert(value != null);
  70. textContent = value; lastGetTextResult = null;
  71. buffer = new char[value.Length + minGapLength];
  72. value.CopyTo(0, buffer, 0, value.Length);
  73. gapBeginOffset = value.Length;
  74. gapEndOffset = buffer.Length;
  75. gapLength = gapEndOffset - gapBeginOffset;
  76. }
  77. }
  78. public char GetCharAt(int offset)
  79. {
  80. return offset < gapBeginOffset ? buffer[offset] : buffer[offset + gapLength];
  81. }
  82. public string GetText(int offset, int length)
  83. {
  84. if (length == 0)
  85. return string.Empty;
  86. if (lastGetTextRequestOffset == offset && lastGetTextResult != null && length == lastGetTextResult.Length)
  87. return lastGetTextResult;
  88. int end = offset + length;
  89. string result;
  90. if (end < gapBeginOffset) {
  91. result = new string(buffer, offset, length);
  92. } else if (offset > gapBeginOffset) {
  93. result = new string(buffer, offset + gapLength, length);
  94. } else {
  95. int block1Size = gapBeginOffset - offset;
  96. int block2Size = end - gapBeginOffset;
  97. StringBuilder buf = new StringBuilder(block1Size + block2Size);
  98. buf.Append(buffer, offset, block1Size);
  99. buf.Append(buffer, gapEndOffset, block2Size);
  100. result = buf.ToString();
  101. }
  102. lastGetTextRequestOffset = offset;
  103. lastGetTextResult = result;
  104. return result;
  105. }
  106. /// <summary>
  107. /// Inserts text at the specified offset.
  108. /// </summary>
  109. public void Insert(int offset, string text)
  110. {
  111. Debug.Assert(offset >= 0 && offset <= Length);
  112. if (text.Length == 0)
  113. return;
  114. textContent = null; lastGetTextResult = null;
  115. PlaceGap(offset, text.Length);
  116. text.CopyTo(0, buffer, gapBeginOffset, text.Length);
  117. gapBeginOffset += text.Length;
  118. gapLength = gapEndOffset - gapBeginOffset;
  119. }
  120. /// <summary>
  121. /// Remove <paramref name="length"/> characters at <paramref name="offset"/>.
  122. /// Leave a gap of at least <paramref name="reserveGapSize"/>.
  123. /// </summary>
  124. public void Remove(int offset, int length, int reserveGapSize)
  125. {
  126. Debug.Assert(offset >= 0 && offset <= Length);
  127. Debug.Assert(length >= 0 && offset + length <= Length);
  128. Debug.Assert(reserveGapSize >= 0);
  129. if (length == 0)
  130. return;
  131. textContent = null; lastGetTextResult = null;
  132. PlaceGap(offset, reserveGapSize - length);
  133. gapEndOffset += length; // delete removed text
  134. gapLength = gapEndOffset - gapBeginOffset;
  135. if (gapLength - reserveGapSize > maxGapLength && gapLength - reserveGapSize > buffer.Length / 4) {
  136. // shrink gap
  137. MakeNewBuffer(gapBeginOffset, reserveGapSize + minGapLength);
  138. }
  139. }
  140. void PlaceGap(int newGapOffset, int minRequiredGapLength)
  141. {
  142. if (gapLength < minRequiredGapLength) {
  143. // enlarge gap
  144. MakeNewBuffer(newGapOffset, minRequiredGapLength + Math.Max(minGapLength, buffer.Length / 8));
  145. } else {
  146. while (newGapOffset < gapBeginOffset) {
  147. buffer[--gapEndOffset] = buffer[--gapBeginOffset];
  148. }
  149. while (newGapOffset > gapBeginOffset) {
  150. buffer[gapBeginOffset++] = buffer[gapEndOffset++];
  151. }
  152. }
  153. }
  154. void MakeNewBuffer(int newGapOffset, int newGapLength)
  155. {
  156. char[] newBuffer = new char[Length + newGapLength];
  157. Debug.WriteLine("GapTextBuffer was reallocated, new size=" + newBuffer.Length);
  158. if (newGapOffset < gapBeginOffset) {
  159. // gap is moving backwards
  160. // first part:
  161. Array.Copy(buffer, 0, newBuffer, 0, newGapOffset);
  162. // moving middle part:
  163. Array.Copy(buffer, newGapOffset, newBuffer, newGapOffset + newGapLength, gapBeginOffset - newGapOffset);
  164. // last part:
  165. Array.Copy(buffer, gapEndOffset, newBuffer, newBuffer.Length - (buffer.Length - gapEndOffset), buffer.Length - gapEndOffset);
  166. } else {
  167. // gap is moving forwards
  168. // first part:
  169. Array.Copy(buffer, 0, newBuffer, 0, gapBeginOffset);
  170. // moving middle part:
  171. Array.Copy(buffer, gapEndOffset, newBuffer, gapBeginOffset, newGapOffset - gapBeginOffset);
  172. // last part:
  173. int lastPartLength = newBuffer.Length - (newGapOffset + newGapLength);
  174. Array.Copy(buffer, buffer.Length - lastPartLength, newBuffer, newGapOffset + newGapLength, lastPartLength);
  175. }
  176. gapBeginOffset = newGapOffset;
  177. gapEndOffset = newGapOffset + newGapLength;
  178. gapLength = newGapLength;
  179. buffer = newBuffer;
  180. }
  181. }
  182. */
  183. }