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