OSDN Git Service

読み込み時に行分割を行わず、レタリング時に行分割を行うようにした
[fooeditengine/FooEditEngine.git] / Core / Document.cs
index 62b9668..5395d20 100644 (file)
@@ -9,20 +9,27 @@
 You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-//#define TEST_ASYNC
-
 using System;
 using System.IO;
+using System.ComponentModel;
 using System.Collections.Generic;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Linq;
+using System.Runtime.CompilerServices;
 
 namespace FooEditEngine
 {
     /// <summary>
+    /// オートインデントを行うためのデリゲートを表す
+    /// </summary>
+    /// <param name="sender">イベント発生元のオブジェクト</param>
+    /// <param name="e">イベントデーター</param>
+    public delegate void AutoIndentHookerHandler(object sender, EventArgs e);
+
+    /// <summary>
     /// 進行状況を表す列挙体
     /// </summary>
     public enum ProgressState
@@ -83,6 +90,10 @@ namespace FooEditEngine
     public sealed class DocumentUpdateEventArgs : EventArgs
     {
         /// <summary>
+        /// 値が指定されていないことを示す
+        /// </summary>
+        public const int EmptyValue = -1;
+        /// <summary>
         /// 更新タイプ
         /// </summary>
         public UpdateType type;
@@ -110,7 +121,7 @@ namespace FooEditEngine
         /// <param name="removeLength">削除された長さ</param>
         /// <param name="insertLength">追加された長さ</param>
         /// <param name="row">開始行。nullを指定することができる</param>
-        public DocumentUpdateEventArgs(UpdateType type, int startIndex, int removeLength, int insertLength, int? row = null)
+        public DocumentUpdateEventArgs(UpdateType type, int startIndex = EmptyValue, int removeLength = EmptyValue, int insertLength = EmptyValue, int? row = null)
         {
             this.type = type;
             this.startIndex = startIndex;
@@ -131,17 +142,17 @@ namespace FooEditEngine
     /// ドキュメントの管理を行う
     /// </summary>
     /// <remarks>この型のすべてのメソッド・プロパティはスレッドセーフです</remarks>
-    public sealed class Document : IEnumerable<char>, IRandomEnumrator<char>
+    public sealed class Document : IEnumerable<char>, IRandomEnumrator<char>, IDisposable
     {
-        const int MaxSemaphoreCount = 1;
         Regex regex;
         Match match;
         StringBuffer buffer;
         LineToIndexTable _LayoutLines;
-        bool _EnableFireUpdateEvent = true,_UrlMark = false, _DrawLineNumber = false, _HideRuler = true;
-        SemaphoreSlim Semaphore = new SemaphoreSlim(MaxSemaphoreCount);
+        bool _EnableFireUpdateEvent = true,_UrlMark = false, _DrawLineNumber = false, _HideRuler = true, _RightToLeft = false;
         LineBreakMethod _LineBreak;
         int _TabStops, _LineBreakCharCount = 80;
+        bool _ShowFullSpace, _ShowHalfSpace, _ShowTab, _ShowLineBreak,_InsertMode, _HideCaret, _HideLineMarker, _RectSelection;
+        IndentMode _IndentMode;
 
         /// <summary>
         /// 一行当たりの最大文字数
@@ -151,22 +162,27 @@ namespace FooEditEngine
         /// <summary>
         /// コンストラクター
         /// </summary>
-        internal Document()
+        public Document()
             : this(null)
         {
         }
 
-        internal Document(Document doc)
+        /// <summary>
+        /// コンストラクター
+        /// </summary>
+        /// <param name="doc">ドキュメントオブジェクト</param>
+        /// <remarks>docが複製されますが、プロパティは引き継がれません</remarks>
+        public Document(Document doc)
         {
             if (doc == null)
                 this.buffer = new StringBuffer();
             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.Markers = new MarkerCollection(this);
+            this.StatusUpdate += new EventHandler((s, e) => { });
+            this.Markers = new MarkerCollection();
             this.UndoManager = new UndoManager();
             this._LayoutLines = new LineToIndexTable(this);
             this._LayoutLines.SpilitString = (s,e)=> {
@@ -175,13 +191,15 @@ namespace FooEditEngine
             this._LayoutLines.Clear();
             this.MarkerPatternSet = new MarkerPatternSet(this._LayoutLines, this.Markers);
             this.MarkerPatternSet.Updated += WacthDogPattern_Updated;
-            this.LineBreakChanged += (s, e) => { };
-            this.TabStopsChanged += (s, e) => { };
-            this.DrawLineNumberChanged += (s, e) => { };
             this.Selections = new SelectCollection();
+            this.CaretPostion = new TextPoint();
             this.HideLineMarker = true;
-            this.HideRulerChanged += (s, e) => { };
-            this.CaretPostion = TextPoint.Null;
+            this.SelectGrippers = new GripperRectangle(new Gripper(), new Gripper());
+            this.SelectionChanged += new EventHandler((s, e) => { });
+            this.CaretChanged += (s, e) => { };
+            this.AutoIndentHook += (s, e) => { };
+            this.LineBreakChanged += (s, e) => { };
+            this.Dirty = false;
         }
 
         void WacthDogPattern_Updated(object sender, EventArgs e)
@@ -190,54 +208,236 @@ namespace FooEditEngine
         }
 
         /// <summary>
-        /// 矩形選択モードなら真を返し、そうでない場合は偽を返す
+        /// ダーティフラグ。保存されていなければ真、そうでなければ偽。
         /// </summary>
-        public bool RectSelection
+        public bool Dirty
         {
             get;
             set;
         }
 
         /// <summary>
-        /// ã\82¤ã\83³ã\83\87ã\83³ã\83\88ã\81®æ\96¹æ³\95ã\82\92表ã\81\99
+        /// ã\82­ã\83£ã\83¬ã\83\83ã\83\88ã\81§ã\81®é\81¸æ\8a\9eã\81®èµ·ç\82¹ã\81¨ã\81ªã\82\8bä½\8dç½®
         /// </summary>
-        public IndentMode IndentMode
+        internal int AnchorIndex
         {
             get;
             set;
         }
 
         /// <summary>
-        /// ã\83©ã\82¤ã\83³ã\83\9eã\83¼ã\82«ã\83¼ã\82\92æ\8f\8fã\81\8fã\81ªã\82\89å\81½ã\80\82ã\81\9dã\81\86ã\81§ã\81ªã\81\91ã\82\8cã\81°ç\9c\9f
+        /// ã\83¬ã\82¿ã\83ªã\83³ã\82°ã\81®é\96\8bå§\8bä½\8dç½®ã\82\92表ã\81\99
         /// </summary>
-        public bool HideLineMarker
+        internal SrcPoint Src
         {
             get;
             set;
         }
 
         /// <summary>
-        /// ã\82­ã\83£ã\83¬ã\83\83ã\83\88ã\82\92æ\8f\8fã\81\8fã\81ªã\82\89å\81½ã\80\82ã\81\9dã\81\86ã\81§ã\81ªã\81\91ã\82\8cã\81°ç\9c\9f
+        /// ã\83\89ã\82­ã\83¥ã\83¡ã\83³ã\83\88ã\81®ã\82¿ã\82¤ã\83\88ã\83«
         /// </summary>
-        public bool HideCaret
+        public string Title
         {
             get;
             set;
         }
 
         /// <summary>
-        /// 挿入モードなら真を返し、上書きモードなら偽を返す
+        /// 補完候補プロセッサーが切り替わったときに発生するイベント
         /// </summary>
-        public bool InsertMode
+        public event EventHandler AutoCompleteChanged;
+
+        AutoCompleteBoxBase _AutoComplete;
+        /// <summary>
+        /// 補完候補プロセッサー
+        /// </summary>
+        public AutoCompleteBoxBase AutoComplete
+        {
+            get
+            {
+                return this._AutoComplete;
+            }
+            set
+            {
+                this._AutoComplete = value;
+                if (this.AutoCompleteChanged != null)
+                    this.AutoCompleteChanged(this, null);
+            }
+        }
+
+        /// <summary>
+        /// 読み込み中に発生するイベント
+        /// </summary>
+        public event ProgressEventHandler LoadProgress;
+
+        /// <summary>
+        /// ルーラーやキャレット・行番号などの表示すべきものが変化した場合に呼び出される。ドキュメントの内容が変化した通知を受け取り場合はUpdateを使用してください
+        /// </summary>
+        public event EventHandler StatusUpdate;
+
+        /// <summary>
+        /// 全角スペースを表示するかどうか
+        /// </summary>
+        public bool ShowFullSpace
         {
+            get { return this._ShowFullSpace; }
+            set
+            {
+                if (this._ShowFullSpace == value)
+                    return;
+                this._ShowFullSpace = value;
+                this.StatusUpdate(this, null);
+            }
+        }
+
+        /// <summary>
+        /// 半角スペースを表示するかどうか
+        /// </summary>
+        public bool ShowHalfSpace
+        {
+            get { return this._ShowHalfSpace; }
+            set
+            {
+                if (this._ShowHalfSpace == value)
+                    return;
+                this._ShowHalfSpace = value;
+                this.StatusUpdate(this, null);
+            }
+        }
+
+        /// <summary>
+        /// TABを表示するかどうか
+        /// </summary>
+        public bool ShowTab
+        {
+            get { return this._ShowTab; }
+            set
+            {
+                if (this._ShowTab == value)
+                    return;
+                this._ShowTab = value;
+                this.StatusUpdate(this, null);
+            }
+        }
+
+        /// <summary>
+        /// 改行を表示するかどうか
+        /// </summary>
+        public bool ShowLineBreak
+        {
+            get { return this._ShowLineBreak; }
+            set
+            {
+                if (this._ShowLineBreak == value)
+                    return;
+                this._ShowLineBreak = value;
+                this.StatusUpdate(this, null);
+            }
+        }
+
+        /// <summary>
+        /// 選択範囲にあるグリッパーのリスト
+        /// </summary>
+        internal GripperRectangle SelectGrippers
+        {
+            private set;
             get;
-            set;
         }
 
         /// <summary>
-        /// HideRulerの値が変わったときに通知します
+        /// 右から左に表示するなら真
+        /// </summary>
+        public bool RightToLeft {
+            get { return this._RightToLeft; }
+            set
+            {
+                if (this._RightToLeft == value)
+                    return;
+                this._RightToLeft = value;
+                this.StatusUpdate(this, null);
+            }
+        }
+
+        /// <summary>
+        /// 矩形選択モードなら真を返し、そうでない場合は偽を返す
+        /// </summary>
+        public bool RectSelection
+        {
+            get
+            {
+                return this._RectSelection;
+            }
+            set
+            {
+                this._RectSelection = value;
+                this.StatusUpdate(this, null);
+            }
+        }
+
+        /// <summary>
+        /// インデントの方法を表す
+        /// </summary>
+        public IndentMode IndentMode
+        {
+            get
+            {
+                return this._IndentMode;
+            }
+            set
+            {
+                this._IndentMode = value;
+                this.StatusUpdate(this, null);
+            }
+        }
+
+        /// <summary>
+        /// ラインマーカーを描くなら偽。そうでなければ真
+        /// </summary>
+        public bool HideLineMarker
+        {
+            get
+            {
+                return this._HideLineMarker;
+            }
+            set
+            {
+                this._HideLineMarker = value;
+                this.StatusUpdate(this, null);
+            }
+        }
+
+        /// <summary>
+        /// キャレットを描くなら偽。そうでなければ真
         /// </summary>
-        public event EventHandler HideRulerChanged;
+        public bool HideCaret
+        {
+            get
+            {
+                return this._HideCaret;
+            }
+            set
+            {
+                this._HideCaret = value;
+                this.StatusUpdate(this, null);
+            }
+        }
+
+        /// <summary>
+        /// 挿入モードなら真を返し、上書きモードなら偽を返す
+        /// </summary>
+        public bool InsertMode
+        {
+            get
+            {
+                return this._InsertMode;
+            }
+            set
+            {
+                this._InsertMode = value;
+                this.StatusUpdate(this, null);
+            }
+        }
 
         /// <summary>
         /// ルーラーを表示しないなら真、そうでないなら偽
@@ -247,20 +447,38 @@ namespace FooEditEngine
             get { return this._HideRuler; }
             set
             {
+                if (this._HideRuler == value)
+                    return;
                 this._HideRuler = value;
                 this.LayoutLines.ClearLayoutCache();
-                this.HideRulerChanged(this, null);
+                this.StatusUpdate(this, null);
             }
         }
 
+        TextPoint _CaretPostion;
         /// <summary>
         /// レイアウト行のどこにキャレットがあるかを表す
         /// </summary>
-        /// <remarks>この値を変更しても反映されないので、EditView側でAdjustCaret()メソッドを呼び出す必要があります</remarks>
         public TextPoint CaretPostion
         {
-            get;
-            set;
+            get
+            {
+                return this._CaretPostion;
+            }
+            set
+            {
+                if(this._CaretPostion != value)
+                {
+                    this._CaretPostion = value;
+                    this.CaretChanged(this, null);
+                }
+            }
+        }
+
+        internal void SetCaretPostionWithoutEvent(TextPoint value)
+        {
+            if (this._CaretPostion != value)
+                this._CaretPostion = value;
         }
 
         /// <summary>
@@ -273,11 +491,6 @@ namespace FooEditEngine
         }
 
         /// <summary>
-        /// DrawLineNumberの値が変わったときに通知される
-        /// </summary>
-        public event EventHandler DrawLineNumberChanged;
-
-        /// <summary>
         /// 行番号を表示するかどうか
         /// </summary>
         public bool DrawLineNumber
@@ -285,9 +498,11 @@ namespace FooEditEngine
             get { return this._DrawLineNumber; }
             set
             {
+                if (this._DrawLineNumber == value)
+                    return;
                 this._DrawLineNumber = value;
                 this._LayoutLines.ClearLayoutCache();
-                this.DrawLineNumberChanged(this, null);
+                this.StatusUpdate(this, null);
             }
         }
 
@@ -299,6 +514,8 @@ namespace FooEditEngine
             get { return this._UrlMark; }
             set
             {
+                if (this._UrlMark == value)
+                    return;
                 this._UrlMark = value;
                 if (value)
                 {
@@ -309,19 +526,21 @@ namespace FooEditEngine
                 {
                     this.MarkerPatternSet.Remove(MarkerIDs.URL);
                 }
+                this.StatusUpdate(this, null);
             }
         }
 
         /// <summary>
-        /// LineBreakが変更されたときに通知される
+        /// 桁折りの方法が変わったことを表す
         /// </summary>
-        public EventHandler LineBreakChanged;
+        public event EventHandler LineBreakChanged;
 
         /// <summary>
         /// 桁折り処理の方法を指定する
         /// </summary>
         /// <remarks>
         /// 変更した場合、呼び出し側で再描写とレイアウトの再構築を行う必要があります
+        /// また、StatusUpdatedではなく、LineBreakChangedイベントが発生します
         /// </remarks>
         public LineBreakMethod LineBreak
         {
@@ -331,6 +550,8 @@ namespace FooEditEngine
             }
             set
             {
+                if (this._LineBreak == value)
+                    return;
                 this._LineBreak = value;
                 this.LineBreakChanged(this, null);
             }
@@ -348,17 +569,14 @@ namespace FooEditEngine
             }
             set
             {
+                if (this._LineBreakCharCount == value)
+                    return;
                 this._LineBreakCharCount = value;
                 this.LineBreakChanged(this, null);
             }
         }
 
         /// <summary>
