Code Coverage Statistics for Source File

c:\Tools\SD3\src\Libraries\ICSharpCode.TextEditor\Project\Src\Document\HighlightingStrategy\DefaultHighlightingStrategy.cs

Sequence Point Coverage
N/A
0 of 0
Branch Coverage
N/A
0 of 0
Lines
906
Highlight: Uncovered Code Covered Code
L V Source
1
// <file>
2
//     <copyright see="prj:///doc/copyright.txt"/>
3
//     <license see="prj:///doc/license.txt"/>
4
//     <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
5
//     <version>$Revision: 2683 $</version>
6
// </file>
7
8
using System;
9
using System.Collections.Generic;
10
using System.Text;
11
using System.Drawing;
12
using System.Windows.Forms;
13
14
namespace ICSharpCode.TextEditor.Document
15
{
16
	public class DefaultHighlightingStrategy : IHighlightingStrategy
17
	{
18
		string    name;
19
		List<HighlightRuleSet> rules = new List<HighlightRuleSet>();
20
		
21
		Dictionary<string, HighlightColor> environmentColors = new Dictionary<string, HighlightColor>();
22
		Dictionary<string, string> properties       = new Dictionary<string, string>();
23
		string[]  extensions;
24
		
25
		HighlightColor   digitColor;
26
		HighlightRuleSet defaultRuleSet = null;
27
		
28
		public HighlightColor DigitColor {
29
			get {
30
				return digitColor;
31
			}
32
			set {
33
				digitColor = value;
34
			}
35
		}
36
		
37
		public IEnumerable<KeyValuePair<string, HighlightColor>> EnvironmentColors {
38
			get {
39
				return environmentColors;
40
			}
41
		}
42
		
43
		protected void ImportSettingsFrom(DefaultHighlightingStrategy source)
44
		{
45
			if (source == null)
46
				throw new ArgumentNullException("source");
47
			properties = source.properties;
48
			extensions = source.extensions;
49
			digitColor = source.digitColor;
50
			defaultRuleSet = source.defaultRuleSet;
51
			name = source.name;
52
			rules = source.rules;
53
			environmentColors = source.environmentColors;
54
			defaultTextColor = source.defaultTextColor;
55
		}
56
		
57
		public DefaultHighlightingStrategy() : this("Default")
58
		{
59
		}
60
		
61
		public DefaultHighlightingStrategy(string name)
62
		{
63
			this.name = name;
64
			
65
			digitColor       = new HighlightColor(SystemColors.WindowText, false, false);
66
			defaultTextColor = new HighlightColor(SystemColors.WindowText, false, false);
67
			
68
			// set small 'default color environment'
69
			environmentColors["Default"]          = new HighlightBackground("WindowText", "Window", false, false);
70
			environmentColors["Selection"]        = new HighlightColor("HighlightText", "Highlight", false, false);
71
			environmentColors["VRuler"]           = new HighlightColor("ControlLight", "Window", false, false);
72
			environmentColors["InvalidLines"]     = new HighlightColor(Color.Red, false, false);
73
			environmentColors["CaretMarker"]      = new HighlightColor(Color.Yellow, false, false);
74
			environmentColors["LineNumbers"]      = new HighlightBackground("ControlDark", "Window", false, false);
75
			
76
			environmentColors["FoldLine"]         = new HighlightColor(Color.FromArgb(0x80, 0x80, 0x80), Color.Black, false, false);
77
			environmentColors["FoldMarker"]       = new HighlightColor(Color.FromArgb(0x80, 0x80, 0x80), Color.White, false, false);
78
			environmentColors["SelectedFoldLine"] = new HighlightColor(Color.Black, false, false);
79
			environmentColors["EOLMarkers"]       = new HighlightColor("ControlLight", "Window", false, false);
80
			environmentColors["SpaceMarkers"]     = new HighlightColor("ControlLight", "Window", false, false);
81
			environmentColors["TabMarkers"]       = new HighlightColor("ControlLight", "Window", false, false);
82
			
83
		}
84
		
85
		public Dictionary<string, string> Properties {
86
			get {
87
				return properties;
88
			}
89
		}
90
		
91
		public string Name
92
		{
93
			get {
94
				return name;
95
			}
96
		}
97
		
98
		public string[] Extensions
99
		{
100
			set {
101
				extensions = value;
102
			}
103
			get {
104
				return extensions;
105
			}
106
		}
107
		
108
		public List<HighlightRuleSet> Rules {
109
			get {
110
				return rules;
111
			}
112
		}
113
		
114
		public HighlightRuleSet FindHighlightRuleSet(string name)
115
		{
116
			foreach(HighlightRuleSet ruleSet in rules) {
117
				if (ruleSet.Name == name) {
118
					return ruleSet;
119
				}
120
			}
121
			return null;
122
		}
123
		
124
		public void AddRuleSet(HighlightRuleSet aRuleSet)
125
		{
126
			HighlightRuleSet existing = FindHighlightRuleSet(aRuleSet.Name);
127
			if (existing != null) {
128
				existing.MergeFrom(aRuleSet);
129
			} else {
130
				rules.Add(aRuleSet);
131
			}
132
		}
133
		
134
		public void ResolveReferences()
135
		{
136
			// Resolve references from Span definitions to RuleSets
137
			ResolveRuleSetReferences();
138
			// Resolve references from RuleSet defintitions to Highlighters defined in an external mode file
139
			ResolveExternalReferences();
140
		}
141
		
142
		void ResolveRuleSetReferences()
143
		{
144
			foreach (HighlightRuleSet ruleSet in Rules) {
145
				if (ruleSet.Name == null) {
146
					defaultRuleSet = ruleSet;
147
				}
148
				
149
				foreach (Span aSpan in ruleSet.Spans) {
150
					if (aSpan.Rule != null) {
151
						bool found = false;
152
						foreach (HighlightRuleSet refSet in Rules) {
153
							if (refSet.Name == aSpan.Rule) {
154
								found = true;
155
								aSpan.RuleSet = refSet;
156
								break;
157
							}
158
						}
159
						if (!found) {
160
							aSpan.RuleSet = null;
161
							throw new HighlightingDefinitionInvalidException("The RuleSet " + aSpan.Rule + " could not be found in mode definition " + this.Name);
162
						}
163
					} else {
164
						aSpan.RuleSet = null;
165
					}
166
				}
167
			}
168
			
169
			if (defaultRuleSet == null) {
170
				throw new HighlightingDefinitionInvalidException("No default RuleSet is defined for mode definition " + this.Name);
171
			}
172
		}
173
		
174
		void ResolveExternalReferences()
175
		{
176
			foreach (HighlightRuleSet ruleSet in Rules) {
177
				if (ruleSet.Reference != null) {
178
					IHighlightingStrategy highlighter = HighlightingManager.Manager.FindHighlighter (ruleSet.Reference);
179
					
180
					if (highlighter != null) {
181
						ruleSet.Highlighter = highlighter;
182
					} else {
183
						ruleSet.Highlighter = this;
184
						throw new HighlightingDefinitionInvalidException("The mode defintion " + ruleSet.Reference + " which is refered from the " + this.Name + " mode definition could not be found");
185
					}
186
				} else {
187
					ruleSet.Highlighter = this;
188
				}
189
			}
190
		}
191
		
192
//		internal void SetDefaultColor(HighlightBackground color)
193
//		{
194
//			return (HighlightColor)environmentColors[name];
195
//			defaultColor = color;
196
//		}
197
		
198
		HighlightColor defaultTextColor;
199
		
200
		public HighlightColor DefaultTextColor {
201
			get {
202
				return defaultTextColor;
203
			}
204
		}
205
		
206
		public void SetColorFor(string name, HighlightColor color)
207
		{
208
			if (name == "Default")
209
				defaultTextColor = new HighlightColor(color.Color, color.Bold, color.Italic);
210
			environmentColors[name] = color;
211
		}
212
213
		public HighlightColor GetColorFor(string name)
214
		{
215
			HighlightColor color;
216
			if (environmentColors.TryGetValue(name, out color))
217
				return color;
218
			else
219
				return defaultTextColor;
220
		}
221
		
222
		public HighlightColor GetColor(IDocument document, LineSegment currentSegment, int currentOffset, int currentLength)
223
		{
224
			return GetColor(defaultRuleSet, document, currentSegment, currentOffset, currentLength);
225
		}
226
227
		protected virtual HighlightColor GetColor(HighlightRuleSet ruleSet, IDocument document, LineSegment currentSegment, int currentOffset, int currentLength)
228
		{
229
			if (ruleSet != null) {
230
				if (ruleSet.Reference != null) {
231
					return ruleSet.Highlighter.GetColor(document, currentSegment, currentOffset, currentLength);
232
				} else {
233
					return (HighlightColor)ruleSet.KeyWords[document,  currentSegment, currentOffset, currentLength];
234
				}
235
			}
236
			return null;
237
		}
238
		
239
		public HighlightRuleSet GetRuleSet(Span aSpan)
240
		{
241
			if (aSpan == null) {
242
				return this.defaultRuleSet;
243
			} else {
244
				if (aSpan.RuleSet != null)
245
				{
246
					if (aSpan.RuleSet.Reference != null) {
247
						return aSpan.RuleSet.Highlighter.GetRuleSet(null);
248
					} else {
249
						return aSpan.RuleSet;
250
					}
251
				} else {
252
					return null;
253
				}
254
			}
255
		}
256
257
		// Line state variable
258
		protected LineSegment currentLine;
259
		protected int currentLineNumber;
260
		
261
		// Span stack state variable
262
		protected SpanStack currentSpanStack;
263
264
		public virtual void MarkTokens(IDocument document)
265
		{
266
			if (Rules.Count == 0) {
267
				return;
268
			}
269
			
270
			int lineNumber = 0;
271
			
272
			while (lineNumber < document.TotalNumberOfLines) {
273
				LineSegment previousLine = (lineNumber > 0 ? document.GetLineSegment(lineNumber - 1) : null);
274
				if (lineNumber >= document.LineSegmentCollection.Count) { // may be, if the last line ends with a delimiter
275
					break;                                                // then the last line is not in the collection :)
276
				}
277
				
278
				currentSpanStack = ((previousLine != null && previousLine.HighlightSpanStack != null) ? previousLine.HighlightSpanStack.Clone() : null);
279
				
280
				if (currentSpanStack != null) {
281
					while (!currentSpanStack.IsEmpty && currentSpanStack.Peek().StopEOL)
282
					{
283
						currentSpanStack.Pop();
284
					}
285
					if (currentSpanStack.IsEmpty) currentSpanStack = null;
286
				}
287
				
288
				currentLine = (LineSegment)document.LineSegmentCollection[lineNumber];
289
				
290
				if (currentLine.Length == -1) { // happens when buffer is empty !
291
					return;
292
				}
293
				
294
				currentLineNumber = lineNumber;
295
				List<TextWord> words = ParseLine(document);
296
				// Alex: clear old words
297
				if (currentLine.Words != null) {
298
					currentLine.Words.Clear();
299
				}
300
				currentLine.Words = words;
301
				currentLine.HighlightSpanStack = (currentSpanStack==null || currentSpanStack.IsEmpty) ? null : currentSpanStack;
302
				
303
				++lineNumber;
304
			}
305
			document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
306
			document.CommitUpdate();
307
			currentLine = null;
308
		}
309
		
310
		bool MarkTokensInLine(IDocument document, int lineNumber, ref bool spanChanged)
311
		{
312
			currentLineNumber = lineNumber;
313
			bool processNextLine = false;
314
			LineSegment previousLine = (lineNumber > 0 ? document.GetLineSegment(lineNumber - 1) : null);
315
			
316
			currentSpanStack = ((previousLine != null && previousLine.HighlightSpanStack != null) ? previousLine.HighlightSpanStack.Clone() : null);
317
			if (currentSpanStack != null) {
318
				while (!currentSpanStack.IsEmpty && currentSpanStack.Peek().StopEOL) {
319
					currentSpanStack.Pop();
320
				}
321
				if (currentSpanStack.IsEmpty) {
322
					currentSpanStack = null;
323
				}
324
			}
325
			
326
			currentLine = (LineSegment)document.LineSegmentCollection[lineNumber];
327
			
328
			if (currentLine.Length == -1) { // happens when buffer is empty !
329
				return false;
330
			}
331
			
332
			List<TextWord> words = ParseLine(document);
333
			
334
			if (currentSpanStack != null && currentSpanStack.IsEmpty) {
335
				currentSpanStack = null;
336
			}
337
			
338
			// Check if the span state has changed, if so we must re-render the next line
339
			// This check may seem utterly complicated but I didn't want to introduce any function calls
340
			// or allocations here for perf reasons.
341
			if(currentLine.HighlightSpanStack != currentSpanStack) {
342
				if (currentLine.HighlightSpanStack == null) {
343
					processNextLine = false;
344
					foreach (Span sp in currentSpanStack) {
345
						if (!sp.StopEOL) {
346
							spanChanged = true;
347
							processNextLine = true;
348
							break;
349
						}
350
					}
351
				} else if (currentSpanStack == null) {
352
					processNextLine = false;
353
					foreach (Span sp in currentLine.HighlightSpanStack) {
354
						if (!sp.StopEOL) {
355
							spanChanged = true;
356
							processNextLine = true;
357
							break;
358
						}
359
					}
360
				} else {
361
					SpanStack.Enumerator e1 = currentSpanStack.GetEnumerator();
362
					SpanStack.Enumerator e2 = currentLine.HighlightSpanStack.GetEnumerator();
363
					bool done = false;
364
					while (!done) {
365
						bool blockSpanIn1 = false;
366
						while (e1.MoveNext()) {
367
							if (!((Span)e1.Current).StopEOL) {
368
								blockSpanIn1 = true;
369
								break;
370
							}
371
						}
372
						bool blockSpanIn2 = false;
373
						while (e2.MoveNext()) {
374
							if (!((Span)e2.Current).StopEOL) {
375
								blockSpanIn2 = true;
376
								break;
377
							}
378
						}
379
						if (blockSpanIn1 || blockSpanIn2) {
380
							if (blockSpanIn1 && blockSpanIn2) {
381
								if (e1.Current != e2.Current) {
382
									done = true;
383
									processNextLine = true;
384
									spanChanged = true;
385
								}
386
							} else {
387
								spanChanged = true;
388
								done = true;
389
								processNextLine = true;
390
							}
391
						} else {
392
							done = true;
393
							processNextLine = false;
394
						}
395
					}
396
				}
397
			} else {
398
				processNextLine = false;
399
			}
400
			
401
			//// Alex: remove old words
402
			if (currentLine.Words!=null) currentLine.Words.Clear();
403
			currentLine.Words = words;
404
			currentLine.HighlightSpanStack = (currentSpanStack != null && !currentSpanStack.IsEmpty) ? currentSpanStack : null;
405
			
406
			return processNextLine;
407
		}
408
		
409
		public virtual void MarkTokens(IDocument document, List<LineSegment> inputLines)
410
		{
411
			if (Rules.Count == 0) {
412
				return;
413
			}
414
			
415
			Dictionary<LineSegment, bool> processedLines = new Dictionary<LineSegment, bool>();
416
			
417
			bool spanChanged = false;
418
			int documentLineSegmentCount = document.LineSegmentCollection.Count;
419
			
420
			foreach (LineSegment lineToProcess in inputLines) {
421
				if (!processedLines.ContainsKey(lineToProcess)) {
422
					int lineNumber = lineToProcess.LineNumber;
423
					bool processNextLine = true;
424
					
425
					if (lineNumber != -1) {
426
						while (processNextLine && lineNumber < documentLineSegmentCount) {
427
							processNextLine = MarkTokensInLine(document, lineNumber, ref spanChanged);
428
							processedLines[currentLine] = true;
429
							++lineNumber;
430
						}
431
					}
432
				}
433
			}
434
			
435
			if (spanChanged || inputLines.Count > 20) {
436
				// if the span was changed (more than inputLines lines had to be reevaluated)
437
				// or if there are many lines in inputLines, it's faster to update the whole
438
				// text area instead of many small segments
439
				document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.WholeTextArea));
440
			} else {
441
//				document.Caret.ValidateCaretPos();
442
//				document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, document.GetLineNumberForOffset(document.Caret.Offset)));
443
//
444
				foreach (LineSegment lineToProcess in inputLines) {
445
					document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, lineToProcess.LineNumber));
446
				}
