OSDN Git Service

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