OSDN Git Service

行更新処理を最適化した
authorgdkhd812 <jbh03215@htmil.co.jp>
Fri, 27 Sep 2013 10:40:07 +0000 (19:40 +0900)
committergdkhd812 <jbh03215@htmil.co.jp>
Fri, 27 Sep 2013 10:40:07 +0000 (19:40 +0900)
Common/LineToIndex.cs
WPF/Test/MainWindow.xaml
WPF/Test/MainWindow.xaml.cs
WPF/UnitTest/UnitTest3.cs

index 8ff4a5f..bfaf981 100644 (file)
@@ -123,27 +123,21 @@ namespace FooEditEngine
 \r
     internal class LineToIndexTableData : IDisposable\r
     {\r
-        ITextLayout _layout;\r
+        /// <summary>\r
+        /// 行の先頭。正しい行の先頭位置を取得するにはGetLineHeadIndex()を使用してください\r
+        /// </summary>\r
         public int Index;\r
+        /// <summary>\r
+        /// 行の長さ\r
+        /// </summary>\r
         public int Length;\r
+        /// <summary>\r
+        /// 改行マークかEOFなら真を返す\r
+        /// </summary>\r
         public bool LineEnd;\r
         public SyntaxInfo[] Syntax;\r
         public EncloserType EncloserType;\r
-        internal ITextLayout Layout\r
-        {\r
-            get\r
-            {\r
-                if (this._layout != null && this._layout.Invaild)\r
-                {\r
-                    this._layout.Dispose();\r
-                    this._layout = null;\r
-                }\r
-                if (this._layout == null || this._layout.Disposed)\r
-                    this._layout = this.CreateLayout(this);\r
-                return _layout;\r
-            }\r
-        }\r
-        internal Func<LineToIndexTableData, ITextLayout> CreateLayout;\r
+        internal ITextLayout Layout;\r
 \r
         /// <summary>\r
         /// コンストラクター。LineToIndexTable以外のクラスで呼び出さないでください\r
@@ -166,8 +160,8 @@ namespace FooEditEngine
 \r
         public void Dispose()\r
         {\r
-            if(this._layout != null)\r
-                this._layout.Dispose();\r
+            if(this.Layout != null)\r
+                this.Layout.Dispose();\r
         }\r
     }\r
 \r
@@ -185,6 +179,8 @@ namespace FooEditEngine
         bool _UrlMarker,_IsSync;\r
         Regex urlPattern = new Regex("(http|https|ftp)(:\\/\\/[-_.!~*\\'()a-zA-Z0-9;\\/?:\\@&=+\\$,%#]+)");\r
         ITextRender render;\r
+        int stepRow = -1,stepLength = 0;\r
+        const int STEP_ROW_IS_NONE = -1;\r
 \r
         internal LineToIndexTable(Document buf, ITextRender r)\r
         {\r
@@ -193,6 +189,15 @@ namespace FooEditEngine
             this.render = r;\r
             this.FoldingCollection = new FoldingCollection();\r
             this._IsSync = true;\r
+#if DEBUG\r
+            if (!Debugger.IsAttached)\r
+            {\r
+                Guid guid = Guid.NewGuid();\r
+                string path = string.Format("{0}\\footextbox_lti_debug_{1}.log", System.IO.Path.GetTempPath(), guid);\r
+                Debug.Listeners.Add(new TextWriterTraceListener(path));\r
+                Debug.AutoFlush = true;\r
+            }\r
+#endif\r
         }\r
 \r
         void Markers_Updated(object sender, EventArgs e)\r
@@ -266,24 +271,36 @@ namespace FooEditEngine
             get\r
             {\r
                 LineToIndexTableData data = this.Lines[n];\r
-                string str = this.Document.ToString(data.Index, data.Length);\r
+                string str = this.Document.ToString(this.GetLineHeadIndex(n), data.Length);\r
 \r
                 return str;\r
             }\r
         }\r
 \r