-        /// TabStopsの値が変わったことを通知する
-        /// </summary>
-        public event EventHandler TabStopsChanged;
-
-        /// <summary>
         /// タブの幅
         /// </summary>
         /// <remarks>変更した場合、呼び出し側で再描写する必要があります</remarks>
@@ -366,8 +584,10 @@ namespace FooEditEngine
         {
             get { return this._TabStops; }
             set {
+                if (this._TabStops == value)
+                    return;
                 this._TabStops = value;
-                this.TabStopsChanged(this, null);
+                this.StatusUpdate(this, null);
             }
         }
 
@@ -430,14 +650,6 @@ namespace FooEditEngine
         public event DocumentUpdateEventHandler Update;
 
         /// <summary>
-        /// ドキュメントが更新された時に呼びされるイベント
-        /// </summary>
-        /// <remarks>
-        /// FireUpdateEventの値に関わらず常に呼びされます
-        /// </remarks>
-        internal event DocumentUpdateEventHandler UpdateCalledAlways;
-
-        /// <summary>
         /// FireUpdateEventの値が変わったときに呼び出されるイベント
         /// </summary>
         public event EventHandler ChangeFireUpdateEvent;
@@ -453,17 +665,6 @@ namespace FooEditEngine
         public const char EndOfFile = '\u001a';
 
         /// <summary>
