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