using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
+using Nito.AsyncEx;
using System.Threading;
using System.Threading.Tasks;
using Slusser.Collections.Generic;
/// </summary>
/// <param name="index">インデックス</param>
/// <returns>Tを返す</returns>
- T this[int index]{get;}
+ T this[int index] { get; }
}
sealed class StringBuffer : IEnumerable<char>, IRandomEnumrator<char>
{
GapBuffer<char> buf = new GapBuffer<char>();
const int MaxSemaphoreCount = 1;
- SemaphoreSlim Semaphore = new SemaphoreSlim(MaxSemaphoreCount);
+ AsyncReaderWriterLock rwlock = new AsyncReaderWriterLock();
public StringBuffer()
{
this.Update = (s, e) => { };
}
- /// <summary>
- /// ロック中なら真を返し、そうでないなら偽を返す
- /// </summary>
- public bool IsLocked
- {
- get
- {
- return this.Semaphore.CurrentCount == 0;
- }
- }
-
- /// <summary>
- /// ロックを解除します
- /// </summary>
- public void UnLock()
- {
- this.Semaphore.Release();
- }
-
- /// <summary>
- /// ロックします
- /// </summary>
- public void Lock()
- {
- this.Semaphore.Wait();
- }
-
- /// <summary>
- /// ロックします
- /// </summary>
- /// <returns>Taskオブジェクト</returns>
- public Task LockAsync()
- {
- return this.Semaphore.WaitAsync();
- }
-
public StringBuffer(StringBuffer buffer)
: this()
{
- buf.AddRange(buffer.buf, buffer.Length);
+ buf.AddRange(buffer.buf);
}
public string ToString(int index, int length)
{
- this.Lock();
-
StringBuilder temp = new StringBuilder();
temp.Clear();
- for (int i = index; i < index + length; i++)
- temp.Append(buf[i]);
-
- this.UnLock();
-
- return temp.ToString();
- }
-
- public IEnumerable<string> GetLines(int startIndex, int endIndex, int maxCharCount = -1)
- {
- foreach (Tuple<int, int> range in this.ForEachLines(startIndex, endIndex, maxCharCount))
+ using (this.rwlock.ReaderLock())
{
- StringBuilder temp = new StringBuilder();
- temp.Clear();
- int lineEndIndex = range.Item1;
- if (range.Item2 > 0)
- lineEndIndex += range.Item2 - 1;
- for (int i = range.Item1; i <= lineEndIndex; i++)
+ for (int i = index; i < index + length; i++)
temp.Append(buf[i]);
- yield return temp.ToString();
}
+ return temp.ToString();
}
- public IEnumerable<Tuple<int,int>> ForEachLines(int startIndex, int endIndex, int maxCharCount = -1)
- {
- int currentLineHeadIndex = startIndex;
- int currentLineLength = 0;
-
- for (int i = startIndex; i <= endIndex; i++)
- {
- currentLineLength++;
- char c = this.buf[i];
- if (c == Document.NewLine ||
- (maxCharCount != -1 && currentLineLength >= maxCharCount))
- {
- UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(c);
- if (uc != UnicodeCategory.NonSpacingMark &&
- uc != UnicodeCategory.SpacingCombiningMark &&
- uc != UnicodeCategory.EnclosingMark &&
- uc != UnicodeCategory.Surrogate)
- {
- yield return new Tuple<int,int>(currentLineHeadIndex, currentLineLength);
- currentLineHeadIndex += currentLineLength;
- currentLineLength = 0;
- }
- }
- }
- if (currentLineLength > 0)
- yield return new Tuple<int, int>(currentLineHeadIndex, currentLineLength);
- }
-
public int Length
{
get { return this.buf.Count; }
internal void Replace(GapBuffer<char> buf)
{
- this.Lock();
-
- this.Clear();
- this.buf = buf;
-
- this.UnLock();
+ using (this.rwlock.WriterLock())
+ {
+ this.Clear();
+ this.buf = buf;
+ }
this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, buf.Count));
}
- internal void Replace(int index, int length, IEnumerable<char> chars,int count)
+ internal void Replace(int index, int length, IEnumerable<char> chars, int count)
{
- this.Lock();
-
- try{
+ using (this.rwlock.WriterLock())
+ {
if (length > 0)
this.buf.RemoveRange(index, length);
- this.buf.InsertRange(index, chars, count);
+ this.buf.InsertRange(index, chars);
}
- finally
- {
- this.UnLock();
- }
-
this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, index, length, count));
}
//内部形式に変換する
var internal_str = from s in str where s != '\r' && s != '\0' select s;
- await this.LockAsync().ConfigureAwait(false);
- //str.lengthは事前に確保しておくために使用するので影響はない
- this.buf.InsertRange(index, internal_str, str.Length);
-
- this.UnLock();
+ using (await this.rwlock.WriterLockAsync())
+ {
+ //str.lengthは事前に確保しておくために使用するので影響はない
+ this.buf.InsertRange(index, internal_str);
+ }
if (tokenSource != null)
tokenSource.Token.ThrowIfCancellationRequested();
} while (readCount > 0);
}
+ internal async Task SaveAsync(TextWriter fs, CancellationTokenSource tokenSource = null)
+ {
+ using(await this.rwlock.ReaderLockAsync())
+ {
+ StringBuilder line = new StringBuilder();
+ for (int i = 0; i < this.Length; i++)
+ {
+ char c = this[i];
+ line.Append(c);
+ if (c == Document.NewLine || i == this.Length - 1)
+ {
+ string str = line.ToString();
+ str = str.Replace(Document.NewLine.ToString(), fs.NewLine);
+ await fs.WriteAsync(str).ConfigureAwait(false);
+ line.Clear();
+ if (tokenSource != null)
+ tokenSource.Token.ThrowIfCancellationRequested();
+#if TEST_ASYNC
+ System.Threading.Thread.Sleep(10);
+#endif
+ }
+ }
+ }
+ }
+
internal void ReplaceRegexAll(LineToIndexTable layoutlines, Regex regex, string pattern, bool groupReplace)
{
for (int i = 0; i < layoutlines.Count; i++)
{
int lineHeadIndex = layoutlines.GetIndexFromLineNumber(i), lineLength = layoutlines.GetLengthFromLineNumber(i);
int left = lineHeadIndex, right = lineHeadIndex;
- string output = regex.Replace(layoutlines[i], (m) => {
+ string output;
+
+ output = regex.Replace(layoutlines[i], (m) => {
if (groupReplace)
return m.Result(pattern);
else
return pattern;
});
- this.Lock();
- try
+ using (this.rwlock.WriterLock())
{
//空行は削除する必要はない
if (lineHeadIndex < this.buf.Count)
this.buf.RemoveRange(lineHeadIndex, lineLength);
- this.buf.InsertRange(lineHeadIndex, output, output.Length);
- }
- finally
- {
- this.UnLock();
+ this.buf.InsertRange(lineHeadIndex, output);
}
this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, lineHeadIndex, lineLength, output.Length, i));
}
}
- internal void ReplaceAll(LineToIndexTable layoutlines,string target, string pattern, bool ci = false)
+ internal void ReplaceAll(LineToIndexTable layoutlines, string target, string pattern, bool ci = false)
{
TextSearch ts = new TextSearch(target, ci);
char[] pattern_chars = pattern.ToCharArray();
- for(int i = 0; i < layoutlines.Count; i++)
+ for (int i = 0; i < layoutlines.Count; i++)
{
int lineHeadIndex = layoutlines.GetIndexFromLineNumber(i), lineLength = layoutlines.GetLengthFromLineNumber(i);
int left = lineHeadIndex, right = lineHeadIndex;
int newLineLength = lineLength;
while ((right = ts.IndexOf(this.buf, left, lineHeadIndex + newLineLength)) != -1)
{
- this.Lock();
- try
+ using (this.rwlock.WriterLock())
{
this.buf.RemoveRange(right, target.Length);
- this.buf.InsertRange(right, pattern_chars, pattern.Length);
- }
- finally
- {
- this.UnLock();
-
+ this.buf.InsertRange(right, pattern_chars);
}
left = right + pattern.Length;
newLineLength += pattern.Length - target.Length;
}
-
this.Update(this, new DocumentUpdateEventArgs(UpdateType.Replace, lineHeadIndex, lineLength, newLineLength, i));
}
}
- internal int IndexOf(string target, int start,bool ci = false)
+ internal int IndexOf(string target, int start, bool ci = false)
{
- this.Lock();
-
- TextSearch ts = new TextSearch(target,ci);
- int patternIndex = ts.IndexOf(this.buf, start,this.buf.Count);
-
- this.UnLock();
-
- return patternIndex;
+ using (this.rwlock.ReaderLock())
+ {
+ TextSearch ts = new TextSearch(target, ci);
+ int patternIndex = ts.IndexOf(this.buf, start, this.buf.Count);
+ return patternIndex;
+ }
}
/// <summary>
internal void Clear()
{
this.buf.Clear();
- this.Update(this, new DocumentUpdateEventArgs(UpdateType.Clear, 0, this.buf.Count,0));
+ this.Update(this, new DocumentUpdateEventArgs(UpdateType.Clear, 0, this.buf.Count, 0));
}
internal IEnumerable<char> GetEnumerator(int start, int length)
int patternLength;
Dictionary<char, int> qsTable = new Dictionary<char, int>();
bool caseInsenstive;
- public TextSearch(string pattern,bool ci = false)
+ public TextSearch(string pattern, bool ci = false)
{
this.patternLength = pattern.Length;
this.caseInsenstive = ci;
this.qsTable[pattern[i]] = len - i;
}
}
- public int IndexOf(GapBuffer<char> buf, int start,int end)
+ public int IndexOf(GapBuffer<char> buf, int start, int end)
{
//QuickSearch法
int buflen = buf.Count - 1;