-        /// ロック中なら真を返し、そうでないなら偽を返す
-        /// </summary>
-        public bool IsLocked
-        {
-            get
-            {
-                return this.Semaphore.CurrentCount == 0;
-            }
-        }
-
-        /// <summary>
         /// アンドゥ管理クラスを表す
         /// </summary>
         public UndoManager UndoManager
@@ -530,37 +731,211 @@ namespace FooEditEngine
         }
 
         /// <summary>
-        /// DocumentReaderを作成します
+        /// 再描写を要求しているなら真
         /// </summary>
-        /// <returns>DocumentReaderオブジェクト</returns>
-        public DocumentReader CreateReader()
+        public bool IsRequestRedraw { get; internal set; }
+
+        /// <summary>
+        /// 再描写を要求する
+        /// </summary>
+        public void RequestRedraw()
         {
-            return new DocumentReader(this.buffer);
+            this.IsRequestRedraw = true;
+        }
+
+        /// <summary>
+        /// レイアウト行が再構成されたときに発生するイベント
+        /// </summary>
+        public event EventHandler PerformLayouted;
+        /// <summary>
+        /// レイアウト行をすべて破棄し、再度レイアウトを行う
+        /// </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.LayoutLines.IsFrozneDirtyFlag = false;
+            if (this.PerformLayouted != null)
+                this.PerformLayouted(this, null);
         }
 
         /// <summary>
