OSDN Git Service

OnRenderChangedメソッドを呼び出すようにした
[fooeditengine/FooEditEngine.git] / Core / Document.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
12 using System;
13 using System.IO;
14 using System.ComponentModel;
15 using System.Collections.Generic;
16 using System.Text;
17 using System.Text.RegularExpressions;
18 using System.Threading;
19 using System.Threading.Tasks;
20 using System.Linq;
21 using System.Runtime.CompilerServices;
22
23 namespace FooEditEngine
24 {
25     /// <summary>
26     /// オートインデントを行うためのデリゲートを表す
27     /// </summary>
28     /// <param name="sender">イベント発生元のオブジェクト</param>
29     /// <param name="e">イベントデーター</param>
30     public delegate void AutoIndentHookerHandler(object sender, EventArgs e);
31
32     /// <summary>
33     /// 進行状況を表す列挙体
34     /// </summary>
35     public enum ProgressState
36     {
37         /// <summary>
38         /// 操作が開始したことを表す
39         /// </summary>
40         Start,
41         /// <summary>
42         /// 操作が終了したことを表す
43         /// </summary>
44         Complete,
45     }
46     /// <summary>
47     /// 進行状況を表すためのイベントデータ
48     /// </summary>
49     public sealed class ProgressEventArgs : EventArgs
50     {
51         /// <summary>
52         /// 進行状況
53         /// </summary>
54         public ProgressState state;
55         /// <summary>
56         /// コンストラクター
57         /// </summary>
58         /// <param name="state">ProgressStateオブジェクト</param>
59         public ProgressEventArgs(ProgressState state)
60         {
61             this.state = state;
62         }
63     }
64
65     /// <summary>
66     /// 進行状況を通知するためのデリゲート
67     /// </summary>
68     /// <param name="sender">送信元クラス</param>
69     /// <param name="e">イベントデータ</param>
70     public delegate void ProgressEventHandler(object sender, ProgressEventArgs e);
71
72     /// <summary>
73     /// 更新タイプを表す列挙体
74     /// </summary>
75     public enum UpdateType
76     {
77         /// <summary>
78         /// ドキュメントが置き換えられたことを表す
79         /// </summary>
80         Replace,
81         /// <summary>
82         /// ドキュメント全体が削除されたことを表す
83         /// </summary>
84         Clear,
85     }
86
87     /// <summary>
88     /// 更新タイプを通知するためのイベントデータ
89     /// </summary>
90     public sealed class DocumentUpdateEventArgs : EventArgs
91     {
92         /// <summary>
93         /// 値が指定されていないことを示す
94         /// </summary>
95         public const int EmptyValue = -1;
96         /// <summary>
97         /// 更新タイプ
98         /// </summary>
99         public UpdateType type;
100         /// <summary>
101         /// 開始位置
102         /// </summary>
103         public int startIndex;
104         /// <summary>
105         /// 削除された長さ
106         /// </summary>
107         public int removeLength;
108         /// <summary>
109         /// 追加された長さ
110         /// </summary>
111         public int insertLength;
112         /// <summary>
113         /// 更新イベントが発生した行。行が不明な場合や行をまたぐ場合はnullを指定すること。
114         /// </summary>
115         public int? row;
116         /// <summary>
117         /// コンストラクター
118         /// </summary>
119         /// <param name="type">更新タイプ</param>
120         /// <param name="startIndex">開始インデックス</param>
121         /// <param name="removeLength">削除された長さ</param>
122         /// <param name="insertLength">追加された長さ</param>
123         /// <param name="row">開始行。nullを指定することができる</param>
124         public DocumentUpdateEventArgs(UpdateType type, int startIndex = EmptyValue, int removeLength = EmptyValue, int insertLength = EmptyValue, int? row = null)
125         {
126             this.type = type;
127             this.startIndex = startIndex;
128             this.removeLength = removeLength;
129             this.insertLength = insertLength;
130             this.row = row;
131         }
132     }
133
134     /// <summary>
135     /// ドキュメントに更新があったことを伝えるためのデリゲート
136     /// </summary>
137     /// <param name="sender">送信元クラス</param>
138     /// <param name="e">イベントデータ</param>
139     public delegate void DocumentUpdateEventHandler(object sender, DocumentUpdateEventArgs e);
140
141     /// <summary>
142     /// ドキュメントの管理を行う
143     /// </summary>
144     /// <remarks>この型のすべてのメソッド・プロパティはスレッドセーフです</remarks>
145     public sealed class Document : IEnumerable<char>, IRandomEnumrator<char>, IDisposable
146     {
147         Regex regex;
148         Match match;
149         StringBuffer buffer;
150         LineToIndexTable _LayoutLines;
151         bool _EnableFireUpdateEvent = true,_UrlMark = false, _DrawLineNumber = false, _HideRuler = true, _RightToLeft = false;
152         LineBreakMethod _LineBreak;
153         int _TabStops, _LineBreakCharCount = 80;
154         bool _ShowFullSpace, _ShowHalfSpace, _ShowTab, _ShowLineBreak,_InsertMode, _HideCaret, _HideLineMarker, _RectSelection;
155         IndentMode _IndentMode;
156
157         /// <summary>
158         /// 一行当たりの最大文字数
159         /// </summary>
160         public const int MaximumLineLength = 1000;
161
162         /// <summary>
163         /// コンストラクター
164         /// </summary>
165         public Document()
166             : this(null)
167         {
168         }
169
170         /// <summary>
171         /// コンストラクター
172         /// </summary>
173         /// <param name="doc">ドキュメントオブジェクト</param>
174         /// <remarks>docが複製されますが、プロパティは引き継がれません</remarks>
175         public Document(Document doc)
176         {
177             if (doc == null)
178                 this.buffer = new StringBuffer();
179             else
180                 this.buffer = new StringBuffer(doc.buffer);
181             this.buffer.Update = new DocumentUpdateEventHandler(buffer_Update);
182             this.Update += new DocumentUpdateEventHandler((s, e) => { });
183             this.ChangeFireUpdateEvent += new EventHandler((s, e) => { });
184             this.StatusUpdate += new EventHandler((s, e) => { });
185             this.Markers = new MarkerCollection();
186             this.UndoManager = new UndoManager();
187             this._LayoutLines = new LineToIndexTable(this);
188             this._LayoutLines.SpilitString = (s,e)=> {
189                 return this.CreateLineList(e.index, e.length, MaximumLineLength);
190             };
191             this._LayoutLines.Clear();
192             this.MarkerPatternSet = new MarkerPatternSet(this._LayoutLines, this.Markers);
193             this.MarkerPatternSet.Updated += WacthDogPattern_Updated;
194             this.Selections = new SelectCollection();
195             this.CaretPostion = new TextPoint();
196             this.HideLineMarker = true;
197             this.SelectGrippers = new GripperRectangle(new Gripper(), new Gripper());
198             this.SelectionChanged += new EventHandler((s, e) => { });
199             this.CaretChanged += (s, e) => { };
200             this.AutoIndentHook += (s, e) => { };
201             this.LineBreakChanged += (s, e) => { };
202             this.Dirty = false;
203         }
204
205         void WacthDogPattern_Updated(object sender, EventArgs e)
206         {
207             this._LayoutLines.ClearLayoutCache();
208         }
209
210         /// <summary>
211         /// ダーティフラグ。保存されていなければ真、そうでなければ偽。
212         /// </summary>
213         public bool Dirty
214         {
215             get;
216             set;
217         }
218
219         /// <summary>
220         /// キャレットでの選択の起点となる位置
221         /// </summary>
222         internal int AnchorIndex
223         {
224             get;
225             set;
226         }
227
228         /// <summary>
229         /// レタリングの開始位置を表す
230         /// </summary>
231         internal SrcPoint Src
232         {
233             get;
234             set;
235         }
236
237         /// <summary>
238         /// ドキュメントのタイトル
239         /// </summary>
240         public string Title
241         {
242             get;
243             set;
244         }
245
246         /// <summary>
247         /// 補完候補プロセッサーが切り替わったときに発生するイベント
248         /// </summary>
249         public event EventHandler AutoCompleteChanged;
250
251         AutoCompleteBoxBase _AutoComplete;
252         /// <summary>
253         /// 補完候補プロセッサー
254         /// </summary>
255         public AutoCompleteBoxBase AutoComplete
256         {
257             get
258             {
259                 return this._AutoComplete;
260             }
261             set
262             {
263                 this._AutoComplete = value;
264                 if (this.AutoCompleteChanged != null)
265                     this.AutoCompleteChanged(this, null);
266             }
267         }
268
269         /// <summary>
270         /// 読み込み中に発生するイベント
271         /// </summary>
272         public event ProgressEventHandler LoadProgress;
273
274         /// <summary>
275         /// ルーラーやキャレット・行番号などの表示すべきものが変化した場合に呼び出される。ドキュメントの内容が変化した通知を受け取り場合はUpdateを使用してください
276         /// </summary>
277         public event EventHandler StatusUpdate;
278
279         /// <summary>
280         /// 全角スペースを表示するかどうか
281         /// </summary>
282         public bool ShowFullSpace
283         {
284             get { return this._ShowFullSpace; }
285             set
286             {
287                 if (this._ShowFullSpace == value)
288                     return;
289                 this._ShowFullSpace = value;
290                 this.StatusUpdate(this, null);
291             }
292         }
293
294         /// <summary>
295         /// 半角スペースを表示するかどうか
296         /// </summary>
297         public bool ShowHalfSpace
298         {
299             get { return this._ShowHalfSpace; }
300             set
301             {
302                 if (this._ShowHalfSpace == value)
303                     return;
304                 this._ShowHalfSpace = value;
305                 this.StatusUpdate(this, null);
306             }
307         }
308
309         /// <summary>
310         /// TABを表示するかどうか
311         /// </summary>
312         public bool ShowTab
313         {
314             get { return this._ShowTab; }
315             set
316             {
317                 if (this._ShowTab == value)
318                     return;
319                 this._ShowTab = value;
320                 this.StatusUpdate(this, null);
321             }
322         }
323
324         /// <summary>
325         /// 改行を表示するかどうか
326         /// </summary>
327         public bool ShowLineBreak
328         {
329             get { return this._ShowLineBreak; }
330             set
331             {
332                 if (this._ShowLineBreak == value)
333                     return;
334                 this._ShowLineBreak = value;
335                 this.StatusUpdate(this, null);
336             }
337         }
338
339         /// <summary>
340         /// 選択範囲にあるグリッパーのリスト
341         /// </summary>
342         internal GripperRectangle SelectGrippers
343         {
344             private set;
345             get;
346         }
347
348         /// <summary>
349         /// 右から左に表示するなら真
350         /// </summary>
351         public bool RightToLeft {
352             get { return this._RightToLeft; }
353             set
354             {
355                 if (this._RightToLeft == value)
356                     return;
357                 this._RightToLeft = value;
358                 this.StatusUpdate(this, null);
359             }
360         }
361
362         /// <summary>
363         /// 矩形選択モードなら真を返し、そうでない場合は偽を返す
364         /// </summary>
365         public bool RectSelection
366         {
367             get
368             {
369                 return this._RectSelection;
370             }
371             set
372             {
373                 this._RectSelection = value;
374                 this.StatusUpdate(this, null);
375             }
376         }
377
378         /// <summary>
379         /// インデントの方法を表す
380         /// </summary>
381         public IndentMode IndentMode
382         {
383             get
384             {
385                 return this._IndentMode;
386             }
387             set
388             {
389                 this._IndentMode = value;
390                 this.StatusUpdate(this, null);
391             }
392         }
393
394         /// <summary>
395         /// ラインマーカーを描くなら偽。そうでなければ真
396         /// </summary>
397         public bool HideLineMarker
398         {
399             get
400             {
401                 return this._HideLineMarker;
402             }
403             set
404             {
405                 this._HideLineMarker = value;
406                 this.StatusUpdate(this, null);
407             }
408         }
409
410         /// <summary>
411         /// キャレットを描くなら偽。そうでなければ真
412         /// </summary>
413         public bool HideCaret
414         {
415             get
416             {
417                 return this._HideCaret;
418             }
419             set
420             {
421                 this._HideCaret = value;
422                 this.StatusUpdate(this, null);
423             }
424         }
425
426         /// <summary>
427         /// 挿入モードなら真を返し、上書きモードなら偽を返す
428         /// </summary>
429         public bool InsertMode
430         {
431             get
432             {
433                 return this._InsertMode;
434             }
435             set
436             {
437                 this._InsertMode = value;
438                 this.StatusUpdate(this, null);
439             }
440         }
441
442         /// <summary>
443         /// ルーラーを表示しないなら真、そうでないなら偽
444         /// </summary>
445         public bool HideRuler
446         {
447             get { return this._HideRuler; }
448             set
449             {
450                 if (this._HideRuler == value)
451                     return;
452                 this._HideRuler = value;
453                 this.LayoutLines.ClearLayoutCache();
454                 this.StatusUpdate(this, null);
455             }
456         }
457
458         TextPoint _CaretPostion;
459         /// <summary>
460         /// レイアウト行のどこにキャレットがあるかを表す
461         /// </summary>
462         public TextPoint CaretPostion
463         {
464             get
465             {
466                 return this._CaretPostion;
467             }
468             set
469             {
470                 if(this._CaretPostion != value)
471                 {
472                     this._CaretPostion = value;
473                     this.CaretChanged(this, null);
474                 }
475             }
476         }
477
478         internal void SetCaretPostionWithoutEvent(TextPoint value)
479         {
480             if (this._CaretPostion != value)
481                 this._CaretPostion = value;
482         }
483
484         /// <summary>
485         /// 選択範囲コレクション
486         /// </summary>
487         internal SelectCollection Selections
488         {
489             get;
490             set;
491         }
492
493         /// <summary>
494         /// 行番号を表示するかどうか
495         /// </summary>
496         public bool DrawLineNumber
497         {
498             get { return this._DrawLineNumber; }
499             set
500             {
501                 if (this._DrawLineNumber == value)
502                     return;
503                 this._DrawLineNumber = value;
504                 this._LayoutLines.ClearLayoutCache();
505                 this.StatusUpdate(this, null);
506             }
507         }
508
509         /// <summary>
510         /// URLをハイパーリンクとして表示するなら真。そうでないなら偽
511         /// </summary>
512         public bool UrlMark
513         {
514             get { return this._UrlMark; }
515             set
516             {
517                 if (this._UrlMark == value)
518                     return;
519                 this._UrlMark = value;
520                 if (value)
521                 {
522                     Regex regex = new Regex("(http|https|ftp)(:\\/\\/[-_.!~*\\'()a-zA-Z0-9;\\/?:\\@&=+\\$,%#]+)");
523                     this.MarkerPatternSet.Add(MarkerIDs.URL, new RegexMarkerPattern(regex, HilightType.Url, new Color()));
524                 }
525                 else
526                 {
527                     this.MarkerPatternSet.Remove(MarkerIDs.URL);
528                 }
529                 this.StatusUpdate(this, null);
530             }
531         }
532
533         /// <summary>
534         /// 桁折りの方法が変わったことを表す
535         /// </summary>
536         public event EventHandler LineBreakChanged;
537
538         /// <summary>
539         /// 桁折り処理の方法を指定する
540         /// </summary>
541         /// <remarks>
542         /// 変更した場合、呼び出し側で再描写とレイアウトの再構築を行う必要があります
543         /// また、StatusUpdatedではなく、LineBreakChangedイベントが発生します
544         /// </remarks>
545         public LineBreakMethod LineBreak
546         {
547             get
548             {
549                 return this._LineBreak;
550             }
551             set
552             {
553                 if (this._LineBreak == value)
554                     return;
555                 this._LineBreak = value;
556                 this.LineBreakChanged(this, null);
557             }
558         }
559
560         /// <summary>
561         /// 折り返し行う文字数。実際に折り返しが行われる幅はem単位×この値となります
562         /// </summary>
563         /// <remarks>この値を変えた場合、LineBreakChangedイベントが発生します</remarks>
564         public int LineBreakCharCount
565         {
566             get
567             {
568                 return this._LineBreakCharCount;
569             }
570             set
571             {
572                 if (this._LineBreakCharCount == value)
573                     return;
574                 this._LineBreakCharCount = value;
575                 this.LineBreakChanged(this, null);
576             }
577         }
578
579         /// <summary>
580         /// タブの幅
581         /// </summary>
582         /// <remarks>変更した場合、呼び出し側で再描写する必要があります</remarks>
583         public int TabStops
584         {
585             get { return this._TabStops; }
586             set {
587                 if (this._TabStops == value)
588                     return;
589                 this._TabStops = value;
590                 this.StatusUpdate(this, null);
591             }
592         }
593
594         /// <summary>
595         /// マーカーパターンセット
596         /// </summary>
597         public MarkerPatternSet MarkerPatternSet
598         {
599             get;
600             private set;
601         }
602
603         /// <summary>
604         /// レイアウト行を表す
605         /// </summary>
606         public LineToIndexTable LayoutLines
607         {
608             get
609             {
610                 return this._LayoutLines;
611             }
612         }
613
614         /// <summary>
615         /// レイアウト行を返す
616         /// </summary>
617         /// <param name="index">開始インデックス</param>
618         /// <param name="length">長さ</param>
619         /// <param name="lineLimitLength">1行当たりの最大文字数。-1で無制限</param>
620         /// <returns>レイアウト行リスト</returns>
621         internal IList<LineToIndexTableData> CreateLineList(int index, int length, int lineLimitLength = -1)
622         {
623             int startIndex = index;
624             int endIndex = index + length - 1;
625             List<LineToIndexTableData> output = new List<LineToIndexTableData>();
626
627             foreach (Tuple<int, int> range in this.ForEachLines(startIndex, endIndex, lineLimitLength))
628             {
629                 int lineHeadIndex = range.Item1;
630                 int lineLength = range.Item2;
631                 char c = this.buffer[lineHeadIndex + lineLength - 1];
632                 bool hasNewLine = c == Document.NewLine;
633                 output.Add(this.LayoutLines.CreateLineToIndexTableData(lineHeadIndex, lineLength, hasNewLine, null));
634             }
635
636             if (output.Count > 0)
637                 output.Last().LineEnd = true;
638
639             return output;
640         }
641
642         internal void FireUpdate(DocumentUpdateEventArgs e)
643         {
644             this.buffer_Update(this.buffer, e);
645         }
646
647         /// <summary>
648         /// ドキュメントが更新された時に呼ばれるイベント
649         /// </summary>
650         public event DocumentUpdateEventHandler Update;
651
652         /// <summary>
653         /// FireUpdateEventの値が変わったときに呼び出されるイベント
654         /// </summary>
655         public event EventHandler ChangeFireUpdateEvent;
656
657         /// <summary>
658         /// 改行コードの内部表現
659         /// </summary>
660         public const char NewLine = '\n';
661
662         /// <summary>
663         /// EOFの内部表現
664         /// </summary>
665         public const char EndOfFile = '\u001a';
666
667         /// <summary>
668         /// アンドゥ管理クラスを表す
669         /// </summary>
670         public UndoManager UndoManager
671         {
672             get;
673             private set;
674         }
675
676         /// <summary>
677         /// 文字列の長さ
678         /// </summary>
679         public int Length
680         {
681             get
682             {
683                 return this.buffer.Length;
684             }
685         }
686
687         /// <summary>
688         /// 変更のたびにUpdateイベントを発生させるかどうか
689         /// </summary>
690         public bool FireUpdateEvent
691         {
692             get
693             {
694                 return this._EnableFireUpdateEvent;
695             }
696             set
697             {
698                 this._EnableFireUpdateEvent = value;
699                 this.ChangeFireUpdateEvent(this, null);
700             }
701         }
702
703         /// <summary>
704         /// インデクサー
705         /// </summary>
706         /// <param name="i">インデックス(自然数でなければならない)</param>
707         /// <returns>Char型</returns>
708         public char this[int i]
709         {
710             get
711             {
712                 return this.buffer[i];
713             }
714         }
715
716         /// <summary>
717         /// マーカーコレクション
718         /// </summary>
719         public MarkerCollection Markers
720         {
721             get;
722             private set;
723         }
724
725         internal StringBuffer StringBuffer
726         {
727             get
728             {
729                 return this.buffer;
730             }
731         }
732
733         /// <summary>
734         /// 再描写を要求しているなら真
735         /// </summary>
736         public bool IsRequestRedraw { get; internal set; }
737
738         /// <summary>
739         /// 再描写を要求する
740         /// </summary>
741         public void RequestRedraw()
742         {
743             this.IsRequestRedraw = true;
744         }
745
746         /// <summary>
747         /// レイアウト行が再構成されたときに発生するイベント
748         /// </summary>
749         public event EventHandler PerformLayouted;
750         /// <summary>
751         /// レイアウト行をすべて破棄し、再度レイアウトを行う
752         /// </summary>
753         public void PerformLayout()
754         {
755             //単に再構築するだけなので行ダーティフラグは更新してはいけない
756             this.LayoutLines.IsFrozneDirtyFlag = true;
757             this.FireUpdate(new DocumentUpdateEventArgs(UpdateType.Clear, -1, -1, -1));
758             this.FireUpdate(new DocumentUpdateEventArgs(UpdateType.Replace, 0, 0, this.Length));
759             this.LayoutLines.IsFrozneDirtyFlag = false;
760             if (this.PerformLayouted != null)
761                 this.PerformLayouted(this, null);
762         }
763
764         /// <summary>
765         /// オードインデントが可能になった時に通知される
766         /// </summary>
767         /// <remarks>
768         /// FireUpdateEventの影響を受けます
769         /// </remarks>
770         public event AutoIndentHookerHandler AutoIndentHook;
771
772         /// <summary>
773         /// 選択領域変更時に通知される
774         /// </summary>
775         public event EventHandler SelectionChanged;
776
777         /// <summary>
778         /// キャレット移動時に通知される
779         /// </summary>
780         public event EventHandler CaretChanged;
781
782         /// <summary>
783         /// 指定された範囲を選択する
784         /// </summary>
785         /// <param name="start"></param>
786         /// <param name="length"></param>
787         /// <remarks>RectSelectionの値によって動作が変わります。真の場合は矩形選択モードに、そうでない場合は行ごとに選択されます</remarks>
788         public void Select(int start, int length)
789         {
790             if (this.FireUpdateEvent == false)
791                 throw new InvalidOperationException("");
792             if (start < 0 || start + length < 0 || start + length > this.Length)
793                 throw new ArgumentOutOfRangeException("startかendが指定できる範囲を超えてます");
794             //選択範囲が消されたとき
795             foreach (Selection sel in this.Selections)
796                 this.LayoutLines.ClearLayoutCache(sel.start, sel.length);
797             this.Selections.Clear();
798             if (length < 0)
799             {
800                 int oldStart = start;
801                 start += length;
802                 length = oldStart - start;
803             }
804             if (this.RectSelection && length != 0)
805             {
806                 TextPoint startTextPoint = this.LayoutLines.GetTextPointFromIndex(start);
807                 TextPoint endTextPoint = this.LayoutLines.GetTextPointFromIndex(start + length);
808                 this.SelectByRectangle(new TextRectangle(startTextPoint, endTextPoint));
809                 this.LayoutLines.ClearLayoutCache(start, length);
810             }
811             else if (length != 0)
812             {
813                 this.Selections.Add(Selection.Create(start, length));
814                 this.LayoutLines.ClearLayoutCache(start, length);
815             }
816             this.SelectionChanged(this, null);
817         }
818
819         /// <summary>
820         /// 矩形選択を行う
821         /// </summary>
822         /// <param name="tp">開始位置</param>
823         /// <param name="width">桁数</param>
824         /// <param name="height">行数</param>
825         public void Select(TextPoint tp, int width, int height)
826         {
827             if (this.FireUpdateEvent == false || !this.RectSelection)
828                 throw new InvalidOperationException("");
829             TextPoint end = tp;
830
831             end.row = tp.row + height;
832             end.col = tp.col + width;
833
834             if (end.row > this.LayoutLines.Count - 1)
835                 throw new ArgumentOutOfRangeException("");
836
837             this.Selections.Clear();
838
839             this.SelectByRectangle(new TextRectangle(tp, end));
840
841             this.SelectionChanged(this, null);
842         }
843
844         private void SelectByRectangle(TextRectangle rect)
845         {
846             if (this.FireUpdateEvent == false)
847                 throw new InvalidOperationException("");
848             if (rect.TopLeft <= rect.BottomRight)
849             {
850                 for (int i = rect.TopLeft.row; i <= rect.BottomLeft.row; i++)
851                 {
852                     int length = this.LayoutLines.GetLengthFromLineNumber(i);
853                     int leftCol = rect.TopLeft.col, rightCol = rect.TopRight.col, lastCol = length;
854                     if (length > 0 && this.LayoutLines[i][length - 1] == Document.NewLine)
855                         lastCol = length - 1;
856                     if (lastCol < 0)
857                         lastCol = 0;
858                     if (rect.TopLeft.col > lastCol)
859                         leftCol = lastCol;
860                     if (rect.TopRight.col > lastCol)
861                         rightCol = lastCol;
862
863                     int StartIndex = this.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, leftCol));
864                     int EndIndex = this.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, rightCol));
865
866                     Selection sel;
867                     sel = Selection.Create(StartIndex, EndIndex - StartIndex);
868
869                     this.Selections.Add(sel);
870                 }
871             }
872         }
873
874         /// <summary>
875         /// 単語単位で選択する
876         /// </summary>
877         /// <param name="index">探索を開始するインデックス</param>
878         /// <param name="changeAnchor">選択の起点となるとインデックスを変更するなら真。そうでなければ偽</param>
879         public void SelectWord(int index, bool changeAnchor = false)
880         {
881             this.SelectSepartor(index, (c) => Util.IsWordSeparator(c), changeAnchor);
882         }
883
884         /// <summary>
885         /// 行単位で選択する
886         /// </summary>
887         /// <param name="index">探索を開始するインデックス</param>
888         /// <param name="changeAnchor">選択の起点となるとインデックスを変更するなら真。そうでなければ偽</param>
889         public void SelectLine(int index,bool changeAnchor = false)
890         {
891             this.SelectSepartor(index, (c) => c == Document.NewLine, changeAnchor);
892         }
893
894         /// <summary>
895         /// セパレーターで囲まれた範囲内を選択する
896         /// </summary>
897         /// <param name="index">探索を開始するインデックス</param>
898         /// <param name="find_sep_func">セパレーターなら真を返し、そうでないなら偽を返す</param>
899         /// <param name="changeAnchor">選択の起点となるとインデックスを変更するなら真。そうでなければ偽</param>
900         public void SelectSepartor(int index,Func<char,bool> find_sep_func, bool changeAnchor = false)
901         {
902             if (this.FireUpdateEvent == false)
903                 throw new InvalidOperationException("");
904
905             if (find_sep_func == null)
906                 throw new ArgumentNullException("find_sep_func must not be null");
907
908             if (this.Length <= 0 || index >= this.Length)
909                 return;
910
911             Document str = this;
912
913             int start = index;
914             while (start > 0 && !find_sep_func(str[start]))
915                 start--;
916
917             if (find_sep_func(str[start]))
918                 start++;
919
920             int end = index;
921             while (end < this.Length && !find_sep_func(str[end]))
922                 end++;
923
924             this.Select(start, end - start);
925
926             if (changeAnchor)
927                 this.AnchorIndex = start;
928         }
929
930         /// <summary>
931         /// DocumentReaderを作成します
932         /// </summary>
933         /// <returns>DocumentReaderオブジェクト</returns>
934         public DocumentReader CreateReader()
935         {
936             return new DocumentReader(this.buffer);
937         }
938
939         /// <summary>
940         /// マーカーを設定する
941         /// </summary>
942         /// <param name="id">マーカーID</param>
943         /// <param name="m">設定したいマーカー</param>
944         public void SetMarker(int id,Marker m)
945         {
946             if (m.start < 0 || m.start + m.length > this.Length)
947                 throw new ArgumentOutOfRangeException("startもしくはendが指定できる範囲を超えています");
948
949             this.Markers.Add(id,m);
950         }
951
952         /// <summary>
953         /// マーカーを削除する
954         /// </summary>
955         /// <param name="id">マーカーID</param>
956         /// <param name="start">開始インデックス</param>
957         /// <param name="length">削除する長さ</param>
958         public void RemoveMarker(int id,int start, int length)
959         {
960             if (start < 0 || start + length > this.Length)
961                 throw new ArgumentOutOfRangeException("startもしくはendが指定できる範囲を超えています");
962
963             this.Markers.RemoveAll(id,start, length);
964         }
965
966         /// <summary>
967         /// マーカーを削除する
968         /// </summary>
969         /// <param name="id">マーカーID</param>
970         /// <param name="type">削除したいマーカーのタイプ</param>
971         public void RemoveMarker(int id, HilightType type)
972         {
973             this.Markers.RemoveAll(id,type);
974         }
975
976         /// <summary>
977         /// すべてのマーカーを削除する
978         /// </summary>
979         /// <param name="id">マーカーID</param>
980         public void RemoveAllMarker(int id)
981         {
982             this.Markers.RemoveAll(id);
983         }
984
985         /// <summary>
986         /// インデックスに対応するマーカーを得る
987         /// </summary>
988         /// <param name="id">マーカーID</param>
989         /// <param name="index">インデックス</param>
990         /// <returns>Marker構造体の列挙子</returns>
991         public IEnumerable<Marker> GetMarkers(int id, int index)
992         {
993             if (index < 0 || index > this.Length)
994                 throw new ArgumentOutOfRangeException("indexが範囲を超えています");
995             return this.Markers.Get(id,index);
996         }
997
998         /// <summary>
999         /// 部分文字列を取得する
1000         /// </summary>
1001         /// <param name="index">開始インデックス</param>
1002         /// <param name="length">長さ</param>
1003         /// <returns>Stringオブジェクト</returns>
1004         public string ToString(int index, int length)
1005         {
1006             return this.buffer.ToString(index, length);
1007         }
1008
1009         /// <summary>
1010         /// インデックスを開始位置とする文字列を返す
1011         /// </summary>
1012         /// <param name="index">開始インデックス</param>
1013         /// <returns>Stringオブジェクト</returns>
1014         public string ToString(int index)
1015         {
1016             return this.ToString(index, this.buffer.Length - index);
1017         }
1018
1019         /// <summary>
1020         /// 行を取得する
1021         /// </summary>
1022         /// <param name="startIndex">開始インデックス</param>
1023         /// <param name="endIndex">終了インデックス</param>
1024         /// <param name="maxCharCount">最大長</param>
1025         /// <returns>行イテレーターが返される</returns>
1026         public IEnumerable<string> GetLines(int startIndex, int endIndex, int maxCharCount = -1)
1027         {
1028             return this.buffer.GetLines(startIndex, endIndex, maxCharCount);
1029         }
1030
1031         internal IEnumerable<Tuple<int, int>> ForEachLines(int startIndex, int endIndex, int maxCharCount = -1)
1032         {
1033             return this.buffer.ForEachLines(startIndex, endIndex, maxCharCount);
1034         }
1035
1036
1037         /// <summary>
1038         /// 文字列を追加する
1039         /// </summary>
1040         /// <param name="s">追加したい文字列</param>
1041         /// <remarks>非同期操作中はこのメソッドを実行することはできません</remarks>
1042         public void Append(string s)
1043         {
1044             this.Replace(this.buffer.Length, 0, s);
1045         }
1046
1047         /// <summary>
1048         /// 文字列を挿入する
1049         /// </summary>
1050         /// <param name="index">開始インデックス</param>
1051         /// <param name="s">追加したい文字列</param>
1052         /// <remarks>読み出し操作中はこのメソッドを実行することはできません</remarks>
1053         public void Insert(int index, string s)
1054         {
1055             this.Replace(index, 0, s);
1056         }
1057
1058         /// <summary>
1059         /// 文字列を削除する
1060         /// </summary>
1061         /// <param name="index">開始インデックス</param>
1062         /// <param name="length">長さ</param>
1063         /// <remarks>読み出し操作中はこのメソッドを実行することはできません</remarks>
1064         public void Remove(int index, int length)
1065         {
1066             this.Replace(index, length, "");
1067         }
1068
1069         /// <summary>
1070         /// ドキュメントを置き換える
1071         /// </summary>
1072         /// <param name="index">開始インデックス</param>
1073         /// <param name="length">長さ</param>
1074         /// <param name="s">文字列</param>
1075         /// <param name="UserInput">ユーザーからの入力として扱うなら真</param>
1076         /// <remarks>読み出し操作中はこのメソッドを実行することはできません</remarks>
1077         public void Replace(int index, int length, string s, bool UserInput = false)
1078         {
1079             if (index < 0 || index > this.buffer.Length || index + length > this.buffer.Length || length < 0)
1080                 throw new ArgumentOutOfRangeException();
1081             if (length == 0 && (s == string.Empty || s == null))
1082                 return;
1083
1084             foreach(int id in this.Markers.IDs)
1085                 this.RemoveMarker(id,index, length);
1086
1087             ReplaceCommand cmd = new ReplaceCommand(this.buffer, index, length, s);
1088             this.UndoManager.push(cmd);
1089             cmd.redo();
1090
1091             if (this.FireUpdateEvent && UserInput)
1092             {
1093                 var input_str = string.Empty;
1094                 if (s == Document.NewLine.ToString())
1095                     input_str = s;
1096                 else if (s == string.Empty && length > 0)
1097                     input_str = "\b";
1098                 //入力は終わっているので空文字を渡すが処理の都合で一部文字だけはそのまま渡す
1099                 if (this.AutoComplete != null)
1100                     this.AutoComplete.ParseInput(input_str);
1101                 if (s == Document.NewLine.ToString())
1102                     this.AutoIndentHook(this, null);
1103             }
1104         }
1105
1106         /// <summary>
1107         /// 物理行をすべて削除する
1108         /// </summary>
1109         /// <remarks>Dirtyフラグも同時にクリアーされます</remarks>
1110         /// <remarks>非同期操作中はこのメソッドを実行することはできません</remarks>
1111         public void Clear()
1112         {
1113             this.buffer.Clear();
1114             this.Dirty = false;
1115         }
1116
1117         /// <summary>
1118         /// ストリームからドキュメントを非同期的に構築します
1119         /// </summary>
1120         /// <param name="fs">IStreamReaderオブジェクト</param>
1121         /// <param name="tokenSource">キャンセルトークン</param>
1122         /// <returns>Taskオブジェクト</returns>
1123         /// <remarks>
1124         /// 読み取り操作は別スレッドで行われます。
1125         /// また、非同期操作中はこのメソッドを実行することはできません。
1126         /// </remarks>
1127         public async Task LoadAsync(TextReader fs, CancellationTokenSource tokenSource = null)
1128         {
1129             if (fs.Peek() == -1)
1130                 return;
1131
1132             if (this.LoadProgress != null)
1133                 this.LoadProgress(this, new ProgressEventArgs(ProgressState.Start));
1134
1135             try
1136             {
1137                 this.Clear();
1138                 await this.buffer.LoadAsync(fs, tokenSource);
1139             }
1140             finally
1141             {
1142                 this.PerformLayout();
1143                 if (this.LoadProgress != null)
1144                     this.LoadProgress(this, new ProgressEventArgs(ProgressState.Complete));
1145             }
1146         }
1147
1148         /// <summary>
1149         /// ストリームに非同期モードで保存します
1150         /// </summary>
1151         /// <param name="fs">IStreamWriterオブジェクト</param>
1152         /// <param name="tokenSource">キャンセルトークン</param>
1153         /// <returns>Taskオブジェクト</returns>
1154         /// <remarks>非同期操作中はこのメソッドを実行することはできません</remarks>
1155         public async Task SaveAsync(TextWriter fs, CancellationTokenSource tokenSource = null)
1156         {
1157             await this.buffer.SaveAsync(fs, tokenSource);
1158         }
1159
1160         /// <summary>
1161         /// Find()およびReplaceAll()で使用するパラメーターをセットします
1162         /// </summary>
1163         /// <param name="pattern">検索したい文字列</param>
1164         /// <param name="UseRegex">正規表現を使用するなら真</param>
1165         /// <param name="opt">RegexOptions列挙体</param>
1166         public void SetFindParam(string pattern, bool UseRegex, RegexOptions opt)
1167         {
1168             this.match = null;
1169             if (UseRegex)
1170                 this.regex = new Regex(pattern, opt);
1171             else
1172                 this.regex = new Regex(Regex.Escape(pattern), opt);
1173         }
1174
1175         /// <summary>
1176         /// 現在の検索パラメーターでWatchDogを生成する
1177         /// </summary>
1178         /// <param name="type">ハイライトタイプ</param>
1179         /// <param name="color">色</param>
1180         /// <returns>WatchDogオブジェクト</returns>
1181         public RegexMarkerPattern CreateWatchDogByFindParam(HilightType type,Color color)
1182         {
1183             if (this.regex == null)
1184                 throw new InvalidOperationException("SetFindParam()を呼び出してください");
1185             return new RegexMarkerPattern(this.regex,type,color);
1186         }
1187
1188         /// <summary>
1189         /// 指定した文字列を検索します
1190         /// </summary>
1191         /// <returns>見つかった場合はSearchResult列挙子を返却します</returns>
1192         /// <remarks>見つかったパターン以外を置き換えた場合、正常に動作しないことがあります</remarks>
1193         public IEnumerator<SearchResult> Find()
1194         {
1195             return this.Find(0, this.Length);
1196         }
1197
1198         /// <summary>
1199         /// 指定した文字列を検索します
1200         /// </summary>
1201         /// <returns>見つかった場合はSearchResult列挙子を返却します</returns>
1202         /// <param name="start">開始インデックス</param>
1203         /// <param name="length">検索する長さ</param>
1204         /// <remarks>見つかったパターン以外を置き換えた場合、正常に動作しないことがあります</remarks>
1205         public IEnumerator<SearchResult> Find(int start, int length)
1206         {
1207             if (this.regex == null)
1208                 throw new InvalidOperationException();
1209             if (start < 0 || start >= this.Length)
1210                 throw new ArgumentOutOfRangeException();
1211
1212             int end = start + length - 1;
1213
1214             if(end > this.Length - 1)
1215                 throw new ArgumentOutOfRangeException();
1216
1217             StringBuilder line = new StringBuilder();
1218             int oldLength = this.Length;
1219             for (int i = start; i <= end; i++)
1220             {
1221                 char c = this[i];
1222                 line.Append(c);
1223                 if (c == Document.NewLine || i == end)
1224                 {
1225                     this.match = this.regex.Match(line.ToString());
1226                     while (this.match.Success)
1227                     {
1228                         int startIndex = i - line.Length + 1 + this.match.Index;
1229                         int endIndex = startIndex + this.match.Length - 1;
1230
1231                         yield return new SearchResult(this.match, startIndex, endIndex);
1232
1233                         if (this.Length != oldLength)   //長さが変わった場合は置き換え後のパターンの終点+1まで戻る
1234                         {
1235                             int delta = this.Length - oldLength;
1236                             i = endIndex + delta;
1237                             end = end + delta;
1238                             oldLength = this.Length;
1239                             break;
1240                         }
1241
1242                         this.match = this.match.NextMatch();
1243                     }
1244                     line.Clear();
1245                 }
1246             }
1247         }
1248
1249         /// <summary>
1250         /// 任意のパターンですべて置き換えます
1251         /// </summary>
1252         /// <param name="replacePattern">置き換え後のパターン</param>
1253         /// <param name="groupReplace">グループ置き換えを行うなら真。そうでないなら偽</param>
1254         public void ReplaceAll(string replacePattern,bool groupReplace)
1255         {
1256             if (this.regex == null)
1257                 throw new InvalidOperationException();
1258             ReplaceAllCommand cmd = new ReplaceAllCommand(this.buffer, this.LayoutLines, this.regex, replacePattern, groupReplace);
1259             this.UndoManager.push(cmd);
1260             cmd.redo();
1261         }
1262
1263         /// <summary>
1264         /// 任意のパターンで置き換える
1265         /// </summary>
1266         /// <param name="target">対象となる文字列</param>
1267         /// <param name="pattern">置き換え後の文字列</param>
1268         /// <param name="ci">大文字も文字を区別しないなら真。そうでないなら偽</param>
1269         /// <remarks>
1270         /// 検索時に大文字小文字を区別します。また、このメソッドでは正規表現を使用することはできません
1271         /// </remarks>
1272         public void ReplaceAll2(string target, string pattern,bool ci = false)
1273         {
1274             FastReplaceAllCommand cmd = new FastReplaceAllCommand(this.buffer, this.LayoutLines, target, pattern,ci);
1275             this.UndoManager.push(cmd);
1276             cmd.redo();
1277         }
1278
1279         #region IEnumerable<char> メンバー
1280
1281         /// <summary>
1282         /// 列挙子を返します
1283         /// </summary>
1284         /// <returns>IEnumeratorオブジェクトを返す</returns>
1285         public IEnumerator<char> GetEnumerator()
1286         {
1287             return this.buffer.GetEnumerator();
1288         }
1289
1290         #endregion
1291
1292         #region IEnumerable メンバー
1293
1294         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
1295         {
1296             throw new NotImplementedException();
1297         }
1298
1299         #endregion
1300
1301         void buffer_Update(object sender, DocumentUpdateEventArgs e)
1302         {
1303             switch (e.type)
1304             {
1305                 case UpdateType.Replace:
1306                     if (e.row == null)
1307                     {
1308                         this._LayoutLines.UpdateAsReplace(e.startIndex, e.removeLength, e.insertLength);
1309                         this.Markers.UpdateMarkers(e.startIndex, e.insertLength, e.removeLength);
1310                     }
1311                     else
1312                     {
1313                         this._LayoutLines.UpdateLineAsReplace(e.row.Value, e.removeLength, e.insertLength);
1314                         this.Markers.UpdateMarkers(this.LayoutLines.GetIndexFromLineNumber(e.row.Value), e.insertLength, e.removeLength);
1315                     }
1316                     break;
1317                 case UpdateType.Clear:
1318                     this._LayoutLines.Clear();
1319                     break;
1320             }
1321             this.Dirty = true;
1322             if(this.FireUpdateEvent)
1323                 this.Update(this, e);
1324         }
1325
1326         #region IDisposable Support
1327         private bool disposedValue = false; // 重複する呼び出しを検出するには
1328
1329         void Dispose(bool disposing)
1330         {
1331             if (!disposedValue)
1332             {
1333                 if (disposing)
1334                 {
1335                     this.buffer.Clear();
1336                     this.LayoutLines.Clear();
1337                 }
1338
1339                 disposedValue = true;
1340             }
1341         }
1342
1343         /// <summary>
1344         /// ドキュメントを破棄する
1345         /// </summary>
1346         public void Dispose()
1347         {
1348             Dispose(true);
1349         }
1350         #endregion
1351     }
1352
1353     /// <summary>
1354     /// 検索結果を表す
1355     /// </summary>
1356     public class SearchResult
1357     {
1358         private Match Match;
1359
1360         /// <summary>
1361         /// 一致した場所の開始位置を表す
1362         /// </summary>
1363         public int Start;
1364
1365         /// <summary>
1366         /// 一致した場所の終了位置を表す
1367         /// </summary>
1368         public int End;
1369
1370         /// <summary>
1371         /// 見つかった文字列を返す
1372         /// </summary>
1373         public string Value
1374         {
1375             get { return this.Match.Value; }
1376         }
1377
1378         /// <summary>
1379         /// 指定したパターンを置き換えて返す
1380         /// </summary>
1381         /// <param name="replacement">置き換える文字列</param>
1382         /// <returns>置き換え後の文字列</returns>
1383         public string Result(string replacement)
1384         {
1385             return this.Match.Result(replacement);
1386         }
1387
1388         /// <summary>
1389         /// コンストラクター
1390         /// </summary>
1391         /// <param name="m">Matchオブジェクト</param>
1392         /// <param name="start">開始インデックス</param>
1393         /// <param name="end">終了インデックス</param>
1394         public SearchResult(Match m, int start,int end)
1395         {
1396             this.Match = m;
1397             this.Start = start;
1398             this.End = end;
1399         }
1400     }
1401
1402     /// <summary>
1403     /// ドキュメントリーダー
1404     /// </summary>
1405     public class DocumentReader : TextReader
1406     {
1407         StringBuffer document;      
1408         int currentIndex;
1409
1410         /// <summary>
1411         /// コンストラクター
1412         /// </summary>
1413         /// <param name="doc"></param>
1414         internal DocumentReader(StringBuffer doc)
1415         {
1416             if (doc == null)
1417                 throw new ArgumentNullException();
1418             this.document = doc;
1419         }
1420
1421         /// <summary>
1422         /// 文字を取得する
1423         /// </summary>
1424         /// <returns>文字。取得できない場合は-1</returns>
1425         public override int Peek()
1426         {
1427             if (this.document == null)
1428                 throw new InvalidOperationException();
1429             if (this.currentIndex >= this.document.Length)
1430                 return -1;
1431             return this.document[this.currentIndex];
1432         }
1433
1434         /// <summary>
1435         /// 文字を取得し、イテレーターを一つ進める
1436         /// </summary>
1437         /// <returns>文字。取得できない場合は-1</returns>
1438         public override int Read()
1439         {
1440             int c = this.Peek();
1441             if(c != -1)
1442                 this.currentIndex++;
1443             return c;
1444         }
1445
1446         /// <summary>
1447         /// 文字列を読み取りバッファーに書き込む
1448         /// </summary>
1449         /// <param name="buffer">バッファー</param>
1450         /// <param name="index">開始インデックス</param>
1451         /// <param name="count">カウント</param>
1452         /// <returns>読み取られた文字数</returns>
1453         public override int Read(char[] buffer, int index, int count)
1454         {
1455             if (this.document == null)
1456                 throw new InvalidOperationException();
1457
1458             if (buffer == null)
1459                 throw new ArgumentNullException();
1460
1461             if (this.document.Length < count)
1462                 throw new ArgumentException();
1463
1464             if (index < 0 || count < 0)
1465                 throw new ArgumentOutOfRangeException();
1466
1467             if (this.document.Length == 0)
1468                 return 0;
1469
1470             int actualCount = count;
1471             if (index + count - 1 > this.document.Length - 1)
1472                 actualCount = this.document.Length - index;
1473
1474             string str = this.document.ToString(index, actualCount);
1475
1476             for (int i = 0; i < str.Length; i++)    //ToCharArray()だと戻った時に消えてしまう
1477                 buffer[i] = str[i];
1478
1479             this.currentIndex = index + actualCount;
1480             
1481             return actualCount;
1482         }
1483
1484         /// <summary>
1485         /// オブジェクトを破棄する
1486         /// </summary>
1487         /// <param name="disposing">真ならアンマネージドリソースを解放する</param>
1488         protected override void Dispose(bool disposing)
1489         {
1490             this.document = null;
1491         }
1492
1493     }
1494 }