Example #1
0
        /// <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));
        }