Created
December 2, 2022 13:03
-
-
Save Alois-xx/28a751118e0ca8bc7932a5ff049335fd to your computer and use it in GitHub Desktop.
ColorConsole Template from old VS Console Extension written by Alois Kraus
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Console template from old VS template from 2010 https://marketplace.visualstudio.com/items?itemName=Alois.ColoredConsoleApplicationTemplate | |
// Original announcement: https://web.archive.org/web/20101115040531/http://geekswithblogs.net/akraus1/archive/2010/11/11/142685.aspx | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
namespace $safeprojectname$ | |
{ | |
/// <summary> | |
/// Severity levels for each parser message. Used by <see cref="ArgParser.AddFormat"/> | |
/// </summary> | |
public enum Levels | |
{ | |
Info, | |
Warning, | |
Error | |
} | |
public class ArgParser | |
{ | |
/// Dictionary of command line switches. The action is called when the switch was found in the command line. | |
Dictionary<string, Action> mySwitches; | |
/// Dictionary of command line switches which expect one parameter. The action is called when the switch was found in the command line | |
Dictionary<string, Action<string>> mySwitchesWithArg; | |
// If at the end of the command line parameters are left this delegate is called. | |
Action<List<string>> myOtherArgs; | |
// List of info, warning, error messages that happened while parsing and verification of the command line switches | |
// They are printed via the PrintHelpWithMessages method. | |
List<Message> myMessages = new List<Message>(); | |
// Supported command line switch tags | |
char[] myDelimiters = new char[] { '/', '-' }; | |
/// <summary> | |
/// Construct a command line argument parser which can parse command line switches of the form /xxx -xxx. | |
/// </summary> | |
/// <param name="switches">Key is the command line switch. Value is an action that normally sets some boolean flag to true.</param> | |
/// <exception cref="ArgumentNullExcepton">switches must not be null.</exception> | |
public ArgParser(Dictionary<string, Action> switches) : this(switches, null, null) | |
{ | |
} | |
/// <summary> | |
/// Construct a command line argument parser which can parse command line switches of the form /xxx -xxx and | |
/// switches with one parameter like -x argument. | |
/// </summary> | |
/// <param name="switches">Key is the command line switch. Value is an action that normally sets some boolean flag to true.</param> | |
/// <param name="switcheswithArg">Key is the command line switch. Value is an action that normally sets the passed parameter for this switch to a member variable. E.g. test.exe /files test.txt will call the delegate with the name files in the dictionary with test.txt as parameter. Can be null.</param> | |
/// <exception cref="ArgumentNullExcepton">switches must not be null.</exception> | |
public ArgParser(Dictionary<string, Action> switches, Dictionary<string, Action<string>> switcheswithArg) : this(switches, switcheswithArg, null) | |
{ | |
} | |
/// <summary> | |
/// Construct a command line argument parser which can parse command line switches of the form /xxx -xxx and | |
/// switches with one parameter like -x argument. | |
/// </summary> | |
/// <param name="switches">Key is the command line switch. Value is an action that normally sets some boolean flag to true.</param> | |
/// <param name="switcheswithArg">Key is the command line switch. Value is an action that normally sets the passed parameter for this switch to a member variable. E.g. test.exe /files test.txt will call the delegate with the name files in the dictionary with test.txt as parameter. Can be null.</param> | |
/// <param name="otherArgs">Callback which is called with a list of the command line arguments which do not belong to a specific command line parameter. E.g. test.exe aaa bbb ccc will call this method with an array with aaa,bbb,ccc. Can be null.</param> | |
/// <exception cref="ArgumentNullExcepton">switches must not be null.</exception> | |
public ArgParser(Dictionary<string, Action> switches, Dictionary<string, Action<string>> switcheswithArg, Action<List<string>> otherArgs) | |
{ | |
if (switches == null) | |
throw new ArgumentNullException("switches"); | |
mySwitches = CreateDictWithShortCuts(switches); | |
myOtherArgs = otherArgs; | |
if (switcheswithArg == null) | |
{ | |
mySwitchesWithArg = new Dictionary<string, Action<string>>(); | |
} | |
else | |
{ | |
mySwitchesWithArg = CreateDictWithShortCuts(switcheswithArg); | |
// check for duplicate keys | |
foreach (string key in mySwitches.Keys) | |
{ | |
if (mySwitchesWithArg.ContainsKey(key)) | |
{ | |
throw new ArgumentException( | |
String.Format("The command line switch -{0} occurs in both switches dictionaries. Please make it unambiguous.", key)); | |
} | |
} | |
} | |
} | |
/// <summary> | |
/// Parse command line and call the corresponding actions passed from the ctor for each | |
/// found command line argument. | |
/// </summary> | |
/// <param name="args">Command line array.</param> | |
/// <returns>true when all command line switched could be parsed. false otherwise. In this case you should call | |
/// PrintHelpWithMessages to print your help string. Then all validation errors will be printed as well.</returns> | |
public bool ParseArgs(string[] args) | |
{ | |
if (args.Length == 0) | |
{ | |
AddFormat(Levels.Error, "No arguments specified."); | |
return false; | |
} | |
for (int i = 0; i < args.Length; i++) | |
{ | |
string arg = args[i]; | |
// get command line parameter for current argument if there is one | |
string parameter = (i + 1 < args.Length) ? args[i + 1] : null; | |
parameter = IsSwitch(parameter) ? null : parameter; | |
if (parameter != null) // Advance counter to next command line argument which does not belong to the current one | |
{ | |
i++; | |
} | |
if (IsSwitch(arg)) | |
{ | |
string strippedArg = arg.Substring(1).ToLower(); // command line switches are not case sensitive | |
if (true == mySwitches.OnValue(strippedArg)) // Set Flag for simple command line switch | |
{ | |
if (parameter != null) | |
{ | |
if (myOtherArgs == null) | |
{ | |
AddFormat(Levels.Error, "Superflous argument ({0}) for command line switch {1}", parameter, arg); | |
} | |
else // Other arguments present then process it if this was the last command line argument | |
{ | |
if (CallOtherArgs(args, i)) // all arguments are processed when this returns true | |
{ | |
break; | |
} | |
} | |
} | |
continue; | |
} | |
// Set for given flag the passed parameter for this flag | |
bool? ret = mySwitchesWithArg.OnValueAndParameterNotNull(strippedArg, parameter); | |
// not found then it must be an unknown command line switch | |
if (null == ret) | |
{ | |
AddFormat(Levels.Error, "Unknown command line switch {0}", arg); | |
} | |
else if (false == ret) // Found but argument was missing | |
{ | |
AddFormat(Levels.Error, "Missing data for command line switch {0}", arg); | |
} | |
else // when command was ok perhaps we have some arguments left if this was the last argument | |
{ | |
if (CallOtherArgs(args, i + 1)) // all arguments are processed when this returns true | |
{ | |
break; | |
} | |
} | |
} | |
else | |
{ | |
if (CallOtherArgs(args, (parameter == null) ? i : i-1)) | |
{ | |
break; // all arguments are processed when this returns true | |
} | |
else | |
{ | |
AddFormat(Levels.Error, "Not a command line switch: {0}", arg); | |
if (parameter != null) | |
{ | |
AddFormat(Levels.Error, "Not a command line switch: {0}", parameter); | |
} | |
} | |
} | |
} | |
return myMessages.Count == 0; | |
} | |
/// <summary> | |
/// Check if first character is one of the allowed command line tags. | |
/// </summary> | |
/// <param name="arg">command line argument to check. Can be null.</param> | |
/// <returns>true if it is an command line switch, false otherwise.</returns> | |
bool IsSwitch(string arg) | |
{ | |
if (String.IsNullOrEmpty(arg)) | |
return false; | |
return Array.Exists(myDelimiters, (char c) => arg[0] == c); | |
} | |
/// <summary> | |
/// Add a message to the list of messages which is displayed after parsing and parameter validation. | |
/// </summary> | |
/// <param name="level">Message type</param> | |
/// <param name="format">Message format string</param> | |
/// <param name="args">Optional message parameters</param> | |
public void AddFormat(Levels level, string format, params object[] args) | |
{ | |
myMessages.Add(new Message { Level = level, Text = String.Format(format, args) }); | |
} | |
/// <summary> | |
/// Call the delegate for the "other" arguments when no more command line switches are present. | |
/// </summary> | |
/// <param name="args">The command line argument array</param> | |
/// <param name="start">The start index from where the search starts.</param> | |
/// <returns>true if the delegate was called with the arguments. false otherwise.</returns> | |
bool CallOtherArgs(string[] args, int start) | |
{ | |
List<string> ret = new List<string>(); | |
for (int i = start; i < args.Length; i++) | |
{ | |
string curr = args[i]; | |
if (IsSwitch(curr)) // when a switch is found this is not the last argument. Do not process it | |
{ | |
ret = null; | |
break; | |
} | |
else | |
{ | |
ret.Add(curr); | |
} | |
} | |
if (myOtherArgs != null && ret != null) | |
{ | |
myOtherArgs(ret); | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
private Dictionary<string, T> CreateDictWithShortCuts<T>(Dictionary<string, T> switches) | |
{ | |
Dictionary<string, T> ret = new Dictionary<string, T>(); | |
// Generate shortcut names from upper case letters of command line arguments | |
foreach (var kvp in switches) | |
{ | |
ret.Add(kvp.Key.ToLower(), kvp.Value); | |
string shortCut = new string( | |
(from c in kvp.Key | |
where Char.IsUpper(c) | |
select c).ToArray()).ToLower(); | |
if (shortCut != "") | |
{ | |
if (ret.ContainsKey(shortCut)) | |
{ | |
throw new ArgumentException( | |
String.Format("The generated shortcut \"-{0}\" from \"-{1}\" collides with another command line switch.", shortCut, kvp.Key)); | |
} | |
ret.Add(shortCut, kvp.Value); | |
} | |
} | |
return ret; | |
} | |
/// <summary> | |
/// Print help and colored error and warning messages to the console. | |
/// </summary> | |
/// <param name="helpString">Help string which is printed first. Can be null.</param> | |
public void PrintHelpWithMessages(string helpString) | |
{ | |
if (helpString != null) | |
{ | |
Console.WriteLine(helpString); | |
} | |
foreach (var message in myMessages) | |
{ | |
string text = message.Text; | |
if (message.Level == Levels.Warning) | |
{ | |
text = "Warning: " + text; | |
} | |
else if (message.Level == Levels.Error) | |
{ | |
text = "Error: " + text; | |
} | |
Console.WriteLine(text); | |
} | |
} | |
struct Message | |
{ | |
public Levels Level; | |
public string Text; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Console template from old VS template from 2010 https://marketplace.visualstudio.com/items?itemName=Alois.ColoredConsoleApplicationTemplate | |
// Original announcement: https://web.archive.org/web/20101115040531/http://geekswithblogs.net/akraus1/archive/2010/11/11/142685.aspx | |
using System; | |
using System.Text; | |
using System.IO; | |
namespace $safeprojectname$ | |
{ | |
/// <summary> | |
/// Colored console class which does color the console output which do contain specific keywords. | |
/// The only thing you need to do is to instantiate this class and use Console.WriteLine as usual. | |
/// </summary> | |
class ColorConsole : TextWriter | |
{ | |
TextWriter myOriginal = Console.Out; | |
Func<string, ConsoleColor?> myColorizer; | |
/// <summary> | |
/// Initializes a new instance of the <see cref="ColorConsole"/> class. | |
/// </summary> | |
/// <param name="colorizer">The colorizer function which does return the color for each line to be printed to console.</param> | |
public ColorConsole(Func<string, ConsoleColor?> colorizer) | |
{ | |
if (colorizer == null) | |
{ | |
throw new ArgumentNullException("colorizer"); | |
} | |
myColorizer = colorizer; | |
if (!IsRedirected) | |
{ | |
// Replace Console.Out with our own colorizing instance which will be removed on dispose | |
Console.SetOut(this); | |
} | |
} | |
public override Encoding Encoding | |
{ | |
get { return myOriginal.Encoding; } | |
} | |
public override void WriteLine(string format) | |
{ | |
// we do not need to set the color every time only for the ones which do return a color | |
ConsoleColor? newColor = myColorizer(format); | |
if (newColor != null) | |
{ | |
ConsoleColor original = Console.ForegroundColor; | |
try | |
{ | |
Console.ForegroundColor = newColor.Value; | |
myOriginal.WriteLine(format); | |
} | |
finally | |
{ | |
Console.ForegroundColor = original; | |
} | |
} | |
else | |
{ | |
myOriginal.WriteLine(format); | |
} | |
} | |
public override void Write(char[] buffer) | |
{ | |
myOriginal.Write(buffer); | |
} | |
protected override void Dispose(bool disposing) | |
{ | |
base.Dispose(disposing); | |
if (!IsRedirected) | |
{ | |
Console.SetOut(myOriginal); | |
} | |
} | |
static bool? myIsRedirected; | |
static bool IsRedirected | |
{ | |
get | |
{ | |
if (myIsRedirected == null) | |
myIsRedirected = IsConsoleRedirected(); | |
return myIsRedirected.Value; | |
} | |
} | |
static bool IsConsoleRedirected() | |
{ | |
try | |
{ | |
// this is the easiest way to check if sdtout is redirected. Then this property throws | |
// an exception. | |
bool visible = Console.CursorVisible; | |
return false; | |
} | |
catch (Exception) | |
{ | |
return true; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Console template from old VS template from 2010 https://marketplace.visualstudio.com/items?itemName=Alois.ColoredConsoleApplicationTemplate | |
// Original announcement: https://web.archive.org/web/20101115040531/http://geekswithblogs.net/akraus1/archive/2010/11/11/142685.aspx | |
using System; | |
using System.Collections.Generic; | |
namespace $safeprojectname$ | |
{ | |
static class Extensions | |
{ | |
/// <summary> | |
/// Execute action inside a dictionary when the key is present. When key is not present do nothing. | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
/// <param name="dict">Dictionary</param> | |
/// <param name="key">key to look up acton</param> | |
/// <returns>true when action was found, false otherwise</returns> | |
public static bool OnValue<T>(this Dictionary<T, Action> dict, T key) | |
{ | |
if (key == null) | |
{ | |
throw new ArgumentNullException("key"); | |
} | |
Action acc = null; | |
if( dict.TryGetValue(key, out acc)) | |
{ | |
acc(); | |
return true; | |
} | |
return false; | |
} | |
/// <summary> | |
/// Execute action inside a dictionary with given parameter. When key is not present do nothing. | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
/// <param name="dict">Dictionary</param> | |
/// <param name="key">key too look up action in dictionary.</param> | |
/// <param name="parameter">Passed parameter to action.</param> | |
/// <returns>true if action was found. false when parameter was null. null when no action for given key was found.</returns> | |
/// <exception cref="ArgumentNullException">When key is null.</exception> | |
/// <remarks>When the parameter is null the action will NOT be called since we expect some data to work with. | |
/// Besides this it makes error handline in the actions easier when they can rely on a non null input argument.</remarks> | |
public static bool? OnValueAndParameterNotNull<T>(this Dictionary<T, Action<T>> dict, T key, T parameter) | |
{ | |
if (key == null) | |
{ | |
throw new ArgumentNullException("key"); | |
} | |
Action<T> acc = null; | |
if( dict.TryGetValue(key, out acc) ) | |
{ | |
if (parameter == null) | |
{ | |
return false; | |
} | |
acc(parameter); | |
return true; | |
} | |
return null; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Console template from old VS template from 2010 https://marketplace.visualstudio.com/items?itemName=Alois.ColoredConsoleApplicationTemplate | |
// Original announcement: https://web.archive.org/web/20101115040531/http://geekswithblogs.net/akraus1/archive/2010/11/11/142685.aspx | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.IO; | |
using System.Diagnostics; | |
using System.Reflection; | |
namespace $safeprojectname$ | |
{ | |
class Program | |
{ | |
static string HelpStr = | |
String.Format("$safeprojectname$ (c) 2010 by Alois Kraus v{0}{1}", Assembly.GetExecutingAssembly().GetName().Version, Environment.NewLine) + | |
"Explain what your application does" + Environment.NewLine + | |
"Usage: " + Environment.NewLine + | |
"$safeprojectname$ [-OptionaL] -ArgSwitch <file> <other arguments>" + Environment.NewLine + | |
" -ArgSwitch <file> Sample switch with argument." + Environment.NewLine + | |
" [-OptionaL] Optional: Optional command line switch" + Environment.NewLine + | |
" Examples: " + Environment.NewLine + | |
" $safeprojectname$ " + Environment.NewLine + | |
" $safeprojectname$ -as file" + Environment.NewLine + | |
" $safeprojectname$ -as file arg1 arg2 arg3" + Environment.NewLine + | |
" $safeprojectname$ -Optional -argswitch file arg1 arg2 arg3" + Environment.NewLine + | |
" $safeprojectname$ -Optional -argswitch file" + Environment.NewLine + | |
Environment.NewLine; | |
#region Parsed Command Line Switches | |
public bool Optional | |
{ | |
get; | |
set; | |
} | |
public string ArgSwitch | |
{ | |
get; | |
set; | |
} | |
public List<string> OtherArgs | |
{ | |
get; | |
set; | |
} | |
#endregion | |
/// <summary> | |
/// Main entry point which is directly called from main where nothing happens execept exception catching. | |
/// </summary> | |
/// <param name="args"></param> | |
public Program(string [] args) | |
{ | |
// define parameterless command line switches. | |
// Please note: Upper case characters define the shortcut name for each switch | |
var switches = new Dictionary<string, Action> | |
{ | |
{"OptionaL", () => Optional=true }, // shortcut -ol | |
}; | |
// define command line switches which take one parameter | |
var switchWithArg = new Dictionary<string, Action<string>> | |
{ | |
{"ArgSwitch", (arg) => ArgSwitch = arg }, // shortcut -AS | |
}; | |
// Handler for <other arguments> if present | |
Action<List<string>> rest = (parameters) => OtherArgs = parameters; | |
ArgParser parser = new ArgParser(switches, switchWithArg, rest); | |
// check if command line is well formed | |
if( !parser.ParseArgs(args) ) | |
{ | |
parser.PrintHelpWithMessages(HelpStr); | |
return; | |
} | |
if(!ValidateArgs(parser)) | |
{ | |
// Display errors but not the help screen. | |
parser.PrintHelpWithMessages(null); | |
} | |
else | |
{ | |
// Ok we can start | |
Run(); | |
} | |
} | |
private void Run() | |
{ | |
// Place here your actual code to execute your logic after the command line has been parsed and validated. | |
Console.WriteLine("Got Optional: {0}", this.Optional); | |
Console.WriteLine("Got ArgSwitch: {0}", this.ArgSwitch); | |
if (this.OtherArgs != null) | |
{ | |
Console.WriteLine("Info: Additional Arg Count: {0}", this.OtherArgs.Count); | |
foreach (var addtionalArg in this.OtherArgs) | |
{ | |
Console.WriteLine("Additional: {0}", addtionalArg); | |
} | |
} | |
} | |
/// <summary> | |
/// Check if the passed command line arguments do contain valid data | |
/// </summary> | |
/// <param name="parser">used for error reporting</param> | |
/// <returns>true if all parameters are valid. false otherwise.</returns> | |
private bool ValidateArgs(ArgParser parser) | |
{ | |
bool lret = true; | |
/* if (String.IsNullOrEmpty(ArgSwitch) ) | |
{ | |
parser.AddFormat(Levels.Error, "No arg for -ArgSwitch passed."); | |
return false; | |
} | |
*/ | |
return lret; | |
} | |
static void Main(string[] args) | |
{ | |
using (ColorConsole console = new ColorConsole(Colorizer)) | |
{ | |
try | |
{ | |
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); | |
Program p = new Program(args); | |
} | |
catch (Exception ex) | |
{ | |
PrintError("Error: {0}", ex); | |
} | |
} | |
} | |
/// <summary> | |
/// Set console color depending on currently to be printed line. | |
/// </summary> | |
/// <param name="line">line to be printed</param> | |
/// <returns>ConsoleColor if color needs to be set. Null otherwise.</returns> | |
static ConsoleColor? Colorizer(string line) | |
{ | |
ConsoleColor? col = null; | |
if (line.StartsWith("Error")) | |
col = ConsoleColor.Red; | |
else if (line.StartsWith("Info")) | |
col = ConsoleColor.Green; | |
return col; | |
} | |
/// <summary> | |
/// Catch uncatched exceptions thrown from other threads. | |
/// </summary> | |
/// <param name="sender"></param> | |
/// <param name="e"></param> | |
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) | |
{ | |
PrintError("Unhandled exception: {0}", (Exception)e.ExceptionObject); | |
} | |
static void PrintError(string format, Exception ex) | |
{ | |
Trace.TraceError(format, ex); | |
Console.WriteLine(format, ex.Message); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment