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