From 64cd02aba8ce81d6a1d8b63fedf7022966996ee6 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Wed, 4 Nov 2026 12:06:41 -0800 Subject: [PATCH] Add option to exclude certain orphaned include files --- cleanrepo/Options.cs | 1 + cleanrepo/Program.cs | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/cleanrepo/Options.cs b/cleanrepo/Options.cs index 08f57873..0c353318 100644 --- a/cleanrepo/Options.cs +++ b/cleanrepo/Options.cs @@ -13,4 +13,5 @@ class Options public string? FilterTextJsonFile { get; set; } public List? ReferencingDirectories { get; set; } public List? ExcludeSnippetDescendantsOfFolders { get; set; } + public List? ExcludeOrphanedIncludeFiles { get; set; } } diff --git a/cleanrepo/Program.cs b/cleanrepo/Program.cs index cc17386f..9e5e40f0 100644 --- a/cleanrepo/Program.cs +++ b/cleanrepo/Program.cs @@ -269,7 +269,7 @@ static async Task RunOptions(Options options) else Console.WriteLine($"\nChecking {includeFiles.Count} include files."); - ListOrphanedIncludes(options.TargetDirectory, includeFiles, options.Delete); + ListOrphanedIncludes(options.TargetDirectory, includeFiles, options.Delete, options.ExcludeOrphanedIncludeFiles); break; } case "FindOrphanedSnippets": @@ -618,10 +618,38 @@ string originalLink /// For each markdown file /// Do a RegEx search for the include file /// If found, BREAK to the next include file - private static void ListOrphanedIncludes(string inputDirectory, Dictionary includeFiles, bool deleteOrphanedIncludes) + private static void ListOrphanedIncludes(string inputDirectory, Dictionary includeFiles, bool deleteOrphanedIncludes, List? excludedIncludeFiles) { DirectoryInfo? rootDirectory = null; + HashSet excludedFileNames = new(StringComparer.InvariantCultureIgnoreCase); + HashSet excludedFullPaths = new(StringComparer.InvariantCultureIgnoreCase); + + if (excludedIncludeFiles is { Count: > 0 }) + { + foreach (string excludedIncludeFile in excludedIncludeFiles) + { + if (string.IsNullOrWhiteSpace(excludedIncludeFile)) + continue; + + string candidate = excludedIncludeFile.Trim(); + + if (candidate.IndexOfAny([Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar]) >= 0 || + Path.IsPathRooted(candidate)) + { + string fullPath = Path.IsPathRooted(candidate) + ? Path.GetFullPath(candidate) + : Path.GetFullPath(Path.Combine(inputDirectory, candidate)); + + excludedFullPaths.Add(fullPath); + } + else + { + excludedFileNames.Add(candidate); + } + } + } + // Get all files that could possibly link to the include files List? mdfiles = HelperMethods.GetAllReferencingFiles("*.md", inputDirectory, ref rootDirectory); List? ymlfiles = HelperMethods.GetAllReferencingFiles("*.yml", inputDirectory, ref rootDirectory); @@ -691,6 +719,12 @@ private static void ListOrphanedIncludes(string inputDirectory, Dictionary includeFile in includeFiles) { + if (excludedFullPaths.Contains(Path.GetFullPath(includeFile.Key)) || + excludedFileNames.Contains(Path.GetFileName(includeFile.Key))) + { + includeFiles[includeFile.Key] = 1; + } + // Special case - consider license.md and readme.md as "referenced" // even if they aren't explicitly referenced in any other file. if (string.Equals(Path.GetFileName(includeFile.Key), "LICENSE.md", StringComparison.InvariantCultureIgnoreCase) ||