OSDN Git Service

読み込み時に行分割を行わず、レタリング時に行分割を行うようにした
[fooeditengine/FooEditEngine.git] / Core / LineToIndex.cs
1 /*
2  * Copyright (C) 2013 FooProject
3  * * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
5
6  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 
7  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
8
9 You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
10  */
11 using System;
12 using System.Text.RegularExpressions;
13 using System.Threading;
14 using System.Linq;
15 using System.Collections.Generic;
16 using System.Diagnostics;
17 using Slusser.Collections.Generic;
18
19 namespace FooEditEngine
20 {
21     internal interface ITextLayout : IDisposable
22     {
23         /// <summary>
24         /// 文字列の幅
25         /// </summary>
26         double Width
27         {
28             get;
29         }
30
31         /// <summary>
32         /// 文字列の高さ
33         /// </summary>
34         double Height
35         {
36             get;
37         }
38
39         /// <summary>
40         /// Disposeされているなら真を返す
41         /// </summary>
42         bool Disposed
43         {
44             get;
45         }
46
47         /// <summary>
48         /// 破棄すべきなら真。そうでなければ偽
49         /// </summary>
50         bool Invaild
51         {
52             get;
53         }
54
55         /// <summary>
56         /// 桁方向の座標に対応するインデックスを得る
57         /// </summary>
58         /// <param name="colpos">桁方向の座標</param>
59         /// <returns>インデックス</returns>
60         /// <remarks>行番号の幅は考慮されてないのでView以外のクラスは呼び出さないでください</remarks>
61         int GetIndexFromColPostion(double colpos);
62
63         /// <summary>
64         /// インデックスに対応する文字の幅を得る
65         /// </summary>
66         /// <param name="index">インデックス</param>
67         /// <returns>文字の幅</returns>
68         double GetWidthFromIndex(int index);
69
70         /// <summary>
71         /// インデックスに対応する桁方向の座標を得る
72         /// </summary>
73         /// <param name="index">インデックス</param>
74         /// <returns>桁方向の座標</returns>
75         /// <remarks>行頭にEOFが含まれている場合、0が返ります</remarks>
76         double GetColPostionFromIndex(int index);
77
78         /// <summary>
79         /// 座標に対応するインデックスを取得する
80         /// </summary>
81         /// <param name="x">桁方向の座標</param>
82         /// <param name="y">行方向の座標</param>
83         /// <returns>インデックス</returns>
84         /// <remarks>行番号の幅は考慮されてないのでView以外のクラスは呼び出さないでください</remarks>
85         int GetIndexFromPostion(double x, double y);
86
87         /// <summary>
88         /// インデックスに対応する座標を得る
89         /// </summary>
90         /// <param name="index">インデックス</param>
91         /// <returns>行方向と桁方向の相対座標</returns>
92         /// <remarks>行頭にEOFが含まれている場合、0が返ります</remarks>
93         Point GetPostionFromIndex(int index);
94
95         /// <summary>
96         /// 適切な位置にインデックスを調整する
97         /// </summary>
98         /// <param name="index">インデックス</param>
99         /// <param name="flow">真の場合は隣接するクラスターを指すように調整し、
100         /// そうでない場合は対応するクラスターの先頭を指すように調整します</param>
101         /// <returns>調整後のインデックス</returns>
102         int AlignIndexToNearestCluster(int index, AlignDirection flow);
103     }
104
105     internal class SpilitStringEventArgs : EventArgs
106     {
107         public Document buffer;
108         public int index;
109         public int length;
110         public int row;
111         public SpilitStringEventArgs(Document buf, int index, int length,int row)
112         {
113             this.buffer = buf;
114             this.index = index;
115             this.length = length;
116             this.row = row;
117         }
118     }
119
120     internal struct SyntaxInfo
121     {
122         public TokenType type;
123         public int index;
124         public int length;
125         public SyntaxInfo(int index, int length, TokenType type)
126         {
127             this.type = type;
128             this.index = index;
129             this.length = length;
130         }
131     }
132
133     internal enum EncloserType
134     {
135         None,
136         Begin,
137         Now,
138         End,
139     }
140
141     interface ILineInfoGenerator
142     {
143         void Update(Document doc, int startIndex, int insertLength, int removeLength);
144         void Clear(LineToIndexTable lti);
145         bool Generate(Document doc, LineToIndexTable lti, bool force = true);
146     }
147
148     internal class LineToIndexTableData : IDisposable
149     {
150         /// <summary>
151         /// 行の先頭。正しい行の先頭位置を取得するにはGetLineHeadIndex()を使用してください
152         /// </summary>
153         public int Index;
154         /// <summary>
155         /// 行の長さ
156         /// </summary>
157         public int Length;
158         /// <summary>
159         /// 改行マークかEOFなら真を返す
160         /// </summary>
161         public bool LineEnd;
162         public SyntaxInfo[] Syntax;
163         public EncloserType EncloserType;
164         internal ITextLayout Layout;
165         public bool Dirty = false;
166
167         /// <summary>
168         /// コンストラクター。LineToIndexTable以外のクラスで呼び出さないでください
169         /// </summary>
170         public LineToIndexTableData()
171         {
172         }
173
174         /// <summary>
175         /// コンストラクター。LineToIndexTable以外のクラスで呼び出さないでください
176         /// </summary>
177         public LineToIndexTableData(int index, int length, bool lineend,bool dirty, SyntaxInfo[] syntax)
178         {
179             this.Index = index;
180             this.Length = length;
181             this.LineEnd = lineend;
182             this.Syntax = syntax;
183             this.EncloserType = EncloserType.None;
184             this.Dirty = dirty;
185         }
186
187         public void Dispose()
188         {
189             if(this.Layout != null)
190                 this.Layout.Dispose();
191         }
192     }
193
194     internal delegate IList<LineToIndexTableData> SpilitStringEventHandler(object sender, SpilitStringEventArgs e);
195
196     internal sealed class CreateLayoutEventArgs
197     {
198         /// <summary>
199         /// 開始インデックス
200         /// </summary>
201         public int Index
202         {
203             get;
204             private set;
205         }
206         /// <summary>
207         /// 長さ
208         /// </summary>
209         public int Length
210         {
211             get;
212             private set;
213         }
214         /// <summary>
215         /// 文字列
216         /// </summary>
217         public string Content
218         {
219             get;
220             private set;
221         }
222         public CreateLayoutEventArgs(int index, int length,string content)
223         {
224             this.Index = index;
225             this.Length = length;
226             this.Content = content;
227         }
228     }
229
230     /// <summary>
231     /// 行番号とインデックスを相互変換するためのクラス
232     /// </summary>
233     public sealed class LineToIndexTable : IEnumerable<string>
234     {
235         const int MaxEntries = 100;
236         GapBuffer<LineToIndexTableData> Lines = new GapBuffer<LineToIndexTableData>();
237         Document Document;
238         ITextRender render;
239         int stepRow = -1,stepLength = 0;
240         const int STEP_ROW_IS_NONE = -1;
241
242         const int FOLDING_INDEX = 0;
243         const int SYNTAX_HIGLITHER_INDEX = 1;
244         ILineInfoGenerator[] _generators = new ILineInfoGenerator[2];
245
246         internal LineToIndexTable(Document buf)
247         {
248             this.Document = buf;
249             this.Document.Markers.Updated += Markers_Updated;
250             this._generators[FOLDING_INDEX] = new FoldingGenerator();
251             this._generators[SYNTAX_HIGLITHER_INDEX] = new SyntaxHilightGenerator();
252             this.WrapWidth = NONE_BREAK_LINE;
253 #if DEBUG && !NETFX_CORE
254             if (!Debugger.IsAttached)
255             {
256                 Guid guid = Guid.NewGuid();
257                 string path = string.Format("{0}\\footextbox_lti_debug_{1}.log", System.IO.Path.GetTempPath(), guid);
258                 Debug.Listeners.Add(new TextWriterTraceListener(path));
259                 Debug.AutoFlush = true;
260             }
261 #endif
262         }
263
264         void Markers_Updated(object sender, EventArgs e)
265         {
266             this.ClearLayoutCache();
267         }
268
269         /// <summary>
270         /// ITextRenderインターフェイスのインスタンス。必ずセットすること
271         /// </summary>
272         internal ITextRender Render
273         {
274             get { return this.render; }
275             set
276             {
277                 this.render = value;
278             }
279         }
280
281         internal SpilitStringEventHandler SpilitString;
282
283         /// <summary>
284         /// 行数を返す
285         /// </summary>
286         public int Count
287         {
288             get { return this.Lines.Count; }
289         }
290
291         /// <summary>
292         /// 折り畳み関係の情報を収めたコレクション
293         /// </summary>
294         public FoldingCollection FoldingCollection
295         {
296             get
297             {
298                 return ((FoldingGenerator)this._generators[FOLDING_INDEX]).FoldingCollection;
299             }
300             private set
301             {
302             }
303         }
304
305         /// <summary>
306         /// シンタックスハイライター
307         /// </summary>
308         public IHilighter Hilighter
309         {
310             get
311             {
312                 return ((SyntaxHilightGenerator)this._generators[SYNTAX_HIGLITHER_INDEX]).Hilighter;
313             }
314             set
315             {
316                 ((SyntaxHilightGenerator)this._generators[SYNTAX_HIGLITHER_INDEX]).Hilighter = value;
317                 if (value == null)
318                     this._generators[FOLDING_INDEX].Clear(this);
319             }
320         }
321
322         /// <summary>
323         /// 折り畳み
324         /// </summary>
325         public IFoldingStrategy FoldingStrategy
326         {
327             get
328             {
329                 return ((FoldingGenerator)this._generators[FOLDING_INDEX]).FoldingStrategy;
330             }
331             set
332             {
333                 ((FoldingGenerator)this._generators[FOLDING_INDEX]).FoldingStrategy = value;
334                 if (value == null)
335                     this._generators[FOLDING_INDEX].Clear(this);
336             }
337         }
338
339         /// <summary>
340         /// ピクセル単位で折り返すかどうか
341         /// </summary>
342         public double WrapWidth
343         {
344             get;
345             set;
346         }
347
348         /// <summary>
349         /// 行を折り返さないことを表す
350         /// </summary>
351         public const double NONE_BREAK_LINE = -1;
352
353         /// <summary>
354         /// 保持しているレイアウトキャッシュをクリアーする
355         /// </summary>
356         public void ClearLayoutCache()
357         {
358             foreach (LineToIndexTableData data in this.Lines)
359             {
360                 data.Dispose();
361             }
362         }
363
364         /// <summary>
365         /// 保持しているレイアウトキャッシュをクリアーする
366         /// </summary>
367         public void ClearLayoutCache(int index,int length)
368         {
369             if (index >= this.Document.Length)
370                 return;
371             int startRow = this.GetLineNumberFromIndex(index);
372             int lastIndex = Math.Min(index + length - 1, this.Document.Length - 1);
373             if (lastIndex < 0)
374                 lastIndex = 0;
375             int endRow = this.GetLineNumberFromIndex(lastIndex);
376             for (int i = startRow; i <= endRow; i++)
377                 this.Lines[i].Dispose();
378         }
379
380         /// <summary>
381         /// 行番号に対応する文字列を返します
382         /// </summary>
383         /// <param name="n"></param>
384         /// <returns></returns>
385         public string this[int n]
386         {
387             get
388             {
389                 LineToIndexTableData data = this.Lines[n];
390                 string str = this.Document.ToString(this.GetLineHeadIndex(n), data.Length);
391
392                 return str;
393             }
394         }
395
396         /// <summary>
397         /// 更新フラグを更新しないなら真
398         /// </summary>
399         public bool IsFrozneDirtyFlag
400         {
401             get;
402             set;
403         }
404
405         int GetLineHeadIndex(int row)
406         {
407             if (this.Lines.Count == 0)
408                 return 0;
409             if (this.stepRow != STEP_ROW_IS_NONE && row > this.stepRow)
410                 return this.Lines[row].Index + this.stepLength;
411             else
412                 return this.Lines[row].Index;
413         }
414
415         internal LineToIndexTableData CreateLineToIndexTableData(int index, int length, bool lineend, SyntaxInfo[] syntax)
416         {
417             LineToIndexTableData result = new LineToIndexTableData(index, length, lineend,this.IsFrozneDirtyFlag == false, syntax);
418             return result;
419         }
420
421         internal void UpdateLineAsReplace(int row,int removedLength, int insertedLength)
422         {
423             int deltaLength = insertedLength - removedLength;
424
425             this.Lines[row] = new LineToIndexTableData(this.GetLineHeadIndex(row), this.GetLengthFromLineNumber(row) + deltaLength, true, true, null);
426
427             //行テーブルを更新する
428             this.UpdateLineHeadIndex(deltaLength, row, 1);
429
430             foreach(var generator in this._generators)
431                 generator.Update(this.Document, this.GetLineHeadIndex(row), insertedLength, removedLength);
432         }
433
434         internal void UpdateAsReplace(int index, int removedLength, int insertedLength)
435         {
436 #if DEBUG
437             Debug.WriteLine("Replaced Index:{0} RemoveLength:{1} InsertLength:{2}", index, removedLength, insertedLength);
438 #endif
439             int startRow, endRow;
440             GetRemoveRange(index, removedLength, out startRow, out endRow);
441
442             int deltaLength = insertedLength - removedLength;
443
444             var result = GetAnalyzeLength(startRow, endRow, index, removedLength, insertedLength);
445             int HeadIndex = result.Item1;
446             int analyzeLength = result.Item2;
447
448             //挿入範囲内のドキュメントから行を生成する
449             SpilitStringEventArgs e = new SpilitStringEventArgs(this.Document, HeadIndex, analyzeLength, startRow);
450             IList<LineToIndexTableData> newLines = SpilitString(this, e);
451
452             //消すべき行が複数ある場合は消すが、そうでない場合は最適化のため長さを変えるだけにとどめておく
453             int removeCount = endRow - startRow + 1;
454             if (removeCount == 1 && newLines.Count == 1)
455             {
456                 this.Lines[startRow] = newLines.First();
457             }
458             else
459             {
460                 this.RemoveLine(startRow, removeCount);
461
462                 //行を挿入する
463                 this.InsertLine(startRow, newLines, removeCount, deltaLength);
464             }
465
466             //行テーブルを更新する
467             this.UpdateLineHeadIndex(deltaLength, startRow, newLines.Count);
468
469             this.AddDummyLine();
470
471             foreach (var generator in this._generators)
472                 generator.Update(this.Document, index, insertedLength, removedLength);
473         }
474
475         void GetRemoveRange(int index,int length,out int startRow,out int endRow)
476         {
477             startRow = this.GetLineNumberFromIndex(index);
478             while (startRow > 0 && this.Lines[startRow - 1].LineEnd == false)
479                 startRow--;
480
481             endRow = this.GetLineNumberFromIndex(index + length);
482             while (endRow < this.Lines.Count && this.Lines[endRow].LineEnd == false)
483                 endRow++;
484             if (endRow >= this.Lines.Count)
485                 endRow = this.Lines.Count - 1;
486         }
487
488         Tuple<int,int> GetAnalyzeLength(int startRow,int endRow,int updateStartIndex,int removedLength,int insertedLength)
489         {
490             int HeadIndex = this.GetIndexFromLineNumber(startRow);
491             int LastIndex = this.GetIndexFromLineNumber(endRow) + this.GetLengthFromLineNumber(endRow) - 1;
492
493             //SpilitStringの対象となる範囲を求める
494             int fisrtPartLength = updateStartIndex - HeadIndex;
495             int secondPartLength = LastIndex - (updateStartIndex + removedLength - 1);
496             int analyzeLength = fisrtPartLength + secondPartLength + insertedLength;
497             Debug.Assert(analyzeLength <= this.Document.Length - 1 - HeadIndex + 1);
498
499             return new Tuple<int, int>(HeadIndex, analyzeLength);
500         }
501
502         void RemoveLine(int startRow, int removeCount)
503         {
504             for (int i = startRow; i < startRow + removeCount; i++)
505                 this.Lines[i].Dispose();
506
507             this.Lines.RemoveRange(startRow, removeCount);
508         }
509
510         void InsertLine(int startRow, IList<LineToIndexTableData> collection,int removeCount, int deltaLength)
511         {
512             int newCount = collection.Count;
513             if (this.stepRow > startRow && newCount > 0 && newCount != removeCount)
514             {
515                 //stepRowは1か2のうち、大きな方になる
516                 // 1.stepRow - (削除された行数 - 挿入された行数)
517                 // 2.行の挿入箇所
518                 //行が削除や置換された場合、1の処理をしないと正しいIndexが求められない
519                 this.stepRow = Math.Max(this.stepRow - (removeCount - newCount), startRow);
520 #if DEBUG
521                 if (this.stepRow < 0 || this.stepRow > this.Lines.Count + newCount)
522                 {
523                     Debug.WriteLine("step row < 0 or step row >= lines.count");
524                     Debugger.Break();
525                 }
526 #endif
527             }
528
529             //startRowが挿入した行の開始位置なのであらかじめ引いておく
530             for (int i = 1; i < collection.Count; i++)
531             {
532                 if (this.stepRow != STEP_ROW_IS_NONE && startRow + i > this.stepRow)
533                     collection[i].Index -= deltaLength + this.stepLength;
534                 else
535                     collection[i].Index -= deltaLength;
536             }
537
538             this.Lines.InsertRange(startRow, collection);
539         }
540
541         void AddDummyLine()
542         {
543             LineToIndexTableData dummyLine = null;
544             if (this.Lines.Count == 0)
545             {
546                 dummyLine = new LineToIndexTableData();
547                 this.Lines.Add(dummyLine);
548                 return;
549             }
550
551             int lastLineRow = this.Lines.Count > 0 ? this.Lines.Count - 1 : 0;
552             int lastLineHeadIndex = this.GetIndexFromLineNumber(lastLineRow);
553             int lastLineLength = this.GetLengthFromLineNumber(lastLineRow);
554
555             if (lastLineLength != 0 && this.Document[Document.Length - 1] == Document.NewLine)
556             {
557                 int realIndex = lastLineHeadIndex + lastLineLength;
558                 if (lastLineRow >= this.stepRow)
559                     realIndex -= this.stepLength;
560                 dummyLine = new LineToIndexTableData(realIndex, 0, true,false, null);
561                 this.Lines.Add(dummyLine);
562             }
563         }
564
565         void UpdateLineHeadIndex(int deltaLength,int startRow,int insertedLineCount)
566         {
567             if (this.Lines.Count == 0)
568             {
569                 this.stepRow = STEP_ROW_IS_NONE;
570                 this.stepLength = 0;
571                 return;
572             }
573
574             if (this.stepRow == STEP_ROW_IS_NONE)
575             {
576                 this.stepRow = startRow;
577                 this.stepLength = deltaLength;
578                 return;
579             }
580
581
582             if (startRow < this.stepRow)
583             {
584                 //ドキュメントの後半部分をごっそり削除した場合、this.stepRow >= this.Lines.Countになる可能性がある
585                 if (this.stepRow >= this.Lines.Count)
586                     this.stepRow = this.Lines.Count - 1;
587                 for (int i = this.stepRow; i > startRow; i--)
588                     this.Lines[i].Index -= this.stepLength;
589             }
590             else if (startRow > this.stepRow)
591             {
592                 for (int i = this.stepRow + 1; i < startRow; i++)
593                     this.Lines[i].Index += this.stepLength;
594             }
595
596             this.stepRow = startRow;
597             this.stepLength += deltaLength;
598
599             this.ValidateLines();
600         }
601
602         void ValidateLines()
603         {
604 #if DEBUG
605             int nextIndex = 0;
606             for (int i = 0; i < this.Lines.Count; i++)
607             {
608                 int lineHeadIndex = this.GetLineHeadIndex(i);
609                 if (lineHeadIndex != nextIndex)
610                 {
611                     Debug.WriteLine("Invaild Line");
612                     System.Diagnostics.Debugger.Break();
613                 }
614                 nextIndex = lineHeadIndex + this.Lines[i].Length;
615             }
616 #endif
617         }
618
619         /// <summary>
620         /// 生データを取得します
621         /// </summary>
622         /// <param name="row">行</param>
623         /// <returns>LineToIndexTableData</returns>
624         /// <remarks>いくつかの値は実態とかけ離れた値を返します。詳しくはLineToIndexTableDataの注意事項を参照すること</remarks>
625         internal LineToIndexTableData GetRaw(int row)
626         {
627             return this.Lines[row];
628         }
629
630         /// <summary>
631         /// 行番号をインデックスに変換します
632         /// </summary>
633         /// <param name="row">行番号</param>
634         /// <returns>0から始まるインデックスを返す</returns>
635         public int GetIndexFromLineNumber(int row)
636         {
637             if (row < 0 || row > this.Lines.Count)
638                 throw new ArgumentOutOfRangeException();
639             return this.GetLineHeadIndex(row);
640         }
641
642         /// <summary>
643         /// 行の長さを得ます
644         /// </summary>
645         /// <param name="row">行番号</param>
646         /// <returns>行の文字長を返します</returns>
647         public int GetLengthFromLineNumber(int row)
648         {
649             if (row < 0 || row > this.Lines.Count)
650                 throw new ArgumentOutOfRangeException();
651             return this.Lines[row].Length;
652         }
653
654         /// <summary>
655         /// 更新フラグを取得します
656         /// </summary>
657         /// <param name="row">行番号</param>
658         /// <returns>更新されていれば真。そうでなければ偽</returns>
659         public bool GetDirtyFlag(int row)
660         {
661             if (row < 0 || row > this.Lines.Count)
662                 throw new ArgumentOutOfRangeException();
663             return this.Lines[row].Dirty;
664         }
665
666         internal ITextLayout GetLayout(int row)
667         {
668             if (this.Lines[row].Layout != null && this.Lines[row].Layout.Invaild)
669             {
670                 this.Lines[row].Layout.Dispose();
671                 this.Lines[row].Layout = null;
672             }
673             if (this.Lines[row].Layout == null || this.Lines[row].Layout.Disposed)
674                 this.Lines[row].Layout = this.CreateLayout(row);
675             return this.Lines[row].Layout;
676         }
677
678         internal event EventHandler<CreateLayoutEventArgs> CreateingLayout;
679
680         ITextLayout CreateLayout(int row)
681         {
682             ITextLayout layout;
683             LineToIndexTableData lineData = this.Lines[row];
684             if (lineData.Length == 0)
685             {
686                 layout = this.render.CreateLaytout("", null, null, null,this.WrapWidth);
687             }
688             else
689             {
690                 int lineHeadIndex = this.GetLineHeadIndex(row);
691
692                 string content = this.Document.ToString(lineHeadIndex, lineData.Length);
693
694                 if (this.CreateingLayout != null)
695                     this.CreateingLayout(this, new CreateLayoutEventArgs(lineHeadIndex, lineData.Length,content));
696
697                 var userMarkerRange = from id in this.Document.Markers.IDs
698                                   from s in this.Document.Markers.Get(id,lineHeadIndex,lineData.Length)
699                                   let n = Util.ConvertAbsIndexToRelIndex(s, lineHeadIndex, lineData.Length)
700                                   select n;
701                 var watchdogMarkerRange = from s in this.Document.MarkerPatternSet.GetMarkers(new CreateLayoutEventArgs(lineHeadIndex, lineData.Length, content))
702                                           let n = Util.ConvertAbsIndexToRelIndex(s, lineHeadIndex, lineData.Length)
703                                           select n;
704                 var markerRange = watchdogMarkerRange.Concat(userMarkerRange);
705                 var selectRange = from s in this.Document.Selections.Get(lineHeadIndex, lineData.Length)
706                                   let n = Util.ConvertAbsIndexToRelIndex(s, lineHeadIndex, lineData.Length)
707                                   select n;
708
709                 layout = this.render.CreateLaytout(content, lineData.Syntax, markerRange, selectRange,this.WrapWidth);
710             }
711
712             return layout;
713         }
714
715         int lastLineNumber;
716         /// <summary>
717         /// インデックスを行番号に変換します
718         /// </summary>
719         /// <param name="index">インデックス</param>
720         /// <returns>行番号を返します</returns>
721         public int GetLineNumberFromIndex(int index)
722         {
723             if (index < 0)
724                 throw new ArgumentOutOfRangeException("indexに負の値を設定することはできません");
725
726             if (index == 0 && this.Lines.Count > 0)
727                 return 0;
728
729             LineToIndexTableData line;
730             int lineHeadIndex;
731
732             if (lastLineNumber < this.Lines.Count - 1)
733             {
734                 line = this.Lines[lastLineNumber];
735                 lineHeadIndex = this.GetLineHeadIndex(lastLineNumber);
736                 if (index >= lineHeadIndex && index < lineHeadIndex + line.Length)
737                     return lastLineNumber;
738             }
739
740             int left = 0, right = this.Lines.Count - 1, mid;
741             while (left <= right)
742             {
743                 mid = (left + right) / 2;
744                 line = this.Lines[mid];
745                 lineHeadIndex = this.GetLineHeadIndex(mid);
746                 if (index >= lineHeadIndex && index < lineHeadIndex + line.Length)
747                 {
748                     lastLineNumber = mid;
749                     return mid;
750                 }
751                 if (index < lineHeadIndex)
752                 {
753                     right = mid - 1;
754                 }
755                 else
756                 {
757                     left = mid + 1;
758                 }
759             }
760
761             int lastRow = this.Lines.Count - 1;
762             line = this.Lines[lastRow];
763             lineHeadIndex = this.GetLineHeadIndex(lastRow);
764             if (index >= lineHeadIndex && index <= lineHeadIndex + line.Length)   //最終行長+1までキャレットが移動する可能性があるので
765             {
766                 lastLineNumber = this.Lines.Count - 1;
767                 return lastLineNumber;
768             }
769
770             throw new ArgumentOutOfRangeException("該当する行が見つかりませんでした");
771         }
772
773         /// <summary>
774         /// インデックスからテキストポイントに変換します
775         /// </summary>
776         /// <param name="index">インデックス</param>
777         /// <returns>TextPoint構造体を返します</returns>
778         public TextPoint GetTextPointFromIndex(int index)
779         {
780             TextPoint tp = new TextPoint();
781             tp.row = GetLineNumberFromIndex(index);
782             tp.col = index - this.GetLineHeadIndex(tp.row);
783             Debug.Assert(tp.row < this.Lines.Count && tp.col <= this.Lines[tp.row].Length);
784             return tp;
785         }
786
787         /// <summary>
788         /// テキストポイントからインデックスに変換します
789         /// </summary>
790         /// <param name="tp">TextPoint構造体</param>
791         /// <returns>インデックスを返します</returns>
792         public int GetIndexFromTextPoint(TextPoint tp)
793         {
794             if (tp == TextPoint.Null)
795                 throw new ArgumentNullException("TextPoint.Null以外の値でなければなりません");
796             if(tp.row < 0 || tp.row > this.Lines.Count)
797                 throw new ArgumentOutOfRangeException("tp.rowが設定できる範囲を超えています");
798             if (tp.col < 0 || tp.col > this.Lines[tp.row].Length)
799                 throw new ArgumentOutOfRangeException("tp.colが設定できる範囲を超えています");
800             return this.GetLineHeadIndex(tp.row) + tp.col;
801         }
802
803         /// <summary>
804         /// フォールディングを再生成します
805         /// </summary>
806         /// <param name="force">ドキュメントが更新されていなくても再生成する</param>
807         /// <returns>生成された場合は真を返す</returns>
808         /// <remarks>デフォルトではドキュメントが更新されている時にだけ再生成されます</remarks>
809         public bool GenerateFolding(bool force = false)
810         {
811             return this._generators[FOLDING_INDEX].Generate(this.Document, this, force);
812         }
813
814         /// <summary>
815         /// フォールディングをすべて削除します
816         /// </summary>
817         public void ClearFolding()
818         {
819             this._generators[FOLDING_INDEX].Clear(this);
820         }
821
822         /// <summary>
823         /// すべての行に対しシンタックスハイライトを行います
824         /// </summary>
825         public bool HilightAll(bool force = false)
826         {
827             return this._generators[SYNTAX_HIGLITHER_INDEX].Generate(this.Document, this, force);
828         }
829
830         /// <summary>
831         /// ハイライト関連の情報をすべて削除します
832         /// </summary>
833         public void ClearHilight()
834         {
835             this._generators[SYNTAX_HIGLITHER_INDEX].Clear(this);
836         }
837
838         /// <summary>
839         /// すべて削除します
840         /// </summary>
841         internal void Clear()
842         {
843             this.ClearLayoutCache();
844             this.ClearFolding();
845             this.Lines.Clear();
846             LineToIndexTableData dummy = new LineToIndexTableData();
847             this.Lines.Add(dummy);
848             this.stepRow = STEP_ROW_IS_NONE;
849             this.stepLength = 0;
850             Debug.WriteLine("Clear");
851         }
852
853         #region IEnumerable<string> メンバー
854
855         /// <summary>
856         /// コレクションを反復処理するためのIEnumeratorを返す
857         /// </summary>
858         /// <returns>IEnumeratorオブジェクト</returns>
859         public IEnumerator<string> GetEnumerator()
860         {
861             for (int i = 0; i < this.Lines.Count; i++)
862                 yield return this[i];
863         }
864
865         #endregion
866
867         #region IEnumerable メンバー
868
869         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
870         {
871             for (int i = 0; i < this.Lines.Count; i++)
872                 yield return this[i];
873         }
874
875         #endregion
876     }
877
878 }