-        /// ロックを解除します
+        /// オードインデントが可能になった時に通知される
+        /// </summary>
+        /// <remarks>
+        /// FireUpdateEventの影響を受けます
+        /// </remarks>
+        public event AutoIndentHookerHandler AutoIndentHook;
+
+        /// <summary>
+        /// 選択領域変更時に通知される
         /// </summary>
-        public void UnLock()
+        public event EventHandler SelectionChanged;
+
+        /// <summary>
+        /// キャレット移動時に通知される
+        /// </summary>
+        public event EventHandler CaretChanged;
+
+        /// <summary>
+        /// 指定された範囲を選択する
+        /// </summary>
+        /// <param name="start"></param>
+        /// <param name="length"></param>
+        /// <remarks>RectSelectionの値によって動作が変わります。真の場合は矩形選択モードに、そうでない場合は行ごとに選択されます</remarks>
+        public void Select(int start, int length)
         {
-            this.Semaphore.Release();
+            if (this.FireUpdateEvent == false)
+                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)
+            {
+                int oldStart = start;
+                start += length;
+                length = oldStart - start;
+            }
+            if (this.RectSelection && length != 0)
+            {
+                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.SelectionChanged(this, null);
         }
 
         /// <summary>
-        /// ロックします
+        /// 矩形選択を行う
         /// </summary>