447
				
448
			}
449
			document.CommitUpdate();
450
			currentLine = null;
451
		}
452
		
453
		// Span state variables
454
		protected bool inSpan;
455
		protected Span activeSpan;
456
		protected HighlightRuleSet activeRuleSet;
457
		
458
		// Line scanning state variables
459
		protected int currentOffset;
460
		protected int currentLength;
461
		
462
		void UpdateSpanStateVariables()
463
		{
464
			inSpan = (currentSpanStack != null && !currentSpanStack.IsEmpty);
465
			activeSpan = inSpan ? currentSpanStack.Peek() : null;
466
			activeRuleSet = GetRuleSet(activeSpan);
467
		}
468
469
		List<TextWord> ParseLine(IDocument document)
470
		{
471
			List<TextWord> words = new List<TextWord>();
472
			HighlightColor markNext = null;
473
			
474
			currentOffset = 0;
475
			currentLength = 0;
476
			UpdateSpanStateVariables();
477
			
478
			int currentLineLength = currentLine.Length;
479
			int currentLineOffset = currentLine.Offset;
480
			
481
			for (int i = 0; i < currentLineLength; ++i) {
482
				char ch = document.GetCharAt(currentLineOffset + i);
483
				switch (ch) {
484
					case '\n':
485
					case '\r':
486
						PushCurWord(document, ref markNext, words);
487
						++currentOffset;
488
						break;
489
					case ' ':
490
						PushCurWord(document, ref markNext, words);
491
						if (activeSpan != null && activeSpan.Color.HasBackground) {
492
							words.Add(new TextWord.SpaceTextWord(activeSpan.Color));
493
						} else {
494
							words.Add(TextWord.Space);
495
						}
496
						++currentOffset;
497
						break;
498
					case '\t':
499
						PushCurWord(document, ref markNext, words);
500
						if (activeSpan != null && activeSpan.Color.HasBackground) {
501
							words.Add(new TextWord.TabTextWord(activeSpan.Color));
502
						} else {
503
							words.Add(TextWord.Tab);
504
						}
505
						++currentOffset;
506
						break;
507
					default:
508
						{
509
							// handle escape characters
510
							char escapeCharacter = '\0';
511
							if (activeSpan != null && activeSpan.EscapeCharacter != '\0') {
512
								escapeCharacter = activeSpan.EscapeCharacter;
513
							} else if (activeRuleSet != null) {
514
								escapeCharacter = activeRuleSet.EscapeCharacter;
515
							}
516
							if (escapeCharacter != '\0' && escapeCharacter == ch) {
517
								// we found the escape character
518
								if (activeSpan != null && activeSpan.End != null && activeSpan.End.Length == 1
519
								    && escapeCharacter == activeSpan.End[0])
520
								{
521
									// the escape character is a end-doubling escape character
522
									// it may count as escape only when the next character is the escape, too
523
									if (i + 1 < currentLineLength) {
524
										if (document.GetCharAt(currentLineOffset + i + 1) == escapeCharacter) {
525
											currentLength += 2;
526
											PushCurWord(document, ref markNext, words);
527
											++i;
528
											continue;
529
										}
530
									}
531
								} else {
532
									// this is a normal \-style escape
533
									++currentLength;
534
									if (i + 1 < currentLineLength) {
535
										++currentLength;
536
									}
537
									PushCurWord(document, ref markNext, words);
538
									++i;
539
									continue;
540
								}
541
							}
542
							
543
							// highlight digits
544
							if (!inSpan && (Char.IsDigit(ch) || (ch == '.' && i + 1 < currentLineLength && Char.IsDigit(document.GetCharAt(currentLineOffset + i + 1)))) && currentLength == 0) {
545
								bool ishex = false;
546
								bool isfloatingpoint = false;
547
								
548
								if (ch == '0' && i + 1 < currentLineLength && Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'X') { // hex digits
549
									const string hex = "0123456789ABCDEF";
550
									++currentLength;
551
									++i; // skip 'x'
552
									++currentLength;
553
									ishex = true;
554
									while (i + 1 < currentLineLength && hex.IndexOf(Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1))) != -1) {
555
										++i;
556
										++currentLength;
557
									}
558
								} else {
559
									++currentLength;
560
									while (i + 1 < currentLineLength && Char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) {
561
										++i;
562
										++currentLength;
563
									}
564
								}
565
								if (!ishex && i + 1 < currentLineLength && document.GetCharAt(currentLineOffset + i + 1) == '.') {
566
									isfloatingpoint = true;
567
									++i;
568
									++currentLength;
569
									while (i + 1 < currentLineLength && Char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) {
570
										++i;
571
										++currentLength;
572
									}
573
								}
574
								
575
								if (i + 1 < currentLineLength && Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'E') {
576
									isfloatingpoint = true;
577
									++i;
578
									++currentLength;
579
									if (i + 1 < currentLineLength && (document.GetCharAt(currentLineOffset + i + 1) == '+' || document.GetCharAt(currentLine.Offset + i + 1) == '-')) {
580
										++i;
581
										++currentLength;
582
									}
583
									while (i + 1 < currentLine.Length && Char.IsDigit(document.GetCharAt(currentLineOffset + i + 1))) {
584
										++i;
585
										++currentLength;
586
									}
587
								}
588
								
589
								if (i + 1 < currentLine.Length) {
590
									char nextch = Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1));
591
									if (nextch == 'F' || nextch == 'M' || nextch == 'D') {
592
										isfloatingpoint = true;
593
										++i;
594
										++currentLength;
595
									}
596
								}
597
								
598
								if (!isfloatingpoint) {
599
									bool isunsigned = false;
600
									if (i + 1 < currentLineLength && Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'U') {
601
										++i;
602
										++currentLength;
603
										isunsigned = true;
604
									}
605
									if (i + 1 < currentLineLength && Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'L') {
606
										++i;
607
										++currentLength;
608
										if (!isunsigned && i + 1 < currentLineLength && Char.ToUpper(document.GetCharAt(currentLineOffset + i + 1)) == 'U') {
609
											++i;
610
											++currentLength;
611
										}
612
									}
613
								}
614
								
615
								words.Add(new TextWord(document, currentLine, currentOffset, currentLength, DigitColor, false));
616
								currentOffset += currentLength;
617
								currentLength = 0;
618
								continue;
619
							}
