OSDN Git Service

Revert "セマフォーだとデッドロックを起こすことがあるのでリーダーライターロッカーに変更した"
[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                 this.LayoutLines.IsFrozneDirtyFlag = true;
1139                 await this.buffer.LoadAsync(fs, tokenSource);
1140             }
1141             finally
1142             {
1143                 this.Dirty = false; //ファイルの内容とドキュメントの中身は同じなのでダーティフラグは偽にする
1144                 this.PerformLayout();
1145                 //これ以降の操作にだけダーティフラグを適用しないとおかしなことになる
1146                 this.LayoutLines.IsFrozneDirtyFlag = false;
1147                 if (this.LoadProgress != null)
1148                     this.LoadProgress(this, new ProgressEventArgs(ProgressState.Complete));
1149             }
1150         }
1151
1152         /// <summary>
1153         /// ストリームに非同期モードで保存します
1154         /// </summary>
1155         /// <param name="fs">IStreamWriterオブジェクト</param>
1156         /// <param name="tokenSource">キャンセルトークン</param>
1157         /// <returns>Taskオブジェクト</returns>
1158         /// <remarks>非同期操作中はこのメソッドを実行することはできません。同時にダーティフラグもクリアされます</remarks>
1159         public async Task SaveAsync(TextWriter fs, CancellationTokenSource tokenSource = null)
1160         {
1161             try
1162             {
1163                 await this.buffer.LockAsync().ConfigureAwait(false);
1164                 StringBuilder line = new StringBuilder();
1165                 for (int i = 0; i < this.Length; i++)
1166                 {
1167                     char c = this[i];
1168                     line.Append(c);
1169                     if (c == Document.NewLine || i == this.Length - 1)
1170                     {
1171                         string str = line.ToString();
1172                         str = str.Replace(Document.NewLine.ToString(), fs.NewLine);
1173                         await fs.WriteAsync(str).ConfigureAwait(false);
1174                         line.Clear();
1175                         if (tokenSource != null)
1176                             tokenSource.Token.ThrowIfCancellationRequested();
1177 #if TEST_ASYNC
1178                     System.Threading.Thread.Sleep(10);
1179 #endif
1180                     }
1181                 }
1182                 this.Dirty = false;
1183             }
1184             finally
1185             {
1186                 this.buffer.UnLock();
1187             }
1188         }
1189
1190         /// <summary>
1191         /// Find()およびReplaceAll()で使用するパラメーターをセットします
1192         /// </summary>
1193         /// <param name="pattern">検索したい文字列</param>
1194         /// <param name="UseRegex">正規表現を使用するなら真</param>
1195         /// <param name="opt">RegexOptions列挙体</param>
1196         public void SetFindParam(string pattern, bool UseRegex, RegexOptions opt)
1197         {
1198             this.match = null;
1199             if (UseRegex)
1200                 this.regex = new Regex(pattern, opt);
1201             else
1202                 this.regex = new Regex(Regex.Escape(pattern), opt);
1203         }
1204
1205         /// <summary>
1206         /// 現在の検索パラメーターでWatchDogを生成する
1207         /// </summary>
1208         /// <param name="type">ハイライトタイプ</param>
1209         /// <param name="color">色</param>
1210         /// <returns>WatchDogオブジェクト</returns>
1211         public RegexMarkerPattern CreateWatchDogByFindParam(HilightType type,Color color)
1212         {
1213             if (this.regex == null)
1214                 throw new InvalidOperationException("SetFindParam()を呼び出してください");
1215             return new RegexMarkerPattern(this.regex,type,color);
1216         }
1217
1218         /// <summary>
1219         /// 指定した文字列を検索します
1220         /// </summary>
1221         /// <returns>見つかった場合はSearchResult列挙子を返却します</returns>
1222         /// <remarks>見つかったパターン以外を置き換えた場合、正常に動作しないことがあります</remarks>
1223         public IEnumerator<SearchResult> Find()
1224         {
1225             return this.Find(0, this.Length);
1226         }
1227
1228         /// <summary>
1229         /// 指定した文字列を検索します
1230         /// </summary>
1231         /// <returns>見つかった場合はSearchResult列挙子を返却します</returns>
1232         /// <param name="start">開始インデックス</param>
1233         /// <param name="length">検索する長さ</param>
1234         /// <remarks>見つかったパターン以外を置き換えた場合、正常に動作しないことがあります</remarks>
1235         public IEnumerator<SearchResult> Find(int start, int length)
1236         {
1237             if (this.regex == null)
1238                 throw new InvalidOperationException();
1239             if (start < 0 || start >= this.Length)
1240                 throw new ArgumentOutOfRangeException();
1241
1242             int end = start + length - 1;
1243
1244             if(end > this.Length - 1)
1245                 throw new ArgumentOutOfRangeException();
1246
1247             StringBuilder line = new StringBuilder();
1248             int oldLength = this.Length;
1249             for (int i = start; i <= end; i++)
1250             {
1251                 char c = this[i];
1252                 line.Append(c);
1253                 if (c == Document.NewLine || i == end)
1254                 {
1255                     this.match = this.regex.Match(line.ToString());
1256                     while (this.match.Success)
1257                     {
1258                         int startIndex = i - line.Length + 1 + this.match.Index;
1259                         int endIndex = startIndex + this.match.Length - 1;
1260
1261                         yield return new SearchResult(this.match, startIndex, endIndex);
1262
1263                         if (this.Length != oldLength)   //長さが変わった場合は置き換え後のパターンの終点+1まで戻る
1264                         {
1265                             int delta = this.Length - oldLength;
1266                             i = endIndex + delta;
1267                             end = end + delta;
1268                             oldLength = this.Length;
1269                             break;
1270                         }
1271
1272                         this.match = this.match.NextMatch();
1273                     }
1274                     line.Clear();
1275                 }
1276             }
1277         }
1278
1279         /// <summary>
1280         /// 任意のパターンですべて置き換えます
1281         /// </summary>
1282         /// <param name="replacePattern">置き換え後のパターン</param>
1283         /// <param name="groupReplace">グループ置き換えを行うなら真。そうでないなら偽</param>
1284         public void ReplaceAll(string replacePattern,bool groupReplace)
1285         {
1286             if (this.regex == null)
1287                 throw new InvalidOperationException();
1288             ReplaceAllCommand cmd = new ReplaceAllCommand(this.buffer, this.LayoutLines, this.regex, replacePattern, groupReplace);
1289             this.UndoManager.push(cmd);
1290             cmd.redo();
1291         }
1292
1293         /// <summary>
1294         /// 任意のパターンで置き換える
1295         /// </summary>
1296         /// <param name="target">対象となる文字列</param>
1297         /// <param name="pattern">置き換え後の文字列</param>
1298         /// <param name="ci">大文字も文字を区別しないなら真。そうでないなら偽</param>
1299         /// <remarks>
1300         /// 検索時に大文字小文字を区別します。また、このメソッドでは正規表現を使用することはできません
1301         /// </remarks>
1302         public void ReplaceAll2(string target, string pattern,bool ci = false)
1303         {
1304             FastReplaceAllCommand cmd = new FastReplaceAllCommand(this.buffer, this.LayoutLines, target, pattern,ci);
1305             this.UndoManager.push(cmd);
1306             cmd.redo();
1307         }
1308
1309         #region IEnumerable<char> メンバー
1310
1311         /// <summary>
1312         /// 列挙子を返します
1313         /// </summary>
1314         /// <returns>IEnumeratorオブジェクトを返す</returns>
1315         public IEnumerator<char> GetEnumerator()
1316         {
1317             return this.buffer.GetEnumerator();
1318         }
1319
1320         #endregion
1321
1322         #region IEnumerable メンバー
1323
1324         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
1325         {
1326             throw new NotImplementedException();
1327         }
1328
1329         #endregion
1330
1331         void buffer_Update(object sender, DocumentUpdateEventArgs e)
1332         {
1333             switch (e.type)
1334             {
1335                 case UpdateType.Replace:
1336                     if (e.row == null)
1337                     {
1338                         this._LayoutLines.UpdateAsReplace(e.startIndex, e.removeLength, e.insertLength);
1339                         this.Markers.UpdateMarkers(e.startIndex, e.insertLength, e.removeLength);
1340                     }
1341                     else
1342                     {
1343                         this._LayoutLines.UpdateLineAsReplace(e.row.Value, e.removeLength, e.insertLength);
1344                         this.Markers.UpdateMarkers(this.LayoutLines.GetIndexFromLineNumber(e.row.Value), e.insertLength, e.removeLength);
1345                     }
1346                     break;
1347                 case UpdateType.Clear:
1348                     this._LayoutLines.Clear();
1349                     break;
1350             }
1351             this.Dirty = true;
1352             if(this.FireUpdateEvent)
1353                 this.Update(this, e);
1354         }
1355
1356         #region IDisposable Support
1357         private bool disposedValue = false; // 重複する呼び出しを検出するには
1358
1359         void Dispose(bool disposing)
1360         {
1361             if (!disposedValue)
1362             {
1363                 if (disposing)
1364                 {
1365                     this.buffer.Clear();
1366                     this.LayoutLines.Clear();
1367                 }
1368
1369                 disposedValue = true;
1370             }
1371         }
1372
1373         /// <summary>
1374         /// ドキュメントを破棄する
1375         /// </summary>
1376         public void Dispose()
1377         {
1378             Dispose(true);
1379         }
1380         #endregion
1381     }
1382
1383     /// <summary>
1384     /// 検索結果を表す
1385     /// </summary>
1386     public class SearchResult
1387     {
1388         private Match Match;
1389
1390         /// <summary>
1391         /// 一致した場所の開始位置を表す
1392         /// </summary>
1393         public int Start;
1394
1395         /// <summary>
1396         /// 一致した場所の終了位置を表す
1397         /// </summary>
1398         public int End;
1399
1400         /// <summary>
1401         /// 見つかった文字列を返す
1402         /// </summary>
1403         public string Value
1404         {
1405             get { return this.Match.Value; }
1406         }
1407
1408         /// <summary>
1409         /// 指定したパターンを置き換えて返す
1410         /// </summary>
1411         /// <param name="replacement">置き換える文字列</param>
1412         /// <returns>置き換え後の文字列</returns>
1413         public string Result(string replacement)
1414         {
1415             return this.Match.Result(replacement);
1416         }
1417
1418         /// <summary>
1419         /// コンストラクター
1420         /// </summary>
1421         /// <param name="m">Matchオブジェクト</param>
1422         /// <param name="start">開始インデックス</param>
1423         /// <param name="end">終了インデックス</param>
1424         public SearchResult(Match m, int start,int end)
1425         {
1426             this.Match = m;
1427             this.Start = start;
1428             this.End = end;
1429         }
1430     }
1431
1432     /// <summary>
1433     /// ドキュメントリーダー
1434     /// </summary>
1435     public class DocumentReader : TextReader
1436     {
1437         StringBuffer document;      
1438         int currentIndex;
1439
1440         /// <summary>
1441         /// コンストラクター
1442         /// </summary>
1443         /// <param name="doc"></param>
1444         internal DocumentReader(StringBuffer doc)
1445         {
1446             if (doc == null)
1447                 throw new ArgumentNullException();
1448             this.document = doc;
1449         }
1450
1451         /// <summary>
1452         /// 文字を取得する
1453         /// </summary>
1454         /// <returns>文字。取得できない場合は-1</returns>
1455         public override int Peek()
1456         {
1457             if (this.document == null)
1458                 throw new InvalidOperationException();
1459             if (this.currentIndex >= this.document.Length)
1460                 return -1;
1461             return this.document[this.currentIndex];
1462         }
1463
1464         /// <summary>
1465         /// 文字を取得し、イテレーターを一つ進める
1466         /// </summary>
1467         /// <returns>文字。取得できない場合は-1</returns>
1468         public override int Read()
1469         {
1470             int c = this.Peek();
1471             if(c != -1)
1472                 this.currentIndex++;
1473             return c;
1474         }
1475
1476         /// <summary>
1477         /// 文字列を読み取りバッファーに書き込む
1478         /// </summary>
1479         /// <param name="buffer">バッファー</param>
1480         /// <param name="index">開始インデックス</param>
1481         /// <param name="count">カウント</param>
1482         /// <returns>読み取られた文字数</returns>
1483         public override int Read(char[] buffer, int index, int count)
1484         {
1485             if (this.document == null)
1486                 throw new InvalidOperationException();
1487
1488             if (buffer == null)
1489                 throw new ArgumentNullException();
1490
1491             if (this.document.Length < count)
1492                 throw new ArgumentException();
1493
1494             if (index < 0 || count < 0)
1495                 throw new ArgumentOutOfRangeException();
1496
1497             if (this.document.Length == 0)
1498                 return 0;
1499
1500             int actualCount = count;
1501             if (index + count - 1 > this.document.Length - 1)
1502                 actualCount = this.document.Length - index;
1503
1504             string str = this.document.ToString(index, actualCount);
1505
1506             for (int i = 0; i < str.Length; i++)    //ToCharArray()だと戻った時に消えてしまう
1507                 buffer[i] = str[i];
1508
1509             this.currentIndex = index + actualCount;
1510             
1511             return actualCount;
1512         }
1513
1514         /// <summary>
1515         /// オブジェクトを破棄する
1516         /// </summary>
1517         /// <param name="disposing">真ならアンマネージドリソースを解放する</param>
1518         protected override void Dispose(bool disposing)
1519         {
1520             this.document = null;
1521         }
1522
1523     }
1524 }