OSDN Git Service

ナレーターと単語単位の選択のロジックが似ていたのでまとめた
[fooeditengine/FooEditEngine.git] / Core / Document.cs
index 14d325a..9c5932f 100644 (file)
@@ -82,6 +82,10 @@ namespace FooEditEngine
         /// ドキュメント全体が削除されたことを表す
         /// </summary>
         Clear,
+        /// <summary>
+        /// レイアウトが再構築されたことを表す
+        /// </summary>
+        RebuildLayout,
     }
 
     /// <summary>
@@ -179,7 +183,6 @@ namespace FooEditEngine
             else
                 this.buffer = new StringBuffer(doc.buffer);
             this.buffer.Update = new DocumentUpdateEventHandler(buffer_Update);
-            this.UpdateCalledAlways += (s, e) => { };
             this.Update += new DocumentUpdateEventHandler((s, e) => { });
             this.ChangeFireUpdateEvent += new EventHandler((s, e) => { });
             this.StatusUpdate += new EventHandler((s, e) => { });
@@ -200,6 +203,7 @@ namespace FooEditEngine
             this.CaretChanged += (s, e) => { };
             this.AutoIndentHook += (s, e) => { };
             this.LineBreakChanged += (s, e) => { };
+            this.Dirty = false;
         }
 
         void WacthDogPattern_Updated(object sender, EventArgs e)
@@ -208,6 +212,15 @@ namespace FooEditEngine
         }
 
         /// <summary>
+        /// ダーティフラグ。保存されていなければ真、そうでなければ偽。
+        /// </summary>
+        public bool Dirty
+        {
+            get;
+            set;
+        }
+
+        /// <summary>
         /// キャレットでの選択の起点となる位置
         /// </summary>
         internal int AnchorIndex
@@ -641,14 +654,6 @@ namespace FooEditEngine
         public event DocumentUpdateEventHandler Update;
 
         /// <summary>
-        /// ドキュメントが更新された時に呼びされるイベント
-        /// </summary>
-        /// <remarks>
-        /// FireUpdateEventの値に関わらず常に呼びされます
-        /// </remarks>
-        internal event DocumentUpdateEventHandler UpdateCalledAlways;
-
-        /// <summary>
         /// FireUpdateEventの値が変わったときに呼び出されるイベント
         /// </summary>
         public event EventHandler ChangeFireUpdateEvent;
@@ -751,10 +756,8 @@ namespace FooEditEngine
         /// </summary>
         public void PerformLayout()
         {
-            //単に再構築するだけなので行ダーティフラグは更新してはいけない
             this.LayoutLines.IsFrozneDirtyFlag = true;
-            this.FireUpdate(new DocumentUpdateEventArgs(UpdateType.Clear, -1, -1, -1));
-            this.FireUpdate(new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, this.Length));
+            this.FireUpdate(new DocumentUpdateEventArgs(UpdateType.RebuildLayout, -1, -1, -1));
             this.LayoutLines.IsFrozneDirtyFlag = false;
             if (this.PerformLayouted != null)
                 this.PerformLayouted(this, null);
@@ -790,6 +793,9 @@ namespace FooEditEngine
                 throw new InvalidOperationException("");
             if (start < 0 || start + length < 0 || start + length > this.Length)
                 throw new ArgumentOutOfRangeException("startかendが指定できる範囲を超えてます");
+            //選択範囲が消されたとき
+            foreach (Selection sel in this.Selections)
+                this.LayoutLines.ClearLayoutCache(sel.start, sel.length);
             this.Selections.Clear();
             if (length < 0)
             {
@@ -802,12 +808,13 @@ namespace FooEditEngine
                 TextPoint startTextPoint = this.LayoutLines.GetTextPointFromIndex(start);
                 TextPoint endTextPoint = this.LayoutLines.GetTextPointFromIndex(start + length);
                 this.SelectByRectangle(new TextRectangle(startTextPoint, endTextPoint));
+                this.LayoutLines.ClearLayoutCache(start, length);
             }
             else if (length != 0)
             {
                 this.Selections.Add(Selection.Create(start, length));
+                this.LayoutLines.ClearLayoutCache(start, length);
             }
-            this.LayoutLines.ClearLayoutCache();
             this.SelectionChanged(this, null);
         }
 
@@ -887,21 +894,18 @@ namespace FooEditEngine
         }
 
         /// <summary>
