/// <summary> /// Run the command-line application. /// </summary> /// <param name="args">The command-line arguments.</param> /// <returns>The exit code.</returns> public static int Run(IReadOnlyList <string> args) { try { var argsReader = new ArgsReader(args); if (argsReader.ReadHelpFlag()) { WriteUsage(Console.Out); return(0); } var isVerify = argsReader.ReadVerifyFlag(); var settings = new XmlDocMarkdownSettings { NewLine = argsReader.ReadNewLineOption(), SourceCodePath = argsReader.ReadSourceOption(), RootNamespace = argsReader.ReadNamespaceOption(), IncludeObsolete = argsReader.ReadObsoleteFlag(), SkipUnbrowsable = argsReader.ReadSkipUnbrowsableFlag(), VisibilityLevel = argsReader.ReadVisibilityOption(), ShouldClean = argsReader.ReadCleanFlag(), IsQuiet = argsReader.ReadQuietFlag(), IsDryRun = isVerify || argsReader.ReadDryRunFlag(), FrontMatter = argsReader.ReadFrontMatter(), PermalinkStyle = argsReader.ReadPermalinkStyle(), GenerateToc = argsReader.ReadTocFlag(), TocPrefix = argsReader.ReadTocPrefix(), NamespacePages = argsReader.ReadNamespacePagesFlag(), }; var externalDocs = new List <ExternalDocumentation>(); string?externalOption; while ((externalOption = argsReader.ReadExternalOption()) != null) { externalDocs.Add(new ExternalDocumentation { Namespace = externalOption }); } if (externalDocs.Count != 0) { settings.ExternalDocs = externalDocs; } var inputPath = argsReader.ReadArgument(); if (inputPath == null) { throw new ArgsReaderException("Missing input path."); } var input = File.Exists(inputPath) ? new XmlDocInput { AssemblyPath = inputPath } : new XmlDocInput { Assembly = Assembly.Load(inputPath) }; var outputPath = argsReader.ReadArgument(); if (outputPath == null) { throw new ArgsReaderException("Missing output path."); } argsReader.VerifyComplete(); var result = XmlDocMarkdownGenerator.Generate(input, outputPath, settings); foreach (var message in result.Messages) { Console.WriteLine(message); } return(isVerify && result.Added.Count + result.Changed.Count + result.Removed.Count != 0 ? 1 : 0); } catch (Exception exception) { if (exception is ArgsReaderException) { Console.Error.WriteLine(exception.Message); Console.Error.WriteLine(); WriteUsage(Console.Error); return(2); } else if (exception is ApplicationException || exception is IOException || exception is UnauthorizedAccessException) { Console.Error.WriteLine(exception.Message); return(3); } else { Console.Error.WriteLine(exception.ToString()); return(3); } } }
/// <summary> /// Generates Markdown from .NET XML documentation comments. /// </summary> /// <param name="input">The input.</param> /// <param name="outputPath">The output directory.</param> /// <param name="settings">The settings.</param> /// <returns>The names of files that were added, changed, or removed.</returns> public static XmlDocMarkdownResult Generate(XmlDocInput input, string outputPath, XmlDocMarkdownSettings settings) { if (input == null) { throw new ArgumentNullException(nameof(input)); } if (outputPath == null) { throw new ArgumentNullException(nameof(outputPath)); } var result = new XmlDocMarkdownResult(); settings = settings ?? new XmlDocMarkdownSettings(); var generator = new MarkdownGenerator { SourceCodePath = settings.SourceCodePath, RootNamespace = settings.RootNamespace, IncludeObsolete = settings.IncludeObsolete, SkipUnbrowsable = settings.SkipUnbrowsable, Visibility = settings.VisibilityLevel ?? XmlDocVisibilityLevel.Protected, ExternalDocs = settings.ExternalDocs, NamespacePages = settings.NamespacePages, FrontMatter = settings.FrontMatter, }; if (settings.NewLine != null) { generator.NewLine = settings.NewLine; } if (string.Compare(settings.PermalinkStyle, "pretty", StringComparison.OrdinalIgnoreCase) == 0) { generator.PermalinkPretty = true; } XmlDocAssembly xmlDocAssembly; var assembly = input.Assembly; if (assembly == null) { assembly = Assembly.LoadFrom(input.AssemblyPath); } var xmlDocPath = input.XmlDocPath; if (xmlDocPath == null) { var assemblyPath = input.AssemblyPath ?? assembly.Location; xmlDocPath = Path.ChangeExtension(assemblyPath, ".xml"); if (!File.Exists(xmlDocPath)) { xmlDocPath = Path.ChangeExtension(assemblyPath, ".XML"); } } if (xmlDocPath != null && File.Exists(xmlDocPath)) { var xDocument = XDocument.Load(xmlDocPath); xmlDocAssembly = new XmlDocAssembly(xDocument); } else { xmlDocAssembly = new XmlDocAssembly(); } var namedTexts = generator.GenerateOutput(assembly, xmlDocAssembly); var namedTextsToWrite = new List <NamedText>(); foreach (var namedText in namedTexts) { string existingFilePath = Path.Combine(outputPath, namedText.Name); if (File.Exists(existingFilePath)) { // ignore CR when comparing files if (namedText.Text.Replace("\r", "") != File.ReadAllText(existingFilePath).Replace("\r", "")) { namedTextsToWrite.Add(namedText); result.Changed.Add(namedText.Name); if (!settings.IsQuiet) { result.Messages.Add("changed " + namedText.Name); } } } else { namedTextsToWrite.Add(namedText); result.Added.Add(namedText.Name); if (!settings.IsQuiet) { result.Messages.Add("added " + namedText.Name); } } } if (settings.GenerateToc) { string tocPath = Path.Combine(outputPath, "toc.yml"); NamedText root = namedTexts.FirstOrDefault(); if (root != null) { XmlDocToc toc = new XmlDocToc() { Path = root.Name, Title = root.Title, Prefix = settings.TocPrefix }; foreach (var namedText in namedTexts.Skip(1)) { toc.AddChild(namedText.Name, namedText.Parent, namedText.Title); } toc.Save(tocPath); } } var namesToDelete = new List <string>(); if (settings.ShouldClean) { var directoryInfo = new DirectoryInfo(outputPath); if (directoryInfo.Exists) { string assemblyName = assembly.GetName().Name; string assemblyFilePath = assembly.Modules.FirstOrDefault()?.FullyQualifiedName; string assemblyFileName = assemblyFilePath != null?Path.GetFileName(assemblyFilePath) : assemblyName; string assemblyFolder = Path.GetFileNameWithoutExtension(assemblyFileName); var patterns = new[] { $"{assemblyFolder}/*.md", $"{assemblyFolder}/*/*.md" }; string codeGenComment = MarkdownGenerator.GetCodeGenComment(assemblyFileName); foreach (string nameMatchingPattern in FindNamesMatchingPatterns(directoryInfo, patterns, codeGenComment)) { if (namedTexts.All(x => x.Name != nameMatchingPattern)) { namesToDelete.Add(nameMatchingPattern); result.Removed.Add(nameMatchingPattern); if (!settings.IsQuiet) { result.Messages.Add("removed " + nameMatchingPattern); } } } } } if (!settings.IsDryRun) { if (!Directory.Exists(outputPath)) { Directory.CreateDirectory(outputPath); } foreach (var namedText in namedTextsToWrite) { string outputFilePath = Path.Combine(outputPath, namedText.Name); string outputFileDirectoryPath = Path.GetDirectoryName(outputFilePath); if (outputFileDirectoryPath != null && outputFileDirectoryPath != outputPath && !Directory.Exists(outputFileDirectoryPath)) { Directory.CreateDirectory(outputFileDirectoryPath); } File.WriteAllText(outputFilePath, namedText.Text); } foreach (string nameToDelete in namesToDelete) { File.Delete(Path.Combine(outputPath, nameToDelete)); } } return(result); }
/// <summary> /// Generates Markdown from .NET XML documentation comments. /// </summary> /// <param name="inputPath">The input assembly.</param> /// <param name="outputPath">The output directory.</param> /// <param name="settings">The settings.</param> /// <returns>The names of files that were added, changed, or removed.</returns> public static XmlDocMarkdownResult Generate(string inputPath, string outputPath, XmlDocMarkdownSettings settings) { if (inputPath == null) { throw new ArgumentNullException(nameof(inputPath)); } if (outputPath == null) { throw new ArgumentNullException(nameof(outputPath)); } var result = new XmlDocMarkdownResult(); settings = settings ?? new XmlDocMarkdownSettings(); var generator = new MarkdownGenerator { SourceCodePath = settings.SourceCodePath, RootNamespace = settings.RootNamespace, IncludeObsolete = settings.IncludeObsolete, Visibility = settings.VisibilityLevel ?? XmlDocVisibilityLevel.Protected, ExternalDocs = settings.ExternalDocs, }; if (settings.NewLine != null) { generator.NewLine = settings.NewLine; } var assembly = Assembly.LoadFrom(inputPath); XmlDocAssembly xmlDocAssembly; var xmlDocPath = Path.ChangeExtension(inputPath, ".xml"); if (!File.Exists(xmlDocPath)) { xmlDocPath = Path.ChangeExtension(inputPath, ".XML"); } if (File.Exists(xmlDocPath)) { var xDocument = XDocument.Load(xmlDocPath); xmlDocAssembly = new XmlDocAssembly(xDocument); } else { xmlDocAssembly = new XmlDocAssembly(); } var namedTexts = generator.GenerateOutput(assembly, xmlDocAssembly); var namedTextsToWrite = new List <NamedText>(); foreach (var namedText in namedTexts) { string existingFilePath = Path.Combine(outputPath, namedText.Name); if (File.Exists(existingFilePath)) { // ignore CR when comparing files if (namedText.Text.Replace("\r", "") != File.ReadAllText(existingFilePath).Replace("\r", "")) { namedTextsToWrite.Add(namedText); result.Changed.Add(namedText.Name); if (!settings.IsQuiet) { result.Messages.Add("changed " + namedText.Name); } } } else { namedTextsToWrite.Add(namedText); result.Added.Add(namedText.Name); if (!settings.IsQuiet) { result.Messages.Add("added " + namedText.Name); } } } var namesToDelete = new List <string>(); if (settings.ShouldClean) { var directoryInfo = new DirectoryInfo(outputPath); if (directoryInfo.Exists) { string assemblyName = assembly.GetName().Name; string assemblyFilePath = assembly.Modules.FirstOrDefault()?.FullyQualifiedName; string assemblyFileName = assemblyFilePath != null?Path.GetFileName(assemblyFilePath) : assemblyName; string assemblyFolder = Path.GetFileNameWithoutExtension(assemblyFileName); var patterns = new[] { $"{assemblyFolder}/*.md", $"{assemblyFolder}/*/*.md" }; string codeGenComment = MarkdownGenerator.GetCodeGenComment(assemblyFileName); foreach (string nameMatchingPattern in FindNamesMatchingPatterns(directoryInfo, patterns, codeGenComment)) { if (namedTexts.All(x => x.Name != nameMatchingPattern)) { namesToDelete.Add(nameMatchingPattern); result.Removed.Add(nameMatchingPattern); if (!settings.IsQuiet) { result.Messages.Add("removed " + nameMatchingPattern); } } } } } if (!settings.IsDryRun) { if (!Directory.Exists(outputPath)) { Directory.CreateDirectory(outputPath); } foreach (var namedText in namedTextsToWrite) { string outputFilePath = Path.Combine(outputPath, namedText.Name); string outputFileDirectoryPath = Path.GetDirectoryName(outputFilePath); if (outputFileDirectoryPath != null && outputFileDirectoryPath != outputPath && !Directory.Exists(outputFileDirectoryPath)) { Directory.CreateDirectory(outputFileDirectoryPath); } File.WriteAllText(outputFilePath, namedText.Text); } foreach (string nameToDelete in namesToDelete) { File.Delete(Path.Combine(outputPath, nameToDelete)); } } return(result); }
/// <summary> /// Generates Markdown from .NET XML documentation comments. /// </summary> /// <param name="inputPath">The input assembly.</param> /// <param name="outputPath">The output directory.</param> /// <param name="settings">The settings.</param> /// <returns>The names of files that were added, changed, or removed.</returns> public static XmlDocMarkdownResult Generate(string inputPath, string outputPath, XmlDocMarkdownSettings settings) { if (inputPath == null) { throw new ArgumentNullException(nameof(inputPath)); } return(Generate(new XmlDocInput { AssemblyPath = inputPath }, outputPath, settings)); }