-        public void Lock()
+        /// <param name="tp">開始位置</param>
+        /// <param name="width">桁数</param>
+        /// <param name="height">行数</param>
+        public void Select(TextPoint tp, int width, int height)
+        {
+            if (this.FireUpdateEvent == false || !this.RectSelection)
+                throw new InvalidOperationException("");
+            TextPoint end = tp;
+
+            end.row = tp.row + height;
+            end.col = tp.col + width;
+
+            if (end.row > this.LayoutLines.Count - 1)
+                throw new ArgumentOutOfRangeException("");
+
+            this.Selections.Clear();
+
+            this.SelectByRectangle(new TextRectangle(tp, end));
+
+            this.SelectionChanged(this, null);
+        }
+
+        private void SelectByRectangle(TextRectangle rect)
         {
-            this.Semaphore.Wait();
+            if (this.FireUpdateEvent == false)
+                throw new InvalidOperationException("");
+            if (rect.TopLeft <= rect.BottomRight)
+            {
+                for (int i = rect.TopLeft.row; i <= rect.BottomLeft.row; i++)
+                {
+                    int length = this.LayoutLines.GetLengthFromLineNumber(i);
+                    int leftCol = rect.TopLeft.col, rightCol = rect.TopRight.col, lastCol = length;
+                    if (length > 0 && this.LayoutLines[i][length - 1] == Document.NewLine)
+                        lastCol = length - 1;
+                    if (lastCol < 0)
+                        lastCol = 0;
+                    if (rect.TopLeft.col > lastCol)
+                        leftCol = lastCol;
+                    if (rect.TopRight.col > lastCol)
+                        rightCol = lastCol;
+
+                    int StartIndex = this.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, leftCol));
+                    int EndIndex = this.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, rightCol));
+
+                    Selection sel;
+                    sel = Selection.Create(StartIndex, EndIndex - StartIndex);
+
+                    this.Selections.Add(sel);
+                }
+            }
         }
 
         /// <summary>
