1
2
3
4
5
6 using System;
7 using System.Collections;
8 using System.Lex;
9
10 namespace System.Windows
11 {
12 public class SourceSpan
13 {
14 public nothrow SourceSpan() : line(0), scol(0), ecol(0)
15 {
16 }
17 public nothrow SourceSpan(int line_, short scol_, short ecol_) : line(line_), scol(scol_), ecol(ecol_)
18 {
19 }
20 public inline nothrow bool IsEmpty() const
21 {
22 return line == 0 && scol == 0 && ecol == 0;
23 }
24 public int line;
25 public short scol;
26 public short ecol;
27 }
28
29 public inline nothrow bool operator==(const SourceSpan& left, const SourceSpan& right)
30 {
31 return left.line == right.line && left.scol == right.scol && left.ecol == right.ecol;
32 }
33
34 public enum SourceCodeTokenKind : int
35 {
36 plain, space, keyword, identifier, string, character, number, comment, lineNumber, beginBlock, endBlock
37 }
38
39 public nothrow ulong GetHashCode(SourceCodeTokenKind tokenKind)
40 {
41 return cast<ulong>(cast<int>(tokenKind));
42 }
43
44 public class SourceCodeTextStyle
45 {
46 public nothrow SourceCodeTextStyle(Font* font_, Brush* brush_) : font(font_), brush(brush_)
47 {
48 }
49 public Font* font;
50 public Brush* brush;
51 }
52
53 public inline nothrow bool operator==(const SourceCodeTextStyle& left, const SourceCodeTextStyle& right)
54 {
55 return left.font == right.font && left.brush == right.brush;
56 }
57
58 public class SourceCodeTokenStyle
59 {
60 public nothrow SourceCodeTokenStyle(const Color& color_, FontStyle fontStyle_) : color(color_), fontStyle(fontStyle_)
61 {
62 }
63 public Color color;
64 public FontStyle fontStyle;
65 }
66
67 public inline nothrow bool operator==(const SourceCodeTokenStyle& left, const SourceCodeTokenStyle& right)
68 {
69 return left.color == right.color && left.fontStyle == right.fontStyle;
70 }
71
72 public nothrow ulong GetHashCode(const SourceCodeTokenStyle& tokenStyle)
73 {
74 ulong code = GetHashCode(tokenStyle.color);
75 code = code + 14695981039346656037u * cast<ulong>(cast<int>(tokenStyle.fontStyle));
76 return code;
77 }
78
79 public nothrow ulong GetHashCode(const SourceCodeTextStyle& textStyle)
80 {
81 ulong code = GetHashCode(textStyle.font);
82 code = code + 14695981039346656037u * GetHashCode(textStyle.brush);
83 return code;
84 }
85
86 public nothrow ControlCreateParams& SourceCodeViewControlCreateParams(ControlCreateParams& controlCreateParams)
87 {
88 return controlCreateParams.SetWindowClassName("System.Windows.SourceCodeView").SetWindowClassStyle(DoubleClickWindowClassStyle()).
89 SetWindowStyle(cast<WindowStyle>(DefaultChildWindowStyle() | WindowStyle.WS_TABSTOP)).
90 SetWindowClassBackgroundColor(SystemColor.COLOR_WINDOW).SetBackgroundColor(Color.White());
91 }
92
93 public class SourceCodeViewCreateParams
94 {
95 public nothrow SourceCodeViewCreateParams(TextViewCreateParams& textViewCreateParams_) : textViewCreateParams(textViewCreateParams_)
96 {
97 }
98 public nothrow SourceCodeViewCreateParams& Defaults()
99 {
100 return *this;
101 }
102 public TextViewCreateParams& textViewCreateParams;
103 }
104
105 public class SourceCodeView : TextView
106 {
107 public SourceCodeView(const FontFamily& fontFamily, float fontSize, const Color& backgroundColor, const Color& textColor, const Point& location,
108 const Size& size, Dock dock, Anchors anchors) :
109 base(fontFamily, fontSize, backgroundColor, textColor, location, size, dock, anchors), numLineNumberDigits(0)
110 {
111 BuildDefaultStyles();
112 }
113 public SourceCodeView(const Point& location, const Size& size, Dock dock, Anchors anchors) :
114 this(FontFamily("Consolas"), 10.0f, Color.White(), Color.Black(), location, size, dock, anchors)
115 {
116 }
117 public SourceCodeView(SourceCodeViewCreateParams& createParams) : base(createParams.textViewCreateParams), numLineNumberDigits(0)
118 {
119 BuildDefaultStyles();
120 }
121 protected override void OnFontChanged()
122 {
123 fontStyleFontMap.Clear();
124 textStyleMap.Clear();
125 tokenStyleTextStyleMap.Clear();
126 for (UniquePtr<SourceCodeTokenStyle>& tokenStyle : sourceCodeTokenStyles)
127 {
128 Brush* brush = GetOrInsertBrush(tokenStyle->color);
129 Font* font = GetOrInsertFont(tokenStyle->fontStyle);
130 SourceCodeTextStyle* textStyle = GetOrInsertTextStyle(brush, font);
131 tokenStyleTextStyleMap[tokenStyle.Get()] = textStyle;
132 }
133 }
134 public void SetSourceCodeTokenStyle(SourceCodeTokenKind kind, SourceCodeTokenStyle style)
135 {
136 SourceCodeTokenStyle* tokenStyle = GetOrInsertTokenStyle(style.color, style.fontStyle);
137 tokenKindMap[kind] = tokenStyle;
138 }
139 protected override nothrow int LineNumberFieldLength() const
140 {
141 return numLineNumberDigits + 1;
142 }
143 protected override nothrow void SetLineNumberFieldLength(int lineCount)
144 {
145 numLineNumberDigits = Log10(lineCount + 1);
146 }
147 protected override void OnLinesChanged()
148 {
149 base->OnLinesChanged();
150 tokenLines.Clear();
151 int state = 0;
152 int n = cast<int>(Lines().Count());
153 numLineNumberDigits = Log10(n + 1);
154 for (int i = 0; i < n; ++i;)
155 {
156 const ustring& line = Lines()[i];
157 TokenLine tokenLine = TokenizeLine(line, i + 1, state);
158 state = tokenLine.endState;
159 tokenLines.Add(Rvalue(tokenLine));
160 }
161 }
162 protected override void OnLineChanged(LineEventArgs& args)
163 {
164 base->OnLineChanged(args);
165 int lineIndex = args.lineIndex;
166 const ustring& line = Lines()[lineIndex];
167 int state = 0;
168 if (lineIndex > 0 && !tokenLines.IsEmpty())
169 {
170 state = tokenLines[lineIndex - 1].endState;
171 }
172 while (lineIndex >= tokenLines.Count())
173 {
174 tokenLines.Add(TokenLine());
175 }
176 tokenLines[lineIndex] = TokenizeLine(line, lineIndex + 1, state);
177 }
178 protected override void OnLineDeleted(LineEventArgs& args)
179 {
180 base->OnLineDeleted(args);
181 int lineIndex = args.lineIndex;
182 tokenLines.Remove(tokenLines.Begin() + lineIndex);
183 }
184 private nothrow bool IsBeginBlockLine(int lineIndex) const
185 {
186 if (lineIndex >= 0 && lineIndex < tokenLines.Count())
187 {
188 const TokenLine& tokenLine = tokenLines[lineIndex];
189 for (const Token& token : tokenLine.tokens)
190 {
191 if (GetTokenKind(token) == SourceCodeTokenKind.beginBlock)
192 {
193 return true;
194 }
195 }
196 }
197 return false;
198 }
199 private nothrow bool IsEndBlockLine(int lineIndex) const
200 {
201 if (lineIndex >= 0 && lineIndex < tokenLines.Count())
202 {
203 const TokenLine& tokenLine = tokenLines[lineIndex];
204 for (const Token& token : tokenLine.tokens)
205 {
206 if (GetTokenKind(token) == SourceCodeTokenKind.endBlock)
207 {
208 return true;
209 }
210 }
211 }
212 return false;
213 }
214 protected override nothrow int RemoveIndent(int lineIndex) const
215 {
216 if (IsEndBlockLine(lineIndex))
217 {
218 return IndentSize();
219 }
220 else
221 {
222 return 0;
223 }
224 }
225 protected override nothrow int GetIndent(const ustring& line, int lineIndex)
226 {
227 for (int i = 0; i < line.Length(); ++i;)
228 {
229 if (line[i] != ' ')
230 {
231 if (IsBeginBlockLine(lineIndex))
232 {
233 return i + IndentSize();
234 }
235 else
236 {
237 return i;
238 }
239 }
240 }
241 return 0;
242 }
243 protected override void OnLineInserted(LineEventArgs& args)
244 {
245 base->OnLineInserted(args);
246 int lineIndex = args.lineIndex;
247 ustring& line = Lines()[lineIndex];
248 int state = 0;
249 while (lineIndex >= tokenLines.Count())
250 {
251 tokenLines.Add(TokenLine());
252 }
253 if (lineIndex > 0)
254 {
255 state = tokenLines[lineIndex - 1].endState;
256 }
257 tokenLines.Insert(tokenLines.Begin() + lineIndex, TokenizeLine(line, lineIndex + 1, state));
258 Invalidate();
259 }
260 public nothrow ustring GetText(const SourceSpan& span) const
261 {
262 if (span.line >= 1 && span.line <= Lines().Count())
263 {
264 const ustring& line = Lines()[span.line - 1];
265 if (span.scol >= 1 && span.scol <= line.Length())
266 {
267 int n = span.ecol - span.scol;
268 if (n > 0)
269 {
270 return line.Substring(span.scol - 1, n);
271 }
272 }
273 }
274 return ustring();
275 }
276 public nothrow ustring GetTokenText(int lineNumber, short columnNumber) const
277 {
278 if (lineNumber >= 1 && lineNumber <= tokenLines.Count())
279 {
280 const TokenLine& tokenLine = tokenLines[lineNumber - 1];
281 int tokenIndex = tokenLine.TokenIndex(columnNumber);
282 if (tokenIndex != -1)
283 {
284 const Token& token = tokenLine.tokens[tokenIndex];
285 if (GetTokenKind(token) == SourceCodeTokenKind.identifier)
286 {
287 return token.match.ToString();
288 }
289 }
290 }
291 return ustring();
292 }
293 protected virtual TokenLine TokenizeLine(const ustring& line, int lineNumber, int startState)
294 {
295 return System.Windows.DefaultTokenizeLine(line, lineNumber, startState);
296 }
297 protected virtual nothrow SourceCodeTokenKind GetTokenKind(const Token& token) const
298 {
299 return SourceCodeTokenKind.plain;
300 }
301 protected override void DrawLine(Graphics& graphics, int lineIndex, const PointF& origin)
302 {
303 int lineNumber = lineIndex + 1;
304 string lineNumberStr = System.ToString(lineNumber);
305 string lineNumberText = Format(lineNumberStr, numLineNumberDigits, FormatJustify.right);
306 PointF pt(origin);
307 SourceCodeTextStyle* lineNumberTextStyle = GetTextStyle(SourceCodeTokenKind.lineNumber);
308 graphics.DrawStringChecked(lineNumberText, *lineNumberTextStyle->font, pt, *lineNumberTextStyle->brush);
309 pt.x = pt.x + CharWidth() * (numLineNumberDigits + 1);
310 const TokenLine& tokenLine = tokenLines[lineIndex];
311 int startState = tokenLine.startState;
312 for (const Token& token : tokenLine.tokens)
313 {
314 SourceCodeTokenKind tokenKind = GetTokenKind(token);
315 SourceCodeTextStyle* tokenTextStyle = GetTextStyle(tokenKind);
316 ustring tokenStr = token.match.ToString();
317 string s(ToUtf8(tokenStr));
318 graphics.DrawStringChecked(s, *tokenTextStyle->font, pt, *tokenTextStyle->brush);
319 pt.x = pt.x + CharWidth() * tokenStr.Length();
320 startState = -1;
321 }
322 PointF hiliteOrigin(origin);
323 hiliteOrigin.x = hiliteOrigin.x + CharWidth() * LineNumberFieldLength();
324 DrawHilites(graphics, lineIndex, hiliteOrigin);
325 }
326 protected virtual void DrawHilites(Graphics& graphics, int lineIndex, const PointF& origin)
327 {
328 }
329 private SourceCodeTextStyle* GetTextStyle(SourceCodeTokenKind tokenKind)
330 {
331 SourceCodeTextStyle* textStyle = null;
332 SourceCodeTokenStyle* tokenStyle = null;
333 HashMap<SourceCodeTokenKind, SourceCodeTokenStyle*>.ConstIterator it = tokenKindMap.CFind(tokenKind);
334 if (it != tokenKindMap.CEnd())
335 {
336 tokenStyle = it->second;
337 }
338 else
339 {
340 throw Exception("source code token style not found");
341 }
342 HashMap<SourceCodeTokenStyle*, SourceCodeTextStyle*>.ConstIterator it2 = tokenStyleTextStyleMap.CFind(tokenStyle);
343 if (it2 != tokenStyleTextStyleMap.CEnd())
344 {
345 textStyle = it2->second;
346 }
347 else
348 {
349 throw Exception("source code text style not found");
350 }
351 return textStyle;
352 }
353 private void BuildDefaultStyles()
354 {
355 SourceCodeTokenStyle* plainStyle = GetOrInsertTokenStyle(Color.Black(), FontStyle.regular);
356 tokenKindMap[SourceCodeTokenKind.plain] = plainStyle;
357 tokenKindMap[SourceCodeTokenKind.beginBlock] = plainStyle;
358 tokenKindMap[SourceCodeTokenKind.endBlock] = plainStyle;
359 SourceCodeTokenStyle* spaceStyle = GetOrInsertTokenStyle(Color.Black(), FontStyle.regular);
360 tokenKindMap[SourceCodeTokenKind.space] = spaceStyle;
361 SourceCodeTokenStyle* keywordStyle = GetOrInsertTokenStyle(Color.Blue(), FontStyle.regular);
362 tokenKindMap[SourceCodeTokenKind.keyword] = keywordStyle;
363 SourceCodeTokenStyle* identifierStyle = GetOrInsertTokenStyle(Color.Black(), FontStyle.regular);
364 tokenKindMap[SourceCodeTokenKind.identifier] = identifierStyle;
365 SourceCodeTokenStyle* stringStyle = GetOrInsertTokenStyle(Color(163u, 21u, 21u), FontStyle.regular);
366 tokenKindMap[SourceCodeTokenKind.string] = stringStyle;
367 SourceCodeTokenStyle* characterStyle = GetOrInsertTokenStyle(Color(163u, 21u, 21u), FontStyle.regular);
368 tokenKindMap[SourceCodeTokenKind.character] = characterStyle;
369 SourceCodeTokenStyle* numberStyle = GetOrInsertTokenStyle(Color.Black(), FontStyle.regular);
370 tokenKindMap[SourceCodeTokenKind.number] = numberStyle;
371 SourceCodeTokenStyle* commentStyle = GetOrInsertTokenStyle(Color(0u, 128u, 0u), FontStyle.regular);
372 tokenKindMap[SourceCodeTokenKind.comment] = commentStyle;
373 SourceCodeTokenStyle* lineNumberStyle = GetOrInsertTokenStyle(Color(43u, 145u, 175u), FontStyle.regular);
374 tokenKindMap[SourceCodeTokenKind.lineNumber] = lineNumberStyle;
375 }
376 private SourceCodeTokenStyle* GetOrInsertTokenStyle(const Color& color, FontStyle fontStyle)
377 {
378 SourceCodeTokenStyle style(color, fontStyle);
379 HashMap<SourceCodeTokenStyle, SourceCodeTokenStyle*>.ConstIterator it = tokenStyleMap.CFind(style);
380 if (it != tokenStyleMap.CEnd())
381 {
382 return it->second;
383 }
384 SourceCodeTokenStyle* sourceCodeTokenStyle = new SourceCodeTokenStyle(color, fontStyle);
385 sourceCodeTokenStyles.Add(UniquePtr<SourceCodeTokenStyle>(sourceCodeTokenStyle));
386 tokenStyleMap[style] = sourceCodeTokenStyle;
387 Brush* brush = GetOrInsertBrush(color);
388 Font* font = GetOrInsertFont(fontStyle);
389 SourceCodeTextStyle* textStyle = GetOrInsertTextStyle(brush, font);
390 tokenStyleTextStyleMap[sourceCodeTokenStyle] = textStyle;
391 return sourceCodeTokenStyle;
392 }
393 public Font* GetOrInsertFont(FontStyle fontStyle)
394 {
395 Map<FontStyle, Font*>.ConstIterator it = fontStyleFontMap.CFind(fontStyle);
396 if (it != fontStyleFontMap.CEnd())
397 {
398 return it->second;
399 }
400 Font* font = new Font(GetFontFamily(), FontSize(), fontStyle, Unit.point);
401 Fonts().Add(UniquePtr<Font>(font));
402 fontStyleFontMap[fontStyle] = font;
403 return font;
404 }
405 public SourceCodeTextStyle* GetOrInsertTextStyle(Brush* brush, Font* font)
406 {
407 SourceCodeTextStyle style(font, brush);
408 HashMap<SourceCodeTextStyle, SourceCodeTextStyle*>.ConstIterator it = textStyleMap.CFind(style);
409 if (it != textStyleMap.CEnd())
410 {
411 return it->second;
412 }
413 SourceCodeTextStyle* textStyle = new SourceCodeTextStyle(font, brush);
414 textStyles.Add(UniquePtr<SourceCodeTextStyle>(textStyle));
415 textStyleMap[style] = textStyle;
416 return textStyle;
417 }
418 private List<TokenLine> tokenLines;
419 private int numLineNumberDigits;
420 private List<UniquePtr<SourceCodeTextStyle>> textStyles;
421 private List<UniquePtr<SourceCodeTokenStyle>> sourceCodeTokenStyles;
422 private Map<FontStyle, Font*> fontStyleFontMap;
423 private HashMap<SourceCodeTokenStyle, SourceCodeTokenStyle*> tokenStyleMap;
424 private HashMap<SourceCodeTokenStyle*, SourceCodeTextStyle*> tokenStyleTextStyleMap;
425 private HashMap<SourceCodeTextStyle, SourceCodeTextStyle*> textStyleMap;
426 private HashMap<SourceCodeTokenKind, SourceCodeTokenStyle*> tokenKindMap;
427 }
428
429 public TokenLine DefaultTokenizeLine(const ustring& line, int lineNumber, int startState)
430 {
431 Token token;
432 TokenLine tokenLine;
433 uchar* begin = line.Chars();
434 uchar* end = line.Chars() + line.Length();
435 token.match.begin = begin;
436 token.match.end = end;
437 token.line = lineNumber;
438 tokenLine.tokens.Add(token);
439 tokenLine.endState = 0;
440 return tokenLine;
441 }
442 }