620
621
							// Check for SPAN ENDs
622
							if (inSpan) {
623
								if (activeSpan.End != null && activeSpan.End.Length > 0) {
624
									if (MatchExpr(currentLine, activeSpan.End, i, document, activeSpan.IgnoreCase)) {
625
										PushCurWord(document, ref markNext, words);
626
										string regex = GetRegString(currentLine, activeSpan.End, i, document);
627
										currentLength += regex.Length;
628
										words.Add(new TextWord(document, currentLine, currentOffset, currentLength, activeSpan.EndColor, false));
629
										currentOffset += currentLength;
630
										currentLength = 0;
631
										i += regex.Length - 1;
632
										currentSpanStack.Pop();
633
										UpdateSpanStateVariables();
634
										continue;
635
									}
636
								}
637
							}
638
							
639
							// check for SPAN BEGIN
640
							if (activeRuleSet != null) {
641
								foreach (Span span in activeRuleSet.Spans) {
642
									if ((!span.IsBeginSingleWord || currentLength == 0)
643
									    && (!span.IsBeginStartOfLine.HasValue || span.IsBeginStartOfLine.Value == (currentLength == 0 && words.TrueForAll(delegate(TextWord textWord) { return textWord.Type != TextWordType.Word; })))
644
									    && MatchExpr(currentLine, span.Begin, i, document, activeRuleSet.IgnoreCase)) {
645
										PushCurWord(document, ref markNext, words);
646
										string regex = GetRegString(currentLine, span.Begin, i, document);
647
										
648
										if (!OverrideSpan(regex, document, words, span, ref i)) {
649
											currentLength += regex.Length;
650
											words.Add(new TextWord(document, currentLine, currentOffset, currentLength, span.BeginColor, false));
651
											currentOffset += currentLength;
652
											currentLength = 0;
653
											
654
											i += regex.Length - 1;
655
											if (currentSpanStack == null) {
656
												currentSpanStack = new SpanStack();
657
											}
658
											currentSpanStack.Push(span);
659
											span.IgnoreCase = activeRuleSet.IgnoreCase;
660
											
661
											UpdateSpanStateVariables();
662
										}
663
										
664
										goto skip;
665
									}
666
								}
667
							}
