/// <summary> /// Prints help text for specific command. /// </summary> /// <param name="command">Command descriptor.</param> /// <param name="unknownArgs">List of unrecognized command-line arguments.</param> private void PrintCommandHelp(CliCommand command, IReadOnlyCollection <string> unknownArgs) { PrintHeader(); PrintBadArgumentsError(unknownArgs); Console.Out.WriteLine(); Console.Out.WriteLine("Usage:"); Console.Out.WriteLine(" dotnet linq2db {0}{1}", command.Name, command.Template != string.Empty ? " " + command.Template : command.Template); Console.Out.WriteLine(); Console.Out.WriteLine("Options:"); // calculate column width for column layout var maxOptionNameWidth = 0; var maxOptionTypeWidth = 0; foreach (var option in command.GetOptionsWithoutCategory()) { CalculateOptionSizes(option); } foreach (var category in command.Categories) { foreach (var option in command.GetCategoryOptions(category)) { CalculateOptionSizes(option); } } // print options help grouped by option category const string indent = " "; foreach (var option in command.GetOptionsWithoutCategory()) { WriteOptionHelp(command, maxOptionNameWidth, indent, null, option); } foreach (var category in command.Categories) { Console.Out.WriteLine(); Console.Out.WriteLine("=== {0} : {1}", category.Name, category.Description); foreach (var option in command.GetCategoryOptions(category)) { WriteOptionHelp(command, maxOptionNameWidth, indent, category, option); } } // print command examples if (command.Examples.Count > 0) { Console.Out.WriteLine(); Console.Out.WriteLine("Examples:"); foreach (var example in command.Examples) { Console.Out.WriteLine(); Console.Out.WriteLine(" {0}", example.Command); Console.Out.WriteLine(" {0}", example.Help); } } void CalculateOptionSizes(CliOption option) { var optionWidth = GetOptionWidth(option); var typeWidth = GetOptionTypeName(option).Length; if (optionWidth > maxOptionNameWidth) { maxOptionNameWidth = optionWidth; } if (typeWidth > maxOptionTypeWidth) { maxOptionTypeWidth = typeWidth; } } }
private void WriteOptionHelp(CliCommand command, int maxOptionNameWidth, string indent, OptionCategory?category, CliOption option) { // workaround for https://github.com/linq2db/linq2db/issues/3612 var consoleWidth = 80; try { consoleWidth = Console.BufferWidth; } catch { }; Console.Out.WriteLine(); Console.Out.Write(" "); var optionNameWidth = GetOptionWidth(option); var type = GetOptionTypeName(option); if (option.AllowInCli) { // -x, --x-option : <help> if (option.ShortName != null) { Console.Out.Write("-{0}, ", option.ShortName.Value); } Console.Out.Write("--{0}", option.Name); Console.Out.Write(new string(' ', maxOptionNameWidth - optionNameWidth + 2)); Console.Out.WriteLine(" : {0}", option.Help); } else if (command.SupportsJSON) { // json-option : (allowed only in JSON) <help> Console.Out.Write(" {0}", option.Name); Console.Out.Write(new string(' ', maxOptionNameWidth - optionNameWidth + 2)); Console.Out.WriteLine(" : (allowed only in JSON) {0}", option.Help); } // option data type Console.Out.Write("{0} type: ", indent); Console.Out.WriteLine(type); // display options, that cannot be used together with current option var incompatibleWith = command.GetIncompatibleOptions(option); if (incompatibleWith != null) { Console.Out.WriteLine("{0} cannot use with: {1}", indent, string.Join(", ", incompatibleWith.Select(o => $"--{o.Name}"))); } if (command.SupportsJSON) { // option property path in json or "not allowed" text if not supported Console.Out.Write("{0} json: ", indent); if (option.AllowInJson) { if (category != null) { Console.Out.WriteLine("{0}.{1}", category.JsonProperty, option.Name); } else { Console.Out.WriteLine("{0}", option.Name); } } else { Console.Out.WriteLine("not allowed"); } } // print default value (if set) for non-required option if (!option.Required) { if (option is BooleanCliOption booleanOption) { Console.Out.WriteLine("{0} default: {1}", indent, booleanOption.Default ? "true" : "false"); Console.Out.WriteLine("{0} default (T4 mode): {1}", indent, booleanOption.T4Default ? "true" : "false"); } if (option is StringCliOption stringOption) { if (stringOption.Default != null) { if (!stringOption.AllowMultiple) { Console.Out.WriteLine("{0} default: {1}", indent, stringOption.Default[0]); } else { Console.Out.WriteLine("{0} default: {1}", indent, string.Join(",", stringOption.Default)); } } if (stringOption.T4Default != null) { if (!stringOption.AllowMultiple) { Console.Out.WriteLine("{0} default (T4 mode): {1}", indent, stringOption.T4Default[0]); } else { Console.Out.WriteLine("{0} default (T4 mode): {1}", indent, string.Join(",", stringOption.T4Default)); } } } else if (option is StringEnumCliOption enumOption) { var defaults = enumOption.Values.Where(o => o.Default).Select(o => o.Value).ToArray(); if (defaults.Length > 0) { Console.Out.WriteLine("{0} default: {1}", indent, string.Join(", ", defaults)); } defaults = enumOption.Values.Where(o => o.T4Default).Select(o => o.Value).ToArray(); if (defaults.Length > 0) { Console.Out.WriteLine("{0} default (T4 mode): {1}", indent, string.Join(", ", defaults)); } } else if (option is NamingCliOption namingOption) { if (namingOption.Default != null) { PrintNamingOptionDefaults(indent, "default", namingOption.Default); } if (namingOption.T4Default != null) { PrintNamingOptionDefaults(indent, "default (T4 mode)", namingOption.T4Default); } } } // for enum-typed option print list of supported values with description if (option is StringEnumCliOption enumStringOption) { Console.Out.WriteLine("{0} supported values:", indent); var maxValueWidth = enumStringOption.Values.Select(_ => _.Value.Length).Max(); foreach (var value in enumStringOption.Values) { Console.Out.WriteLine("{0}{0} {1}{3} : {2}", indent, value.Value, value.Help, new string(' ', maxValueWidth - value.Value.Length)); } } // print option CLI and JSON examples if provided if (option.Examples != null) { Console.Out.WriteLine("{0} examples:", indent); foreach (var example in option.Examples) { Console.Out.WriteLine("{0}{0} {1}", indent, example); } } if (option.JsonExamples != null) { Console.Out.WriteLine("{0} JSON examples:", indent); foreach (var example in option.JsonExamples) { Console.Out.WriteLine("{0}{0} {1}", indent, example); } } // print detailed option help if provided if (option.DetailedHelp != null) { Console.Out.WriteLine(); // split long text into lines manually and prepend each line with help indent for nicer formatting // TODO: dunno wether it works on linux/macos, not tested yet var lines = option.DetailedHelp.Split("\r\n"); for (var i = 0; i < lines.Length; i++) { var line = lines[i]; var lineWidth = consoleWidth - indent.Length - 1; var incompleteLineLength = line.Length % lineWidth; var partsCount = line.Length / lineWidth + (incompleteLineLength > 0 ? 1 : 0); for (var j = 0; j < partsCount; j++) { var part = line.Substring( j * lineWidth, j == partsCount - 1 && incompleteLineLength > 0 ? incompleteLineLength : lineWidth); Console.Out.Write(indent); Console.Out.WriteLine(part); } } } }