OSDN Git Service

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