+        int GetLineHeadIndex(int row)\r
+        {\r
+            if (this.Lines.Count == 0)\r
+                return 0;\r
+            if (this.stepRow != STEP_ROW_IS_NONE && row > this.stepRow)\r
+                return this.Lines[row].Index + this.stepLength;\r
+            else\r
+                return this.Lines[row].Index;\r
+        }\r
+\r
         internal LineToIndexTableData CreateLineToIndexTableData(int index, int length, bool lineend, SyntaxInfo[] syntax)\r
         {\r
             LineToIndexTableData result = new LineToIndexTableData(index, length, lineend, syntax);\r
-            result.CreateLayout = this.LineToIndexTableData_CreatLayout;\r
             return result;\r
         }\r
 \r
         internal void UpdateAsReplace(int index, int removedLength, int insertedLength)\r
         {\r
+#if DEBUG\r
+            Debug.WriteLine("Replaced Index:{0} RemoveLength:{1} InsertLength:{2}", index, removedLength, insertedLength);\r
+#endif\r
             //削除すべき行の開始位置と終了位置を求める\r
             int startRow = this.GetLineNumberFromIndex(index);\r
-            if (startRow > 0 && this.Lines[startRow - 1].LineEnd == false)\r
+            while(startRow > 0 && this.Lines[startRow - 1].LineEnd == false)\r
                 startRow--;\r
 \r
             int endRow = this.GetLineNumberFromIndex(index + removedLength);\r
@@ -303,21 +320,33 @@ namespace FooEditEngine
 \r
             int analyzeLength = fisrtPartLength + secondPartLength + insertedLength;\r
 \r
-            System.Diagnostics.Debug.Assert(analyzeLength <= this.Document.Length - 1 - HeadIndex + 1);\r
+            Debug.Assert(analyzeLength <= this.Document.Length - 1 - HeadIndex + 1);\r
 \r
-            //行を削除する\r
             int removeCount = endRow - startRow + 1;\r
-            for (int i = startRow; i < removeCount; i++)\r
-                this.Lines[i].Dispose();\r
-\r
-            this.Lines.RemoveRange(startRow, removeCount);\r
+            this.RemoveLine(startRow, removeCount);\r
 \r
             SpilitStringEventArgs e = new SpilitStringEventArgs(this.Document, HeadIndex, analyzeLength, startRow);\r
             IList<LineToIndexTableData> newLines = SpilitString(this, e);\r
 \r
-            this.Lines.InsertRange(startRow, newLines);\r
+            int newCount = newLines.Count;\r
 \r
-            this.UpdateLineHeadIndex(insertedLength - removedLength, startRow + newLines.Count);\r
+            if (this.stepRow > startRow && newCount > 0 && newCount != removeCount)\r
+            {\r
+                this.stepRow = Math.Max(this.stepRow - (removeCount - newCount),startRow);\r
+#if DEBUG\r
+                if (this.stepRow < 0 || this.stepRow > this.Lines.Count + newCount)\r
+                {\r
+                    Debug.WriteLine("step row < 0 or step row >= lines.count");\r
+                    Debugger.Break();\r
+                }\r
+#endif\r
+            }\r
+\r
+            int deltaLength = insertedLength - removedLength;\r
+\r
+            this.InsertLine(startRow, newLines, deltaLength);\r
+\r
+            this.UpdateLineHeadIndex(deltaLength, startRow, newLines.Count);\r
 \r
             this.FoldingCollection.UpdateData(this.Document, index, insertedLength, removedLength);\r
 \r
@@ -332,61 +361,104 @@ namespace FooEditEngine
             this._IsSync = false;\r
         }\r
 \r
