VectSharp  2.2.1
A light library for C# vector graphics
SyntaxHighlighting.cs
1 /*
2  VectSharp - A light library for C# vector graphics.
3  Copyright (C) 2020-2022 Giorgio Bianchini
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU Lesser General Public License as published by
7  the Free Software Foundation, version 3.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU Lesser General Public License for more details.
13 
14  You should have received a copy of the GNU Lesser General Public License
15  along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17 
18 using Highlight;
19 using Highlight.Engines;
20 using Highlight.Patterns;
21 using System;
22 using System.Collections.Generic;
23 using System.Linq;
24 using System.Text;
25 using System.Text.RegularExpressions;
26 
27 namespace VectSharp.Markdown
28 {
29  /// <summary>
30  /// Represents a string with associated formatting information.
31  /// </summary>
32  public struct FormattedString
33  {
34  /// <summary>
35  /// The text represented by this object.
36  /// </summary>
37  public string Text { get; }
38 
39  /// <summary>
40  /// The colour of the text.
41  /// </summary>
42  public Colour Colour { get; }
43 
44  /// <summary>
45  /// Whether the text should be rendered as bold or not.
46  /// </summary>
47  public bool IsBold { get; }
48 
49  /// <summary>
50  /// Whether the text should be rendered as italic or not.
51  /// </summary>
52  public bool IsItalic { get; }
53 
54  /// <summary>
55  /// Creates a new <see cref="FormattedString"/> instance.
56  /// </summary>
57  /// <param name="text">The text of the object.</param>
58  /// <param name="colour">The colour of the text.</param>
59  /// <param name="isBold">Whether the text should be rendered as bold or not.</param>
60  /// <param name="isItalic">Whether the text should be rendered as italic or not.</param>
61  public FormattedString(string text, Colour colour, bool isBold, bool isItalic)
62  {
63  this.Text = text;
64  this.Colour = colour;
65  this.IsBold = isBold;
66  this.IsItalic = isItalic;
67  }
68  }
69 
70  /// <summary>
71  /// Contains methods to perform syntax highlighting.
72  /// </summary>
73  public static class SyntaxHighlighter
74  {
75  private static Dictionary<string, string[]> LanguageAliases = new Dictionary<string, string[]>()
76  {
77  { "ASPX", new string[] { "ASP.NET", "aspx", "aspx-vb" } },
78  { "C", new string[] { } },
79  { "C++", new string[] { "cpp" } },
80  { "C#", new string[] { "csharp" } },
81  { "COBOL", new string[] { } },
82  { "Eiffel", new string[] { } },
83  { "Fortran", new string[] { } },
84  { "Haskell", new string[] { } },
85  { "HTML", new string[] { "xhtml" } },
86  { "Java", new string[] { } },
87  { "JavaScript", new string[] { "js", "node" } },
88  { "Mercury", new string[] { } },
89  { "MSIL", new string[] { } },
90  { "Pascal", new string[] { "delphi", "objectpascal" } },
91  { "Perl", new string[] { "cperl" } },
92  { "PHP", new string[] { "inc" } },
93  { "Python", new string[] { "python3", "rusthon" } },
94  { "Ruby", new string[] { "jruby", "macruby", "rake", "rb", "rbx" } },
95  { "SQL", new string[] { } },
96  { "Visual Basic", new string[] { "vba", "vb6", "visual basic 6", "visual basic for applications" } },
97  { "VBScript", new string[] { } },
98  { "VB.NET", new string[] { "Visual Basic .NET", "vbnet", "vb .net" } },
99  { "XML", new string[] { "rss", "xsd", "wsdl" } },
100  };
101 
102  private static string GetLanguage(string language)
103  {
104  foreach (KeyValuePair<string, string[]> element in LanguageAliases)
105  {
106  if (element.Key.Equals(language, StringComparison.OrdinalIgnoreCase))
107  {
108  return element.Key;
109  }
110 
111  foreach (string alias in element.Value)
112  {
113  if (alias.Equals(language, StringComparison.OrdinalIgnoreCase))
114  {
115  return element.Key;
116  }
117  }
118  }
119 
120  return null;
121  }
122 
123  /// <summary>
124  /// Performs syntax highlighting for a specified language on some source code.
125  /// </summary>
126  /// <param name="sourceCode">The source code to be highlighted.</param>
127  /// <param name="language">The name of the language to use for the highlighting.</param>
128  /// <returns>A list of lists of <see cref="FormattedString"/>s. Each list of <see cref="FormattedString"/>s represents a line.</returns>
129  public static List<List<FormattedString>> GetSyntaxHighlightedLines(string sourceCode, string language)
130  {
131  language = GetLanguage(language);
132 
133  if (string.IsNullOrEmpty(language))
134  {
135  return null;
136  }
137 
138  HighlighterEngine engine = new HighlighterEngine();
139 
140  Highlighter highlighter = new Highlighter(engine);
141 
142  highlighter.Highlight(language, sourceCode);
143 
144  List<List<FormattedString>> tbr = new List<List<FormattedString>>();
145 
146  List<FormattedString> currentLine = new List<FormattedString>();
147 
148  for (int i = 0; i < engine.HighlightedSpans.Count; i++)
149  {
150  string[] split = engine.HighlightedSpans[i].Text.Replace("\r", "").Split('\n');
151 
152  for (int j = 0; j < split.Length; j++)
153  {
154  currentLine.Add(new FormattedString(split[j], engine.HighlightedSpans[i].Colour, engine.HighlightedSpans[i].IsBold, engine.HighlightedSpans[i].IsItalic));
155 
156  if (j < split.Length - 1)
157  {
158  tbr.Add(currentLine);
159  currentLine = new List<FormattedString>();
160  }
161  }
162  }
163 
164  tbr.Add(currentLine);
165 
166  return tbr;
167  }
168  }
169 
170  internal class HighlighterEngine : Engine, IEngine
171  {
172  private const RegexOptions DefaultRegexOptions = RegexOptions.ExplicitCapture | RegexOptions.IgnorePatternWhitespace;
173 
174  public List<FormattedString> HighlightedSpans { get; } = new List<FormattedString>();
175 
176  protected override string ProcessBlockPatternMatch(Definition definition, BlockPattern pattern, Match match)
177  {
178  if (!string.IsNullOrEmpty(pattern.RawRegex))
179  {
180  Match reMatch = Regex.Match(match.Value, pattern.RawRegex);
181 
182  if (reMatch.Groups.Count > 1)
183  {
184  HighlightedSpans.Add(new FormattedString(match.Value.Substring(0, reMatch.Groups[1].Index), Colours.Black, false, false));
185  HighlightedSpans.Add(new FormattedString(reMatch.Groups[1].Value, pattern.Style.Colors.ForeColor, pattern.Style.Font.IsBold, pattern.Style.Font.IsItalic));
186  HighlightedSpans.Add(new FormattedString(match.Value.Substring(reMatch.Groups[1].Index + reMatch.Groups[1].Length), Colours.Black, false, false));
187  }
188  else
189  {
190  HighlightedSpans.Add(new FormattedString(match.Value, pattern.Style.Colors.ForeColor, pattern.Style.Font.IsBold, pattern.Style.Font.IsItalic));
191  }
192  }
193  else
194  {
195  HighlightedSpans.Add(new FormattedString(match.Value, pattern.Style.Colors.ForeColor, pattern.Style.Font.IsBold, pattern.Style.Font.IsItalic));
196  }
197  return match.Value;
198  }
199 
200  protected override string ProcessMarkupPatternMatch(Definition definition, MarkupPattern pattern, Match match)
201  {
202  HighlightedSpans.Add(new FormattedString(match.Value, pattern.Style.Colors.ForeColor, pattern.Style.Font.IsBold, pattern.Style.Font.IsItalic));
203  return match.Value;
204  }
205 
206  protected override string ProcessWordPatternMatch(Definition definition, WordPattern pattern, Match match)
207  {
208  HighlightedSpans.Add(new FormattedString(match.Value, pattern.Style.Colors.ForeColor, pattern.Style.Font.IsBold, pattern.Style.Font.IsItalic));
209  return match.Value;
210  }
211 
212  string IEngine.Highlight(Definition definition, string input)
213  {
214  if (definition == null)
215  {
216  throw new ArgumentNullException("definition");
217  }
218 
219  var output = PreHighlight(definition, input);
220  output = HighlightUsingRegex(definition, output);
221  output = PostHighlight(definition, output);
222 
223  return output;
224  }
225 
226  private string HighlightUsingRegex(Definition definition, string input)
227  {
228  var regexOptions = GetRegexOptions(definition);
229  var evaluator = GetMatchEvaluator(definition);
230  var regexPattern = definition.GetRegexPattern();
231 
232  int currentIndex = 0;
233 
234  foreach (Match match in Regex.Matches(input, regexPattern))
235  {
236  if (match.Index > currentIndex)
237  {
238  this.HighlightedSpans.Add(new FormattedString(input.Substring(currentIndex, match.Index - currentIndex), Colours.Black, false, false));
239  }
240 
241  currentIndex = match.Index + match.Length;
242 
243  evaluator(match);
244  }
245 
246  if (currentIndex < input.Length)
247  {
248  this.HighlightedSpans.Add(new FormattedString(input.Substring(currentIndex, input.Length - currentIndex), Colours.Black, false, false));
249  }
250 
251  return null;
252  }
253 
254  private RegexOptions GetRegexOptions(Definition definition)
255  {
256  if (definition.CaseSensitive)
257  {
258  return DefaultRegexOptions | RegexOptions.IgnoreCase;
259  }
260 
261  return DefaultRegexOptions;
262  }
263 
264  private string ElementMatchHandler(Definition definition, Match match)
265  {
266  if (definition == null)
267  {
268  throw new ArgumentNullException("definition");
269  }
270  if (match == null)
271  {
272  throw new ArgumentNullException("match");
273  }
274 
275  var pattern = definition.Patterns.First(x => match.Groups[x.Key].Success).Value;
276  if (pattern != null)
277  {
278  if (pattern is BlockPattern)
279  {
280  return ProcessBlockPatternMatch(definition, (BlockPattern)pattern, match);
281  }
282  if (pattern is MarkupPattern)
283  {
284  return ProcessMarkupPatternMatch(definition, (MarkupPattern)pattern, match);
285  }
286  if (pattern is WordPattern)
287  {
288  return ProcessWordPatternMatch(definition, (WordPattern)pattern, match);
289  }
290  }
291 
292  return match.Value;
293  }
294 
295  private MatchEvaluator GetMatchEvaluator(Definition definition)
296  {
297  return match => ElementMatchHandler(definition, match);
298  }
299  }
300 }
VectSharp.Markdown.FormattedString.IsBold
bool IsBold
Whether the text should be rendered as bold or not.
Definition: SyntaxHighlighting.cs:47
VectSharp.Colour
Represents an RGB colour.
Definition: Colour.cs:26
VectSharp.Markdown.FormattedString.FormattedString
FormattedString(string text, Colour colour, bool isBold, bool isItalic)
Creates a new FormattedString instance.
Definition: SyntaxHighlighting.cs:61
VectSharp.Markdown.FormattedString
Represents a string with associated formatting information.
Definition: SyntaxHighlighting.cs:33
VectSharp.Markdown.FormattedString.IsItalic
bool IsItalic
Whether the text should be rendered as italic or not.
Definition: SyntaxHighlighting.cs:52
VectSharp.Markdown
Definition: HtmlTag.cs:26
VectSharp.Markdown.SyntaxHighlighter.GetSyntaxHighlightedLines
static List< List< FormattedString > > GetSyntaxHighlightedLines(string sourceCode, string language)
Performs syntax highlighting for a specified language on some source code.
Definition: SyntaxHighlighting.cs:129
VectSharp.Markdown.SyntaxHighlighter
Contains methods to perform syntax highlighting.
Definition: SyntaxHighlighting.cs:74
VectSharp.Markdown.FormattedString.Text
string Text
The text represented by this object.
Definition: SyntaxHighlighting.cs:37