Skip to content

Commit a88aa28

Browse files
committed
Add SyntaxHighlighter for syntax highlighting
Introduced a new `SyntaxHighlighter` class in `SyntaxHighlighter.cs` to handle syntax highlighting for different programming languages. This class includes a dictionary mapping languages to syntax rules, a method to display highlighted code, and a `SyntaxRule` class. Updated `InteractiveMode` and `Program` classes to use `SyntaxHighlighter` for displaying code with syntax highlighting instead of plain text. Added a user prompt to press any key at the end of the `InteractiveMode` method to improve user interaction.
1 parent 29b9d0b commit a88aa28

File tree

3 files changed

+314
-3
lines changed

3 files changed

+314
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
using System.Text.RegularExpressions;
2+
3+
namespace Riverside.JsonBinder.Console.Helpers;
4+
5+
public class SyntaxHighlighter
6+
{
7+
private static readonly Dictionary<SerializableLanguage, List<SyntaxRule>> SyntaxRulesByLanguage = new()
8+
{
9+
{
10+
SerializableLanguage.CSharp, new List<SyntaxRule>
11+
{
12+
// Core keywords
13+
new(@"\b(abstract|as|base|break|case|catch|checked|class|const|continue|default|delegate|do|else|enum|event|explicit|extern|finally|fixed|for|foreach|goto|if|implicit|in|interface|internal|is|lock|namespace|new|operator|out|override|params|private|protected|public|readonly|ref|return|sealed|sizeof|stackalloc|static|switch|this|throw|try|typeof|unchecked|unsafe|using|virtual|volatile|while|get|set)\b", ConsoleColor.Magenta),
14+
15+
// Types
16+
new(@"\b(bool|byte|char|decimal|double|float|int|long|object|sbyte|short|string|uint|ulong|ushort|void|var)\b", ConsoleColor.DarkCyan),
17+
18+
// Type definitions (classes, interfaces, etc.)
19+
new(@"\b[A-Z][a-zA-Z0-9_]*\b", ConsoleColor.Cyan),
20+
21+
// Properties and methods
22+
new(@"\b[a-z][a-zA-Z0-9_]*(?=\s*[{<])", ConsoleColor.Yellow),
23+
24+
// Generic type parameters
25+
new(@"<[^>]+>", ConsoleColor.DarkYellow),
26+
27+
// Numbers
28+
new(@"\b\d+\b", ConsoleColor.Green),
29+
30+
// Strings
31+
new(@"""[^""\\]*(?:\\.[^""\\]*)*""", ConsoleColor.DarkGreen),
32+
}
33+
},
34+
{
35+
SerializableLanguage.Python, new List<SyntaxRule>
36+
{
37+
// Keywords
38+
new(@"\b(and|as|assert|async|await|break|class|continue|def|del|elif|else|except|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|raise|return|try|while|with|yield)\b", ConsoleColor.Magenta),
39+
40+
// Built-in types and values
41+
new(@"\b(True|False|None|bool|int|float|str|list|dict|set|tuple)\b", ConsoleColor.DarkCyan),
42+
43+
// Class names
44+
new(@"(?<=\bclass\s+)\w+", ConsoleColor.Cyan),
45+
46+
// Function definitions
47+
new(@"(?<=\bdef\s+)\w+", ConsoleColor.Yellow),
48+
49+
// Decorators
50+
new(@"@\w+", ConsoleColor.DarkYellow),
51+
52+
// Numbers
53+
new(@"\b\d+\b", ConsoleColor.Green),
54+
55+
// Strings
56+
new(@"""[^""\\]*(?:\\.[^""\\]*)*""|'[^'\\]*(?:\\.[^'\\]*)*'", ConsoleColor.DarkGreen),
57+
}
58+
},
59+
{
60+
SerializableLanguage.JavaScript, new List<SyntaxRule>
61+
{
62+
// Keywords
63+
new(@"\b(async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|export|extends|finally|for|function|if|import|in|instanceof|let|new|of|return|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b", ConsoleColor.Magenta),
64+
65+
// Built-in objects and types
66+
new(@"\b(Array|Boolean|Date|Error|Function|JSON|Math|Number|Object|RegExp|String|Promise|Map|Set|Symbol|BigInt)\b", ConsoleColor.DarkCyan),
67+
68+
// Class names and custom types
69+
new(@"\b[A-Z][a-zA-Z0-9_]*\b", ConsoleColor.Cyan),
70+
71+
// Function declarations
72+
new(@"(?<=\bfunction\s+)\w+", ConsoleColor.Yellow),
73+
74+
// Arrow functions
75+
new(@"=>\s*{", ConsoleColor.DarkYellow),
76+
77+
// Numbers
78+
new(@"\b\d+(\.\d+)?\b", ConsoleColor.Green),
79+
80+
// Strings
81+
new(@"""[^""\\]*(?:\\.[^""\\]*)*""|'[^'\\]*(?:\\.[^'\\]*)*'|`[^`\\]*(?:\\.[^`\\]*)*`", ConsoleColor.DarkGreen),
82+
}
83+
},
84+
{
85+
SerializableLanguage.TypeScript, new List<SyntaxRule>
86+
{
87+
// TypeScript-specific keywords
88+
new(@"\b(abstract|as|asserts|async|await|break|case|catch|class|const|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|if|implements|import|in|infer|instanceof|interface|is|keyof|let|module|namespace|new|null|of|return|super|switch|this|throw|try|type|typeof|undefined|unique|unknown|var|void|while|with|yield)\b", ConsoleColor.Magenta),
89+
90+
// Types and type operators
91+
new(@"\b(any|boolean|number|string|symbol|never|object|bigint|readonly|private|protected|public|static)\b", ConsoleColor.DarkCyan),
92+
93+
// Classes and interfaces
94+
new(@"\b[A-Z][a-zA-Z0-9_]*\b", ConsoleColor.Cyan),
95+
96+
// Generics
97+
new(@"<[^>]+>", ConsoleColor.Yellow),
98+
99+
// Decorators
100+
new(@"@\w+", ConsoleColor.DarkYellow),
101+
102+
// Numbers
103+
new(@"\b\d+(\.\d+)?\b", ConsoleColor.Green),
104+
105+
// Strings
106+
new(@"""[^""\\]*(?:\\.[^""\\]*)*""|'[^'\\]*(?:\\.[^'\\]*)*'|`[^`\\]*(?:\\.[^`\\]*)*`", ConsoleColor.DarkGreen),
107+
}
108+
},
109+
{
110+
SerializableLanguage.Java, new List<SyntaxRule>
111+
{
112+
// Keywords
113+
new(@"\b(abstract|assert|break|case|catch|class|const|continue|default|do|else|enum|extends|final|finally|for|if|implements|import|instanceof|interface|native|new|package|private|protected|public|return|static|strictfp|super|switch|synchronized|this|throw|throws|transient|try|volatile|while)\b", ConsoleColor.Magenta),
114+
115+
// Types
116+
new(@"\b(boolean|byte|char|double|float|int|long|short|void|var)\b", ConsoleColor.DarkCyan),
117+
118+
// Class names and custom types
119+
new(@"\b[A-Z][a-zA-Z0-9_]*\b", ConsoleColor.Cyan),
120+
121+
// Method declarations
122+
new(@"(?<=\b\w+\s+)\w+(?=\s*\()", ConsoleColor.Yellow),
123+
124+
// Generics
125+
new(@"<[^>]+>", ConsoleColor.DarkYellow),
126+
127+
// Annotations
128+
new(@"@\w+", ConsoleColor.DarkYellow),
129+
130+
// Numbers
131+
new(@"\b\d+(\.\d+)?[dDfFlL]?\b", ConsoleColor.Green),
132+
133+
// Strings
134+
new(@"""[^""\\]*(?:\\.[^""\\]*)*""", ConsoleColor.DarkGreen),
135+
}
136+
},
137+
{
138+
SerializableLanguage.PHP, new List<SyntaxRule>
139+
{
140+
// Keywords
141+
new(@"\b(abstract|and|array|as|break|callable|case|catch|class|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|eval|exit|extends|final|finally|fn|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|match|namespace|new|or|print|private|protected|public|require|require_once|return|static|switch|throw|trait|try|unset|use|var|while|yield|yield from)\b", ConsoleColor.Magenta),
142+
143+
// Types and type hints
144+
new(@"\b(bool|boolean|int|integer|float|double|string|array|object|callable|iterable|void|null|mixed|never|true|false)\b", ConsoleColor.DarkCyan),
145+
146+
// Class names and namespaces
147+
new(@"\b[A-Z][a-zA-Z0-9_]*\b", ConsoleColor.Cyan),
148+
149+
// Function declarations
150+
new(@"(?<=\bfunction\s+)\w+", ConsoleColor.Yellow),
151+
152+
// Variables
153+
new(@"\$\w+", ConsoleColor.DarkYellow),
154+
155+
// Numbers
156+
new(@"\b\d+(\.\d+)?\b", ConsoleColor.Green),
157+
158+
// Strings
159+
new(@"""[^""\\]*(?:\\.[^""\\]*)*""|'[^'\\]*(?:\\.[^'\\]*)*'", ConsoleColor.DarkGreen),
160+
}
161+
},
162+
{
163+
SerializableLanguage.Ruby, new List<SyntaxRule>
164+
{
165+
// Keywords
166+
new(@"\b(alias|and|begin|break|case|class|def|defined\?|do|else|elsif|end|ensure|false|for|if|in|module|next|nil|not|or|redo|rescue|retry|return|self|super|then|true|undef|unless|until|when|while|yield)\b", ConsoleColor.Magenta),
167+
168+
// Built-in classes and modules
169+
new(@"\b(Array|Hash|String|Integer|Float|Symbol|Range|Regexp|Time|File|Dir|Class|Module|Enumerable|Comparable|Kernel|Process|Thread)\b", ConsoleColor.DarkCyan),
170+
171+
// Class and module names
172+
new(@"\b[A-Z][a-zA-Z0-9_]*\b", ConsoleColor.Cyan),
173+
174+
// Method declarations
175+
new(@"(?<=\bdef\s+)\w+", ConsoleColor.Yellow),
176+
177+
// Symbols
178+
new(@":\w+", ConsoleColor.DarkYellow),
179+
180+
// Instance variables
181+
new(@"@{1,2}\w+", ConsoleColor.DarkYellow),
182+
183+
// Numbers
184+
new(@"\b\d+(\.\d+)?(e[+-]?\d+)?\b", ConsoleColor.Green),
185+
186+
// Strings
187+
new(@"""[^""\\]*(?:\\.[^""\\]*)*""|'[^'\\]*(?:\\.[^'\\]*)*'|%[qQ]?\{[^}]*\}|%[qQ]?\[[^\]]*\]|%[qQ]?\([^)]*\)|%[qQ]?<[^>]*>", ConsoleColor.DarkGreen),
188+
}
189+
},
190+
{
191+
SerializableLanguage.Swift, new List<SyntaxRule>
192+
{
193+
// Keywords
194+
new(@"\b(associatedtype|class|deinit|enum|extension|fileprivate|func|import|init|inout|internal|let|open|operator|private|precedencegroup|protocol|public|rethrows|static|struct|subscript|typealias|var|break|case|continue|default|defer|do|else|fallthrough|for|guard|if|in|repeat|return|switch|where|while|as|Any|catch|false|is|nil|super|self|Self|throw|throws|true|try)\b", ConsoleColor.Magenta),
195+
196+
// Types
197+
new(@"\b(Int|Double|Float|Bool|String|Character|Optional|Array|Dictionary|Set|Result|Error)\b", ConsoleColor.DarkCyan),
198+
199+
// Type names and protocols
200+
new(@"\b[A-Z][a-zA-Z0-9_]*\b", ConsoleColor.Cyan),
201+
202+
// Function declarations
203+
new(@"(?<=\bfunc\s+)\w+", ConsoleColor.Yellow),
204+
205+
// Property wrappers and attributes
206+
new(@"@\w+", ConsoleColor.DarkYellow),
207+
208+
// Generics
209+
new(@"<[^>]+>", ConsoleColor.DarkYellow),
210+
211+
// Numbers
212+
new(@"\b\d+(\.\d+)?([eE][+-]?\d+)?\b", ConsoleColor.Green),
213+
214+
// Strings
215+
new(@"""[^""\\]*(?:\\.[^""\\]*)*""", ConsoleColor.DarkGreen),
216+
}
217+
}
218+
};
219+
220+
221+
222+
public void DisplayCodeWithColors(string code, SerializableLanguage language)
223+
{
224+
if (!SyntaxRulesByLanguage.ContainsKey(language))
225+
{
226+
System.Console.WriteLine(code);
227+
return;
228+
}
229+
230+
var rules = SyntaxRulesByLanguage[language];
231+
var spans = new List<(int start, int end, ConsoleColor color)>();
232+
233+
// Collect all matches
234+
foreach (var rule in rules)
235+
{
236+
var matches = Regex.Matches(code, rule.Pattern, RegexOptions.Compiled);
237+
foreach (Match match in matches)
238+
{
239+
spans.Add((match.Index, match.Index + match.Length, rule.Color));
240+
}
241+
}
242+
243+
// Sort spans by start position and handle overlaps
244+
spans.Sort((a, b) => a.start.CompareTo(b.start));
245+
var mergedSpans = new List<(int start, int end, ConsoleColor color)>();
246+
247+
for (int i = 0; i < spans.Count; i++)
248+
{
249+
var current = spans[i];
250+
bool overlap = false;
251+
252+
// Check if this span overlaps with any previous span
253+
for (int j = 0; j < mergedSpans.Count; j++)
254+
{
255+
if (current.start < mergedSpans[j].end && current.end > mergedSpans[j].start)
256+
{
257+
overlap = true;
258+
break;
259+
}
260+
}
261+
262+
if (!overlap)
263+
{
264+
mergedSpans.Add(current);
265+
}
266+
}
267+
268+
// Print with colors
269+
int currentPos = 0;
270+
foreach (var span in mergedSpans)
271+
{
272+
// Print text before the colored span
273+
if (currentPos < span.start)
274+
{
275+
System.Console.ResetColor();
276+
System.Console.Write(code.Substring(currentPos, span.start - currentPos));
277+
}
278+
279+
// Print the colored span
280+
System.Console.ForegroundColor = span.color;
281+
System.Console.Write(code.Substring(span.start, span.end - span.start));
282+
currentPos = span.end;
283+
}
284+
285+
// Print any remaining text
286+
if (currentPos < code.Length)
287+
{
288+
System.Console.ResetColor();
289+
System.Console.Write(code.Substring(currentPos));
290+
}
291+
292+
System.Console.ResetColor();
293+
System.Console.WriteLine();
294+
}
295+
}
296+
297+
public class SyntaxRule
298+
{
299+
public string Pattern { get; }
300+
public ConsoleColor Color { get; }
301+
302+
public SyntaxRule(string pattern, ConsoleColor color)
303+
{
304+
Pattern = pattern;
305+
Color = color;
306+
}
307+
}

Diff for: src/Riverside.JsonBinder.Console/InteractiveMode.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ private static void ConvertJsonToClasses()
8181
System.Console.WriteLine("=========================================");
8282
System.Console.WriteLine(" Generating Classes");
8383
System.Console.WriteLine("=========================================");
84-
84+
var syntaxHighlighter = new SyntaxHighlighter();
8585
foreach (var language in selectedLanguages)
8686
{
8787
try
@@ -90,7 +90,9 @@ private static void ConvertJsonToClasses()
9090
System.Console.ForegroundColor = ConsoleColor.Green;
9191
System.Console.WriteLine($"\n{language} Classes:\n");
9292
System.Console.ResetColor();
93-
System.Console.WriteLine(result);
93+
94+
// Use SyntaxHighlighter to display the code with colors
95+
syntaxHighlighter.DisplayCodeWithColors(result, language);
9496
}
9597
catch (JsonException ex)
9698
{
@@ -102,6 +104,7 @@ private static void ConvertJsonToClasses()
102104
}
103105
}
104106

107+
105108
ConsoleHelpers.PressAnyKey();
106109
}
107110

Diff for: src/Riverside.JsonBinder.Console/Program.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ private static void ConvertJsonToClasses(string json, string[] languages)
5353
return;
5454
}
5555

56+
var syntaxHighlighter = new SyntaxHighlighter();
5657
foreach (string choice in languages)
5758
{
5859
if (Enum.TryParse<SerializableLanguage>(choice.Trim(), true, out var selectedLanguage))
@@ -61,7 +62,7 @@ private static void ConvertJsonToClasses(string json, string[] languages)
6162
{
6263
string result = JsonSerializer.ConvertTo(json, selectedLanguage);
6364
System.Console.WriteLine($"\n{selectedLanguage} Classes:\n");
64-
System.Console.WriteLine(result);
65+
syntaxHighlighter.DisplayCodeWithColors(result, selectedLanguage);
6566
}
6667
catch (JsonException ex)
6768
{

0 commit comments

Comments
 (0)