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 |
} |