diff options
Diffstat (limited to 'Drawing2D/src/TextLineCollection.cs')
| -rw-r--r-- | Drawing2D/src/TextLineCollection.cs | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/Drawing2D/src/TextLineCollection.cs b/Drawing2D/src/TextLineCollection.cs new file mode 100644 index 00000000..5031496b --- /dev/null +++ b/Drawing2D/src/TextLineCollection.cs @@ -0,0 +1,209 @@ +// Copyright (c) 2013-2021 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace Crow.Text +{ + public class LineCollection : IList<TextLine> + { + TextLine[] lines; + int length; + + #region CTOR + public LineCollection (int capacity) { + lines = new TextLine[capacity]; + length = 0; + } + public LineCollection (TextLine[] _lines, int capacity = -1) { + if (capacity >= _lines.Length) { + lines = new TextLine[capacity]; + _lines.AsSpan ().CopyTo (lines); + } else + lines =_lines; + + length = _lines.Length; + } + + public LineCollection (string _text, int capacity = 4) : this (capacity) { + Update (_text.AsSpan ()); + } + #endregion + + public void Update (ReadOnlySpan<char> _text) { + length = 0; + int start = 0, i = 0; + while (i < _text.Length) { + char c = _text[i]; + if (c == '\r') { + if (++i < _text.Length) { + if (_text[i] == '\n') + Add (new TextLine (start, i - 1, ++i)); + else + Add (new TextLine (start, i - 1, i)); + } else + Add (new TextLine (start, i - 1, i)); + start = i; + } else if (c == '\n') { + if (++i < _text.Length) { + if (_text[i] == '\r') + Add (new TextLine (start, i - 1, ++i)); + else + Add (new TextLine (start, i - 1, i)); + } else + Add (new TextLine (start, i - 1, i)); + start = i; + + } else if (c == '\u0085' || c == '\u2028' || c == '\u2029') + Add (new TextLine (start, i - 1, i)); + else + i++; + } + + if (start < i) + Add (new TextLine (start, _text.Length, _text.Length)); + else + Add (new TextLine (_text.Length, _text.Length, _text.Length)); + } + + public void Update (TextChange change) { + CharLocation locStart = GetLocation (change.Start); + int charsDiff = change.ChangedText.Length - change.Length; + int lineEnd = locStart.Line; + while (lineEnd < length - 1 && change.End >= lines[lineEnd + 1].Start) + lineEnd++; + int columnEnd = change.End - lines[lineEnd].Start; + int lineEndLineBreakLength = lines[lineEnd].LineBreakLength; + + LineCollection newLines = new LineCollection (change.ChangedText); + int linesDiff = newLines.length - 1 - (lineEnd - locStart.Line); + TextLine endTl = lines[lineEnd]; + + if (linesDiff < 0) + RemoveAt (locStart.Line + 1, -linesDiff); + else if (linesDiff > 0) { + for (int i = 0; i < linesDiff; i++) + Insert (locStart.Line + 1, default); + } + + int remainingColumns = endTl.Length - columnEnd; + lineEnd += linesDiff; + lines[lineEnd].SetLength (0); + lines[locStart.Line].SetLength (locStart.Column + newLines[0].Length); + lines[lineEnd].Length += remainingColumns; + if (newLines.Count > 1) { + lines[lineEnd].Length += newLines[newLines.Count - 1].Length; + lines[locStart.Line].LengthIncludingLineBreak = lines[locStart.Line].Length + newLines[0].LineBreakLength; + } + lines[lineEnd].LengthIncludingLineBreak = lines[lineEnd].Length + endTl.LineBreakLength; + + for (int i = 1; i < newLines.Count - 1; i++) { + int l = locStart.Line + i; + lines[l] = newLines[i]; + lines[l].Start = lines[l - 1].EndIncludingLineBreak; + } + if (lineEnd > 0) + lines[lineEnd].Start = lines[lineEnd - 1].EndIncludingLineBreak; + + //shift start for remaining lines + for (int i = lineEnd + 1; i < length; i++) + lines[i].Start += charsDiff; + } + public int GetAbsolutePosition (CharLocation loc) => lines[loc.Line].Start + loc.Column; + public CharLocation GetLocation (int absolutePosition) { + TextLine tl = new TextLine (absolutePosition); + int result = lines.AsSpan (0, length).BinarySearch (tl); + if (result < 0) { + result = ~result; + return new CharLocation (result - 1, absolutePosition - lines[result - 1].Start); + } + return new CharLocation (result, absolutePosition - lines[result].Start); + } + [Obsolete] + public void UpdateLineLengthInPixel (int index, int lengthInPixel) { + lines[index].LengthInPixel = lengthInPixel; + } + public int Count => length; + public bool IsReadOnly => false; + public bool IsEmpty => length == 0; + + public TextLine this[int index] { get => lines[index]; set => lines[index] = value; } + + public void Add (TextLine item) { + if (lines.Length < length + 1) { + TextLine[] tmp = new TextLine[length * 2]; + lines.AsSpan ().CopyTo (tmp); + lines = tmp; + } + lines[length] = item; + length++; + } + + public void Clear () { + length = 0; + } + + public bool Contains (TextLine item) => Array.IndexOf<TextLine> (lines, item) >= 0; + + public void CopyTo (TextLine[] array, int arrayIndex) { + lines.AsSpan (0, length).CopyTo (array.AsSpan (arrayIndex)); + } + + public bool Remove (TextLine item) { + int idx = Array.IndexOf<TextLine> (lines, item); + if (idx < 0) + return false; + if (idx + 1 < length) + lines.AsSpan (idx + 1, length - idx - 1).CopyTo (lines.AsSpan (idx)); + length--; + return true; + } + public IEnumerator<TextLine> GetEnumerator () => new Enumerator (this); + IEnumerator IEnumerable.GetEnumerator () => GetEnumerator (); + + public int IndexOf (TextLine item) => Array.IndexOf (lines, item); + + public void Insert (int index, TextLine item) { + if (lines.Length < length + 1) { + TextLine[] tmp = new TextLine[length * 2]; + lines.AsSpan (0, index).CopyTo (tmp); + lines.AsSpan (index).CopyTo (tmp.AsSpan (index + 1)); + lines = tmp; + }else + lines.AsSpan (index, length - index).CopyTo (lines.AsSpan (index + 1)); + lines[index] = item; + length++; + } + + public void RemoveAt (int index) { + if (index + 1 < length) + lines.AsSpan (index + 1, length - index - 1).CopyTo (lines.AsSpan (index)); + length--; + } + public void RemoveAt (int index, int count) { + if (index + count < length) + lines.AsSpan (index + count, length - index - count).CopyTo (lines.AsSpan (index)); + length -= count; + } + + public class Enumerator : IEnumerator<TextLine> + { + TextLine[] lines; + int length, position = -1; + public Enumerator (LineCollection coll) { + lines = coll.lines; + length = coll.length; + } + public TextLine Current => lines[position]; + object IEnumerator.Current => Current; + public void Dispose () { } + public bool MoveNext () => ++position < length; + public void Reset () { + position = -1; + } + } + } +} |