2 * Copyright (C) 2013 FooProject
3 * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
6 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
7 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9 You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
12 using System.Collections.Generic;
13 using System.Globalization;
16 using System.Text.RegularExpressions;
17 using System.Threading;
18 using System.Threading.Tasks;
19 using Slusser.Collections.Generic;
21 namespace FooEditEngine
24 /// ランダムアクセス可能な列挙子を提供するインターフェイス
26 /// <typeparam name="T"></typeparam>
27 public interface IRandomEnumrator<T>
32 /// <param name="index">インデックス</param>
33 /// <returns>Tを返す</returns>
34 T this[int index]{get;}
37 sealed class StringBuffer : IEnumerable<char>, IRandomEnumrator<char>
39 GapBuffer<char> buf = new GapBuffer<char>();
43 this.Update = (s, e) => { };
46 public StringBuffer(StringBuffer buffer)
49 buf.AddRange(buffer.buf, buffer.Length);
52 public char this[int index]
61 public string ToString(int index, int length)
63 StringBuilder temp = new StringBuilder();
65 for (int i = index; i < index + length; i++)
67 return temp.ToString();
70 public IEnumerable<string> GetLines(int startIndex, int endIndex, int maxCharCount = -1)
72 foreach (Tuple<int, int> range in this.ForEachLines(startIndex, endIndex, maxCharCount))
74 StringBuilder temp = new StringBuilder();
76 int lineEndIndex = range.Item1;
78 lineEndIndex += range.Item2 - 1;
79 for (int i = range.Item1; i <= lineEndIndex; i++)
81 yield return temp.ToString();
85 public IEnumerable<Tuple<int,int>> ForEachLines(int startIndex, int endIndex, int maxCharCount = -1)
87 int currentLineHeadIndex = startIndex;
88 int currentLineLength = 0;
90 for (int i = startIndex; i <= endIndex; i++)
94 if (c == Document.NewLine ||
95 (maxCharCount != -1 && currentLineLength >= maxCharCount))
97 UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(c);
98 if (uc != UnicodeCategory.NonSpacingMark &&
99 uc != UnicodeCategory.SpacingCombiningMark &&
100 uc != UnicodeCategory.EnclosingMark &&
101 uc != UnicodeCategory.Surrogate)
103 yield return new Tuple<int,int>(currentLineHeadIndex, currentLineLength);
104 currentLineHeadIndex += currentLineLength;
105 currentLineLength = 0;
109 if (currentLineLength > 0)
110 yield return new Tuple<int, int>(currentLineHeadIndex, currentLineLength);
115 get { return this.buf.Count; }
118 internal DocumentUpdateEventHandler Update;
120 internal void Replace(StringBuffer buf)
122 this.Replace(buf.buf);
125 internal void Replace(GapBuffer<char> buf)
129 this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, buf.Count));
132 internal void Replace(int index, int length, IEnumerable<char> chars,int count)
135 this.buf.RemoveRange(index, length);
136 this.buf.InsertRange(index, chars,count);
137 this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, index, length, count));
140 internal async Task LoadAsync(IStreamReader fs, CancellationTokenSource tokenSource = null)
142 char[] str = new char[1024 * 256];
146 int index = this.Length;
150 readCount = await fs.ReadAsync(str, 0, str.Length).ConfigureAwait(false);
153 var internal_str = from s in str where s != '\r' && s != '\0' select s;
155 //str.lengthは事前に確保しておくために使用するので影響はない
156 this.buf.InsertRange(index, internal_str, str.Length);
158 if (tokenSource != null)
159 tokenSource.Token.ThrowIfCancellationRequested();
161 System.Threading.Thread.Sleep(10);
163 Array.Clear(str, 0, str.Length);
164 } while (readCount > 0);
165 this.Update(this, new DocumentUpdateEventArgs(UpdateType.Clear, -1, -1, -1));
166 this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, buf.Count));
169 internal void ReplaceRegexAll(LineToIndexTable layoutlines, Regex regex, string pattern, bool groupReplace)
171 for (int i = 0; i < layoutlines.Count; i++)
173 int lineHeadIndex = layoutlines.GetIndexFromLineNumber(i), lineLength = layoutlines.GetLengthFromLineNumber(i);
174 int left = lineHeadIndex, right = lineHeadIndex;
175 string output = regex.Replace(layoutlines[i], (m) => {
177 return m.Result(pattern);
181 this.buf.RemoveRange(lineHeadIndex, lineLength);
182 this.buf.InsertRange(lineHeadIndex, output, output.Length);
183 this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, lineHeadIndex, lineLength, output.Length, i));
187 internal void ReplaceAll(LineToIndexTable layoutlines,string target, string pattern, bool ci = false)
189 TextSearch ts = new TextSearch(target, ci);
190 char[] pattern_chars = pattern.ToCharArray();
191 for(int i = 0; i < layoutlines.Count; i++)
193 int lineHeadIndex = layoutlines.GetIndexFromLineNumber(i), lineLength = layoutlines.GetLengthFromLineNumber(i);
194 int left = lineHeadIndex, right = lineHeadIndex;
195 int newLineLength = lineLength;
196 while ((right = ts.IndexOf(this.buf, left, lineHeadIndex + newLineLength)) != -1)
198 this.buf.RemoveRange(right, target.Length);
199 this.buf.InsertRange(right, pattern_chars, pattern.Length);
200 left = right + pattern.Length;
201 newLineLength += pattern.Length - target.Length;
203 this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, lineHeadIndex, lineLength, newLineLength, i));
207 internal int IndexOf(string target, int start,bool ci = false)
209 TextSearch ts = new TextSearch(target,ci);
210 return ts.IndexOf(this.buf, start,this.buf.Count);
216 internal void Clear()
219 this.Update(this, new DocumentUpdateEventArgs(UpdateType.Clear, 0, this.buf.Count,0));
222 internal IEnumerable<char> GetEnumerator(int start, int length)
224 for (int i = start; i < start + length; i++)
225 yield return this.buf[i];
228 #region IEnumerable<char> メンバー
230 public IEnumerator<char> GetEnumerator()
232 for (int i = 0; i < this.Length; i++)
233 yield return this.buf[i];
238 #region IEnumerable メンバー
240 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
242 for (int i = 0; i < this.Length; i++)
243 yield return this[i];
249 sealed class TextSearch
253 Dictionary<char, int> qsTable = new Dictionary<char, int>();
255 public TextSearch(string pattern,bool ci = false)
257 this.patternLength = pattern.Length;
258 this.caseInsenstive = ci;
261 this.CreateQSTable(pattern.ToLower());
262 this.CreateQSTable(pattern.ToUpper());
263 this.pattern = new char[pattern.Length];
264 for (int i = 0; i < pattern.Length; i++)
265 this.pattern[i] = CharTool.ToUpperFastIf(pattern[i]);
269 this.CreateQSTable(pattern);
270 this.pattern = pattern.ToCharArray();
273 void CreateQSTable(string pattern)
275 int len = pattern.Length;
276 for (int i = 0; i < len; i++)
278 if (!this.qsTable.ContainsKey(pattern[i]))
279 this.qsTable.Add(pattern[i], len - i);
281 this.qsTable[pattern[i]] = len - i;
284 public int IndexOf(GapBuffer<char> buf, int start,int end)
287 int buflen = buf.Count - 1;
288 int plen = this.patternLength;
290 int search_end = end - plen;
292 if (this.caseInsenstive)
294 while (i <= search_end)
299 if (CharTool.ToUpperFastIf(buf[i + j]) != this.pattern[j])
310 if (k <= buflen) //buffer以降にアクセスする可能性がある
313 if (this.qsTable.TryGetValue(buf[k], out moveDelta))
328 while (i <= search_end)
333 if (buf[i + j] != this.pattern[j])
344 if (k <= buflen) //buffer以降にアクセスする可能性がある
347 if (this.qsTable.TryGetValue(buf[k], out moveDelta))
362 static class CharTool
365 /// Converts characters to lowercase.
367 const string _lookupStringL =
368 "---------------------------------!-#$%&-()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[-]^_`abcdefghijklmnopqrstuvwxyz{|}~-";
371 /// Converts characters to uppercase.
373 const string _lookupStringU =
374 "---------------------------------!-#$%&-()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[-]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~-";
377 /// Get lowercase version of this ASCII character.
379 public static char ToLower(char c)
381 return _lookupStringL[c];
385 /// Get uppercase version of this ASCII character.
387 public static char ToUpper(char c)
389 return _lookupStringU[c];
393 /// Translate uppercase ASCII characters to lowercase.
395 public static char ToLowerFastIf(char c)
397 if (c >= 'A' && c <= 'Z')
399 return (char)(c + 32);
408 /// Translate lowercase ASCII characters to uppercase.
410 public static char ToUpperFastIf(char c)
412 if (c >= 'a' && c <= 'z')
414 return (char)(c - 32);