+        void RemoveLine(int startRow, int removeCount)\r
+        {\r
+            for (int i = startRow; i < startRow + removeCount; i++)\r
+                this.Lines[i].Dispose();\r
+\r
+            this.Lines.RemoveRange(startRow, removeCount);\r
+        }\r
+\r
+        void InsertLine(int startRow, IList<LineToIndexTableData> collection, int deltaLength)\r
+        {\r
+            //startRowが挿入した行の開始位置なのであらかじめ引いておく\r
+            for (int i = 1; i < collection.Count; i++)\r
+            {\r
+                if (this.stepRow != STEP_ROW_IS_NONE && startRow + i > this.stepRow)\r
+                    collection[i].Index -= deltaLength + this.stepLength;\r
+                else\r
+                    collection[i].Index -= deltaLength;\r
+            }\r
+\r
+            this.Lines.InsertRange(startRow, collection);\r
+        }\r
+\r
         void AddDummyLine()\r
         {\r
-            //最終行が削除された場合は追加する\r
             LineToIndexTableData dummyLine = null;\r
             if (this.Lines.Count == 0)\r
             {\r
                 dummyLine = new LineToIndexTableData();\r
-                dummyLine.CreateLayout = this.LineToIndexTableData_CreatLayout;\r
                 this.Lines.Add(dummyLine);\r
                 return;\r
             }\r
-            \r
-            int lastLineRow = this.Lines.Count - 1;\r
+\r
+            int lastLineRow = this.Lines.Count > 0 ? this.Lines.Count - 1 : 0;\r
             int lastLineHeadIndex = this.GetIndexFromLineNumber(lastLineRow);\r
             int lastLineLength = this.GetLengthFromLineNumber(lastLineRow);\r
 \r
             if (lastLineLength != 0 && this.Document[Document.Length - 1] == Document.NewLine)\r
             {\r
-                dummyLine = new LineToIndexTableData(lastLineHeadIndex + lastLineLength, 0, true, null);\r
-                dummyLine.CreateLayout = this.LineToIndexTableData_CreatLayout;\r
+                int realIndex = lastLineHeadIndex + lastLineLength;\r
+                if (lastLineRow >= this.stepRow)\r
+                    realIndex -= this.stepLength;\r
+                dummyLine = new LineToIndexTableData(realIndex, 0, true, null);\r
                 this.Lines.Add(dummyLine);\r
             }\r
         }\r
 \r
-        void UpdateLineHeadIndex(int deltaLength,int startRow)\r
+        void UpdateLineHeadIndex(int deltaLength,int startRow,int insertedLineCount)\r
         {\r
-            for (int i = startRow; i < this.Lines.Count; i++)\r
+            if (this.Lines.Count == 0)\r
             {\r
-                this.Lines[i].Index += deltaLength;\r
+                this.stepRow = STEP_ROW_IS_NONE;\r
+                this.stepLength = 0;\r
+                return;\r
             }\r
-        }\r
 \r