-        /// ロックします
+        /// 単語単位で選択する
         /// </summary>
-        /// <returns>Taskオブジェクト</returns>
-        public Task LockAsync()
+        /// <param name="index">探索を開始するインデックス</param>
+        /// <param name="changeAnchor">選択の起点となるとインデックスを変更するなら真。そうでなければ偽</param>
+        public void SelectWord(int index, bool changeAnchor = false)
+        {
+            this.SelectSepartor(index, (c) => Util.IsWordSeparator(c), changeAnchor);
+        }
+
+        /// <summary>
+        /// 行単位で選択する
+        /// </summary>
+        /// <param name="index">探索を開始するインデックス</param>
+        /// <param name="changeAnchor">選択の起点となるとインデックスを変更するなら真。そうでなければ偽</param>
+        public void SelectLine(int index,bool changeAnchor = false)
+        {
+            this.SelectSepartor(index, (c) => c == Document.NewLine, changeAnchor);
+        }
+
+        /// <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)
         {
-            return this.Semaphore.WaitAsync();
+            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;
+
+            Document str = this;
+
+            int start = index;
+            while (start > 0 && !find_sep_func(str[start]))
+                start--;
+
+            if (find_sep_func(str[start]))
+            {
+                start++;
+            }
+
+            int end = index;
+            while (end < this.Length && !find_sep_func(str[end]))
+                end++;
+
+            this.Select(start, end - start);
+
+            if (changeAnchor)
+                this.AnchorIndex = start;
+        }
+
+        /// <summary>
+        /// DocumentReaderを作成します
+        /// </summary>
+        /// <returns>DocumentReaderオブジェクト</returns>
+        public DocumentReader CreateReader()
+        {
+            return new DocumentReader(this.buffer);
         }
 
         /// <summary>
@@ -601,6 +976,15 @@ namespace FooEditEngine
         }
 
         /// <summary>
+        /// すべてのマーカーを削除する
+        /// </summary>
+        /// <param name="id">マーカーID</param>
+        public void RemoveAllMarker(int id)
+        {
+            this.Markers.RemoveAll(id);
+        }
+
+        /// <summary>
         /// インデックスに対応するマーカーを得る
         /// </summary>
         /// <param name="id">マーカーID</param>
@@ -690,8 +1074,9 @@ namespace FooEditEngine
         /// <param name="index">開始インデックス</param>
         /// <param name="length">長さ</param>
         /// <param name="s">文字列</param>
+        /// <param name="UserInput">ユーザーからの入力として扱うなら真</param>
         /// <remarks>読み出し操作中はこのメソッドを実行することはできません</remarks>
-        public void Replace(int index, int length, string s)
+        public void Replace(int index, int length, string s, bool UserInput = false)
         {
             if (index < 0 || index > this.buffer.Length || index + length > this.buffer.Length || length < 0)
                 throw new ArgumentOutOfRangeException();
@@ -704,6 +1089,20 @@ namespace FooEditEngine
             ReplaceCommand cmd = new ReplaceCommand(this.buffer, index, length, s);
             this.UndoManager.push(cmd);
             cmd.redo();
+
+            if (this.FireUpdateEvent && UserInput)
+            {
+                var input_str = string.Empty;
+                if (s == Document.NewLine.ToString())
+                    input_str = s;
+                else if (s == string.Empty && length > 0)
+                    input_str = "\b";
+                //入力は終わっているので空文字を渡すが処理の都合で一部文字だけはそのまま渡す
+                if (this.AutoComplete != null)
+                    this.AutoComplete.ParseInput(input_str);
+                if (s == Document.NewLine.ToString())
+                    this.AutoIndentHook(this, null);
+            }
         }
 
         /// <summary>
@@ -714,6 +1113,7 @@ namespace FooEditEngine
         public void Clear()
         {
             this.buffer.Clear();
+            this.Dirty = false;
         }
 
         /// <summary>
@@ -726,22 +1126,24 @@ namespace FooEditEngine
         /// 読み取り操作は別スレッドで行われます。
         /// また、非同期操作中はこのメソッドを実行することはできません。
         /// </remarks>
