OSDN Git Service

初コミット
[fooeditengine/FooEditEngine.git] / Common / LineToIndex.cs
1 /*\r
2  * Copyright (C) 2013 FooProject\r
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\r
4  * the Free Software Foundation; either version 3 of the License, or (at your option) any later version.\r
5 \r
6  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of \r
7  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\r
8 \r
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/>.\r
10  */\r
11 using System;\r
12 using System.Text.RegularExpressions;\r
13 using System.Threading;\r
14 using System.Linq;\r
15 using System.Collections.Generic;\r
16 using System.Diagnostics;\r
17 using Slusser.Collections.Generic;\r
18 \r
19 namespace FooEditEngine\r
20 {\r
21     internal interface ITextLayout : IDisposable\r
22     {\r
23         /// <summary>\r
24         /// 文字列の幅\r
25         /// </summary>\r
26         double Width\r
27         {\r
28             get;\r
29         }\r
30 \r
31         /// <summary>\r
32         /// 文字列の高さ\r
33         /// </summary>\r
34         double Height\r
35         {\r
36             get;\r
37         }\r
38 \r
39         /// <summary>\r
40         /// Disposeされているなら真を返す\r
41         /// </summary>\r
42         bool Disposed\r
43         {\r
44             get;\r
45         }\r
46 \r
47         /// <summary>\r
48         /// 破棄すべきなら真。そうでなければ偽\r
49         /// </summary>\r
50         bool Invaild\r
51         {\r
52             get;\r
53         }\r
54 \r
55         /// <summary>\r
56         /// X座標に対応するインデックスを得る\r
57         /// </summary>\r
58         /// <param name="x">X座標</param>\r
59         /// <returns>インデックス</returns>\r
60         /// <remarks>行番号の幅は考慮されてないのでView以外のクラスは呼び出さないでください</remarks>\r
61         int GetIndexFromX(double x);\r
62 \r
63         /// <summary>\r
64         /// インデックスに対応する文字の幅を得る\r
65         /// </summary>\r
66         /// <param name="index">インデックス</param>\r
67         /// <returns>文字の幅</returns>\r
68         double GetWidthFromIndex(int index);\r
69 \r
70         /// <summary>\r
71         /// インデックスに対応するX座標を得る\r
72         /// </summary>\r
73         /// <param name="index">インデックス</param>\r
74         /// <returns>X座標</returns>\r
75         /// <remarks>行頭にEOFが含まれている場合、0が返ります</remarks>\r
76         double GetXFromIndex(int index);\r
77 \r
78         /// <summary>\r
79         /// 適切な位置にインデックスを調整する\r
80         /// </summary>\r
81         /// <param name="index">インデックス</param>\r
82         /// <param name="flow">真の場合は隣接するクラスターを指すように調整し、\r
83         /// そうでない場合は対応するクラスターの先頭を指すように調整します</param>\r
84         /// <returns>調整後のインデックス</returns>\r
85         int AlignIndexToNearestCluster(int index, AlignDirection flow);\r
86     }\r
87 \r
88     internal class SpilitStringEventArgs : EventArgs\r
89     {\r
90         public Document buffer;\r
91         public int index;\r
92         public int length;\r
93         public int row;\r
94         public SpilitStringEventArgs(Document buf, int index, int length,int row)\r
95         {\r
96             this.buffer = buf;\r
97             this.index = index;\r
98             this.length = length;\r
99             this.row = row;\r
100         }\r
101     }\r
102 \r
103     internal struct SyntaxInfo\r
104     {\r
105         public TokenType type;\r
106         public int index;\r
107         public int length;\r
108         public SyntaxInfo(int index, int length, TokenType type)\r
109         {\r
110             this.type = type;\r
111             this.index = index;\r
112             this.length = length;\r
113         }\r
114     }\r
115 \r
116     internal enum EncloserType\r
117     {\r
118         None,\r
119         Begin,\r
120         Now,\r
121         End,\r
122     }\r
123 \r
124     internal class LineToIndexTableData : IDisposable\r
125     {\r
126         ITextLayout _layout;\r
127         public int Index;\r
128         public int Length;\r
129         public bool LineEnd;\r
130         public SyntaxInfo[] Syntax;\r
131         public EncloserType EncloserType;\r
132         internal ITextLayout Layout\r
133         {\r
134             get\r
135             {\r
136                 if (this._layout != null && this._layout.Invaild)\r
137                 {\r
138                     this._layout.Dispose();\r
139                     this._layout = null;\r
140                 }\r
141                 if (this._layout == null || this._layout.Disposed)\r
142                     this._layout = this.CreateLayout(this.Index, this.Length, this.Syntax);\r
143                 return _layout;\r
144             }\r
145         }\r
146         internal Func<int, int,SyntaxInfo[], ITextLayout> CreateLayout;\r
147         public LineToIndexTableData()\r
148         {\r
149         }\r
150         public LineToIndexTableData(int index, int length, bool lineend, SyntaxInfo[] syntax)\r
151         {\r
152             this.Index = index;\r
153             this.Length = length;\r
154             this.LineEnd = lineend;\r
155             this.Syntax = syntax;\r
156             this.EncloserType = EncloserType.None;\r
157         }\r
158 \r
159         public void Dispose()\r
160         {\r
161             if(this._layout != null)\r
162                 this._layout.Dispose();\r
163         }\r
164     }\r
165 \r
166     internal delegate IList<LineToIndexTableData> SpilitStringEventHandler(object sender, SpilitStringEventArgs e);\r
167 \r
168     /// <summary>\r
169     /// 行番号とインデックスを相互変換するためのクラス\r
170     /// </summary>\r
171     public sealed class LineToIndexTable : IEnumerable<string>\r
172     {\r
173         const int MaxEntries = 100;\r
174         Queue<ITextLayout> CacheEntries = new Queue<ITextLayout>();\r
175         GapBuffer<LineToIndexTableData> Lines = new GapBuffer<LineToIndexTableData>();\r
176         Document Document;\r
177         bool _UrlMarker,_IsSync;\r
178         Regex urlPattern = new Regex("(http|https|ftp)(:\\/\\/[-_.!~*\\'()a-zA-Z0-9;\\/?:\\@&=+\\$,%#]+)");\r
179         ITextRender render;\r
180 \r
181         internal LineToIndexTable(Document buf, ITextRender r)\r
182         {\r
183             this.Document = buf;\r
184             this.Document.Markers.Updated += Markers_Updated;\r
185             this.render = r;\r
186             this.FoldingCollection = new FoldingCollection();\r
187             this._IsSync = true;\r
188         }\r
189 \r
190         void Markers_Updated(object sender, EventArgs e)\r
191         {\r
192             this.ClearLayoutCache();\r
193         }\r
194 \r
195         internal SpilitStringEventHandler SpilitString;\r
196 \r
197         /// <summary>\r
198         /// 行数を返す\r
199         /// </summary>\r
200         public int Count\r
201         {\r
202             get { return this.Lines.Count; }\r
203         }\r
204 \r
205         /// <summary>\r
206         /// 折り畳み関係の情報を収めたコレクション\r
207         /// </summary>\r
208         public FoldingCollection FoldingCollection\r
209         {\r
210             get;\r
211             private set;\r
212         }\r
213 \r
214         /// <summary>\r
215         /// シンタックスハイライター\r
216         /// </summary>\r
217         internal IHilighter Hilighter { get; set; }\r
218 \r
219         internal IFoldingStrategy FoldingStrategy { get; set; }\r
220 \r
221         /// <summary>\r
222         /// Urlに下線を引くなら真\r
223         /// </summary>\r
224         /// <remarks>変更を反映させるためには再描写する必要があります</remarks>\r
225         internal bool UrlMark\r
226         {\r
227             get\r
228             {\r
229                 return this._UrlMarker;\r
230             }\r
231             set\r
232             {\r
233                 this._UrlMarker = value;\r
234                 this.Document.RemoveMarker(HilightType.Url);\r
235                 this.SetUrlMarker(0, this.Count);\r
236             }\r
237         }\r
238 \r
239         /// <summary>\r
240         /// 保持しているレイアウトキャッシュをクリアーする\r
241         /// </summary>\r
242         public void ClearLayoutCache()\r
243         {\r
244             foreach (ITextLayout data in this.CacheEntries)\r
245             {\r
246                 data.Dispose();\r
247             }\r
248             this.CacheEntries.Clear();\r
249         }\r
250 \r
251         /// <summary>\r
252         /// 行番号に対応する文字列を返します\r
253         /// </summary>\r
254         /// <param name="n"></param>\r
255         /// <returns></returns>\r
256         public string this[int n]\r
257         {\r
258             get\r
259             {\r
260                 LineToIndexTableData data = this.Lines[n];\r
261                 string str = this.Document.ToString(data.Index, data.Length);\r
262 \r
263                 return str;\r
264             }\r
265         }\r
266 \r
267         internal void UpdateAsReplace(int index, int removedLength, int insertedLength)\r
268         {\r
269             //削除すべき行の開始位置と終了位置を求める\r
270             int startRow = this.GetLineNumberFromIndex(index);\r
271             if (startRow > 0 && this.Lines[startRow - 1].LineEnd == false)\r
272                 startRow--;\r
273 \r
274             int endRow = this.GetLineNumberFromIndex(index + removedLength);\r
275             while (endRow < this.Lines.Count && this.Lines[endRow].LineEnd == false)\r
276                 endRow++;\r
277             if (endRow >= this.Lines.Count)\r
278                 endRow = this.Lines.Count - 1;\r
279 \r
280             //SpilitStringの対象となる範囲を求める\r
281             int HeadIndex = this.GetIndexFromLineNumber(startRow);\r
282 \r
283             int LastIndex = this.GetIndexFromLineNumber(endRow) + this.GetLengthFromLineNumber(endRow) - 1;\r
284 \r
285             int fisrtPartLength = index - HeadIndex;\r
286 \r
287             int secondPartLength = LastIndex - (index + removedLength - 1);\r
288 \r
289             int analyzeLength = fisrtPartLength + secondPartLength + insertedLength;\r
290 \r
291             System.Diagnostics.Debug.Assert(analyzeLength <= this.Document.Length - 1 - HeadIndex + 1);\r
292 \r
293             //行を削除する\r
294             int removeCount = endRow - startRow + 1;\r
295             for (int i = startRow; i < removeCount; i++)\r
296                 this.Lines[i].Dispose();\r
297 \r
298             this.Lines.RemoveRange(startRow, removeCount);\r
299 \r
300             //行を分割し、削除した位置に挿入する\r
301             SpilitStringEventArgs e = new SpilitStringEventArgs(this.Document, HeadIndex, analyzeLength, startRow);\r
302             IList<LineToIndexTableData> newLines = SpilitString(this, e);\r
303             foreach (LineToIndexTableData data in newLines)\r
304             {\r
305                 data.CreateLayout = this.LineToIndexTableData_CreatLayout;\r
306             }\r
307 \r
308             this.Lines.InsertRange(startRow, newLines);\r
309 \r
310             //挿入された行以降を更新する\r
311             int deltaLength = insertedLength - removedLength;\r
312 \r
313             for (int i = startRow + newLines.Count; i < this.Lines.Count; i++)\r
314             {\r
315                 this.Lines[i].Index += deltaLength;\r
316             }\r
317 \r
318             this.FoldingCollection.UpdateData(this.Document, index, insertedLength, removedLength);\r
319 \r
320             LineToIndexTableData lastLine = this.Lines.Count > 0 ? this.Lines.Last() : null;\r
321             int lastLineIndex = 0;\r
322             if(lastLine != null)\r
323                 lastLineIndex = lastLine.Index + lastLine.Length - 1;\r
324 \r
325             //最終行が削除された場合は追加する\r
326             LineToIndexTableData dummyLine = null;\r
327             if (this.Lines.Count == 0)\r
328             {\r
329                 dummyLine = new LineToIndexTableData();\r
330                 dummyLine.CreateLayout = this.LineToIndexTableData_CreatLayout;\r
331                 this.Lines.Add(dummyLine);\r
332             }\r
333             else\r
334             {\r
335                 if (lastLine.Length != 0 && this.Document[lastLineIndex] == Document.NewLine)\r
336                 {\r
337                     dummyLine = new LineToIndexTableData(lastLine.Index + lastLine.Length, 0, true, null);\r
338                     dummyLine.CreateLayout = this.LineToIndexTableData_CreatLayout;\r
339                     this.Lines.Add(dummyLine);\r
340                 }\r
341             }\r
342 \r
343             this.Hilight(startRow, newLines.Count);\r
344             \r
345             SetUrlMarker(startRow, newLines.Count);\r
346 \r
347             if (lastLine != null)\r
348             {\r
349                 foreach (FoldingItem foldingData in this.FoldingCollection.GetRange(index, lastLineIndex))\r
350                     if (foldingData.Start == foldingData.End)\r
351                         this.FoldingCollection.Remove(foldingData);\r
352             }\r
353 \r
354             this._IsSync = false;\r
355         }\r
356 \r
357         ITextLayout LineToIndexTableData_CreatLayout(int index, int length,SyntaxInfo[] syntax)\r
358         {\r
359             ITextLayout layout;\r
360             if (length == 0)\r
361             {\r
362                 layout = this.render.CreateLaytout("", null, null);\r
363             }\r
364             else\r
365             {\r
366                 var markerRange = from s in this.Document.Markers.Get(index, length)\r
367                                   let n = Util.ConvertAbsIndexToRelIndex(s, index, length)\r
368                                   select n;\r
369                 layout = this.render.CreateLaytout(this.Document.ToString(index, length).TrimEnd(Document.NewLine), syntax, markerRange);\r
370             }\r
371 \r
372             if (this.CacheEntries.Count > MaxEntries)\r
373             {\r
374                 ITextLayout oldItem = this.CacheEntries.Dequeue();\r
375                 oldItem.Dispose();\r
376             }\r
377             this.CacheEntries.Enqueue(layout);\r
378             \r
379             return layout;\r
380         }\r
381 \r
382         /// <summary>\r
383         /// 行番号をインデックスに変換します\r
384         /// </summary>\r
385         /// <param name="row">行番号</param>\r
386         /// <returns>0から始まるインデックスを返す</returns>\r
387         public int GetIndexFromLineNumber(int row)\r
388         {\r
389             if (row < 0 || row > this.Lines.Count)\r
390                 throw new ArgumentOutOfRangeException();\r
391             return this.Lines[row].Index;\r
392         }\r
393 \r
394         /// <summary>\r
395         /// 行の長さを得ます\r
396         /// </summary>\r
397         /// <param name="row">行番号</param>\r
398         /// <returns>行の文字長を返します</returns>\r
399         public int GetLengthFromLineNumber(int row)\r
400         {\r
401             if (row < 0 || row > this.Lines.Count)\r
402                 throw new ArgumentOutOfRangeException();\r
403             return this.Lines[row].Length;\r
404         }\r
405 \r
406         internal ITextLayout GetLayout(int row)\r
407         {\r
408             return this.Lines[row].Layout;\r
409         }\r
410 \r
411         internal LineToIndexTableData GetData(int row)\r
412         {\r
413             if (row < 0 || row > this.Lines.Count)\r
414                 throw new ArgumentOutOfRangeException();\r
415             return this.Lines[row];\r
416         }\r
417 \r
418         int lastLineNumber;\r
419         /// <summary>\r
420         /// インデックスを行番号に変換します\r
421         /// </summary>\r
422         /// <param name="index">インデックス</param>\r
423         /// <returns>行番号を返します</returns>\r
424         public int GetLineNumberFromIndex(int index)\r
425         {\r
426             if (index < 0)\r
427                 throw new ArgumentOutOfRangeException("indexに負の値を設定することはできません");\r
428             \r
429             if (index == 0 && this.Lines.Count > 0)\r
430                 return 0;\r
431 \r
432             LineToIndexTableData line;\r
433 \r
434             if (lastLineNumber < this.Lines.Count - 1)\r
435             {\r
436                 line = this.Lines[lastLineNumber];\r
437                 if (index >= line.Index && index < line.Index + line.Length)\r
438                     return lastLineNumber;\r
439             }\r
440 \r
441             int left = 0, right = this.Lines.Count - 1, mid;\r
442             while (left <= right)\r
443             {\r
444                 mid = (left + right) / 2;\r
445                 line = this.Lines[mid];\r
446                 if (index >= line.Index && index < line.Index + line.Length)\r
447                 {\r
448                     lastLineNumber = mid;\r
449                     return mid;\r
450                 }\r
451                 if (index < line.Index)\r
452                 {\r
453                     right = mid - 1;\r
454                 }\r
455                 else\r
456                 {\r
457                     left = mid + 1;\r
458                 }\r
459             }\r
460 \r
461             line = this.Lines.Last();\r
462             if (index >= line.Index && index <= line.Index + line.Length)   //最終行長+1までキャレットが移動する可能性があるので\r
463             {\r
464                 lastLineNumber = this.Lines.Count - 1;\r
465                 return lastLineNumber;\r
466             }\r
467             \r
468             throw new ArgumentOutOfRangeException("該当する行が見つかりませんでした");\r
469         }\r
470 \r
471         /// <summary>\r
472         /// インデックスからテキストポイントに変換します\r
473         /// </summary>\r
474         /// <param name="index">インデックス</param>\r
475         /// <returns>TextPoint構造体を返します</returns>\r
476         public TextPoint GetTextPointFromIndex(int index)\r
477         {\r
478             TextPoint tp = new TextPoint();\r
479             tp.row = GetLineNumberFromIndex(index);\r
480             tp.col = index - this.Lines[tp.row].Index;\r
481             Debug.Assert(tp.row < this.Lines.Count && tp.col <= this.Lines[tp.row].Length);\r
482             return tp;\r
483         }\r
484 \r
485         /// <summary>\r
486         /// テキストポイントからインデックスに変換します\r
487         /// </summary>\r
488         /// <param name="tp">TextPoint構造体</param>\r
489         /// <returns>インデックスを返します</returns>\r
490         public int GetIndexFromTextPoint(TextPoint tp)\r
491         {\r
492             if (tp == TextPoint.Null)\r
493                 throw new ArgumentNullException("TextPoint.Null以外の値でなければなりません");\r
494             if(tp.row < 0 || tp.row > this.Lines.Count)\r
495                 throw new ArgumentOutOfRangeException("tp.rowが設定できる範囲を超えています");\r
496             if (tp.col < 0 || tp.col > this.Lines[tp.row].Length)\r
497                 throw new ArgumentOutOfRangeException("tp.colが設定できる範囲を超えています");\r
498             return this.Lines[tp.row].Index + tp.col;\r
499         }\r
500 \r
501         /// <summary>\r
502         /// フォールディングを再生成します\r
503         /// </summary>\r
504         /// <param name="force">ドキュメントが更新されていなくても再生成する</param>\r
505         /// <returns>生成された場合は真を返す</returns>\r
506         /// <remarks>デフォルトではドキュメントが更新されている時にだけ再生成されます</remarks>\r
507         public bool GenerateFolding(bool force = false)\r
508         {\r
509             if (force)\r
510                 this._IsSync = false;\r
511             if (this.Document.Length == 0 || this._IsSync)\r
512                 return false;\r
513             this.GenerateFolding(0, this.Document.Length - 1);\r
514             this._IsSync = true;\r
515             return true;\r
516         }\r
517 \r
518         void GenerateFolding(int start, int end)\r
519         {\r
520             if (start > end)\r
521                 throw new ArgumentException("start <= endである必要があります");\r
522             if (this.FoldingStrategy != null)\r
523             {\r
524                 var items = this.FoldingStrategy.AnalyzeDocument(this.Document, start, end)\r
525                     .Where((item) =>\r
526                     {\r
527                         int startRow = this.GetLineNumberFromIndex(item.Start);\r
528                         int endRow = this.GetLineNumberFromIndex(item.End);\r
529                         return startRow != endRow;\r
530                     })\r
531                     .Select((item) => item);\r
532                 this.FoldingCollection.AddRange(items);\r
533             }\r
534         }\r
535 \r
536         /// <summary>\r
537         /// フォールディングをすべて削除します\r
538         /// </summary>\r
539         public void ClearFolding()\r
540         {\r
541             this.FoldingCollection.Clear();\r
542             this._IsSync = false;\r
543         }\r
544 \r
545         /// <summary>\r
546         /// すべての行に対しシンタックスハイライトを行います\r
547         /// </summary>\r
548         public void HilightAll()\r
549         {\r
550             this.Hilight(0, this.Lines.Count);\r
551             SetUrlMarker(0, this.Lines.Count);\r
552             this.ClearLayoutCache();\r
553         }\r
554 \r
555         /// <summary>\r
556         /// ハイライト関連の情報をすべて削除します\r
557         /// </summary>\r
558         public void ClearHilight()\r
559         {\r
560             foreach (LineToIndexTableData line in this.Lines)\r
561                 line.Syntax = null;\r
562             this.ClearLayoutCache();\r
563         }\r
564 \r
565         /// <summary>\r
566         /// すべて削除します\r
567         /// </summary>\r
568         internal void Clear()\r
569         {\r
570             this.ClearLayoutCache();\r
571             this.FoldingCollection.Clear();\r
572             this.Lines.Clear();\r
573             LineToIndexTableData dummy = new LineToIndexTableData();\r
574             dummy.CreateLayout = this.LineToIndexTableData_CreatLayout;\r
575             this.Lines.Add(dummy);\r
576         }\r
577 \r
578         void Hilight(int row, int rowCount)\r
579         {\r
580             if (this.Hilighter == null || rowCount == 0)\r
581                 return;\r
582 \r
583             //シンタックスハイライトの開始行を求める\r
584             int startRow = row;\r
585             EncloserType type = this.Lines[startRow].EncloserType;\r
586             EncloserType prevLineType = startRow > 0 ? this.Lines[startRow - 1].EncloserType : EncloserType.None;\r
587             if (type == EncloserType.Now || type == EncloserType.End ||\r
588                 prevLineType == EncloserType.Now || prevLineType == EncloserType.End)\r
589                 startRow = SearchStartRow(startRow);\r
590             else if (prevLineType == EncloserType.Begin)\r
591                 startRow = startRow - 1;\r
592 \r
593             //シンタックスハイライトの終了行を求める\r
594             int endRow = row + rowCount - 1;\r
595             type = this.Lines[endRow].EncloserType;\r
596             prevLineType = endRow > 0 ? this.Lines[endRow - 1].EncloserType : EncloserType.None;\r
597             if (type == EncloserType.Begin || type == EncloserType.Now ||\r
598                 prevLineType == EncloserType.Begin || prevLineType == EncloserType.Now)\r
599                 endRow = SearchEndRow(endRow);\r
600             else if (endRow + 1 <= this.Lines.Count - 1 && this.Lines[endRow + 1].EncloserType == EncloserType.Now)\r
601                 endRow = SearchEndRow(endRow + 1);\r
602 \r
603             //シンタックスハイライトを行う\r
604             bool hasBeginEncloser = false;\r
605             int i;\r
606             for (i = startRow; i <= endRow; i++)\r
607             {\r
608                 this.HilightLine(i, ref hasBeginEncloser);\r
609             }\r
610 \r
611             if (hasBeginEncloser)   //終了エンクロージャーが見つかったかどうか\r
612             {\r
613                 for (; i < this.Lines.Count; i++)\r
614                 {\r
615                     if (this.HilightLine(i, ref hasBeginEncloser) < 0)\r
616                         break;\r
617                 }\r
618             }\r
619 \r
620             this.Hilighter.Reset();\r
621         }\r
622 \r
623         private int HilightLine(int row, ref bool hasBeginEncloser)\r
624         {\r
625             //シンタックスハイライトを行う\r
626             List<SyntaxInfo> syntax = new List<SyntaxInfo>();\r
627             string str = this[row];\r
628             int level = this.Hilighter.DoHilight(str, str.Length, (s) =>\r
629             {\r
630                 if (s.type == TokenType.None || s.type == TokenType.Control)\r
631                     return;\r
632                 if (str[s.index + s.length - 1] == Document.NewLine)\r
633                     s.length--;\r
634                 syntax.Add(new SyntaxInfo(s.index, s.length, s.type));\r
635             });\r
636 \r
637             LineToIndexTableData lineData = this.GetData(row);\r
638             lineData.Syntax = syntax.ToArray();\r
639 \r
640             if (level > 0 && hasBeginEncloser == false)  //開始エンクロージャー\r
641             {\r
642                 lineData.EncloserType = EncloserType.Begin;\r
643                 hasBeginEncloser = true;\r
644             }\r
645             else if (level < 0) //終了エンクロージャー\r
646             {\r
647                 lineData.EncloserType = EncloserType.End;\r
648                 hasBeginEncloser = false;\r
649             }\r
650             else if (hasBeginEncloser) //エンクロージャーの範囲内\r
651                 lineData.EncloserType = EncloserType.Now;\r
652             else\r
653                 lineData.EncloserType = EncloserType.None;\r
654 \r
655             return level;\r
656         }\r
657 \r
658         private int SearchStartRow(int startRow)\r
659         {\r
660             for (startRow--; startRow >= 0; startRow--)\r
661             {\r
662                 EncloserType type = this.Lines[startRow].EncloserType;\r
663                 if (type == EncloserType.Begin || type == EncloserType.None)\r
664                     return startRow;\r
665             }\r
666             return 0;\r
667         }\r
668 \r
669         private int SearchEndRow(int startRow)\r
670         {\r
671             for (startRow++ ; startRow < this.Lines.Count; startRow++)\r
672             {\r
673                 EncloserType type = this.Lines[startRow].EncloserType;\r
674                 if (type == EncloserType.End)\r
675                     return startRow;\r
676             }\r
677             return this.Lines.Count - 1;\r
678         }\r
679 \r
680         void SetUrlMarker(int row, int count)\r
681         {\r
682             if (this.UrlMark == false)\r
683                 return;\r
684 \r
685             int startRow = row;\r
686             int endRow = row + count - 1;\r
687 \r
688             for (int i = startRow; i <= endRow; i++)\r
689             {\r
690                 Match m = this.urlPattern.Match(this[i]);\r
691 \r
692                 if (m.Success)\r
693                 {\r
694                     int lineHeadIndex = this.GetIndexFromLineNumber(i);\r
695                     int start = lineHeadIndex + m.Index;\r
696                     this.Document.RemoveMarker(start, 1);\r
697                     this.Document.Markers.Add(Marker.Create(start, m.Length, HilightType.Url));\r
698                 }\r
699             }\r
700         }\r
701 \r
702         #region IEnumerable<string> メンバー\r
703 \r
704         /// <summary>\r
705         /// コレクションを反復処理するためのIEnumeratorを返す\r
706         /// </summary>\r
707         /// <returns>IEnumeratorオブジェクト</returns>\r
708         public IEnumerator<string> GetEnumerator()\r
709         {\r
710             for (int i = 0; i < this.Lines.Count; i++)\r
711                 yield return this[i];\r
712         }\r
713 \r
714         #endregion\r
715 \r
716         #region IEnumerable メンバー\r
717 \r
718         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()\r
719         {\r
720             for (int i = 0; i < this.Lines.Count; i++)\r
721                 yield return this[i];\r
722         }\r
723 \r
724         #endregion\r
725     }\r
726 \r
727 }\r