OSDN Git Service

GapBufferが更新されていた
[fooeditengine/FooEditEngine.git] / Core / StringBuffer.cs
index 0fdc448..b1a03c9 100644 (file)
@@ -16,6 +16,7 @@ using System.Globalization;
 using System.Linq;
 using System.Text;
 using System.Text.RegularExpressions;
+using Nito.AsyncEx;
 using System.Threading;
 using System.Threading.Tasks;
 using Slusser.Collections.Generic;
@@ -33,60 +34,24 @@ namespace FooEditEngine
         /// </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);
         }
 
 
@@ -101,61 +66,16 @@ namespace FooEditEngine
 
         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; }
@@ -170,30 +90,23 @@ namespace FooEditEngine
 
         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));
         }
 
@@ -212,11 +125,11 @@ namespace FooEditEngine
                 //内部形式に変換する
                 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();
@@ -228,77 +141,90 @@ namespace FooEditEngine
             } 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>
@@ -307,7 +233,7 @@ namespace FooEditEngine
         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)
@@ -343,7 +269,7 @@ namespace FooEditEngine
         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;
@@ -372,7 +298,7 @@ namespace FooEditEngine
                     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;