-        ITextLayout LineToIndexTableData_CreatLayout(LineToIndexTableData lineData)\r
-        {\r
-            ITextLayout layout;\r
-            if (lineData.Length == 0)\r
+            if (this.stepRow == STEP_ROW_IS_NONE)\r
             {\r
-                layout = this.render.CreateLaytout("", null, null);\r
+                this.stepRow = startRow;\r
+                this.stepLength = deltaLength;\r
+                return;\r
             }\r
-            else\r
+\r
+\r
+            if (startRow < this.stepRow)\r
             {\r
-                var markerRange = from s in this.Document.Markers.Get(lineData.Index, lineData.Length)\r
-                                  let n = Util.ConvertAbsIndexToRelIndex(s, lineData.Index, lineData.Length)\r
-                                  select n;\r
-                layout = this.render.CreateLaytout(this.Document.ToString(lineData.Index, lineData.Length), lineData.Syntax, markerRange);\r
+                //ドキュメントの後半部分をごっそり削除した場合、this.stepRow >= this.Lines.Countになる可能性がある\r
+                if (this.stepRow >= this.Lines.Count)\r
+                    this.stepRow = this.Lines.Count - 1;\r
+                for (int i = this.stepRow; i > startRow; i--)\r
+                    this.Lines[i].Index -= this.stepLength;\r
+            }\r
+            else if (startRow > this.stepRow)\r
+            {\r
+                for (int i = this.stepRow + 1; i < startRow; i++)\r
+                    this.Lines[i].Index += this.stepLength;\r
             }\r
 \r
-            if (this.CacheEntries.Count > MaxEntries)\r
+            this.stepRow = startRow;\r
+            this.stepLength += deltaLength;\r
+\r
+            this.ValidateLines();\r
+        }\r
+\r
+        void ValidateLines()\r
+        {\r
+#if DEBUG\r
+            int nextIndex = 0;\r
+            for (int i = 0; i < this.Lines.Count; i++)\r
             {\r
-                ITextLayout oldItem = this.CacheEntries.Dequeue();\r
-                oldItem.Dispose();\r
+                int lineHeadIndex = this.GetLineHeadIndex(i);\r
+                if (lineHeadIndex != nextIndex)\r
+                {\r
+                    Debug.WriteLine("Invaild Line");\r
+                    System.Diagnostics.Debugger.Break();\r
+                }\r
+                nextIndex = lineHeadIndex + this.Lines[i].Length;\r
             }\r
-            this.CacheEntries.Enqueue(layout);\r
-            \r
-            return layout;\r
+#endif\r
         }\r
 \r
         /// <summary>\r
@@ -398,7 +470,7 @@ namespace FooEditEngine
         {\r
             if (row < 0 || row > this.Lines.Count)\r
                 throw new ArgumentOutOfRangeException();\r
-            return this.Lines[row].Index;\r
+            return this.GetLineHeadIndex(row);\r
         }\r
 \r
         /// <summary>\r
@@ -415,9 +487,43 @@ namespace FooEditEngine
 \r
         internal ITextLayout GetLayout(int row)\r
         {\r
+            if (this.Lines[row].Layout != null && this.Lines[row].Layout.Invaild)\r
+            {\r
+                this.Lines[row].Layout.Dispose();\r
+                this.Lines[row].Layout = null;\r
+            }\r
+            if (this.Lines[row].Layout == null || this.Lines[row].Layout.Disposed)\r
+                this.Lines[row].Layout = this.CreateLayout(row);\r
             return this.Lines[row].Layout;\r
         }\r
 \r
+        ITextLayout CreateLayout(int row)\r
+        {\r
+            ITextLayout layout;\r
+            LineToIndexTableData lineData = this.Lines[row];\r
+            if (lineData.Length == 0)\r
+            {\r
+                layout = this.render.CreateLaytout("", null, null);\r
+            }\r
+            else\r
+            {\r
+                int lineHeadIndex = this.GetLineHeadIndex(row);\r
+                var markerRange = from s in this.Document.Markers.Get(lineHeadIndex, lineData.Length)\r
+                                  let n = Util.ConvertAbsIndexToRelIndex(s, lineHeadIndex, lineData.Length)\r
+                                  select n;\r
+                layout = this.render.CreateLaytout(this.Document.ToString(lineHeadIndex, lineData.Length), lineData.Syntax, markerRange);\r
+            }\r
+\r
+            if (this.CacheEntries.Count > MaxEntries)\r
+            {\r
+                ITextLayout oldItem = this.CacheEntries.Dequeue();\r
+                oldItem.Dispose();\r
+            }\r
+            this.CacheEntries.Enqueue(layout);\r
+\r
+            return layout;\r
+        }\r
+\r
         int lastLineNumber;\r
         /// <summary>\r
         /// インデックスを行番号に変換します\r