668
							
669
							// check if the char is a delimiter
670
							if (activeRuleSet != null && (int)ch < 256 && activeRuleSet.Delimiters[(int)ch]) {
671
								PushCurWord(document, ref markNext, words);
672
								if (currentOffset + currentLength +1 < currentLine.Length) {
673
									++currentLength;
674
									PushCurWord(document, ref markNext, words);
675
									goto skip;
676
								}
677
							}
678
							
679
							++currentLength;
680
							skip: continue;
681
						}
682
				}
683
			}
684
			
685
			PushCurWord(document, ref markNext, words);
686
			
687
			OnParsedLine(document, currentLine, words);
688
			
689
			return words;
690
		}
691
		
692
		protected virtual void OnParsedLine(IDocument document, LineSegment currentLine, List<TextWord> words)
693
		{
694
		}
695
		
696
		protected virtual bool OverrideSpan(string spanBegin, IDocument document, List<TextWord> words, Span span, ref int lineOffset)
697
		{
698
			return false;
699
		}
700
		
701
		/// <summary>
702
		/// pushes the curWord string on the word list, with the
703
		/// correct color.
704
		/// </summary>
705
		void PushCurWord(IDocument document, ref HighlightColor markNext, List<TextWord> words)
706
		{
707
			// Svante Lidman : Need to look through the next prev logic.
708
			if (currentLength > 0) {
709
				if (words.Count > 0 && activeRuleSet != null) {
710
					TextWord prevWord = null;
711
					int pInd = words.Count - 1;
712
					while (pInd >= 0) {
713
						if (!((TextWord)words[pInd]).IsWhiteSpace) {
714
							prevWord = (TextWord)words[pInd];
715
							if (prevWord.HasDefaultColor) {
716
								PrevMarker marker = (PrevMarker)activeRuleSet.PrevMarkers[document, currentLine, currentOffset, currentLength];
717
								if (marker != null) {
718
									prevWord.SyntaxColor = marker.Color;
719
//									document.Caret.ValidateCaretPos();
720
//									document.RequestUpdate(new TextAreaUpdate(TextAreaUpdateType.SingleLine, document.GetLineNumberForOffset(document.Caret.Offset)));
721
								}
722
							}
723
							break;
724
						}
725
						pInd--;
726
					}
727
				}
728
				
729
				if (inSpan) {
730
					HighlightColor c = null;
731
					bool hasDefaultColor = true;
732
					if (activeSpan.Rule == null) {
733
						c = activeSpan.Color;
734
					} else {
735
						c = GetColor(activeRuleSet, document, currentLine, currentOffset, currentLength);
736
						hasDefaultColor = false;
737
					}
738
					
739
					if (c == null) {
740
						c = activeSpan.Color;
741
						if (c.Color == Color.Transparent) {
742
							c = this.DefaultTextColor;
743
						}
744
						hasDefaultColor = true;
745
					}
746
					words.Add(new TextWord(document, currentLine, currentOffset, currentLength, markNext != null ? markNext : c, hasDefaultColor));
747
				} else {
748
					HighlightColor c = markNext != null ? markNext : GetColor(activeRuleSet, document, currentLine, currentOffset, currentLength);
749
					if (c == null) {
750
						words.Add(new TextWord(document, currentLine, currentOffset, currentLength, this.DefaultTextColor, true));
751
					} else {
752
						words.Add(new TextWord(document, currentLine, currentOffset, currentLength, c, false));
753
					}
754
				}
755
				
756
				if (activeRuleSet != null) {
757
					NextMarker nextMarker = (NextMarker)activeRuleSet.NextMarkers[document, currentLine, currentOffset, currentLength];
758
					if (nextMarker != null) {
759
						if (nextMarker.MarkMarker && words.Count > 0) {
760
							TextWord prevword = ((TextWord)words[words.Count - 1]);
761
							prevword.SyntaxColor = nextMarker.Color;
762
						}
763
						markNext = nextMarker.Color;
764
					} else {
765
						markNext = null;
766
					}
767
				}
768
				currentOffset += currentLength;
769
				currentLength = 0;
770
			}
771
		}