-        internal async Task LoadAsync(IStreamReader fs, CancellationTokenSource tokenSource = null)
+        public async Task LoadAsync(TextReader fs, CancellationTokenSource tokenSource = null)
         {
-            if (fs.IsEnd())
+            if (fs.Peek() == -1)
                 return;
 
+            if (this.LoadProgress != null)
+                this.LoadProgress(this, new ProgressEventArgs(ProgressState.Start));
+
             try
             {
-                await this.LockAsync().ConfigureAwait(false);
                 this.Clear();
-                this.FireUpdateEvent = false;
                 await this.buffer.LoadAsync(fs, tokenSource);
             }
             finally
             {
-                this.FireUpdateEvent = true;
-                this.UnLock();
+                this.PerformLayout();
+                if (this.LoadProgress != null)
+                    this.LoadProgress(this, new ProgressEventArgs(ProgressState.Complete));
             }
         }
 
@@ -752,34 +1154,9 @@ namespace FooEditEngine
         /// <param name="tokenSource">キャンセルトークン</param>
         /// <returns>Taskオブジェクト</returns>
         /// <remarks>非同期操作中はこのメソッドを実行することはできません</remarks>
-        internal async Task SaveAsync(IStreamWriter fs, CancellationTokenSource tokenSource = null)
+        public async Task SaveAsync(TextWriter fs, CancellationTokenSource tokenSource = null)
         {
-            try
-            {
-                await this.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.UnLock();
-            }
+            await this.buffer.SaveAsync(fs, tokenSource);
         }
 
         /// <summary>
@@ -929,56 +1306,50 @@ namespace FooEditEngine
             {
                 case UpdateType.Replace:
                     if (e.row == null)
+                    {
                         this._LayoutLines.UpdateAsReplace(e.startIndex, e.removeLength, e.insertLength);
+                        this.Markers.UpdateMarkers(e.startIndex, e.insertLength, e.removeLength);
+                    }
                     else
+                    {
                         this._LayoutLines.UpdateLineAsReplace(e.row.Value, e.removeLength, e.insertLength);
+                        this.Markers.UpdateMarkers(this.LayoutLines.GetIndexFromLineNumber(e.row.Value), e.insertLength, e.removeLength);
+                    }
                     break;
                 case UpdateType.Clear:
                     this._LayoutLines.Clear();
                     break;
             }
-            this.UpdateCalledAlways(this, e);
+            this.Dirty = true;
             if(this.FireUpdateEvent)
                 this.Update(this, e);
         }
-    }
 
-    public interface IStreamReader
-    {
-        /// <summary>
-        /// ストリームが空かどうかを返す
-        /// </summary>
-        bool IsEnd();
+        #region IDisposable Support
+        private bool disposedValue = false; // 重複する呼び出しを検出するには
 
-        /// <summary>
-        /// ストリームから行を読み取った物を返す。LoadAsyncを呼び出す場合は必ず実装してください
-        /// </summary>
-        Task<string> ReadLineAsync();
-        /// <summary>
-        /// ストリームから指定した文字数だけ読み取る
-        /// </summary>
-        /// <param name="buffer">書き込み先バッファー</param>
-        /// <param name="index">書き込み先バッファーのインデックス</param>
-        /// <param name="count">読み取る文字数</param>
-        /// <returns>読み取った文字数</returns>
-        Task<int> ReadAsync(char[] buffer, int index, int count);
-    }
+        void Dispose(bool disposing)
+        {
+            if (!disposedValue)
+            {
+                if (disposing)
+                {
+                    this.buffer.Clear();
+                    this.LayoutLines.Clear();
+                }
 
-    public interface IStreamWriter
-    {
-        /// <summary>
-        /// ストリームに書き込む。SaveAsyncを呼び出す場合は必ず実装してください
-        /// </summary>
-        Task WriteAsync(string str);
+                disposedValue = true;
+            }
+        }
 
         /// <summary>
-        /// 書き込む際に使用する改行コード
+        /// ドキュメントを破棄する
         /// </summary>
-        string NewLine
+        public void Dispose()
         {
-            get;
-            set;
+            Dispose(true);
         }
+        #endregion
     }
 
     /// <summary>
@@ -1120,5 +1491,6 @@ namespace FooEditEngine
         {
             this.document = null;
         }
+
     }
 }