@@ -428,16 +534,18 @@ namespace FooEditEngine
         {\r
             if (index < 0)\r
                 throw new ArgumentOutOfRangeException("indexに負の値を設定することはできません");\r
-            \r
+\r
             if (index == 0 && this.Lines.Count > 0)\r
                 return 0;\r
 \r
             LineToIndexTableData line;\r
+            int lineHeadIndex;\r
 \r
             if (lastLineNumber < this.Lines.Count - 1)\r
             {\r
                 line = this.Lines[lastLineNumber];\r
-                if (index >= line.Index && index < line.Index + line.Length)\r
+                lineHeadIndex = this.GetLineHeadIndex(lastLineNumber);\r
+                if (index >= lineHeadIndex && index < lineHeadIndex + line.Length)\r
                     return lastLineNumber;\r
             }\r
 \r
@@ -446,12 +554,13 @@ namespace FooEditEngine
             {\r
                 mid = (left + right) / 2;\r
                 line = this.Lines[mid];\r
-                if (index >= line.Index && index < line.Index + line.Length)\r
+                lineHeadIndex = this.GetLineHeadIndex(mid);\r
+                if (index >= lineHeadIndex && index < lineHeadIndex + line.Length)\r
                 {\r
                     lastLineNumber = mid;\r
                     return mid;\r
                 }\r
-                if (index < line.Index)\r
+                if (index < lineHeadIndex)\r
                 {\r
                     right = mid - 1;\r
                 }\r
@@ -461,13 +570,15 @@ namespace FooEditEngine
                 }\r
             }\r
 \r
-            line = this.Lines.Last();\r
-            if (index >= line.Index && index <= line.Index + line.Length)   //最終行長+1までキャレットが移動する可能性があるので\r
+            int lastRow = this.Lines.Count - 1;\r
+            line = this.Lines[lastRow];\r
+            lineHeadIndex = this.GetLineHeadIndex(lastRow);\r
+            if (index >= lineHeadIndex && index <= lineHeadIndex + line.Length)   //最終行長+1までキャレットが移動する可能性があるので\r
             {\r
                 lastLineNumber = this.Lines.Count - 1;\r
                 return lastLineNumber;\r
             }\r
-            \r
+\r
             throw new ArgumentOutOfRangeException("該当する行が見つかりませんでした");\r
         }\r
 \r
@@ -480,7 +591,7 @@ namespace FooEditEngine
         {\r
             TextPoint tp = new TextPoint();\r
             tp.row = GetLineNumberFromIndex(index);\r
-            tp.col = index - this.Lines[tp.row].Index;\r
+            tp.col = index - this.GetLineHeadIndex(tp.row);\r
             Debug.Assert(tp.row < this.Lines.Count && tp.col <= this.Lines[tp.row].Length);\r
             return tp;\r
         }\r
@@ -498,7 +609,7 @@ namespace FooEditEngine
                 throw new ArgumentOutOfRangeException("tp.rowが設定できる範囲を超えています");\r
             if (tp.col < 0 || tp.col > this.Lines[tp.row].Length)\r
                 throw new ArgumentOutOfRangeException("tp.colが設定できる範囲を超えています");\r
-            return this.Lines[tp.row].Index + tp.col;\r
+            return this.GetLineHeadIndex(tp.row) + tp.col;\r
         }\r
 \r
         /// <summary>\r
@@ -574,8 +685,10 @@ namespace FooEditEngine
             this.FoldingCollection.Clear();\r
             this.Lines.Clear();\r
             LineToIndexTableData dummy = new LineToIndexTableData();\r
-            dummy.CreateLayout = this.LineToIndexTableData_CreatLayout;\r
             this.Lines.Add(dummy);\r
+            this.stepRow = STEP_ROW_IS_NONE;\r
+            this.stepLength = 0;\r
+            Debug.WriteLine("Clear");\r
         }\r
 \r
         void Hilight(int row, int rowCount)\r
index 2c52b80..e43482e 100644 (file)
         </Menu>
         <MyNamespace:FooTextBox x:Name="fooTextBox" Grid.Row="1"/>
         <StackPanel Grid.Row="2" Orientation="Horizontal">