-        /// ã\82»ã\83\91ã\83¬ã\83¼ã\82¿ã\83¼ã\81§å\9b²ã\81¾ã\82\8cã\81\9fç¯\84å\9b²å\86\85ã\82\92é\81¸æ\8a\9eする
+        /// ã\82»ã\83\91ã\83¬ã\83¼ã\82¿ã\83¼ã\81§å\8cºå\88\87ã\82\89ã\82\8cã\81\9fé \98å\9f\9fã\82\92å\8f\96å¾\97する
         /// </summary>
         /// <param name="index">探索を開始するインデックス</param>
         /// <param name="find_sep_func">セパレーターなら真を返し、そうでないなら偽を返す</param>
-        /// <param name="changeAnchor">選択の起点となるとインデックスを変更するなら真。そうでなければ偽</param>
-        public void SelectSepartor(int index,Func<char,bool> find_sep_func, bool changeAnchor = false)
+        /// <returns>開始インデックス、終了インデックス</returns>
+        public Tuple<int,int> GetSepartor(int index, Func<char, bool> find_sep_func)
         {
-            if (this.FireUpdateEvent == false)
-                throw new InvalidOperationException("");
-
             if (find_sep_func == null)
                 throw new ArgumentNullException("find_sep_func must not be null");
 
             if (this.Length <= 0 || index >= this.Length)
-                return;
+                return null;
 
             Document str = this;
 
@@ -910,12 +914,37 @@ namespace FooEditEngine
                 start--;
 
             if (find_sep_func(str[start]))
+            {
                 start++;
+            }
 
             int end = index;
             while (end < this.Length && !find_sep_func(str[end]))
                 end++;
 
+            return new Tuple<int, int>(start, end);
+        }
+
+        /// <summary>
+        /// セパレーターで囲まれた範囲内を選択する
+        /// </summary>
+        /// <param name="index">探索を開始するインデックス</param>
+        /// <param name="find_sep_func">セパレーターなら真を返し、そうでないなら偽を返す</param>
+        /// <param name="changeAnchor">選択の起点となるとインデックスを変更するなら真。そうでなければ偽</param>
+        public void SelectSepartor(int index,Func<char,bool> find_sep_func, bool changeAnchor = false)
+        {
+            if (this.FireUpdateEvent == false)
+                throw new InvalidOperationException("");
+
+            if (find_sep_func == null)
+                throw new ArgumentNullException("find_sep_func must not be null");
+
+            var t = this.GetSepartor(index, find_sep_func);
+            if (t == null)
+                return;
+
+            int start = t.Item1, end = t.Item2;
+
             this.Select(start, end - start);
 
             if (changeAnchor)
@@ -1106,6 +1135,7 @@ namespace FooEditEngine
         public void Clear()
         {
             this.buffer.Clear();
+            this.Dirty = false;
         }
 
         /// <summary>
@@ -1129,14 +1159,11 @@ namespace FooEditEngine
             try
             {
                 this.Clear();
-                this.LayoutLines.IsFrozneDirtyFlag = true;
                 await this.buffer.LoadAsync(fs, tokenSource);
             }
             finally
             {
                 this.PerformLayout();
-                //これ以降の操作にだけダーティフラグを適用しないとおかしなことになる
-                this.LayoutLines.IsFrozneDirtyFlag = false;
                 if (this.LoadProgress != null)
                     this.LoadProgress(this, new ProgressEventArgs(ProgressState.Complete));
             }
@@ -1151,32 +1178,7 @@ namespace FooEditEngine
         /// <remarks>非同期操作中はこのメソッドを実行することはできません</remarks>
         public async Task SaveAsync(TextWriter fs, CancellationTokenSource tokenSource = null)
         {
-            try
-            {
-                await this.buffer.LockAsync().ConfigureAwait(false);
-                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
-                    }
-                }
-            }
-            finally
-            {
-                this.buffer.UnLock();
-            }
+            await this.buffer.SaveAsync(fs, tokenSource);
         }
 
         /// <summary>
@@ -1324,6 +1326,10 @@ namespace FooEditEngine
         {
             switch (e.type)
             {
+                case UpdateType.RebuildLayout:
+                    this._LayoutLines.Clear();
+                    this._LayoutLines.UpdateAsReplace(0, 0, this.Length);
+                    break;
                 case UpdateType.Replace:
                     if (e.row == null)
                     {
@@ -1335,12 +1341,13 @@ namespace FooEditEngine
                         this._LayoutLines.UpdateLineAsReplace(e.row.Value, e.removeLength, e.insertLength);
                         this.Markers.UpdateMarkers(this.LayoutLines.GetIndexFromLineNumber(e.row.Value), e.insertLength, e.removeLength);
                     }
+                    this.Dirty = true;
                     break;
                 case UpdateType.Clear:
                     this._LayoutLines.Clear();
+                    this.Dirty = true;
                     break;
             }
-            this.UpdateCalledAlways(this, e);
             if(this.FireUpdateEvent)
                 this.Update(this, e);
         }