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.
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.
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/>.
16 using System.ComponentModel;
17 using System.Collections.Generic;
19 using System.Text.RegularExpressions;
20 using System.Threading;
21 using System.Threading.Tasks;
23 using System.Runtime.CompilerServices;
25 namespace FooEditEngine
30 public enum ProgressState
44 public sealed class ProgressEventArgs : EventArgs
49 public ProgressState state;
53 /// <param name="state">ProgressStateオブジェクト</param>
54 public ProgressEventArgs(ProgressState state)
63 /// <param name="sender">送信元クラス</param>
64 /// <param name="e">イベントデータ</param>
65 public delegate void ProgressEventHandler(object sender, ProgressEventArgs e);
70 public enum UpdateType
73 /// ドキュメントが置き換えられたことを表す
77 /// ドキュメント全体が削除されたことを表す
83 /// 更新タイプを通知するためのイベントデータ
85 public sealed class DocumentUpdateEventArgs : EventArgs
90 public const int EmptyValue = -1;
94 public UpdateType type;
98 public int startIndex;
102 public int removeLength;
106 public int insertLength;
108 /// 更新イベントが発生した行。行が不明な場合や行をまたぐ場合はnullを指定すること。
114 /// <param name="type">更新タイプ</param>
115 /// <param name="startIndex">開始インデックス</param>
116 /// <param name="removeLength">削除された長さ</param>
117 /// <param name="insertLength">追加された長さ</param>
118 /// <param name="row">開始行。nullを指定することができる</param>
119 public DocumentUpdateEventArgs(UpdateType type, int startIndex = EmptyValue, int removeLength = EmptyValue, int insertLength = EmptyValue, int? row = null)
122 this.startIndex = startIndex;
123 this.removeLength = removeLength;
124 this.insertLength = insertLength;
130 /// ドキュメントに更新があったことを伝えるためのデリゲート
132 /// <param name="sender">送信元クラス</param>
133 /// <param name="e">イベントデータ</param>
134 public delegate void DocumentUpdateEventHandler(object sender, DocumentUpdateEventArgs e);
139 /// <remarks>この型のすべてのメソッド・プロパティはスレッドセーフです</remarks>
140 public sealed class Document : IEnumerable<char>, IRandomEnumrator<char>
142 const int MaxSemaphoreCount = 1;
146 LineToIndexTable _LayoutLines;
147 bool _EnableFireUpdateEvent = true,_UrlMark = false, _DrawLineNumber = false, _HideRuler = true, _RightToLeft = false;
148 SemaphoreSlim Semaphore = new SemaphoreSlim(MaxSemaphoreCount);
149 LineBreakMethod _LineBreak;
150 int _TabStops, _LineBreakCharCount = 80;
151 bool _ShowFullSpace, _ShowHalfSpace, _ShowTab, _ShowLineBreak,_InsertMode, _HideCaret, _HideLineMarker, _RectSelection;
152 IndentMode _IndentMode;
157 public const int MaximumLineLength = 1000;
170 /// <param name="doc">ドキュメントオブジェクト</param>
171 /// <remarks>docが複製されますが、プロパティは引き継がれません</remarks>
172 public Document(Document doc)
175 this.buffer = new StringBuffer();
177 this.buffer = new StringBuffer(doc.buffer);
178 this.buffer.Update = new DocumentUpdateEventHandler(buffer_Update);
179 this.UpdateCalledAlways += (s, e) => { };
180 this.Update += new DocumentUpdateEventHandler((s, e) => { });
181 this.ChangeFireUpdateEvent += new EventHandler((s, e) => { });
182 this.StatusUpdate += new EventHandler((s, e) => { });
183 this.Markers = new MarkerCollection();
184 this.UndoManager = new UndoManager();
185 this._LayoutLines = new LineToIndexTable(this);
186 this._LayoutLines.SpilitString = (s,e)=> {
187 return this.CreateLineList(e.index, e.length, MaximumLineLength);
189 this._LayoutLines.Clear();
190 this.MarkerPatternSet = new MarkerPatternSet(this._LayoutLines, this.Markers);
191 this.MarkerPatternSet.Updated += WacthDogPattern_Updated;
192 this.Selections = new SelectCollection();
193 this.CaretPostion = new TextPoint();
194 this.HideLineMarker = true;
195 this.SelectGrippers = new GripperRectangle(new Gripper(), new Gripper());
196 this.SelectionChanged += new EventHandler((s, e) => { });
199 void WacthDogPattern_Updated(object sender, EventArgs e)
201 this._LayoutLines.ClearLayoutCache();
205 /// キャレットでの選択の起点となる位置
207 internal int AnchorIndex
216 internal SrcPoint Src
232 /// ルーラーやキャレット・行番号などの表示すべきものが変化した場合に呼び出される。ドキュメントの内容が変化した通知を受け取り場合はUpdateを使用してください
234 public event EventHandler StatusUpdate;
239 public bool ShowFullSpace
241 get { return this._ShowFullSpace; }
244 if (this._ShowFullSpace == value)
246 this._ShowFullSpace = value;
247 this.StatusUpdate(this, null);
254 public bool ShowHalfSpace
256 get { return this._ShowHalfSpace; }
259 if (this._ShowHalfSpace == value)
261 this._ShowHalfSpace = value;
262 this.StatusUpdate(this, null);
271 get { return this._ShowTab; }
274 if (this._ShowTab == value)
276 this._ShowTab = value;
277 this.StatusUpdate(this, null);
284 public bool ShowLineBreak
286 get { return this._ShowLineBreak; }
289 if (this._ShowLineBreak == value)
291 this._ShowLineBreak = value;
292 this.StatusUpdate(this, null);
299 internal GripperRectangle SelectGrippers
308 public bool RightToLeft {
309 get { return this._RightToLeft; }
312 if (this._RightToLeft == value)
314 this._RightToLeft = value;
315 this.StatusUpdate(this, null);
320 /// 矩形選択モードなら真を返し、そうでない場合は偽を返す
322 public bool RectSelection
326 return this._RectSelection;
330 this._RectSelection = value;
331 this.StatusUpdate(this, null);
338 public IndentMode IndentMode
342 return this._IndentMode;
346 this._IndentMode = value;
347 this.StatusUpdate(this, null);
352 /// ラインマーカーを描くなら偽。そうでなければ真
354 public bool HideLineMarker
358 return this._HideLineMarker;
362 this._HideLineMarker = value;
363 this.StatusUpdate(this, null);
368 /// キャレットを描くなら偽。そうでなければ真
370 public bool HideCaret
374 return this._HideCaret;
378 this._HideCaret = value;
379 this.StatusUpdate(this, null);
384 /// 挿入モードなら真を返し、上書きモードなら偽を返す
386 public bool InsertMode
390 return this._InsertMode;
394 this._InsertMode = value;
395 this.StatusUpdate(this, null);
400 /// ルーラーを表示しないなら真、そうでないなら偽
402 public bool HideRuler
404 get { return this._HideRuler; }
407 if (this._HideRuler == value)
409 this._HideRuler = value;
410 this.LayoutLines.ClearLayoutCache();
411 this.StatusUpdate(this, null);
416 /// レイアウト行のどこにキャレットがあるかを表す
418 /// <remarks>この値を変更しても反映されないので、EditView側でAdjustCaret()メソッドを呼び出す必要があります</remarks>
419 public TextPoint CaretPostion
428 internal SelectCollection Selections
437 public bool DrawLineNumber
439 get { return this._DrawLineNumber; }
442 if (this._DrawLineNumber == value)
444 this._DrawLineNumber = value;
445 this._LayoutLines.ClearLayoutCache();
446 this.StatusUpdate(this, null);
451 /// URLをハイパーリンクとして表示するなら真。そうでないなら偽
455 get { return this._UrlMark; }
458 if (this._UrlMark == value)
460 this._UrlMark = value;
463 Regex regex = new Regex("(http|https|ftp)(:\\/\\/[-_.!~*\\'()a-zA-Z0-9;\\/?:\\@&=+\\$,%#]+)");
464 this.MarkerPatternSet.Add(MarkerIDs.URL, new RegexMarkerPattern(regex, HilightType.Url, new Color()));
468 this.MarkerPatternSet.Remove(MarkerIDs.URL);
470 this.StatusUpdate(this, null);
474 public event EventHandler LineBreakChanged;
480 /// 変更した場合、呼び出し側で再描写とレイアウトの再構築を行う必要があります
481 /// また、StatusUpdatedではなく、LineBreakChangedイベントが発生します
483 public LineBreakMethod LineBreak
487 return this._LineBreak;
491 if (this._LineBreak == value)
493 this._LineBreak = value;
494 this.LineBreakChanged(this, null);
499 /// 折り返し行う文字数。実際に折り返しが行われる幅はem単位×この値となります
501 /// <remarks>この値を変えた場合、LineBreakChangedイベントが発生します</remarks>
502 public int LineBreakCharCount
506 return this._LineBreakCharCount;
510 if (this._LineBreakCharCount == value)
512 this._LineBreakCharCount = value;
513 this.LineBreakChanged(this, null);
520 /// <remarks>変更した場合、呼び出し側で再描写する必要があります</remarks>
523 get { return this._TabStops; }
525 if (this._TabStops == value)
527 this._TabStops = value;
528 this.StatusUpdate(this, null);
535 public MarkerPatternSet MarkerPatternSet
544 public LineToIndexTable LayoutLines
548 return this._LayoutLines;
555 /// <param name="index">開始インデックス</param>
556 /// <param name="length">長さ</param>
557 /// <param name="lineLimitLength">1行当たりの最大文字数。-1で無制限</param>
558 /// <returns>レイアウト行リスト</returns>
559 internal IList<LineToIndexTableData> CreateLineList(int index, int length, int lineLimitLength = -1)
561 int startIndex = index;
562 int endIndex = index + length - 1;
563 List<LineToIndexTableData> output = new List<LineToIndexTableData>();
565 foreach (Tuple<int, int> range in this.ForEachLines(startIndex, endIndex, lineLimitLength))
567 int lineHeadIndex = range.Item1;
568 int lineLength = range.Item2;
569 char c = this.buffer[lineHeadIndex + lineLength - 1];
570 bool hasNewLine = c == Document.NewLine;
571 output.Add(this.LayoutLines.CreateLineToIndexTableData(lineHeadIndex, lineLength, hasNewLine, null));
574 if (output.Count > 0)
575 output.Last().LineEnd = true;
580 internal void FireUpdate(DocumentUpdateEventArgs e)
582 this.buffer_Update(this.buffer, e);
586 /// ドキュメントが更新された時に呼ばれるイベント
588 public event DocumentUpdateEventHandler Update;
591 /// ドキュメントが更新された時に呼びされるイベント
594 /// FireUpdateEventの値に関わらず常に呼びされます
596 internal event DocumentUpdateEventHandler UpdateCalledAlways;
599 /// FireUpdateEventの値が変わったときに呼び出されるイベント
601 public event EventHandler ChangeFireUpdateEvent;
606 public const char NewLine = '\n';
611 public const char EndOfFile = '\u001a';
614 /// ロック中なら真を返し、そうでないなら偽を返す
620 return this.Semaphore.CurrentCount == 0;
627 public UndoManager UndoManager
640 return this.buffer.Length;
645 /// 変更のたびにUpdateイベントを発生させるかどうか
647 public bool FireUpdateEvent
651 return this._EnableFireUpdateEvent;
655 this._EnableFireUpdateEvent = value;
656 this.ChangeFireUpdateEvent(this, null);
663 /// <param name="i">インデックス(自然数でなければならない)</param>
664 /// <returns>Char型</returns>
665 public char this[int i]
669 return this.buffer[i];
676 public MarkerCollection Markers
682 internal StringBuffer StringBuffer
693 public event EventHandler SelectionChanged;
698 /// <param name="start"></param>
699 /// <param name="length"></param>
700 /// <remarks>RectSelectionの値によって動作が変わります。真の場合は矩形選択モードに、そうでない場合は行ごとに選択されます</remarks>
701 public void Select(int start, int length)
703 if (this.FireUpdateEvent == false)
704 throw new InvalidOperationException("");
705 if (start < 0 || start + length < 0 || start + length > this.Length)
706 throw new ArgumentOutOfRangeException("startかendが指定できる範囲を超えてます");
707 this.Selections.Clear();
710 int oldStart = start;
712 length = oldStart - start;
714 if (this.RectSelection && length != 0)
716 TextPoint startTextPoint = this.LayoutLines.GetTextPointFromIndex(start);
717 TextPoint endTextPoint = this.LayoutLines.GetTextPointFromIndex(start + length);
718 this.SelectByRectangle(new TextRectangle(startTextPoint, endTextPoint));
720 else if (length != 0)
722 this.Selections.Add(Selection.Create(start, length));
724 this.SelectionChanged(this, null);
730 /// <param name="tp">開始位置</param>
731 /// <param name="width">桁数</param>
732 /// <param name="height">行数</param>
733 public void Select(TextPoint tp, int width, int height)
735 if (this.FireUpdateEvent == false || !this.RectSelection)
736 throw new InvalidOperationException("");
739 end.row = tp.row + height;
740 end.col = tp.col + width;
742 if (end.row > this.LayoutLines.Count - 1)
743 throw new ArgumentOutOfRangeException("");
745 this.Selections.Clear();
747 this.SelectByRectangle(new TextRectangle(tp, end));
749 this.SelectionChanged(this, null);
752 private void SelectByRectangle(TextRectangle rect)
754 if (this.FireUpdateEvent == false)
755 throw new InvalidOperationException("");
756 if (rect.TopLeft <= rect.BottomRight)
758 for (int i = rect.TopLeft.row; i <= rect.BottomLeft.row; i++)
760 int length = this.LayoutLines.GetLengthFromLineNumber(i);
761 int leftCol = rect.TopLeft.col, rightCol = rect.TopRight.col, lastCol = length;
762 if (length > 0 && this.LayoutLines[i][length - 1] == Document.NewLine)
763 lastCol = length - 1;
766 if (rect.TopLeft.col > lastCol)
768 if (rect.TopRight.col > lastCol)
771 int StartIndex = this.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, leftCol));
772 int EndIndex = this.LayoutLines.GetIndexFromTextPoint(new TextPoint(i, rightCol));
775 sel = Selection.Create(StartIndex, EndIndex - StartIndex);
777 this.Selections.Add(sel);
785 /// <param name="index">探索を開始するインデックス</param>
786 /// <param name="changeAnchor">選択の起点となるとインデックスを変更するなら真。そうでなければ偽</param>
787 public void SelectWord(int index, bool changeAnchor = false)
789 if (this.FireUpdateEvent == false)
790 throw new InvalidOperationException("");
792 if (this.Length <= 0 || index >= this.Length)
798 while (start > 0 && !Util.IsWordSeparator(str[start]))
801 if (Util.IsWordSeparator(str[start]))
805 while (end < this.Length && !Util.IsWordSeparator(str[end]))
808 this.Select(start, end - start);
811 this.AnchorIndex = start;
815 /// DocumentReaderを作成します
817 /// <returns>DocumentReaderオブジェクト</returns>
818 public DocumentReader CreateReader()
820 return new DocumentReader(this.buffer);
828 this.Semaphore.Release();
836 this.Semaphore.Wait();
842 /// <returns>Taskオブジェクト</returns>
843 public Task LockAsync()
845 return this.Semaphore.WaitAsync();
851 /// <param name="id">マーカーID</param>
852 /// <param name="m">設定したいマーカー</param>
853 public void SetMarker(int id,Marker m)
855 if (m.start < 0 || m.start + m.length > this.Length)
856 throw new ArgumentOutOfRangeException("startもしくはendが指定できる範囲を超えています");
858 this.Markers.Add(id,m);
864 /// <param name="id">マーカーID</param>
865 /// <param name="start">開始インデックス</param>
866 /// <param name="length">削除する長さ</param>
867 public void RemoveMarker(int id,int start, int length)
869 if (start < 0 || start + length > this.Length)
870 throw new ArgumentOutOfRangeException("startもしくはendが指定できる範囲を超えています");
872 this.Markers.RemoveAll(id,start, length);
878 /// <param name="id">マーカーID</param>
879 /// <param name="type">削除したいマーカーのタイプ</param>
880 public void RemoveMarker(int id, HilightType type)
882 this.Markers.RemoveAll(id,type);
888 /// <param name="id">マーカーID</param>
889 public void RemoveAllMarker(int id)
891 this.Markers.RemoveAll(id);
895 /// インデックスに対応するマーカーを得る
897 /// <param name="id">マーカーID</param>
898 /// <param name="index">インデックス</param>
899 /// <returns>Marker構造体の列挙子</returns>
900 public IEnumerable<Marker> GetMarkers(int id, int index)
902 if (index < 0 || index > this.Length)
903 throw new ArgumentOutOfRangeException("indexが範囲を超えています");
904 return this.Markers.Get(id,index);
910 /// <param name="index">開始インデックス</param>
911 /// <param name="length">長さ</param>
912 /// <returns>Stringオブジェクト</returns>
913 public string ToString(int index, int length)
915 return this.buffer.ToString(index, length);
919 /// インデックスを開始位置とする文字列を返す
921 /// <param name="index">開始インデックス</param>
922 /// <returns>Stringオブジェクト</returns>
923 public string ToString(int index)
925 return this.ToString(index, this.buffer.Length - index);
931 /// <param name="startIndex">開始インデックス</param>
932 /// <param name="endIndex">終了インデックス</param>
933 /// <param name="maxCharCount">最大長</param>
934 /// <returns>行イテレーターが返される</returns>
935 public IEnumerable<string> GetLines(int startIndex, int endIndex, int maxCharCount = -1)
937 return this.buffer.GetLines(startIndex, endIndex, maxCharCount);
940 internal IEnumerable<Tuple<int, int>> ForEachLines(int startIndex, int endIndex, int maxCharCount = -1)
942 return this.buffer.ForEachLines(startIndex, endIndex, maxCharCount);
949 /// <param name="s">追加したい文字列</param>
950 /// <remarks>非同期操作中はこのメソッドを実行することはできません</remarks>
951 public void Append(string s)
953 this.Replace(this.buffer.Length, 0, s);
959 /// <param name="index">開始インデックス</param>
960 /// <param name="s">追加したい文字列</param>
961 /// <remarks>読み出し操作中はこのメソッドを実行することはできません</remarks>
962 public void Insert(int index, string s)
964 this.Replace(index, 0, s);
970 /// <param name="index">開始インデックス</param>
971 /// <param name="length">長さ</param>
972 /// <remarks>読み出し操作中はこのメソッドを実行することはできません</remarks>
973 public void Remove(int index, int length)
975 this.Replace(index, length, "");
981 /// <param name="index">開始インデックス</param>
982 /// <param name="length">長さ</param>
983 /// <param name="s">文字列</param>
984 /// <remarks>読み出し操作中はこのメソッドを実行することはできません</remarks>
985 public void Replace(int index, int length, string s)
987 if (index < 0 || index > this.buffer.Length || index + length > this.buffer.Length || length < 0)
988 throw new ArgumentOutOfRangeException();
989 if (length == 0 && (s == string.Empty || s == null))
992 foreach(int id in this.Markers.IDs)
993 this.RemoveMarker(id,index, length);
995 ReplaceCommand cmd = new ReplaceCommand(this.buffer, index, length, s);
996 this.UndoManager.push(cmd);
1003 /// <remarks>Dirtyフラグも同時にクリアーされます</remarks>
1004 /// <remarks>非同期操作中はこのメソッドを実行することはできません</remarks>
1007 this.buffer.Clear();
1011 /// ストリームからドキュメントを非同期的に構築します
1013 /// <param name="fs">IStreamReaderオブジェクト</param>
1014 /// <param name="tokenSource">キャンセルトークン</param>
1015 /// <returns>Taskオブジェクト</returns>
1017 /// 読み取り操作は別スレッドで行われます。
1018 /// また、非同期操作中はこのメソッドを実行することはできません。
1020 internal async Task LoadAsync(IStreamReader fs, CancellationTokenSource tokenSource = null)
1027 await this.LockAsync().ConfigureAwait(false);
1029 this.FireUpdateEvent = false;
1030 this.LayoutLines.IsFrozneDirtyFlag = true;
1031 await this.buffer.LoadAsync(fs, tokenSource);
1035 this.FireUpdateEvent = true;
1036 //これ以降の操作にだけダーティフラグを適用しないとおかしなことになる
1037 this.LayoutLines.IsFrozneDirtyFlag = false;
1043 /// ストリームに非同期モードで保存します
1045 /// <param name="fs">IStreamWriterオブジェクト</param>
1046 /// <param name="tokenSource">キャンセルトークン</param>
1047 /// <returns>Taskオブジェクト</returns>
1048 /// <remarks>非同期操作中はこのメソッドを実行することはできません</remarks>
1049 internal async Task SaveAsync(IStreamWriter fs, CancellationTokenSource tokenSource = null)
1053 await this.LockAsync().ConfigureAwait(false);
1054 StringBuilder line = new StringBuilder();
1055 for (int i = 0; i < this.Length; i++)
1059 if (c == Document.NewLine || i == this.Length - 1)
1061 string str = line.ToString();
1062 str = str.Replace(Document.NewLine.ToString(), fs.NewLine);
1063 await fs.WriteAsync(str).ConfigureAwait(false);
1065 if (tokenSource != null)
1066 tokenSource.Token.ThrowIfCancellationRequested();
1068 System.Threading.Thread.Sleep(10);
1080 /// Find()およびReplaceAll()で使用するパラメーターをセットします
1082 /// <param name="pattern">検索したい文字列</param>
1083 /// <param name="UseRegex">正規表現を使用するなら真</param>
1084 /// <param name="opt">RegexOptions列挙体</param>
1085 public void SetFindParam(string pattern, bool UseRegex, RegexOptions opt)
1089 this.regex = new Regex(pattern, opt);
1091 this.regex = new Regex(Regex.Escape(pattern), opt);
1095 /// 現在の検索パラメーターでWatchDogを生成する
1097 /// <param name="type">ハイライトタイプ</param>
1098 /// <param name="color">色</param>
1099 /// <returns>WatchDogオブジェクト</returns>
1100 public RegexMarkerPattern CreateWatchDogByFindParam(HilightType type,Color color)
1102 if (this.regex == null)
1103 throw new InvalidOperationException("SetFindParam()を呼び出してください");
1104 return new RegexMarkerPattern(this.regex,type,color);
1110 /// <returns>見つかった場合はSearchResult列挙子を返却します</returns>
1111 /// <remarks>見つかったパターン以外を置き換えた場合、正常に動作しないことがあります</remarks>
1112 public IEnumerator<SearchResult> Find()
1114 return this.Find(0, this.Length);
1120 /// <returns>見つかった場合はSearchResult列挙子を返却します</returns>
1121 /// <param name="start">開始インデックス</param>
1122 /// <param name="length">検索する長さ</param>
1123 /// <remarks>見つかったパターン以外を置き換えた場合、正常に動作しないことがあります</remarks>
1124 public IEnumerator<SearchResult> Find(int start, int length)
1126 if (this.regex == null)
1127 throw new InvalidOperationException();
1128 if (start < 0 || start >= this.Length)
1129 throw new ArgumentOutOfRangeException();
1131 int end = start + length - 1;
1133 if(end > this.Length - 1)
1134 throw new ArgumentOutOfRangeException();
1136 StringBuilder line = new StringBuilder();
1137 int oldLength = this.Length;
1138 for (int i = start; i <= end; i++)
1142 if (c == Document.NewLine || i == end)
1144 this.match = this.regex.Match(line.ToString());
1145 while (this.match.Success)
1147 int startIndex = i - line.Length + 1 + this.match.Index;
1148 int endIndex = startIndex + this.match.Length - 1;
1150 yield return new SearchResult(this.match, startIndex, endIndex);
1152 if (this.Length != oldLength) //長さが変わった場合は置き換え後のパターンの終点+1まで戻る
1154 int delta = this.Length - oldLength;
1155 i = endIndex + delta;
1157 oldLength = this.Length;
1161 this.match = this.match.NextMatch();
1169 /// 任意のパターンですべて置き換えます
1171 /// <param name="replacePattern">置き換え後のパターン</param>
1172 /// <param name="groupReplace">グループ置き換えを行うなら真。そうでないなら偽</param>
1173 public void ReplaceAll(string replacePattern,bool groupReplace)
1175 if (this.regex == null)
1176 throw new InvalidOperationException();
1177 ReplaceAllCommand cmd = new ReplaceAllCommand(this.buffer, this.LayoutLines, this.regex, replacePattern, groupReplace);
1178 this.UndoManager.push(cmd);
1185 /// <param name="target">対象となる文字列</param>
1186 /// <param name="pattern">置き換え後の文字列</param>
1187 /// <param name="ci">大文字も文字を区別しないなら真。そうでないなら偽</param>
1189 /// 検索時に大文字小文字を区別します。また、このメソッドでは正規表現を使用することはできません
1191 public void ReplaceAll2(string target, string pattern,bool ci = false)
1193 FastReplaceAllCommand cmd = new FastReplaceAllCommand(this.buffer, this.LayoutLines, target, pattern,ci);
1194 this.UndoManager.push(cmd);
1198 #region IEnumerable<char> メンバー
1203 /// <returns>IEnumeratorオブジェクトを返す</returns>
1204 public IEnumerator<char> GetEnumerator()
1206 return this.buffer.GetEnumerator();
1211 #region IEnumerable メンバー
1213 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
1215 throw new NotImplementedException();
1220 void buffer_Update(object sender, DocumentUpdateEventArgs e)
1224 case UpdateType.Replace:
1227 this._LayoutLines.UpdateAsReplace(e.startIndex, e.removeLength, e.insertLength);
1228 this.Markers.UpdateMarkers(e.startIndex, e.insertLength, e.removeLength);
1232 this._LayoutLines.UpdateLineAsReplace(e.row.Value, e.removeLength, e.insertLength);
1233 this.Markers.UpdateMarkers(this.LayoutLines.GetIndexFromLineNumber(e.row.Value), e.insertLength, e.removeLength);
1236 case UpdateType.Clear:
1237 this._LayoutLines.Clear();
1240 this.UpdateCalledAlways(this, e);
1241 if(this.FireUpdateEvent)
1242 this.Update(this, e);
1246 public interface IStreamReader
1254 /// ストリームから行を読み取った物を返す。LoadAsyncを呼び出す場合は必ず実装してください
1256 Task<string> ReadLineAsync();
1258 /// ストリームから指定した文字数だけ読み取る
1260 /// <param name="buffer">書き込み先バッファー</param>
1261 /// <param name="index">書き込み先バッファーのインデックス</param>
1262 /// <param name="count">読み取る文字数</param>
1263 /// <returns>読み取った文字数</returns>
1264 Task<int> ReadAsync(char[] buffer, int index, int count);
1267 public interface IStreamWriter
1270 /// ストリームに書き込む。SaveAsyncを呼び出す場合は必ず実装してください
1272 Task WriteAsync(string str);
1287 public class SearchResult
1289 private Match Match;
1306 get { return this.Match.Value; }
1310 /// 指定したパターンを置き換えて返す
1312 /// <param name="replacement">置き換える文字列</param>
1313 /// <returns>置き換え後の文字列</returns>
1314 public string Result(string replacement)
1316 return this.Match.Result(replacement);
1322 /// <param name="m">Matchオブジェクト</param>
1323 /// <param name="start">開始インデックス</param>
1324 /// <param name="end">終了インデックス</param>
1325 public SearchResult(Match m, int start,int end)
1336 public class DocumentReader : TextReader
1338 StringBuffer document;
1344 /// <param name="doc"></param>
1345 internal DocumentReader(StringBuffer doc)
1348 throw new ArgumentNullException();
1349 this.document = doc;
1355 /// <returns>文字。取得できない場合は-1</returns>
1356 public override int Peek()
1358 if (this.document == null)
1359 throw new InvalidOperationException();
1360 if (this.currentIndex >= this.document.Length)
1362 return this.document[this.currentIndex];
1366 /// 文字を取得し、イテレーターを一つ進める
1368 /// <returns>文字。取得できない場合は-1</returns>
1369 public override int Read()
1371 int c = this.Peek();
1373 this.currentIndex++;
1378 /// 文字列を読み取りバッファーに書き込む
1380 /// <param name="buffer">バッファー</param>
1381 /// <param name="index">開始インデックス</param>
1382 /// <param name="count">カウント</param>
1383 /// <returns>読み取られた文字数</returns>
1384 public override int Read(char[] buffer, int index, int count)
1386 if (this.document == null)
1387 throw new InvalidOperationException();
1390 throw new ArgumentNullException();
1392 if (this.document.Length < count)
1393 throw new ArgumentException();
1395 if (index < 0 || count < 0)
1396 throw new ArgumentOutOfRangeException();
1398 if (this.document.Length == 0)
1401 int actualCount = count;
1402 if (index + count - 1 > this.document.Length - 1)
1403 actualCount = this.document.Length - index;
1405 string str = this.document.ToString(index, actualCount);
1407 for (int i = 0; i < str.Length; i++) //ToCharArray()だと戻った時に消えてしまう
1410 this.currentIndex = index + actualCount;
1418 /// <param name="disposing">真ならアンマネージドリソースを解放する</param>
1419 protected override void Dispose(bool disposing)
1421 this.document = null;