-            <TextBlock Text="FindPatter"/>
+            <TextBlock Name="CaretIndex"/>
+            <TextBlock Name="SelectLength" Margin="10,0,0,0"/>
+            <TextBlock Text="FindPattern" Margin="10,0,0,0"/>
             <TextBox Name="FindPattern" Width="100"/>
-            <TextBlock Text="ReplaceAll"/>
+            <TextBlock Text="ReplaceAll" Margin="10,0,0,0"/>
             <TextBox Name="ReplacePattern" Width="100"/>
-            <Button Name="ReplaceAll" Content="ReplaceAll" Click="ReplaceAll_Click"/>
+            <Button Name="ReplaceAll" Content="ReplaceAll" Click="ReplaceAll_Click" Margin="10,0,0,0"/>
         </StackPanel>
     </Grid>
 </Window>
index 6fda266..d0e4da3 100644 (file)
@@ -30,6 +30,7 @@ namespace Test
         {
             InitializeComponent();
             this.fooTextBox.MouseDoubleClick += new System.Windows.Input.MouseButtonEventHandler(fooTextBox_MouseDoubleClick);
+            this.fooTextBox.CaretMoved += fooTextBox_CaretMoved;
             this.fooTextBox.ShowTab = true;
             this.fooTextBox.ShowFullSpace = true;
             this.fooTextBox.ShowLineBreak = true;
@@ -40,9 +41,16 @@ namespace Test
             this.Closed += MainWindow_Closed;
         }
 
+        void fooTextBox_CaretMoved(object sender, System.EventArgs e)
+        {
+            this.CaretIndex.Text = string.Format("Index:{0}", this.fooTextBox.SelectionStart);
+            this.SelectLength.Text = string.Format("Select Length:{0}", this.fooTextBox.SelectionLength);
+        }
+
         void MainWindow_Closed(object sender, System.EventArgs e)
         {
             this.cancleTokenSrc.Cancel();
+            this.fooTextBox.Dispose();
         }
 
         void fooTextBox_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
@@ -132,9 +140,10 @@ namespace Test
         private void MenuItem_Click_4(object sender, RoutedEventArgs e)
         {
             if (this.fooTextBox.LineBreakMethod == LineBreakMethod.None)
-                this.fooTextBox.LineBreakMethod = LineBreakMethod.PageBound;
+                this.fooTextBox.LineBreakMethod = LineBreakMethod.CharUnit;
             else
                 this.fooTextBox.LineBreakMethod = LineBreakMethod.None;
+            this.fooTextBox.LineBreakCharCount = 10;
             this.fooTextBox.PerfomLayouts();
             this.fooTextBox.Refresh();
         }
@@ -204,7 +213,7 @@ namespace Test
             System.Diagnostics.Stopwatch time = new System.Diagnostics.Stopwatch();
             time.Start();
             this.fooTextBox.Document.FireUpdateEvent = false;
-            this.fooTextBox.Document.ReplaceAll2(this.FindPattern.Text, this.ReplacePattern.Text);
+            this.fooTextBox.Document.ReplaceAll2(this.FindPattern.Text, this.ReplacePattern.Text,true);
             this.fooTextBox.Document.FireUpdateEvent = true;
             time.Stop();
             this.fooTextBox.Refresh();