772
		
773
		#region Matching
774
		/// <summary>
775
		/// get the string, which matches the regular expression expr,
776
		/// in string s2 at index
777
		/// </summary>
778
		static string GetRegString(LineSegment lineSegment, char[] expr, int index, IDocument document)
779
		{
780
			int j = 0;
781
			StringBuilder regexpr = new StringBuilder();
782
			
783
			for (int i = 0; i < expr.Length; ++i, ++j) {
784
				if (index + j >= lineSegment.Length)
785
					break;
786
				
787
				switch (expr[i]) {
788
					case '@': // "special" meaning
789
						++i;
790
						switch (expr[i]) {
791
							case '!': // don't match the following expression
792
								StringBuilder whatmatch = new StringBuilder();
793
								++i;
794
								while (i < expr.Length && expr[i] != '@') {
795
									whatmatch.Append(expr[i++]);
796
								}
797
								break;
798
							case '@': // matches @
799
								regexpr.Append(document.GetCharAt(lineSegment.Offset + index + j));
800
								break;
801
						}
802
						break;
803
					default:
804
						if (expr[i] != document.GetCharAt(lineSegment.Offset + index + j)) {
805
							return regexpr.ToString();
806
						}
807
						regexpr.Append(document.GetCharAt(lineSegment.Offset + index + j));
808
						break;
809
				}
810
			}
811
			return regexpr.ToString();
812
		}
