aboutsummaryrefslogtreecommitdiffstatsci
path: root/Drawing2D/src/TextBuffer.cs
blob: 290fb757384f47b83813351b31e2d2d261d6a0b5 (plain)
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
// Copyright (c) 2021-2025  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.Linq;
using System.Collections.Generic;
using Crow.Text;
using Crow;

namespace Crow.Text
{
	public class TextBuffer {
		static int bufferExpension = 100;
		int length;
		Memory<char> buffer;
		ReadOnlyMemory<char> origBuffer;
		LineCollection lines;
		public bool mixedLineBreak = false;
		public string lineBreak = null;

		public LineCollection GetLineListCopy() => new LineCollection(lines.ToArray());
		public Span<char> Span => buffer.Span.Slice(0, length);
		public ReadOnlySpan<char> ReadOnlySpan => buffer.Span.Slice(0, length);
		public bool IsEmpty => length == 0;
		public bool IsDirty => !origBuffer.Span.Equals(Span, StringComparison.Ordinal);
		public int LinesCount => lines.Count;
		public int Length => length;
		public ReadOnlyMemory<char> ReadOnlyCopy {
			get {
				return ReadOnlySpan.ToArray();
			}
		} 
		public void ResetDirtyState () {
			origBuffer = Span.ToArray();
		}
		public TextBuffer(ReadOnlySpan<char> origText) {
			length = origText.Length;
			buffer = new char[length + bufferExpension];
			origText.CopyTo(buffer.Span);
			lines = new LineCollection (10);
			if (IsEmpty)
				lines.Add (new TextLine (0, 0, 0));
			else
				lines.Update (Span);
			ResetDirtyState ();
		}
		public void Update (TextChange change) {
			ReadOnlySpan<char> orig = buffer.Span;
			char[] newBuff = null;
			Span<char> tmp;
			if (buffer.Length < length + change.CharDiff) {
				newBuff = new char[length + change.CharDiff + bufferExpension];
				tmp = newBuff;
				orig.Slice(0, change.Start).CopyTo(tmp);
				if (change.CharDiff == 0)
					orig.Slice(change.End, length - change.End).CopyTo(tmp.Slice(change.End));
			} else
				tmp = buffer.Span;
			
			if (change.CharDiff != 0)
				orig.Slice(change.End, length - change.End).CopyTo(tmp.Slice(change.End2));
			if (!string.IsNullOrEmpty (change.ChangedText))
				change.ChangedText.AsSpan ().CopyTo (tmp.Slice (change.Start));
			
			if (newBuff != null)
				buffer = newBuff;
			length += change.CharDiff;

			lines.Update (change);
		}
		public string GetLineBreak () {
			if (string.IsNullOrEmpty (lineBreak)) {
				mixedLineBreak = false;

				if (lines.Count == 0 || lines[0].LineBreakLength == 0)
					lineBreak = Environment.NewLine;
				else {
					lineBreak = ReadOnlySpan.GetLineBreak (lines[0]).ToString ();
					for (int i = 1; i < lines.Count; i++) {
						if (!ReadOnlySpan.GetLineBreak (lines[i]).SequenceEqual (lineBreak)) {
							mixedLineBreak = true;
							break;
						}
					}
				}
			}
			return lineBreak;
		}
		public CharLocation GetLocation (int absolutePosition) => lines.GetLocation (absolutePosition);
		public TextLine GetLine (int index) => lines[index];
		public void SetLine (int index, TextLine line) => lines[index] = line;
		public ReadOnlySpan<char> GetText (TextLine line) => GetText(line.Span);
		public ReadOnlySpan<char> GetText (TextSpan textSpan) => buffer.Span.Slice(textSpan.Start, textSpan.Length);
		public int GetAbsolutePosition (CharLocation loc) => lines.GetAbsolutePosition (loc);
		public CharLocation EndLocation => new CharLocation (lines.Count - 1, lines[lines.Count - 1].Length);
		public virtual CharLocation GetWordStart (CharLocation loc) {
			int pos = GetAbsolutePosition (loc);
			//skip white spaces
			ReadOnlySpan<char> txt = ReadOnlySpan;
			while (pos > 0 && !char.IsLetterOrDigit (txt[pos-1]))
				pos--;
			while (pos > 0 && char.IsLetterOrDigit (txt[pos-1]))
				pos--;
			return GetLocation (pos);
		}
		public virtual CharLocation GetWordEnd (CharLocation loc) {
			int pos = GetAbsolutePosition (loc);
			//skip white spaces
			ReadOnlySpan<char> txt = ReadOnlySpan;
			while (pos < Length - 1 && !char.IsLetterOrDigit (txt[pos]))
				pos++;
			while (pos < Length - 1 && char.IsLetterOrDigit (txt[pos]))
				pos++;
			return GetLocation (pos);
		}

        public override string ToString() => ReadOnlySpan.ToString();
    }
}