index 29fc69f..6843570 100644 (file)
@@ -23,16 +23,166 @@ namespace UnitTest
     public class UnitTest3
     {
         [TestMethod]
-        public void UpdateLineTest()
+        public void InsertSingleLineTest()
         {
             Document doc = new Document();
             DummyRender render = new DummyRender();
             DummyView view = new DummyView(doc,render);
             doc.Clear();
-            doc.Append("a\nb\nc");
-            Assert.IsTrue(view.LayoutLines[0] == "a\n" && view.LayoutLines[1] == "b\n" && view.LayoutLines[2] == "c");
-            doc.Replace(0, 3, "x");
-            Assert.IsTrue(view.LayoutLines[0] == "x\n");
+            doc.Append("a\nb\nc\nd");
+            Assert.IsTrue(view.LayoutLines[0] == "a\n" &&
+                view.LayoutLines[1] == "b\n" &&
+                view.LayoutLines[2] == "c\n" &&
+                view.LayoutLines[3] == "d");
+
+            doc.Insert(2, "x");
+            Assert.IsTrue(view.LayoutLines[0] == "a\n" &&
+                view.LayoutLines[1] == "xb\n" &&
+                view.LayoutLines[2] == "c\n" &&
+                view.LayoutLines[3] == "d");
+
+            doc.Insert(3, "x");
+            Assert.IsTrue(view.LayoutLines[0] == "a\n" &&
+                view.LayoutLines[1] == "xxb\n" &&
+                view.LayoutLines[2] == "c\n" &&
+                view.LayoutLines[3] == "d");
+            
+            doc.Insert(6, "x");
+            Assert.IsTrue(view.LayoutLines[0] == "a\n" &&
+                view.LayoutLines[1] == "xxb\n" &&
+                view.LayoutLines[2] == "xc\n" &&
+                view.LayoutLines[3] == "d");
+            
+            doc.Insert(0, "x");
+            Assert.IsTrue(view.LayoutLines[0] == "xa\n" &&
+                view.LayoutLines[1] == "xxb\n" &&
+                view.LayoutLines[2] == "xc\n" &&
+                view.LayoutLines[3] == "d");
+
+        }
+
+        [TestMethod]
+        public void InsertMultiLineTest()
+        {
+            Document doc = new Document();
+            DummyRender render = new DummyRender();
+            DummyView view = new DummyView(doc, render);
+
+            doc.Clear();
+            doc.Append("a\nb\nc\nd");
+
+            doc.Insert(2, "f\ne");
+            Assert.IsTrue(
+                view.LayoutLines[0] == "a\n" &&
+                view.LayoutLines[1] == "f\n" &&
+                view.LayoutLines[2] == "eb\n" &&
+                view.LayoutLines[3] == "c\n" &&
+                view.LayoutLines[4] == "d");
+
+            doc.Insert(3, "g\nh");
+            Assert.IsTrue(
+                view.LayoutLines[0] == "a\n" &&
+                view.LayoutLines[1] == "fg\n" &&
+                view.LayoutLines[2] == "h\n" &&
+                view.LayoutLines[3] == "eb\n" &&
+                view.LayoutLines[4] == "c\n" &&
+                view.LayoutLines[5] == "d");
+
+            doc.Insert(0, "x\ny");
+            Assert.IsTrue(
+                view.LayoutLines[0] == "x\n" &&
+                view.LayoutLines[1] == "ya\n" &&
+                view.LayoutLines[2] == "fg\n" &&
+                view.LayoutLines[3] == "h\n" &&
+                view.LayoutLines[4] == "eb\n" &&
+                view.LayoutLines[5] == "c\n" &&
+                view.LayoutLines[6] == "d");
+        }
+
+        [TestMethod]
+        public void RemoveSingleLineTest()
+        {
+            Document doc = new Document();
+            DummyRender render = new DummyRender();
+            DummyView view = new DummyView(doc, render);
+            doc.Clear();
+            doc.Append("aa\nbb\ncc\ndd");
+
+            doc.Remove(9, 1);
+            Assert.IsTrue(
+                view.LayoutLines[0] == "aa\n" && 
+                view.LayoutLines[1] == "bb\n" && 
+                view.LayoutLines[2] == "cc\n" && 
+                view.LayoutLines[3] == "d"
+                );
+
+            doc.Remove(9, 1);
+            Assert.IsTrue(
+                view.LayoutLines[0] == "aa\n" &&
+                view.LayoutLines[1] == "bb\n" &&
+                view.LayoutLines[2] == "cc\n" &&
+                view.LayoutLines[3] == ""
+                );
+
+            doc.Remove(0, 1);
+            Assert.IsTrue(
+                view.LayoutLines[0] == "a\n" &&
+                view.LayoutLines[1] == "bb\n" &&
+                view.LayoutLines[2] == "cc\n" &&
+                view.LayoutLines[3] == ""
+                );
+        }
+
+        [TestMethod]
+        public void RemoveMultiLineTest()
+        {
+            Document doc = new Document();
+            DummyRender render = new DummyRender();
+            DummyView view = new DummyView(doc, render);
+
+            doc.Clear();
+            doc.Append("a\n");
+            doc.Append("b\n");
+            doc.Append("c\n");
+            doc.Append("d\n");
+            doc.Append("e\n");
+            doc.Append("f\n");
+
+            doc.Remove(2, 4);
+            Assert.IsTrue(
+                view.LayoutLines[0] == "a\n" &&
+                view.LayoutLines[1] == "d\n" &&
+                view.LayoutLines[2] == "e\n" &&
+                view.LayoutLines[3] == "f\n");
+
+            doc.Remove(4, 4);
+            Assert.IsTrue(
+                view.LayoutLines[0] == "a\n" &&
+                view.LayoutLines[1] == "d\n");
+
+            doc.Clear();
+            doc.Append("a\n");
+            doc.Append("b\n");
+            doc.Append("c\n");
+            doc.Append("d\n");
+
+            doc.Remove(2, 6);
+            Assert.IsTrue(view.LayoutLines[0] == "a\n");
+
+            doc.Clear();
+            doc.Append("a\n");
+            doc.Append("b\n");
+            doc.Append("c\n");
+            doc.Append("d\n");
+            doc.Append("e\n");
+            doc.Append("f\n");
+            doc.Insert(4, "a");
+            doc.Remove(2, 5);
+            Assert.IsTrue(
+                view.LayoutLines[0] == "a\n" &&
+                view.LayoutLines[1] == "d\n" &&
+                view.LayoutLines[2] == "e\n" &&
+                view.LayoutLines[3] == "f\n");
         }
 
         [TestMethod]
@@ -43,12 +193,29 @@ namespace UnitTest
             DummyView view = new DummyView(doc, render);
             doc.Clear();
             doc.Append("a\nb\nc");
+
             Assert.IsTrue(view.LayoutLines.GetIndexFromLineNumber(1) == 2);
             Assert.IsTrue(view.LayoutLines.GetLengthFromLineNumber(1) == 2);
             Assert.IsTrue(view.LayoutLines.GetLineNumberFromIndex(2) == 1);
             TextPoint tp = view.LayoutLines.GetTextPointFromIndex(2);
             Assert.IsTrue(tp.row == 1 && tp.col == 0);
             Assert.IsTrue(view.LayoutLines.GetIndexFromTextPoint(tp) == 2);
+
+            doc.Insert(2, "a");
+
+            Assert.IsTrue(view.LayoutLines.GetIndexFromLineNumber(2) == 5);
+            Assert.IsTrue(view.LayoutLines.GetLineNumberFromIndex(5) == 2);
+            tp = view.LayoutLines.GetTextPointFromIndex(5);
+            Assert.IsTrue(tp.row == 2 && tp.col == 0);
+            Assert.IsTrue(view.LayoutLines.GetIndexFromTextPoint(tp) == 5);
+
+            doc.Insert(0, "a");
+
+            Assert.IsTrue(view.LayoutLines.GetIndexFromLineNumber(2) == 6);
+            Assert.IsTrue(view.LayoutLines.GetLineNumberFromIndex(6) == 2);
+            tp = view.LayoutLines.GetTextPointFromIndex(6);
+            Assert.IsTrue(tp.row == 2 && tp.col == 0);
+            Assert.IsTrue(view.LayoutLines.GetIndexFromTextPoint(tp) == 6);
         }
     }
 }