/// <summary> /// Retrieves the help text for the given options container type. /// </summary> /// <param name="containerType"> /// The type of options container to retrieve the help text for. /// </param> /// <returns> /// A collection of text lines, the help text. /// </returns> /// <exception cref="ArgumentNullException"> /// <para><paramref name="containerType"/> is <c>null</c>.</para> /// </exception> public static IEnumerable <string> GetHelp(Type containerType) { if (containerType == null) { throw new ArgumentNullException("containerType"); } var map = new PropertyMap(containerType); // Command line help var parts = new List <string>(); int argumentIndex = 1; foreach (PropertyInfo prop in map.ArgumentProperties) { var attr = (ArgumentAttribute)prop.GetCustomAttributes(typeof(ArgumentAttribute), true)[0]; string name = attr.Name; if (StringEx.IsNullOrWhiteSpace(name)) { name = "ARG" + argumentIndex; } if (attr.Optional) { parts.Add("[" + name + "]"); } else { parts.Add(name); } argumentIndex++; } if (map.ArgumentsProperty != null) { parts.Add("[ARG]..."); } if (map.MappedProperties.Any()) { parts.Add("[OPTIONS]..."); } yield return(Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location).ToLower() + " " + string.Join(" ", parts.ToArray())); yield return(string.Empty); string[] containerHelp = GetHelpTextFor(containerType).ToArray(); if (containerHelp.Length > 0) { foreach (string line in containerHelp) { yield return(line); } yield return(string.Empty); } var argumentsWithDescription = (from entry in map.ArgumentProperties.Select((property, index) => new { property, index }) let descriptionAttr = entry.property.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault() as DescriptionAttribute where descriptionAttr != null let attr = (ArgumentAttribute)entry.property.GetCustomAttributes(typeof(ArgumentAttribute), true)[0] let argName = !StringEx.IsNullOrWhiteSpace(attr.Name) ? attr.Name : ("ARG" + (entry.index + 1)) select new { argName, text = StringEx.SplitLines(descriptionAttr.Description).ToArray() }).ToArray(); if (argumentsWithDescription.Length > 0) { yield return("arguments:"); yield return(string.Empty); int maxLongLength = argumentsWithDescription.Max(p => p.argName.Length); foreach (var arg in argumentsWithDescription) { int lines = arg.text.Length; if (arg.argName.Length > 20) { yield return(" " + arg.argName); var indent = new string(' ', maxLongLength + 3); foreach (string line in arg.text) { yield return(indent + line); } } else { yield return(" " + arg.argName.PadRight(maxLongLength, ' ') + " " + arg.text[0]); var indent = new string(' ', maxLongLength + 3); foreach (string line in arg.text.Skip(1)) { yield return(indent + line); } } } yield return(string.Empty); } var propertiesWithHelpText = (from propMap in map.MappedProperties let text = GetHelpTextFor(propMap.Key).ToArray() where text.Length > 0 select new { prop = propMap.Key, option = propMap.Value.Option, parameter = propMap.Value.ParameterName, text } into entry group entry by entry.prop into g select new { options = g.Select(e => e.option).OrderBy(o => o.Length).ToArray(), g.First().text, g.First().parameter } into entry2 orderby(entry2.options.First() == "-h" || entry2.options.First() == "--help") ? 0 : 1, entry2.options.First() let shortOption = entry2.options.Where(o => o.Length == 2).FirstOrDefault() ?? string.Empty let longOption = entry2.options.Where(o => o.Length > 2).FirstOrDefault() ?? string.Empty select new { shortOption, longOption, entry2.options, entry2.parameter, entry2.text }).ToArray(); if (propertiesWithHelpText.Length > 0) { yield return("options:"); yield return(string.Empty); int maxLongLength = propertiesWithHelpText.Max(p => p.longOption.Length + p.parameter.Length); foreach (var prop in propertiesWithHelpText) { int lines = prop.text.Length; if (prop.longOption.Length > 20) { yield return(" " + (prop.shortOption.PadRight(2, ' ') + " " + prop.longOption + " " + prop.parameter).Trim()); var indent = new string(' ', 27); foreach (string line in prop.text) { yield return(indent + line); } } else { yield return(" " + prop.shortOption.PadRight(2, ' ') + " " + (prop.longOption + " " + prop.parameter).PadRight(20, ' ') + " " + prop.text[0]); var indent = new string(' ', 27); foreach (string line in prop.text.Skip(1)) { yield return(indent + line); } } } } }