public static bool TryParseAsEnumFlags <TEnum>( IEnumerable <string> values, string optionName, out TEnum result, TEnum?defaultValue = null, OptionValueProvider provider = null) where TEnum : struct { result = (TEnum)(object)0; if (values?.Any() != true) { if (defaultValue != null) { result = (TEnum)(object)defaultValue; } return(true); } int flags = 0; foreach (string value in values) { if (!TryParseAsEnum(value, optionName, out TEnum result2, provider: provider)) { return(false); } flags |= (int)(object)result2; } result = (TEnum)(object)flags; return(true); }
public static bool TryParseAsEnumValues <TEnum>( IEnumerable <string> values, string optionName, out ImmutableArray <TEnum> result, ImmutableArray <TEnum> defaultValue = default, OptionValueProvider provider = null) where TEnum : struct { if (values?.Any() != true) { result = (defaultValue.IsDefault) ? ImmutableArray <TEnum> .Empty : defaultValue; return(true); } ImmutableArray <TEnum> .Builder builder = ImmutableArray.CreateBuilder <TEnum>(); foreach (string value in values) { if (!TryParseAsEnum(value, optionName, out TEnum result2, provider: provider)) { return(false); } builder.Add(result2); } result = builder.ToImmutableArray(); return(true); }
public static ImmutableArray <OptionValueList> GetOptionValues( IEnumerable <CommandOption> options, IEnumerable <OptionValueProvider> providers, Filter?filter = null) { providers = OptionValueProvider.GetProviders(options, providers).ToImmutableArray(); IEnumerable <OptionValue> allValues = providers.SelectMany(p => p.Values.Where(v => !v.Hidden)); ImmutableArray <OptionValueList> .Builder builder = ImmutableArray.CreateBuilder <OptionValueList>(); (int width1, int width2) = CalculateOptionValuesWidths(allValues); foreach (OptionValueProvider provider in providers) { IEnumerable <OptionValue> values = provider.Values.Where(f => !f.Hidden); ImmutableArray <OptionValueItem> valueItems = GetOptionValueItems(values, width1, width2); builder.Add(new OptionValueList(provider.Name, valueItems)); } return((filter != null) ? FilterOptionValues(builder, filter) : builder.ToImmutableArray()); }
public static bool TryParseAsEnum <TEnum>( string value, string optionName, out TEnum result, TEnum?defaultValue = null, OptionValueProvider provider = null) where TEnum : struct { if (!TryParseAsEnum(value, out result, defaultValue, provider)) { WriteParseError(value, optionName, provider?.GetHelpText() ?? OptionValue.GetDefaultHelpText <TEnum>()); return(false); } return(true); }
public override void WriteOptionDescription(CommandOption option) { _writer.WriteString(option.Description); OptionValueProvider provider = null !; if (TryGetProvider()) { if (!string.IsNullOrEmpty(option.Description)) { _writer.WriteString(" "); } _writer.WriteString((provider.Values.Length == 1) ? "Allowed value is " : "Allowed values are "); _writer.WriteString(OptionValueProviders.GetHelpText(provider, f => !f.Hidden)); _writer.WriteString("."); } if (!string.IsNullOrEmpty(option.Description) || provider != null) { _writer.WriteLine(); _writer.WriteLine(); } bool TryGetProvider() { if (option.ValueProviderName != null) { return(OptionValueProviders.ProvidersByName.TryGetValue(option.ValueProviderName, out provider)); } if (option.MetaValue != null && OptionValueProviders.ProvidersByName.TryGetValue(option.MetaValue, out provider)) { return(true); } Match match = _metaValueRegex.Match(option.Description); return(match.Success && OptionValueProviders.ProvidersByName.TryGetValue(match.Value, out provider)); } }
public static bool TryParse( IEnumerable <string> values, string optionName, OptionValueProvider provider, out Filter?filter, bool allowNull = false, OptionValueProvider?namePartProvider = null, FileNamePart defaultNamePart = FileNamePart.Name, PatternOptions includedPatternOptions = PatternOptions.None) { return(TryParse( values: values, optionName: optionName, provider: provider, filter: out filter, namePart: out _, allowNull: allowNull, namePartProvider: namePartProvider, defaultNamePart: defaultNamePart, includedPatternOptions: includedPatternOptions)); }
public static bool TryParseAsEnum <TEnum>( string value, out TEnum result, TEnum?defaultValue = null, OptionValueProvider provider = default) where TEnum : struct { if (value == null && defaultValue != null) { result = defaultValue.Value; return(true); } if (provider != null) { return(provider.TryParseEnum(value, out result)); } else { return(Enum.TryParse(value?.Replace("-", ""), ignoreCase: true, out result)); } }
private static void WriteParseError(string value, string optionName, OptionValueProvider provider) { string helpText = provider.GetHelpText(); WriteParseError(value, optionName, helpText); }
internal static bool TryParseRegexOptions( IEnumerable <string> options, string optionsParameterName, out RegexOptions regexOptions, out PatternOptions patternOptions, PatternOptions includedPatternOptions = PatternOptions.None, OptionValueProvider provider = null) { regexOptions = RegexOptions.None; if (!TryParseAsEnumFlags(options, optionsParameterName, out patternOptions, provider: provider ?? OptionValueProviders.PatternOptionsProvider)) { return(false); } Debug.Assert((patternOptions & (PatternOptions.CaseSensitive | PatternOptions.IgnoreCase)) != (PatternOptions.CaseSensitive | PatternOptions.IgnoreCase)); if ((patternOptions & PatternOptions.CaseSensitive) != 0) { includedPatternOptions &= ~PatternOptions.IgnoreCase; } else if ((patternOptions & PatternOptions.IgnoreCase) != 0) { includedPatternOptions &= ~PatternOptions.CaseSensitive; } patternOptions |= includedPatternOptions; if ((patternOptions & PatternOptions.Compiled) != 0) { regexOptions |= RegexOptions.Compiled; } if ((patternOptions & PatternOptions.CultureInvariant) != 0) { regexOptions |= RegexOptions.CultureInvariant; } if ((patternOptions & PatternOptions.ECMAScript) != 0) { regexOptions |= RegexOptions.ECMAScript; } if ((patternOptions & PatternOptions.ExplicitCapture) != 0) { regexOptions |= RegexOptions.ExplicitCapture; } if ((patternOptions & PatternOptions.IgnoreCase) != 0) { regexOptions |= RegexOptions.IgnoreCase; } if ((patternOptions & PatternOptions.IgnorePatternWhitespace) != 0) { regexOptions |= RegexOptions.IgnorePatternWhitespace; } if ((patternOptions & PatternOptions.Multiline) != 0) { regexOptions |= RegexOptions.Multiline; } if ((patternOptions & PatternOptions.RightToLeft) != 0) { regexOptions |= RegexOptions.RightToLeft; } if ((patternOptions & PatternOptions.Singleline) != 0) { regexOptions |= RegexOptions.Singleline; } return(true); }
public static bool TryParseDisplay( IEnumerable <string> values, string optionName, out ContentDisplayStyle?contentDisplayStyle, out PathDisplayStyle?pathDisplayStyle, out LineDisplayOptions lineDisplayOptions, out DisplayParts displayParts, out ImmutableArray <FileProperty> fileProperties, out string indent, out string separator, OptionValueProvider contentDisplayStyleProvider = null, OptionValueProvider pathDisplayStyleProvider = null) { contentDisplayStyle = null; pathDisplayStyle = null; lineDisplayOptions = LineDisplayOptions.None; displayParts = DisplayParts.None; fileProperties = ImmutableArray <FileProperty> .Empty; indent = null; separator = null; ImmutableArray <FileProperty> .Builder builder = null; foreach (string value in values) { int index = value.IndexOf('='); if (index >= 0) { string key = value.Substring(0, index); string value2 = value.Substring(index + 1); if (OptionValues.Display_Content.IsKeyOrShortKey(key)) { if (!TryParseAsEnum(value2, optionName, out ContentDisplayStyle contentDisplayStyle2, provider: contentDisplayStyleProvider)) { return(false); } contentDisplayStyle = contentDisplayStyle2; } else if (OptionValues.Display_Path.IsKeyOrShortKey(key)) { if (!TryParseAsEnum(value2, optionName, out PathDisplayStyle pathDisplayStyle2, provider: pathDisplayStyleProvider)) { return(false); } pathDisplayStyle = pathDisplayStyle2; } else if (OptionValues.Display_Indent.IsKeyOrShortKey(key)) { indent = value2; } else if (OptionValues.Display_Separator.IsKeyOrShortKey(key)) { separator = value2; } else { ThrowException(value); } } else if (OptionValues.Display_Summary.IsValueOrShortValue(value)) { displayParts |= DisplayParts.Summary; } else if (OptionValues.Display_Count.IsValueOrShortValue(value)) { displayParts |= DisplayParts.Count; } else if (OptionValues.Display_CreationTime.IsValueOrShortValue(value)) { (builder ?? (builder = ImmutableArray.CreateBuilder <FileProperty>())).Add(FileProperty.CreationTime); } else if (OptionValues.Display_ModifiedTime.IsValueOrShortValue(value)) { (builder ?? (builder = ImmutableArray.CreateBuilder <FileProperty>())).Add(FileProperty.ModifiedTime); } else if (OptionValues.Display_Size.IsValueOrShortValue(value)) { (builder ?? (builder = ImmutableArray.CreateBuilder <FileProperty>())).Add(FileProperty.Size); } else if (OptionValues.Display_LineNumber.IsValueOrShortValue(value)) { lineDisplayOptions |= LineDisplayOptions.IncludeLineNumber; } else if (OptionValues.Display_TrimLine.IsValueOrShortValue(value)) { lineDisplayOptions |= LineDisplayOptions.TrimLine; } else if (OptionValues.Display_TrimLine.IsValueOrShortValue(value)) { lineDisplayOptions |= LineDisplayOptions.TrimLine; } else if (OptionValues.Display_TrimLine.IsValueOrShortValue(value)) { lineDisplayOptions |= LineDisplayOptions.TrimLine; } else { ThrowException(value); } } if (builder != null) { fileProperties = builder.ToImmutableArray(); } return(true); void ThrowException(string value) { string helpText = OptionValueProviders.DisplayProvider.GetHelpText(); throw new ArgumentException($"Option '{OptionNames.GetHelpText(optionName)}' has invalid value '{value}'. Allowed values: {helpText}.", nameof(values)); } }
public static bool TryParse( IEnumerable <string> values, string optionName, OptionValueProvider provider, out Filter?filter, out FileNamePart namePart, bool allowNull = false, OptionValueProvider?namePartProvider = null, FileNamePart defaultNamePart = FileNamePart.Name, PatternOptions includedPatternOptions = PatternOptions.None) { filter = null; namePart = defaultNamePart; string?pattern = values.FirstOrDefault(); if (pattern == null) { if (allowNull) { return(true); } else { throw new InvalidOperationException($"Option '{OptionNames.GetHelpText(optionName)}' is required."); } } TimeSpan matchTimeout = Regex.InfiniteMatchTimeout; string? groupName = null; string? separator = null; Func <string, bool>?predicate = null; List <string>?options = null; foreach (string option in values.Skip(1)) { int index = option.IndexOfAny(_equalsOrLessThanOrGreaterThanChars); if (index != -1) { string key = option.Substring(0, index); if (!provider.ContainsKeyOrShortKey(key)) { WriteOptionError(option, optionName, provider); return(false); } string value = option.Substring(index + 1); if (OptionValues.Group.IsKeyOrShortKey(key)) { groupName = value; continue; } else if (OptionValues.ListSeparator.IsKeyOrShortKey(key)) { separator = value; continue; } else if (OptionValues.Part.IsKeyOrShortKey(key)) { if (!TryParseAsEnum( value, out namePart, provider: namePartProvider ?? OptionValueProviders.NamePartKindProvider)) { WriteOptionValueError( value, OptionValues.Part, namePartProvider ?? OptionValueProviders.NamePartKindProvider); return(false); } continue; } else if (OptionValues.Timeout.IsKeyOrShortKey(key)) { if (!TryParseMatchTimeout(value, out matchTimeout)) { WriteOptionValueError(value, OptionValues.Timeout); return(false); } continue; } } if (Expression.TryParse(option, out Expression? expression)) { if (OptionValues.Length.IsKeyOrShortKey(expression.Identifier) && provider.ContainsKeyOrShortKey(expression.Identifier)) { try { predicate = PredicateHelpers.GetLengthPredicate(expression); continue; } catch (ArgumentException) { WriteOptionValueError( expression.Value, OptionValues.Length, HelpProvider.GetExpressionsText(" ", includeDate: false)); return(false); } } else { WriteOptionError(option, optionName, provider); return(false); } } (options ??= new List <string>()).Add(option); } if (!TryParseRegexOptions( options, optionName, out RegexOptions regexOptions, out PatternOptions patternOptions, includedPatternOptions, provider)) { return(false); } switch (patternOptions & (PatternOptions.WholeWord | PatternOptions.WholeLine)) { case PatternOptions.None: case PatternOptions.WholeWord: case PatternOptions.WholeLine: { break; } default: { string helpValue = OptionValueProviders.PatternOptionsProvider .GetValue(nameof(PatternOptions.WholeWord)).HelpValue; string helpValue2 = OptionValueProviders.PatternOptionsProvider .GetValue(nameof(PatternOptions.WholeLine)).HelpValue; WriteError($"Values '{helpValue}' and '{helpValue2}' cannot be combined."); return(false); } } if ((patternOptions & PatternOptions.FromFile) != 0 && !FileSystemHelpers.TryReadAllText(pattern, out pattern, ex => WriteError(ex))) { return(false); } pattern = BuildPattern(pattern, patternOptions, separator); if (pattern.Length == 0) { throw new InvalidOperationException( $"Option '{OptionNames.GetHelpText(optionName)}' is invalid: pattern cannot be empty."); } Regex?regex = null; try { regex = new Regex(pattern, regexOptions, matchTimeout); } catch (ArgumentException ex) { WriteError(ex, $"Could not parse regular expression: {ex.Message}"); return(false); } int groupIndex = -1; if (groupName != null) { groupIndex = regex.GroupNumberFromName(groupName); if (groupIndex == -1) { string message = $"Group '{groupName}' does not exist."; string[] groupNames = regex.GetGroupNames(); if (groupNames.Length > 1) { message += " Existing group names: " + $"{TextHelpers.Join(", ", " and ", groupNames.Where(f => f != "0"))}."; } WriteError(message); return(false); } } filter = new Filter( regex, isNegative: (patternOptions & PatternOptions.Negative) != 0, groupNumber: groupIndex, predicate: predicate); return(true); }
private static void Main(params string[] args) { IEnumerable <Command> commands = CommandLoader.LoadCommands(typeof(CommandLoader).Assembly) .Select(c => c.WithOptions(c.Options.OrderBy(f => f, CommandOptionComparer.Name))) .OrderBy(c => c.Name, StringComparer.InvariantCulture); var application = new CommandLineApplication( "orang", "Search, replace, rename and delete files and its content using the power of .NET regular expressions.", commands.OrderBy(f => f.Name, StringComparer.InvariantCulture)); string?destinationDirectoryPath = null; string?dataDirectoryPath = null; if (Debugger.IsAttached) { destinationDirectoryPath = (args.Length > 0) ? args[0] : @"..\..\..\..\..\docs\cli"; dataDirectoryPath = @"..\..\..\data"; } else { destinationDirectoryPath = args[0]; dataDirectoryPath = @"..\src\DocumentationGenerator\data"; } string readmeFilePath = Path.GetFullPath(Path.Combine(destinationDirectoryPath, "README.md")); var settings = new MarkdownWriterSettings( MarkdownFormat.Default.WithTableOptions( MarkdownFormat.Default.TableOptions | TableOptions.FormatHeaderAndContent)); using (var sw = new StreamWriter(readmeFilePath, append: false, Encoding.UTF8)) using (MarkdownWriter mw = MarkdownWriter.Create(sw, settings)) { mw.WriteStartHeading(1); mw.WriteString("Orang Command-Line Interface"); mw.WriteRaw(" <img align=\"left\" src=\"../../images/icon48.png\">"); mw.WriteEndHeading(); mw.WriteString(application.Description); mw.WriteLine(); mw.WriteHeading2("Commands"); Table( TableRow("Command", "Description"), application .Commands .Select(f => TableRow(Link(f.Name, f.Name + "-command.md"), f.Description))) .WriteTo(mw); mw.WriteLine(); string readmeLinksFilePath = Path.Combine(dataDirectoryPath, "readme_bottom.md"); if (File.Exists(readmeLinksFilePath)) { mw.WriteRaw(File.ReadAllText(readmeLinksFilePath)); } WriteFootNote(mw); Console.WriteLine(readmeFilePath); } string valuesFilePath = Path.GetFullPath(Path.Combine(destinationDirectoryPath, "OptionValues.md")); ImmutableArray <OptionValueProvider> providers = OptionValueProvider.GetProviders( commands.SelectMany(f => f.Options), OptionValueProviders.ProvidersByName.Select(f => f.Value)) .ToImmutableArray(); MDocument document = Document( Heading1("List of Option Values"), BulletList(providers.Select(f => Link( f.Name, MarkdownHelpers.CreateGitHubHeadingLink(f.Name)))), providers.Select(provider => { return(new MObject[] { Heading2(provider.Name), Table( TableRow("Value", " ", "Description"), provider.Values.Select(f => CreateTableRow(f, providers))) }); })); document.Add( Heading2("Expression Syntax"), Table( TableRow("Expression", "Description"), HelpProvider.GetExpressionItems(includeDate: true) .Select(f => TableRow(InlineCode(f.expression), f.description)))); AddFootnote(document); var markdownFormat = new MarkdownFormat( tableOptions: MarkdownFormat.Default.TableOptions | TableOptions.FormatContent); File.WriteAllText(valuesFilePath, document.ToString(markdownFormat)); foreach (Command command in application.Commands) { readmeFilePath = Path.GetFullPath(Path.Combine(destinationDirectoryPath, $"{command.Name}-command.md")); using (var sw = new StreamWriter(readmeFilePath, append: false, Encoding.UTF8)) using (MarkdownWriter mw = MarkdownWriter.Create(sw)) { var writer = new DocumentationWriter(mw); writer.WriteCommandHeading(command, application); writer.WriteCommandDescription(command); mw.WriteLink("Home", "README.md#readme"); foreach (string section in new[] { "Synopsis", "Arguments", "Options", "Samples" }) { mw.WriteString(" "); mw.WriteCharEntity((char)0x2022); mw.WriteString(" "); mw.WriteLink(section, "#" + section); } mw.WriteLine(); writer.WriteCommandSynopsis(command, application); writer.WriteArguments(command.Arguments); writer.WriteOptions(command.Options); string samplesFilePath = Path.Combine(dataDirectoryPath, command.Name + "_bottom.md"); if (File.Exists(samplesFilePath)) { string content = File.ReadAllText(samplesFilePath); mw.WriteRaw(content); } WriteFootNote(mw); Console.WriteLine(readmeFilePath); } } Console.WriteLine("Done"); if (Debugger.IsAttached) { Console.ReadKey(); } }
public static bool TryParse( IEnumerable <string> values, string optionName, OptionValueProvider provider, out Filter filter, bool allowNull = false, NamePartKind defaultNamePart = NamePartKind.Name, PatternOptions includedPatternOptions = PatternOptions.None) { filter = null; string pattern = values.FirstOrDefault(); if (pattern == null) { if (allowNull) { return(true); } else { throw new InvalidOperationException($"Option '{OptionNames.GetHelpText(optionName)}' is required."); } } TimeSpan matchTimeout = Regex.InfiniteMatchTimeout; string groupName = null; NamePartKind namePart = defaultNamePart; string separator = null; Func <Capture, bool> predicate = null; List <string> options = null; foreach (string option in values.Skip(1)) { int index = option.IndexOf("="); if (index != -1) { string key = option.Substring(0, index); string value = option.Substring(index + 1); if (OptionValues.Group.IsKeyOrShortKey(key)) { groupName = value; continue; } else if (OptionValues.ListSeparator.IsKeyOrShortKey(key)) { separator = value; continue; } else if (OptionValues.Part.IsKeyOrShortKey(key)) { if (!TryParseAsEnum(value, out namePart, provider: OptionValueProviders.NamePartKindProvider)) { string helpText = OptionValueProviders.NamePartKindProvider.GetHelpText(); WriteError($"Option '{OptionValues.Part.HelpValue}' has invalid value '{value}'. Allowed values: {helpText}."); return(false); } continue; } else if (OptionValues.Timeout.IsKeyOrShortKey(key)) { if (!TryParseMatchTimeout(value, out matchTimeout)) { WriteError($"Option '{OptionValues.Timeout.HelpValue}' has invalid value '{value}'."); return(false); } continue; } } if (Expression.TryParse(option, out Expression expression)) { if (OptionValues.Length.IsKeyOrShortKey(expression.Identifier)) { try { predicate = PredicateHelpers.GetLengthPredicate(expression); continue; } catch (ArgumentException) { WriteError($"Option '{OptionNames.GetHelpText(optionName)}' has invalid value '{option}'."); return(false); } } else { WriteError($"Option '{OptionNames.GetHelpText(optionName)}' has invalid value '{option}'. Allowed values: {provider.GetHelpText()}."); return(false); } } (options ?? (options = new List <string>())).Add(option); } if (!TryParseRegexOptions(options, optionName, out RegexOptions regexOptions, out PatternOptions patternOptions, includedPatternOptions, provider)) { return(false); } switch (patternOptions & (PatternOptions.WholeWord | PatternOptions.WholeLine)) { case PatternOptions.None: case PatternOptions.WholeWord: case PatternOptions.WholeLine: { break; } default: { WriteError($"Values '{OptionValueProviders.PatternOptionsProvider.GetValue(nameof(PatternOptions.WholeWord)).HelpValue}' and '{OptionValueProviders.PatternOptionsProvider.GetValue(nameof(PatternOptions.WholeLine)).HelpValue}' cannot be combined."); return(false); } } if ((patternOptions & PatternOptions.FromFile) != 0 && !FileSystemHelpers.TryReadAllText(pattern, out pattern)) { return(false); } pattern = BuildPattern(pattern, patternOptions, separator); Regex regex = null; try { regex = new Regex(pattern, regexOptions, matchTimeout); } catch (ArgumentException ex) { WriteError(ex, $"Could not parse regular expression: {ex.Message}"); return(false); } int groupIndex = -1; if (groupName != null) { groupIndex = regex.GroupNumberFromName(groupName); if (groupIndex == -1) { string message = $"Group '{groupName}' does not exist."; string[] groupNames = regex.GetGroupNames(); if (groupNames.Length > 1) { message += $" Existing group names: {TextHelpers.Join(", ", " and ", groupNames.Where(f => f != "0"))}."; } WriteError(message); return(false); } } filter = new Filter( regex, namePart: namePart, groupNumber: groupIndex, isNegative: (patternOptions & PatternOptions.Negative) != 0, predicate); return(true); }
private static void Main(params string[] args) { IEnumerable <Command> commands = CommandLoader.LoadCommands(typeof(CommandLoader).Assembly) .Select(c => c.WithOptions(c.Options.OrderBy(f => f, CommandOptionComparer.Instance))) .OrderBy(c => c.Name, StringComparer.InvariantCulture); var application = new CommandLineApplication( "orang", "Search, replace, rename and delete files and its content using the power of .NET regular expressions.", commands.OrderBy(f => f.Name, StringComparer.InvariantCulture)); string?destinationDirectoryPath = null; string?dataDirectoryPath = null; if (Debugger.IsAttached) { destinationDirectoryPath = (args.Length > 0) ? args[0] : @"..\..\..\..\..\docs\cli"; dataDirectoryPath = @"..\..\..\data"; } else { destinationDirectoryPath = args[0]; dataDirectoryPath = @"..\src\DocumentationGenerator\data"; } string readmeFilePath = Path.GetFullPath(Path.Combine(destinationDirectoryPath, "README.md")); using (var sw = new StreamWriter(readmeFilePath, append: false, Encoding.UTF8)) using (MarkdownWriter mw = MarkdownWriter.Create(sw)) { mw.WriteHeading1("Orang Command-Line Interface"); mw.WriteString(application.Description); mw.WriteLine(); mw.WriteHeading2("Commands"); foreach (Command command in application.Commands) { mw.WriteStartBulletItem(); mw.WriteLink(command.Name, command.Name + "-command.md"); mw.WriteEndBulletItem(); } mw.WriteLine(); string readmeLinksFilePath = Path.Combine(dataDirectoryPath, "readme_bottom.md"); if (File.Exists(readmeLinksFilePath)) { mw.WriteRaw(File.ReadAllText(readmeLinksFilePath)); } WriteFootNote(mw); Console.WriteLine(readmeFilePath); } string valuesFilePath = Path.GetFullPath(Path.Combine(destinationDirectoryPath, "AllowedValues.md")); ImmutableArray <OptionValueProvider> providers = OptionValueProvider.GetProviders(commands.SelectMany(f => f.Options), OptionValueProviders.ProvidersByName.Select(f => f.Value)).ToImmutableArray(); MDocument document = Document( Heading1("List of Allowed Values"), BulletList(providers.Select(f => Link(f.Name, "#" + f.Name .ToLower() .Replace("<", "") .Replace(">", "") .Replace("_", "-")))), providers.Select(provider => { return(new MObject[] { Heading2(provider.Name), Table( TableRow("Value", "Description"), provider.Values.Select(f => TableRow(f.HelpValue, f.Description))) }); })); AddFootnote(document); var markdownFormat = new MarkdownFormat(tableOptions: MarkdownFormat.Default.TableOptions | TableOptions.FormatContent); File.WriteAllText(valuesFilePath, document.ToString(markdownFormat)); foreach (Command command in application.Commands) { readmeFilePath = Path.GetFullPath(Path.Combine(destinationDirectoryPath, $"{command.Name}-command.md")); using (var sw = new StreamWriter(readmeFilePath, append: false, Encoding.UTF8)) using (MarkdownWriter mw = MarkdownWriter.Create(sw)) { var writer = new DocumentationWriter(mw); writer.WriteCommandHeading(command, application); writer.WriteCommandDescription(command); writer.WriteCommandSynopsis(command, application); writer.WriteArguments(command.Arguments); writer.WriteOptions(command.Options); string samplesFilePath = Path.Combine(dataDirectoryPath, command.Name + "_bottom.md"); if (File.Exists(samplesFilePath)) { string content = File.ReadAllText(samplesFilePath); mw.WriteRaw(content); } WriteFootNote(mw); Console.WriteLine(readmeFilePath); } } Console.WriteLine("Done"); if (Debugger.IsAttached) { Console.ReadKey(); } }