813
		
814
		/// <summary>
815
		/// returns true, if the get the string s2 at index matches the expression expr
816
		/// </summary>
817
		static bool MatchExpr(LineSegment lineSegment, char[] expr, int index, IDocument document, bool ignoreCase)
818
		{
819
			for (int i = 0, j = 0; i < expr.Length; ++i, ++j) {
820
				switch (expr[i]) {
821
					case '@': // "special" meaning
822
						++i;
823
						if (i < expr.Length) {
824
							switch (expr[i]) {
825
								case 'C': // match whitespace or punctuation
826
									if (index + j == lineSegment.Offset || index + j >= lineSegment.Offset + lineSegment.Length) {
827
										// nothing (EOL or SOL)
828
									} else {
829
										char ch = document.GetCharAt(lineSegment.Offset + index + j);
830
										if (!Char.IsWhiteSpace(ch) && !Char.IsPunctuation(ch)) {
831
											return false;
832
										}
833
									}
834
									break;
835
								case '!': // don't match the following expression
836
									{
837
										StringBuilder whatmatch = new StringBuilder();
838
										++i;
839
										while (i < expr.Length && expr[i] != '@') {
840
											whatmatch.Append(expr[i++]);
841
										}
842
										if (lineSegment.Offset + index + j + whatmatch.Length < document.TextLength) {
843
											int k = 0;
844
											for (; k < whatmatch.Length; ++k) {
845
												char docChar = ignoreCase ? Char.ToUpperInvariant(document.GetCharAt(lineSegment.Offset + index + j + k)) : document.GetCharAt(lineSegment.Offset + index + j + k);
846
												char spanChar = ignoreCase ? Char.ToUpperInvariant(whatmatch[k]) : whatmatch[k];
847
												if (docChar != spanChar) {
848
													break;
849
												}
850
											}
851
											if (k >= whatmatch.Length) {
852
												return false;
853
											}
854
										}
855
//									--j;
856
										break;
857
									}
858
								case '-': // don't match the  expression before
859
									{
860
										StringBuilder whatmatch = new StringBuilder();
861
										++i;
862
										while (i < expr.Length && expr[i] != '@') {
863
											whatmatch.Append(expr[i++]);
864
										}
865
										if (index - whatmatch.Length >= 0) {
866
											int k = 0;
867
											for (; k < whatmatch.Length; ++k) {
868
												char docChar = ignoreCase ? Char.ToUpperInvariant(document.GetCharAt(lineSegment.Offset + index - whatmatch.Length + k)) : document.GetCharAt(lineSegment.Offset + index - whatmatch.Length + k);
869
												char spanChar = ignoreCase ? Char.ToUpperInvariant(whatmatch[k]) : whatmatch[k];
870
												if (docChar != spanChar)
871
													break;
872
											}
873
											if (k >= whatmatch.Length) {
874
												return false;
875
											}
876
										}
877
//									--j;
878
										break;
879
									}
880
								case '@': // matches @
881
									if (index + j >= lineSegment.Length || '@' != document.GetCharAt(lineSegment.Offset + index + j)) {
882
										return false;
883
									}
884
									break;
885
							}
886
						}
887
						break;
888
					default:
889
						{
890
							if (index + j >= lineSegment.Length) {
891
								return false;
892
							}
893
							char docChar = ignoreCase ? Char.ToUpperInvariant(document.GetCharAt(lineSegment.Offset + index + j)) : document.GetCharAt(lineSegment.Offset + index + j);
894
							char spanChar = ignoreCase ? Char.ToUpperInvariant(expr[i]) : expr[i];
895
							if (docChar != spanChar) {
896
								return false;
897
							}
898
							break;
899
						}
900
				}
901
			}
902
			return true;
903
		}
904
		#endregion
905
	}
906
}