diff --git a/README.md b/README.md
index c0201ec..6b2b9c1 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,137 @@
-```bash
-dotnet run --project .\src\FileTree.CLI\FileTree.CLI.csproj -- -d 5 -f Unicode -e .cs .json -h
+## FileTree.Core
+
+FileTree.Core is a .NET 8 library and CLI tool for generating directory trees with rich filtering, depth/width limits, and support for `.gitignore` rules.
+It can be used both as a reusable library in your applications and as a command‑line tool for quickly inspecting project structures.
+
+## Features
+
+- **Multiple output formats**: `Ascii`, `Unicode`, `Markdown`
+- **Respects `.gitignore`**: optionally skip ignored files and folders
+- **Depth/width limits**: constrain recursion depth, per‑level width, and total node count
+- **Flexible filtering**: include/exclude by extension or name, skip empty folders, hide hidden files
+- **Library + CLI**: use `FileTreeService` in code or via `dotnet run`
+
+## Requirements
+
+- **.NET SDK**: 8.0 or later
+
+## Getting started
+
+Clone the repository and restore/build:
+
+```bash
+dotnet restore
+dotnet build
```
+
+### Running the CLI
+
+From the repository root:
+
+```bash
+dotnet run --project .\src\FileTree.CLI\FileTree.CLI.csproj -- -d 5 -f Unicode -e .cs,.json -h
+```
+
+This command:
+
+- **Scans** the current directory (no explicit path provided)
+- **Limits depth** to 5 levels (`-d 5`)
+- **Uses Unicode** tree characters (`-f Unicode`)
+- **Excludes** `.cs` and `.json` files (`-e .cs,.json`)
+- **Skips hidden** files and folders (`-h`)
+
+### Command‑line options
+
+All options are optional; sensible defaults are applied when they are omitted.
+
+| Option | Long name | Type | Description |
+|--------|------------------|-----------|-------------|
+| `path` | — | value | Path to the directory to scan. Defaults to the current directory. |
+| `-d` | `--max-depth` | `int` | Maximum depth of the tree. `-1` (default) means unlimited. |
+| `-w` | `--max-width` | `int` | Maximum number of siblings per level. `-1` (default) means unlimited. |
+| `-n` | `--max-nodes` | `int` | Maximum total number of nodes in the tree. `-1` (default) means unlimited. |
+| `-g` | `--use-gitignore`| `bool` | Use `.gitignore` rules to filter files. Defaults to `true` when omitted. |
+| `-f` | `--format` | enum | Output format: `Ascii`, `Markdown`, or `Unicode`. Default is `Ascii`. |
+| `-i` | `--include-ext` | list | Comma‑separated list of extensions to include, e.g. `-i .cs,.csproj`. |
+| `-e` | `--exclude-ext` | list | Comma‑separated list of extensions to exclude, e.g. `-e .dll,.pdb`. |
+| — | `--include-names`| list | Comma‑separated file/directory names to always include. |
+| — | `--exclude-names`| list | Comma‑separated file/directory names to exclude. |
+| — | `--ignore-empty` | flag | Skip empty folders. |
+| `-h` | `--hidden` | flag | Exclude hidden files and folders. Defaults to `true` when omitted. |
+
+#### More examples
+
+Scan a specific folder with Markdown output:
+
+```bash
+dotnet run --project .\src\FileTree.CLI\FileTree.CLI.csproj -- "C:\Projects\MyApp" -f Markdown
+```
+
+Include only C# sources and ignore empty directories:
+
+```bash
+dotnet run --project .\src\FileTree.CLI\FileTree.CLI.csproj -- -i .cs -d 4 --ignore-empty
+```
+
+Disable `.gitignore` handling and show hidden files:
+
+```bash
+dotnet run --project .\src\FileTree.CLI\FileTree.CLI.csproj -- -g false -h false
+```
+
+## Library usage
+
+You can use the core scanning and formatting functionality directly from C# via `FileTreeService` in `FileTree.Core`.
+
+```csharp
+using FileTree.Core.Models;
+using FileTree.Core.Services;
+
+var options = new FileTreeOptions
+{
+ MaxDepth = 5,
+ MaxWidth = -1,
+ MaxNodes = -1,
+ UseGitIgnore = true,
+ SkipHidden = true,
+ Format = OutputFormat.Unicode,
+ Filter = new FilterOptions
+ {
+ IncludeExtensions = new() { ".cs", ".csproj" },
+ IgnoreEmptyFolders = true
+ }
+};
+
+FileTreeService service = new();
+string tree = service.Generate(@"C:\Projects\MyApp", options);
+
+Console.WriteLine(tree);
+```
+
+## Output formats
+
+- **Ascii**: Plain ASCII tree (`|--`, `+--`)
+- **Unicode**: Box‑drawing characters for a nicer console tree
+- **Markdown**: Tree formatted for embedding in Markdown documents
+
+The format is selected via `OutputFormat` in code or `-f/--format` in the CLI.
+
+## Development
+
+- **Run tests**:
+
+```bash
+dotnet test
+```
+
+The main projects are:
+
+- `src/FileTree.Core` — core library (scanning, filtering, formatting)
+- `src/FileTree.CLI` — command‑line interface
+- `tests/FileTree.Core.Tests` — unit tests
+
+## License
+
+This project is licensed under the **Apache License 2.0**.
+See the `LICENSE` file for full details.
+
diff --git a/src/FileTree.CLI/AppPaths.cs b/src/FileTree.CLI/AppPaths.cs
index 87b6ec0..788df1c 100644
--- a/src/FileTree.CLI/AppPaths.cs
+++ b/src/FileTree.CLI/AppPaths.cs
@@ -2,9 +2,16 @@
namespace FileTree.CLI;
+///
+/// Utility for determining FileTree executable and global configuration file paths.
+/// Handles creation/reset/deletion/opening of FileTree.ignore.txt (filters) and FileTree.settings.txt (CLI defaults).
+/// All paths relative to executable directory.
+///
internal static class AppPaths
{
+ /// Filename for global gitignore-style filter rules near executable.
internal const string GlobalIgnoreFileName = "FileTree.ignore.txt";
+ /// Filename for default CLI options/settings near executable.
internal const string GlobalSettingsFileName = "FileTree.settings.txt";
private const string DefaultGlobalIgnoreContents =
"# FileTree global ignore rules\n" +
@@ -35,6 +42,11 @@ internal static string GetExecutablePath()
return modulePath;
}
+ ///
+ /// Gets directory containing the FileTree executable.
+ /// Prefers AppContext.BaseDirectory, falls back to parent of exe path.
+ ///
+ /// Normalized full directory path.
internal static string GetExecutableDirectory()
{
if (!string.IsNullOrWhiteSpace(AppContext.BaseDirectory))
@@ -45,16 +57,26 @@ internal static string GetExecutableDirectory()
return Path.GetDirectoryName(GetExecutablePath())!;
}
+ /// Gets path to global FileTree.ignore.txt next to executable.
+ /// Full path (dir may not exist).
internal static string GetGlobalIgnorePath()
{
return Path.Combine(GetExecutableDirectory(), GlobalIgnoreFileName);
}
+ /// Gets path to global FileTree.settings.txt next to executable.
+ /// Full path (dir may not exist).
internal static string GetGlobalSettingsPath()
{
return Path.Combine(GetExecutableDirectory(), GlobalSettingsFileName);
}
+ ///
+ /// Ensures FileTree.ignore.txt exists (creates with defaults if missing).
+ ///
+ /// Output: full file path.
+ /// Output: null on success, else exception message.
+ /// true if exists or created, false on error.
internal static bool TryEnsureGlobalIgnoreFileExists(out string path, out string? error)
{
path = GetGlobalIgnorePath();
@@ -77,6 +99,10 @@ internal static bool TryEnsureGlobalIgnoreFileExists(out string path, out string
}
}
+ /// Overwrites FileTree.ignore.txt with default content.
+ /// Output: full file path.
+ /// Output: null on success, else exception message.
+ /// true if written, false on error (file/dir perms).
internal static bool TryResetGlobalIgnoreFile(out string path, out string? error)
{
path = GetGlobalIgnorePath();
@@ -94,6 +120,10 @@ internal static bool TryResetGlobalIgnoreFile(out string path, out string? error
}
}
+ /// Ensures FileTree.settings.txt exists (creates with defaults if missing).
+ /// Output: full file path.
+ /// Output: null on success, else exception message.
+ /// true if exists or created, false on error.
internal static bool TryEnsureGlobalSettingsFileExists(out string path, out string? error)
{
path = GetGlobalSettingsPath();
@@ -116,6 +146,10 @@ internal static bool TryEnsureGlobalSettingsFileExists(out string path, out stri
}
}
+ /// Overwrites FileTree.settings.txt with default content.
+ /// Output: full file path.
+ /// Output: null on success, else exception message.
+ /// true if written, false on error (perms).
internal static bool TryResetGlobalSettingsFile(out string path, out string? error)
{
path = GetGlobalSettingsPath();
@@ -133,6 +167,10 @@ internal static bool TryResetGlobalSettingsFile(out string path, out string? err
}
}
+ /// Deletes FileTree.ignore.txt if exists.
+ /// Output: attempted file path.
+ /// Output: null on success, else exception.
+ /// true if absent or deleted, false on error.
internal static bool TryDeleteGlobalIgnoreFile(out string path, out string? error)
{
path = GetGlobalIgnorePath();
@@ -154,6 +192,10 @@ internal static bool TryDeleteGlobalIgnoreFile(out string path, out string? erro
}
}
+ /// Deletes FileTree.settings.txt if exists.
+ /// Output: attempted file path.
+ /// Output: null on success, else exception.
+ /// true if absent or deleted, false on error.
internal static bool TryDeleteGlobalSettingsFile(out string path, out string? error)
{
path = GetGlobalSettingsPath();
@@ -175,6 +217,10 @@ internal static bool TryDeleteGlobalSettingsFile(out string path, out string? er
}
}
+ /// Opens FileTree.settings.txt in default editor (creates if missing).
+ /// Output: full file path.
+ /// Output: null on success/open, else exception.
+ /// true if opened (or created+opened), false on error.
internal static bool TryOpenGlobalSettingsFile(out string path, out string? error)
{
path = GetGlobalSettingsPath();
@@ -203,6 +249,10 @@ internal static bool TryOpenGlobalSettingsFile(out string path, out string? erro
}
}
+ /// Opens FileTree.ignore.txt in default editor (creates if missing).
+ /// Output: full file path.
+ /// Output: null on success/open, else exception.
+ /// true if opened (or created+opened), false on error.
internal static bool TryOpenGlobalIgnoreFile(out string path, out string? error)
{
path = GetGlobalIgnorePath();
diff --git a/src/FileTree.CLI/CommandLineOptions.cs b/src/FileTree.CLI/CommandLineOptions.cs
index 37b3d4b..5eaf4ba 100644
--- a/src/FileTree.CLI/CommandLineOptions.cs
+++ b/src/FileTree.CLI/CommandLineOptions.cs
@@ -4,62 +4,84 @@
namespace FileTree.CLI;
[Verb("scan", isDefault: true, HelpText = "Scan and print the directory tree.")]
+///
+/// Options for the default 'scan' command that generates and prints a visual directory tree.
+/// Supports tree limits, formatting, filtering (gitignore-style or legacy), hidden files, and output behaviors.
+/// Example: filetree . --max-depth 3 --format Markdown --filter-rules '*.log,bin/'
+///
public class ScanCommandOptions
{
+ /// Target directory path (positional argument). Defaults to current directory if omitted.
[Value(0, MetaName = "path", HelpText = "Path to the directory to scan.", Required = false)]
public string? Path { get; set; }
+ /// Alternative path specification via short/long option. Overrides positional if both provided.
[Option('p', "path", HelpText = "Path to the directory to scan.", Required = false)]
public string? PathOption { get; set; }
+ /// Limit tree recursion depth. -1 = unlimited (default).
[Option('d', "max-depth", HelpText = "Maximum depth of the tree.", Required = false)]
public int? MaxDepth { get; set; }
+ /// Limit siblings per directory. -1 = unlimited (default). Helps wide dirs.
[Option('w', "max-width", HelpText = "Maximum width (number of siblings) per level.", Required = false)]
public int? MaxWidth { get; set; }
+ /// Global cap on tree nodes. -1 = unlimited (default). Prevents huge outputs.
[Option('n', "max-nodes", HelpText = "Maximum total number of nodes in the tree.", Required = false)]
public int? MaxNodes { get; set; }
+ /// Apply .gitignore/.git/info/exclude rules from repo root. Default: true if .git found.
[Option('g', "use-gitignore", HelpText = "Use .gitignore rules to filter files (true|false).", Required = false)]
public bool? UseGitIgnore { get; set; }
+ /// Tree rendering style. Default: .
[Option('f', "format", HelpText = "Output format (Ascii, Markdown, Unicode).", Required = false)]
public OutputFormat? Format { get; set; }
+ /// Collapse dirs with > N children. Null = auto based on width/depth.
[Option("collapse-threshold",
HelpText = "Collapse directories when item count exceeds this value.", Required = false)]
public int? CollapseThreshold { get; set; }
+ /// Show first N children before placeholder. Default: 1.
[Option("collapse-keep-start",
HelpText = "Number of items to keep at the start when collapsing.", Required = false)]
public int? CollapseKeepStart { get; set; }
+ /// Show last N children after placeholder. Default: 1.
[Option("collapse-keep-end",
HelpText = "Number of items to keep at the end when collapsing.", Required = false)]
public int? CollapseKeepEnd { get; set; }
+ /// Placeholder representation. Default: .
[Option("collapse-style",
HelpText = "Collapse placeholder style (Simple, Count, ByExtension).", Required = false)]
public CollapseStyle? CollapseStyle { get; set; }
+ /// Min depth for collapsing (1=root children). Default: 1.
[Option("collapse-from",
HelpText = "Start collapsing only from this depth (1 = first level under root).", Required = false)]
public int? CollapseFrom { get; set; }
- // ============================================================================
- // New gitignore-style filtering options (recommended)
- // ============================================================================
-
+ ///
+ ///
+ /// - Recommended modern filtering with gitignore syntax.
+ /// - Precedence: inline > local/global files > gitignore.
+ ///
+ ///
[Option("filter-rules",
HelpText = "Filter rules in gitignore format (comma-separated). Example: '*.log,bin/,!important.txt'",
Required = false, Separator = ',')]
+ /// Inline gitignore-style patterns. !negates. Multiple via comma or repeat.
public IEnumerable? FilterRules { get; set; }
+ /// Path to .filetreeignore in target dir or subdir. Auto-searched if omitted.
[Option("filter-file", HelpText = "Path to local filter configuration file with gitignore-style rules.",
Required = false)]
public string? FilterFile { get; set; }
+ /// Override app default/global .filetreeignore (near exe). Multiple levels supported.
[Option("global-filter-file", HelpText = "Path to global filter configuration file with gitignore-style rules.",
Required = false)]
public string? GlobalFilterFile { get; set; }
@@ -69,11 +91,13 @@ public class ScanCommandOptions
Required = false)]
public bool? NoDefaultFilters { get; set; }
+ /// Skip FileTree.ignore near executable. Short: '!'.
[Option('!', "no-app-global-ignore",
HelpText = "Don't load app-level global filter file (FileTree.ignore near the executable) (true|false).",
Required = false)]
public bool? NoAppGlobalIgnore { get; set; }
+ /// Disable recursive .filetreeignore loading. Default: load.
[Option("no-local-filters",
HelpText = "Don't load local .filetreeignore files from the directory tree (true|false).", Required = false)]
public bool? NoLocalFilters { get; set; }
@@ -82,10 +106,12 @@ public class ScanCommandOptions
Required = false)]
public bool? NoDefaultSettings { get; set; }
+ /// Legacy filtering - use instead for gitignore syntax.
// ============================================================================
// Legacy filtering options (deprecated but maintained for backward compatibility)
// ============================================================================
+ /// Only show files with these extensions (e.g. 'cs,js'). DEPRECATED.
[Option('i', "include-ext",
HelpText = "[DEPRECATED] Include only these extensions (comma-separated). Use --filter-rules instead.",
Required = false, Separator = ',')]
@@ -109,52 +135,76 @@ public class ScanCommandOptions
[Option("ignore-empty", HelpText = "Skip empty folders (true|false).", Required = false)]
public bool? IgnoreEmptyFolders { get; set; }
+ /// Hide hidden files/dirs (Unix dotfiles, Windows hidden attr). Default: true. Short: 'h'.
[Option('h', "hidden", HelpText = "Exclude hidden files and folders (true|false).", Required = false)]
public bool? SkipHidden { get; set; }
+ /// Style hidden nodes (if not skipped). Default: true.
[Option("highlight-hidden", HelpText = "Highlight hidden files and folders in output (true|false).",
Required = false)]
public bool? HighlightHiddenFiles { get; set; }
+ /// Marker for hidden (Prefix/Suffix/Minimal). Default: Suffix.
[Option("hidden-style", HelpText = "Hidden file style (Prefix, Suffix, Minimal).", Required = false)]
public HiddenStyle? HiddenStyle { get; set; }
+ /// Interactive mode: accept more options until 'show'.
[Option("wait",
HelpText =
"Do not run immediately; enter interactive mode, accept more options, and run on 'show' (true|false).",
Required = false)]
public bool? Wait { get; set; }
+ /// Copy tree to clipboard after generation. Short: 'c'.
[Option('c', "copy", HelpText = "Copy output to clipboard")]
public bool Copy { get; set; }
+ /// Suppress console output (use with --copy). Short: 's'.
[Option('s', "silent", HelpText = "Do not print output to console")]
public bool Silent { get; set; }
+ /// Print summary options before tree.
[Option("show-options", HelpText = "Print core scan options before output")]
public bool ShowOptions { get; set; }
+ /// Print verbose all-options before tree.
[Option("show-options-all", HelpText = "Print all scan options before output")]
public bool ShowOptionsAll { get; set; }
}
[Verb("install", HelpText = "Install FileTree into the system (PATH, context menu, etc.).")]
+///
+/// Options for the 'install' command. Installs FileTree to user PATH, creates shims/aliases, adds Windows context menus, ensures global config files.
+/// Platform-specific: Full on Windows, instructions on Linux.
+///
public class InstallCommandOptions
{
}
[Verb("uninstall", HelpText = "Uninstall FileTree from the system for the current user.")]
+///
+/// Options for the 'uninstall' command. Removes user-specific installation (PATH, shims, aliases, context menus, config files).
+///
public class UninstallCommandOptions
{
}
[Verb("uninstall-deep",
HelpText = "Deep uninstall FileTree (search and remove all FileTree entries for the current user).")]
+///
+/// Options for the 'uninstall-deep' command. Scans and prompts removal of all FileTree entries (PATH/shims/aliases/context menus).
+/// Safer than uninstall for multiple/unknown installs.
+///
public class UninstallDeepCommandOptions
{
}
[Verb("paths", HelpText = "Show the executable location and global filter file path.")]
+///
+/// Options for the 'paths' command. Displays executable location, directory, and global config file paths.
+/// Useful for scripting/config verification.
+/// Example output: Executable: C:\path\to\FileTree.exe, Global ignore: ...\FileTree.ignore.txt
+///
public class PathsCommandOptions
{
}
diff --git a/src/FileTree.CLI/Program.cs b/src/FileTree.CLI/Program.cs
index dd03222..286e1a9 100644
--- a/src/FileTree.CLI/Program.cs
+++ b/src/FileTree.CLI/Program.cs
@@ -7,11 +7,22 @@
namespace FileTree.CLI;
+///
+/// Main entry point for FileTree CLI application.
+/// Handles argument parsing, global options (--pause-exit), config commands (config rules/settings), scan/install/uninstall.
+/// Uses CommandLineParser, supports interactive mode, settings from FileTree.settings.txt, platform install via SystemIntegrator.
+///
internal class Program
{
private const string PauseExitLongOption = "pause-exit";
private const char PauseExitShortOption = 'k';
+ ///
+ /// Entry point. Processes args, handles globals/config/help, dispatches to scan/install/uninstall/paths.
+ /// Applies settings, normalizes booleans, pauses if --pause-exit/'-k'.
+ ///
+ /// CLI arguments.
+ /// 0 success, 1 error/parse fail.
private static int Main(string[] args)
{
var argsList = args.ToList();
@@ -57,6 +68,9 @@ private static int Main(string[] args)
return res;
}
+ /// Orchestrates scan: CLI to state, apply defaults, dispatch interactive/once.
+ /// Parsed scan options.
+ /// 0 success, 1 error.
private static int RunScan(ScanCommandOptions opts)
{
var state = ScanOptionsState.FromCli(opts);
@@ -70,6 +84,9 @@ private static int RunScan(ScanCommandOptions opts)
return RunScanOnce(state);
}
+ /// Executes single tree scan/print/copy. Handles validation/output/errors.
+ /// Resolved scan state/options.
+ /// 0 success, 1 validation error.
private static int RunScanOnce(ScanOptionsState state)
{
var targetPath = state.GetTargetPath();
@@ -120,6 +137,9 @@ private static int RunScanOnce(ScanOptionsState state)
}
}
+ /// Interactive shell for scan options. Parses input until 'show'/'exit'.
+ /// Initial scan state, merged with interactive inputs.
+ /// 0 success/exit, 1 error.
private static int RunScanInteractive(ScanOptionsState state)
{
var current = state;
@@ -474,6 +494,10 @@ private static int RunPaths()
return 0;
}
+ /// Handles 'config rules/settings' subcommands (open/reset/path).
+ /// Remaining args after 'config'.
+ /// Output: 0 success, 1 invalid.
+ /// true if handled, false otherwise.
private static bool TryHandleConfigCommand(string[] args, out int exitCode)
{
exitCode = 0;
@@ -636,6 +660,8 @@ private static void ApplySettingsDefaultsIfNeeded(ScanOptionsState state)
state.ApplyDefaults(defaults);
}
+ /// Loads defaults from FileTree.settings.txt, parses as CLI args, converts to state.
+ /// Parsed state or null if missing/unparseable.
private static ScanOptionsState? LoadSettingsDefaults()
{
var path = AppPaths.GetGlobalSettingsPath();
@@ -696,6 +722,8 @@ private static void ApplySettingsDefaultsIfNeeded(ScanOptionsState state)
}
}
+ /// Delegates to platform integrator for install.
+ /// 0 success.
private static async Task RunInstallAsync()
{
var integrator = SystemIntegratorFactory.Create();
@@ -703,6 +731,8 @@ private static async Task RunInstallAsync()
return 0;
}
+ /// Delegates to platform integrator for user uninstall.
+ /// 0 success.
private static async Task RunUninstallAsync()
{
var integrator = SystemIntegratorFactory.Create();
@@ -710,6 +740,8 @@ private static async Task RunUninstallAsync()
return 0;
}
+ /// Delegates to platform integrator for deep uninstall.
+ /// 0 success.
private static async Task RunUninstallDeepAsync()
{
var integrator = SystemIntegratorFactory.Create();
diff --git a/src/FileTree.CLI/ScanOptionsState.cs b/src/FileTree.CLI/ScanOptionsState.cs
index 4aff9eb..a708b03 100644
--- a/src/FileTree.CLI/ScanOptionsState.cs
+++ b/src/FileTree.CLI/ScanOptionsState.cs
@@ -3,6 +3,10 @@
namespace FileTree.CLI;
+///
+/// Mirrors + conversion/merge/defaults to .
+/// Handles path prioritization, legacy/modern filter mapping.
+///
internal sealed class ScanOptionsState
{
public string? Path { get; set; }
@@ -38,6 +42,10 @@ internal sealed class ScanOptionsState
public bool? ShowOptions { get; set; }
public bool? ShowOptionsAll { get; set; }
+ /// Creates state from parsed CLI options, normalizes paths.
+ /// Raw CLI options.
+ /// New state instance.
+ /// cli null.
public static ScanOptionsState FromCli(ScanCommandOptions cli)
{
if (cli == null)
@@ -89,6 +97,8 @@ public static ScanOptionsState FromCli(ScanCommandOptions cli)
return state;
}
+ /// Merges non-null values from source (path prio: PathOption > Path, overrides hasValue).
+ /// Options to merge in.
public void MergeFrom(ScanOptionsState source)
{
if (source == null)
@@ -139,6 +149,8 @@ public void MergeFrom(ScanOptionsState source)
if (source.ShowOptionsAll.HasValue) ShowOptionsAll = source.ShowOptionsAll;
}
+ /// Applies defaults only to null/unset fields (path prio: PathOption > Path).
+ /// Defaults to apply.
public void ApplyDefaults(ScanOptionsState defaults)
{
if (defaults == null)
@@ -188,6 +200,8 @@ public void ApplyDefaults(ScanOptionsState defaults)
if (!ShowOptionsAll.HasValue && defaults.ShowOptionsAll.HasValue) ShowOptionsAll = defaults.ShowOptionsAll;
}
+ /// Converts to core , applies defaults (-1 unlimited, etc.), builds RulesSource.
+ /// Ready core options.
public FileTreeOptions ToFileTreeOptions()
{
return new FileTreeOptions
diff --git a/src/FileTree.CLI/SystemIntegrator/ISystemIntegrator.cs b/src/FileTree.CLI/SystemIntegrator/ISystemIntegrator.cs
index c269c7c..ec6045d 100644
--- a/src/FileTree.CLI/SystemIntegrator/ISystemIntegrator.cs
+++ b/src/FileTree.CLI/SystemIntegrator/ISystemIntegrator.cs
@@ -1,8 +1,15 @@
namespace FileTree.CLI.SystemIntegrator;
+///
+/// Platform-specific system integration (PATH, shims, aliases, context menus).
+/// Windows: full auto. Linux: prints manual instructions. Other: NoOp.
+///
public interface ISystemIntegrator
{
+ /// Adds FileTree to user PATH/context menu/shims/configs.
Task InstallAsync();
+ /// Removes user install (known locations).
Task UninstallAsync();
+ /// Scans/prompts removal of all FileTree entries (unknown installs).
Task UninstallDeepAsync();
}
diff --git a/src/FileTree.CLI/SystemIntegrator/LinuxSystemIntegrator.cs b/src/FileTree.CLI/SystemIntegrator/LinuxSystemIntegrator.cs
index 5b2f445..e908a54 100644
--- a/src/FileTree.CLI/SystemIntegrator/LinuxSystemIntegrator.cs
+++ b/src/FileTree.CLI/SystemIntegrator/LinuxSystemIntegrator.cs
@@ -3,8 +3,13 @@
namespace FileTree.CLI.SystemIntegrator;
+///
+/// Linux integrator: prints manual PATH/~.local/bin instructions, handles configs.
+/// No auto-modification (user perms).
+///
internal sealed class LinuxSystemIntegrator : ISystemIntegrator
{
+ ///
public Task InstallAsync()
{
var exePath = GetExecutablePath();
@@ -61,6 +66,7 @@ public Task InstallAsync()
return Task.CompletedTask;
}
+ ///
public Task UninstallAsync()
{
var ignorePath = AppPaths.GetGlobalIgnorePath();
@@ -94,6 +100,7 @@ public Task UninstallAsync()
return Task.CompletedTask;
}
+ ///
public Task UninstallDeepAsync()
{
var ignorePath = AppPaths.GetGlobalIgnorePath();
diff --git a/src/FileTree.CLI/SystemIntegrator/NoOpSystemIntegrator.cs b/src/FileTree.CLI/SystemIntegrator/NoOpSystemIntegrator.cs
index f47da27..d1e8195 100644
--- a/src/FileTree.CLI/SystemIntegrator/NoOpSystemIntegrator.cs
+++ b/src/FileTree.CLI/SystemIntegrator/NoOpSystemIntegrator.cs
@@ -1,5 +1,9 @@
namespace FileTree.CLI.SystemIntegrator;
+///
+/// No-operation integrator for unsupported OS.
+/// Prints "unsupported" message and completes.
+///
internal sealed class NoOpSystemIntegrator : ISystemIntegrator
{
public Task InstallAsync()
diff --git a/src/FileTree.CLI/SystemIntegrator/SystemIntegratorFactory.cs b/src/FileTree.CLI/SystemIntegrator/SystemIntegratorFactory.cs
index 872c490..ea6939b 100644
--- a/src/FileTree.CLI/SystemIntegrator/SystemIntegratorFactory.cs
+++ b/src/FileTree.CLI/SystemIntegrator/SystemIntegratorFactory.cs
@@ -2,8 +2,14 @@
namespace FileTree.CLI.SystemIntegrator;
+///
+/// Creates platform-appropriate .
+/// Windows/Linux/NoOp based on RuntimeInformation.IsOSPlatform.
+///
internal static class SystemIntegratorFactory
{
+ /// Detects OS and instantiates integrator.
+ /// WindowsSystemIntegrator on Windows, Linux on Linux, NoOp otherwise.
public static ISystemIntegrator Create()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
diff --git a/src/FileTree.CLI/SystemIntegrator/WindowsSystemIntegrator.cs b/src/FileTree.CLI/SystemIntegrator/WindowsSystemIntegrator.cs
index 9152ec0..c320bbf 100644
--- a/src/FileTree.CLI/SystemIntegrator/WindowsSystemIntegrator.cs
+++ b/src/FileTree.CLI/SystemIntegrator/WindowsSystemIntegrator.cs
@@ -7,6 +7,10 @@
namespace FileTree.CLI.SystemIntegrator;
[SupportedOSPlatform("windows")]
+///
+/// Windows-specific implementation: adds to user PATH, BAT/PowerShell shims/aliases, registry context menus (dirs/files/desktop/background, default/custom).
+/// Deep uninstall prompts/scans for entries. Broadcasts env changes.
+///
internal sealed class WindowsSystemIntegrator : ISystemIntegrator
{
private const string DirectoryMenuKey =
@@ -33,6 +37,7 @@ internal sealed class WindowsSystemIntegrator : ISystemIntegrator
private const string DirectoryBackgroundMenuKeyCustom =
@"Software\Classes\Directory\Background\shell\FileTreeCustom";
+ ///
public Task InstallAsync()
{
var exePath = GetExecutablePath();
@@ -92,6 +97,7 @@ public Task InstallAsync()
return Task.CompletedTask;
}
+ ///
public Task UninstallAsync()
{
var exePath = GetExecutablePath();
@@ -154,6 +160,7 @@ public Task UninstallAsync()
return Task.CompletedTask;
}
+ ///
public Task UninstallDeepAsync()
{
var exePath = GetExecutablePath();
diff --git a/src/FileTree.Core/Abstractions/IFileTreeService.cs b/src/FileTree.Core/Abstractions/IFileTreeService.cs
index b7b1b01..f6c766c 100644
--- a/src/FileTree.Core/Abstractions/IFileTreeService.cs
+++ b/src/FileTree.Core/Abstractions/IFileTreeService.cs
@@ -2,7 +2,16 @@
namespace FileTree.Core.Abstractions;
+///
+/// Core tree generation service interface.
+///
public interface IFileTreeService
{
+ /// Main entrypoint: Generates formatted tree string.
+ /// Root dir path (validated).
+ /// Scan/format config.
+ /// Formatted tree.
+ /// Invalid/inaccessible path.
+ /// Scan access denied.
string Generate(string rootPath, FileTreeOptions options);
}
diff --git a/src/FileTree.Core/FileTree.Core.xml b/src/FileTree.Core/FileTree.Core.xml
new file mode 100644
index 0000000..2566015
--- /dev/null
+++ b/src/FileTree.Core/FileTree.Core.xml
@@ -0,0 +1,745 @@
+
+
+
+ FileTree.Core
+
+
+
+
+ Core tree generation service interface.
+
+
+
+ Main entrypoint: Generates formatted tree string.
+ Root dir path (validated).
+ Scan/format config.
+ Formatted tree.
+ Invalid/inaccessible path.
+ Scan access denied.
+
+
+
+ Legacy file filter implementation using simple list-based filtering.
+
+
+ This class is obsolete and maintained only for backward compatibility.
+ Use FilterRulesSource with gitignore-style rules instead.
+ This class will be removed in v2.0.0.
+
+
+
+ Initializes with filter options lists.
+ Lists of include/exclude.
+
+
+ Include if in includeNames or not in excludes; checks extension if IncludeExtensions set.
+
+
+ Include if in includeNames or not in excludeNames (no extension logic).
+
+
+
+ Context for filter operations: combines with optional gitignore rules.
+ Passed to rule loaders/evaluators.
+
+
+
+ Initializes with options and optional gitignore.
+
+
+ Filter options from user input.
+
+
+ Loaded .gitignore rules if applicable.
+
+
+
+ Loads filtering rules from multiple sources and combines them into a single ordered list.
+ Rules are applied in order of precedence: App global -> Default global -> Custom global -> Local config -> Inline rules.
+
+
+ Loads filter rules from various sources (app/global/local configs, inline) in precedence order.
+ Parses .filetreeignore files, combines into ordered list for gitignore matching.
+ Cross-platform home dir detection.
+
+
+
+
+ Loads and combines filtering rules from all configured sources.
+
+ Configuration specifying which rule sources to load.
+ A list of combined rules in the correct order.
+ Loads rules from all sources in order, adding to list.
+
+
+
+ Gets the default global configuration file path based on the operating system.
+
+ Path to default global config, or null if home directory cannot be determined.
+
+
+
+ Gets the user's home directory path in a cross-platform manner.
+
+ User's home directory path, or null if it cannot be determined.
+
+
+
+ Loads rules from a file and adds them to the provided GitIgnoreRules instance.
+
+ The rules list to add rules to.
+ Path to the file containing rules.
+ Thrown if the specified file does not exist.
+ Thrown if an error occurs while reading the file.
+
+
+
+ Legacy filtering interface for file/dir inclusion decisions.
+ Used for backward compatibility with list-based filters.
+ New code should use gitignore-style FilterRulesSource.
+
+
+
+ Decides if file should be included based on name/path lists.
+
+
+ Decides if directory should be included based on name/path lists.
+
+
+
+ Converts legacy FilterOptions properties to gitignore-style filtering rules.
+ This ensures backward compatibility with code using the old filtering API.
+
+
+ Converts legacy list-based FilterOptions to gitignore-style rules for compatibility.
+ Generates patterns like "**/*.ext" or "!**/*name".
+
+
+
+
+ Converts legacy filter options to gitignore-style rules.
+
+ The FilterOptions containing legacy filter properties.
+ A list of gitignore-style rule strings.
+
+ Conversion rules:
+ - ExcludeExtensions: Converts to "**/*{ext}" patterns
+ - ExcludeNames: Converts to "{name}" patterns
+ - IncludeExtensions: Converts to "*" (exclude all) followed by "!**/*{ext}" (except these)
+ - IncludeNames: Converts to "!{name}" (negation patterns)
+
+ Note: The order matters in gitignore. Include rules must come after exclude rules
+ to properly negate them.
+
+ Main conversion: lists to gitignore patterns.
+
+
+
+ Checks if the legacy filter properties contain any filtering rules.
+
+ The FilterOptions to check.
+ True if any legacy filter properties are configured, false otherwise.
+
+
+
+ Normalizes a file extension by ensuring it starts with a dot.
+
+ The extension to normalize (e.g., "txt" or ".txt").
+ Normalized extension starting with a dot (e.g., ".txt").
+
+
+
+ ASCII art tree formatter using | / \ - characters.
+ Compatible with all terminals.
+
+
+
+ Formats tree using ASCII characters. Root printed first, then children with connectors.
+
+
+ Formats single node name, handling collapsed placeholders and hidden styling.
+
+
+
+ Formats placeholder strings for collapsed nodes based on : Simple "...", Count, ByExtension.
+ Called by formatters for IsCollapsedPlaceholder nodes.
+
+
+
+ Main entry: Builds text, adds Markdown backticks if needed.
+ Collapsed node with counts.
+ Context for format-specific wrapping.
+ Placeholder string.
+
+
+ Builds core placeholder text based on style/counts/extension hint.
+ Node stats.
+ Collapse style/options.
+ Text like "... (3 files)" or "... (12 .cs)".
+
+
+
+ Immutable context passed to formatters, providing access to .
+ Enables format-specific options without passing options directly.
+
+
+
+ Creates context from options.
+ Formatting options.
+
+
+ Original options.
+
+
+ Shortcut to .
+
+
+
+ Contract for formatting a tree into string representation.
+ Supports different output styles (ASCII, Unicode, Markdown).
+
+
+
+
+ Formats the tree hierarchy into a string.
+
+ Root node of the tree.
+ Formatting context (options, styles).
+ Formatted tree string ready for output.
+
+
+
+ Markdown list-style tree formatter.
+ Indented bullet-less list with / for dirs.
+ Ideal for GitHub README.
+
+
+
+ Formats tree as indented Markdown list. Recursive write.
+
+
+ Recursively writes node at depth, with 2-space indent.
+ Current node.
+ Builder to append to.
+ Indent level.
+ Formatting context.
+
+
+ Formats node name: dir / suffix, hidden styling, collapsed placeholders.
+
+
+
+ Utility for styling node names: applies hidden file markers (prefix/suffix/minimal) based on options.
+ Used by all tree formatters.
+
+
+
+ Applies hidden styling if enabled and node is hidden.
+ Base name.
+ Node metadata.
+ Context for style.
+ Styled name.
+
+
+ Applies the configured hidden style to name.
+
+
+
+ Factory for selecting concrete implementations based on .
+ Simple switch, extensible for new formats.
+
+
+
+ Creates formatter for the given format.
+ Desired output format.
+ Appropriate formatter instance.
+ Unknown format.
+
+
+
+ Unicode box-drawing tree formatter (│ ├ └ ─). Pretty terminal output.
+ Similar structure to Ascii, different chars.
+
+
+
+ Formats tree using Unicode box characters. Root first, then children.
+
+
+ Formats node name, delegating to placeholder/styler.
+
+
+
+ Factory methods for from empty, lines, or .gitignore file.
+ Handles parsing, comment stripping, trimming.
+
+
+
+ Creates empty rules instance.
+
+
+ Parses lines, ignores empty/comments, adds to new rules.
+
+
+ Loads from file path, reads lines then FromLines.
+
+
+
+ Wrapper around Ignore library for gitignore-style path matching.
+ Fluent Add rules, query IsIgnored/Filter.
+
+
+
+ Initializes empty matcher.
+
+
+ Adds single rule, fluent.
+ Pattern.
+
+
+ Adds rules enumerable, cleans empty/comments, fluent.
+
+
+ Filters paths not ignored.
+
+
+ Tests if path ignored.
+
+
+ ... placeholder.
+
+
+ ... (5 more) with count.
+
+
+ ... [.cs x12] by extension.
+
+
+
+ Represents a node in the file tree.
+ Leaf for files, container for dirs. Supports collapse placeholders.
+ Immutable except children/collapsed props.
+
+
+
+ Constructor.
+ Basename.
+ Absolute path.
+ Dir or file.
+ Hidden flag.
+
+
+ Basename (no path).
+
+
+ Absolute path.
+
+
+ True if directory.
+
+
+ True if hidden.
+
+
+ Child nodes (read-only).
+
+
+ True if placeholder for collapsed children.
+
+
+ Total hidden children count.
+
+
+ Hidden file children count.
+
+
+ Hidden folder children count.
+
+
+ Extension hint for ByExtension collapse.
+
+
+ Adds child (during scan).
+ Child node.
+
+
+ Removes child (post-filter).
+ Child to remove.
+
+
+
+ Core configuration for tree generation.
+ Controls scanning limits, filtering, formatting, collapse.
+ Defaults favor reasonable output; -1/-1 unlimited.
+
+
+
+ Max recursion depth. -1 unlimited (default).
+
+
+ Max siblings per dir. -1 unlimited (default).
+
+
+ Global node cap. -1 unlimited (default).
+
+
+ Load/apply .gitignore rules from root. Default: false.
+
+
+ Exclude hidden files/dirs. Default: false.
+
+
+ Special style for hidden (if not skipped). Default: true.
+
+
+ Hidden marker style. Default: Prefix.
+
+
+ Tree render style (Ascii/Markdown/Unicode). Default: Ascii.
+
+
+ Filtering config (rules, legacy).
+
+
+ Collapse dirs > N children. Null=auto.
+
+
+ First N children to show in collapsed. Default: 1.
+
+
+ Last N children to show in collapsed. Default: 1.
+
+
+ Placeholder style. Default: Count.
+
+
+ Start collapsing from this depth (1=root children). Default: 1.
+
+
+
+ Filtering configuration.
+ Modern: RulesSource gitignore-style. Legacy: ext/name lists (deprecated).
+
+
+
+
+ Source configuration for gitignore-style filtering rules.
+ This is the recommended way to configure filtering.
+
+
+
+
+ Skip folders that have no children after filtering is applied.
+
+
+
+
+ Whether to load local .filetreeignore files during scanning.
+
+
+
+
+ Include only these extensions if list is not empty.
+
+
+
+
+ Extensions to always exclude.
+
+
+
+
+ File/directory names to include always.
+
+
+
+
+ File/directory names to exclude.
+
+
+
+
+ Defines the sources from which filtering rules should be loaded.
+ Rules are applied in order: Global config -> Local config -> Inline rules,
+ with later rules taking precedence over earlier ones.
+
+
+
+
+ Path to the app-level global filter configuration file (e.g., FileTree.ignore in the app directory).
+ If null, no app-level global config will be loaded.
+
+
+
+
+ Whether to load the app-level global configuration file.
+
+
+
+
+ Filtering rules provided directly (e.g., from CLI arguments).
+ These rules use gitignore-style syntax.
+
+
+
+
+ Path to a global filter configuration file.
+ If null, only the default global config will be used (if enabled).
+
+
+
+
+ Path to a local filter configuration file (e.g., .filetreeignore in the working directory).
+ If null, no local config file will be loaded.
+
+
+
+
+ Whether to load the default global configuration file from the user's home directory.
+ Default location: ~/.filetreeignore (Linux/Mac) or %USERPROFILE%\.filetreeignore (Windows)
+
+
+
+
+ Style for marking hidden nodes.
+
+
+
+ [hidden] prefix.
+
+
+ (hidden) suffix.
+
+
+ * minimal marker.
+
+
+
+ Tree rendering format.
+
+
+
+ ASCII box chars (| / \ -).
+
+
+ Markdown tree list.
+
+
+ Unicode tree chars (│ ├ └ ─).
+
+
+
+ Default implementing tree collapse logic.
+ Recursively clones tree, replacing large dirs with placeholders.
+ Supports KeepStart/KeepEnd, from depth, different styles.
+
+
+
+ Processes tree if threshold set, using recursive clone/collapse.
+
+
+
+ Post-scan tree processor pipeline interface.
+ Transforms tree (e.g., collapse nodes, prune, etc.) based on options.
+
+
+
+ Processes/transforms the tree root.
+ Input tree.
+ Processing options (threshold etc.).
+ Processed tree (may mutate/replace nodes).
+
+
+
+ Implements tree scanning with limits, filtering, gitignore.
+ Recursive, depth/node aware.
+
+
+ Default implementation of . Handles recursive directory scanning with support for
+ depth/width/node limits, .gitignore rules, custom filters (legacy/new), hidden files, empty folder pruning.
+ Uses stack-based Enter/Exit for local .filetreeignore inheritance.
+
+
+
+ Total nodes scanned (for MaxNodes limit).
+
+
+ Whether to prune empty folders post-scan.
+
+
+ Evaluator for inclusion rules (limits, filters, gitignore).
+
+
+
+ Scans the directory tree starting from the specified root path.
+
+ Validated full path to the root directory (caller must validate existence).
+ Scanning options.
+ FileNode representing the scanned tree.
+
+
+
+ Recursively scans directory contents, applying inclusion rules and limits.
+ Builds child FileNode, recurses dirs, prunes empty if configured.
+
+ Current directory.
+ Parent node to add children to.
+ Recursion depth.
+ Scan options.
+
+
+
+ Cross-platform detector for hidden files and directories.
+ On Windows, checks the Hidden file attribute.
+ On Unix-like systems, checks for dot-prefix in name (e.g., .git).
+
+
+
+
+ Determines if the specified file system item is hidden.
+
+ The file or directory information to check.
+
+ true if the item is hidden; otherwise, false.
+
+
+ Platform-specific logic:
+ - Windows: Checks .
+ - Other OS: Checks if name starts with '.'.
+
+
+
+
+ Defines the scanning contract for building file tree structures from directories.
+ Implementations handle recursion, limits, filtering during scan.
+ Note: Named IFileScanner internally, but file is IFileTreeScanner.cs.
+
+
+
+
+ Scans the directory tree starting from , building a hierarchy.
+ Applies scanning limits, hidden/gitignore checks based on .
+
+ Absolute path to root directory.
+ Scan configuration (depth, width, filters).
+ Root representing the tree.
+ Access denied during scan.
+
+
+
+ Evaluates whether file system items should be included in the scan based on limits, filters, gitignore rules, hidden status, etc.
+ Supports hierarchical .filetreeignore files and legacy filter conversion.
+ Uses stack for local rules scope.
+
+
+
+ Name of local ignore file for directory-specific rules.
+
+
+
+ Creates a new instance of ScanInclusionEvaluator.
+
+ The root directory path being scanned.
+ File tree scanning options.
+ Optional .gitignore rules to apply.
+ Optional custom filter rules to apply.
+ Initializes evaluator with root path, options, and rules sources.
+ Combines gitignore, custom rules, legacy filters. Builds initial filter rules.
+
+
+
+ Determines whether a file or directory should be included in the scan results.
+
+ The file or directory to evaluate.
+ Current depth in the directory tree.
+ Current number of nodes scanned.
+ True if the item should be included, false otherwise.
+
+
+
+ Loads .filetreeignore from dir if exists, pushes to stack, rebuilds rules.
+
+ Directory to check for local ignore.
+ True if rules loaded/pushed.
+
+
+ Pops local rules from stack if previously pushed, rebuilds effective rules.
+ True if EnterDirectory loaded rules.
+
+
+
+ Main service orchestrating tree generation: scan -> process -> format.
+ DI-friendly ctor for testing.
+
+
+
+ DI ctor.
+ Tree scanner.
+ Formatter selector.
+ Optional processors (default collapse).
+
+
+ Default ctor with built-in scanner/factory/collapse.
+
+
+
+ Generates a formatted file tree representation for the specified directory.
+
+ Path to the root directory to scan. Must be a valid, accessible directory.
+ Scanning and formatting options.
+ Formatted tree string.
+ Thrown if rootPath is invalid, not a directory, or inaccessible.
+ Thrown if scanning encounters permission issues inside dirs.
+
+
+
+ Represents different types of path validation errors.
+
+
+
+
+ The path has invalid characters or format.
+
+
+
+
+ The path does not exist.
+
+
+
+
+ The path exists but is a file, not a directory.
+
+
+
+
+ No permission to access the directory.
+
+
+
+
+ Exception thrown when directory path validation fails.
+ Provides detailed error type and path for user-friendly messages.
+
+
+
+
+ Gets the error type.
+
+
+
+
+ Gets the full normalized path that failed validation.
+
+
+
+
+ Initializes a new instance with error type and path.
+
+
+
+
+ Initializes with inner exception (e.g. ArgumentException from Path.GetFullPath).
+
+
+
+
diff --git a/src/FileTree.Core/Filtering/FileFilter.cs b/src/FileTree.Core/Filtering/FileFilter.cs
index e25a218..0c4d921 100644
--- a/src/FileTree.Core/Filtering/FileFilter.cs
+++ b/src/FileTree.Core/Filtering/FileFilter.cs
@@ -12,15 +12,22 @@ namespace FileTree.Core.Filtering;
/// This class will be removed in v2.0.0.
///
[Obsolete("Use FilterRulesSource with gitignore-style rules instead. This class will be removed in v2.0.0")]
+///
+/// Default implementation of . Matches against include/exclude lists for names/extensions.
+/// Backward-compatible legacy filter.
+///
internal class FileFilter : IFileFilter
{
private readonly FilterOptions _options;
+ /// Initializes with filter options lists.
+ /// Lists of include/exclude.
public FileFilter(FilterOptions options)
{
_options = options;
}
+ /// Include if in includeNames or not in excludes; checks extension if IncludeExtensions set.
public bool ShouldIncludeFile(string fileName, string fullPath)
{
if (_options.IncludeNames.Contains(fileName, StringComparer.OrdinalIgnoreCase))
@@ -40,6 +47,7 @@ public bool ShouldIncludeFile(string fileName, string fullPath)
return true;
}
+ /// Include if in includeNames or not in excludeNames (no extension logic).
public bool ShouldIncludeDirectory(string directoryName, string fullPath)
{
if (_options.IncludeNames.Contains(directoryName, StringComparer.OrdinalIgnoreCase))
diff --git a/src/FileTree.Core/Filtering/FilterContext.cs b/src/FileTree.Core/Filtering/FilterContext.cs
index c93a5e8..ca9168c 100644
--- a/src/FileTree.Core/Filtering/FilterContext.cs
+++ b/src/FileTree.Core/Filtering/FilterContext.cs
@@ -3,14 +3,21 @@
namespace FileTree.Core.Filtering;
+///
+/// Context for filter operations: combines with optional gitignore rules.
+/// Passed to rule loaders/evaluators.
+///
internal class FilterContext
{
+ /// Initializes with options and optional gitignore.
public FilterContext(FilterOptions options, GitIgnoreRules? gitIgnoreRules = null)
{
Options = options;
GitIgnoreRules = gitIgnoreRules;
}
+ /// Filter options from user input.
public FilterOptions Options { get; }
+ /// Loaded .gitignore rules if applicable.
public GitIgnoreRules? GitIgnoreRules { get; }
}
\ No newline at end of file
diff --git a/src/FileTree.Core/Filtering/FilterRulesLoader.cs b/src/FileTree.Core/Filtering/FilterRulesLoader.cs
index 2a91735..c85f52c 100644
--- a/src/FileTree.Core/Filtering/FilterRulesLoader.cs
+++ b/src/FileTree.Core/Filtering/FilterRulesLoader.cs
@@ -10,6 +10,11 @@ namespace FileTree.Core.Filtering;
/// Loads filtering rules from multiple sources and combines them into a single ordered list.
/// Rules are applied in order of precedence: App global -> Default global -> Custom global -> Local config -> Inline rules.
///
+///
+/// Loads filter rules from various sources (app/global/local configs, inline) in precedence order.
+/// Parses .filetreeignore files, combines into ordered list for gitignore matching.
+/// Cross-platform home dir detection.
+///
internal class FilterRulesLoader
{
private const string DefaultGlobalConfigFileName = ".filetreeignore";
@@ -19,6 +24,7 @@ internal class FilterRulesLoader
///
/// Configuration specifying which rule sources to load.
/// A list of combined rules in the correct order.
+ /// Loads rules from all sources in order, adding to list.
public List LoadFilterRules(FilterRulesSource source)
{
if (source == null)
diff --git a/src/FileTree.Core/Filtering/IFileFilter.cs b/src/FileTree.Core/Filtering/IFileFilter.cs
index 447e43c..6401ecc 100644
--- a/src/FileTree.Core/Filtering/IFileFilter.cs
+++ b/src/FileTree.Core/Filtering/IFileFilter.cs
@@ -1,7 +1,14 @@
namespace FileTree.Core.Filtering;
+///
+/// Legacy filtering interface for file/dir inclusion decisions.
+/// Used for backward compatibility with list-based filters.
+/// New code should use gitignore-style FilterRulesSource.
+///
internal interface IFileFilter
{
+ /// Decides if file should be included based on name/path lists.
bool ShouldIncludeFile(string fileName, string fullPath);
+ /// Decides if directory should be included based on name/path lists.
bool ShouldIncludeDirectory(string directoryName, string fullPath);
}
diff --git a/src/FileTree.Core/Filtering/LegacyFilterConverter.cs b/src/FileTree.Core/Filtering/LegacyFilterConverter.cs
index 6c05acb..aedf78b 100644
--- a/src/FileTree.Core/Filtering/LegacyFilterConverter.cs
+++ b/src/FileTree.Core/Filtering/LegacyFilterConverter.cs
@@ -9,6 +9,10 @@ namespace FileTree.Core.Filtering;
/// Converts legacy FilterOptions properties to gitignore-style filtering rules.
/// This ensures backward compatibility with code using the old filtering API.
///
+///
+/// Converts legacy list-based FilterOptions to gitignore-style rules for compatibility.
+/// Generates patterns like "**/*.ext" or "!**/*name".
+///
internal static class LegacyFilterConverter
{
///
@@ -26,6 +30,7 @@ internal static class LegacyFilterConverter
/// Note: The order matters in gitignore. Include rules must come after exclude rules
/// to properly negate them.
///
+ /// Main conversion: lists to gitignore patterns.
public static List ConvertToGitIgnoreRules(FilterOptions options)
{
if (options == null)
diff --git a/src/FileTree.Core/Formatting/AsciiTreeFormatter.cs b/src/FileTree.Core/Formatting/AsciiTreeFormatter.cs
index f088661..7a0feb1 100644
--- a/src/FileTree.Core/Formatting/AsciiTreeFormatter.cs
+++ b/src/FileTree.Core/Formatting/AsciiTreeFormatter.cs
@@ -3,8 +3,13 @@
namespace FileTree.Core.Formatting;
+///
+/// ASCII art tree formatter using | / \ - characters.
+/// Compatible with all terminals.
+///
internal class AsciiTreeFormatter : ITreeFormatter
{
+ /// Formats tree using ASCII characters. Root printed first, then children with connectors.
public string Format(FileNode root, FormatContext context)
{
var sb = new StringBuilder();
@@ -21,6 +26,7 @@ public string Format(FileNode root, FormatContext context)
return sb.ToString();
}
+ /// Formats single node name, handling collapsed placeholders and hidden styling.
private static string FormatNode(FileNode node, FormatContext context)
{
if (node.IsCollapsedPlaceholder)
diff --git a/src/FileTree.Core/Formatting/CollapsePlaceholderFormatter.cs b/src/FileTree.Core/Formatting/CollapsePlaceholderFormatter.cs
index 127d526..320db5a 100644
--- a/src/FileTree.Core/Formatting/CollapsePlaceholderFormatter.cs
+++ b/src/FileTree.Core/Formatting/CollapsePlaceholderFormatter.cs
@@ -2,14 +2,26 @@
namespace FileTree.Core.Formatting;
+///
+/// Formats placeholder strings for collapsed nodes based on : Simple "...", Count, ByExtension.
+/// Called by formatters for IsCollapsedPlaceholder nodes.
+///
internal static class CollapsePlaceholderFormatter
{
+ /// Main entry: Builds text, adds Markdown backticks if needed.
+ /// Collapsed node with counts.
+ /// Context for format-specific wrapping.
+ /// Placeholder string.
internal static string Format(FileNode node, FormatContext context)
{
var text = BuildText(node, context.Options);
return context.Format == OutputFormat.Markdown ? $"`{text}`" : text;
}
+ /// Builds core placeholder text based on style/counts/extension hint.
+ /// Node stats.
+ /// Collapse style/options.
+ /// Text like "... (3 files)" or "... (12 .cs)".
internal static string BuildText(FileNode node, FileTreeOptions options)
{
if (options.CollapseStyle == CollapseStyle.Simple)
diff --git a/src/FileTree.Core/Formatting/FormatContext.cs b/src/FileTree.Core/Formatting/FormatContext.cs
index 4cb2fad..9c13ac3 100644
--- a/src/FileTree.Core/Formatting/FormatContext.cs
+++ b/src/FileTree.Core/Formatting/FormatContext.cs
@@ -2,13 +2,21 @@
namespace FileTree.Core.Formatting;
+///
+/// Immutable context passed to formatters, providing access to .
+/// Enables format-specific options without passing options directly.
+///
internal sealed class FormatContext
{
+ /// Creates context from options.
+ /// Formatting options.
public FormatContext(FileTreeOptions options)
{
Options = options ?? throw new ArgumentNullException(nameof(options));
}
+ /// Original options.
public FileTreeOptions Options { get; }
+ /// Shortcut to .
public OutputFormat Format => Options.Format;
}
diff --git a/src/FileTree.Core/Formatting/ITreeFormatter.cs b/src/FileTree.Core/Formatting/ITreeFormatter.cs
index 8672bd8..201c5d3 100644
--- a/src/FileTree.Core/Formatting/ITreeFormatter.cs
+++ b/src/FileTree.Core/Formatting/ITreeFormatter.cs
@@ -2,7 +2,17 @@
namespace FileTree.Core.Formatting;
+///
+/// Contract for formatting a tree into string representation.
+/// Supports different output styles (ASCII, Unicode, Markdown).
+///
internal interface ITreeFormatter
{
+ ///
+ /// Formats the tree hierarchy into a string.
+ ///
+ /// Root node of the tree.
+ /// Formatting context (options, styles).
+ /// Formatted tree string ready for output.
string Format(FileNode root, FormatContext context);
}
diff --git a/src/FileTree.Core/Formatting/MarkdownTreeFormatter.cs b/src/FileTree.Core/Formatting/MarkdownTreeFormatter.cs
index 0850e80..16bad32 100644
--- a/src/FileTree.Core/Formatting/MarkdownTreeFormatter.cs
+++ b/src/FileTree.Core/Formatting/MarkdownTreeFormatter.cs
@@ -3,8 +3,14 @@
namespace FileTree.Core.Formatting;
+///
+/// Markdown list-style tree formatter.
+/// Indented bullet-less list with / for dirs.
+/// Ideal for GitHub README.
+///
internal class MarkdownTreeFormatter : ITreeFormatter
{
+ /// Formats tree as indented Markdown list. Recursive write.
public string Format(FileNode root, FormatContext context)
{
var sb = new StringBuilder();
@@ -12,6 +18,11 @@ public string Format(FileNode root, FormatContext context)
return sb.ToString();
}
+ /// Recursively writes node at depth, with 2-space indent.
+ /// Current node.
+ /// Builder to append to.
+ /// Indent level.
+ /// Formatting context.
private void WriteNode(FileNode node, StringBuilder sb, int depth, FormatContext context)
{
var indent = new string(' ', depth * 2);
@@ -22,6 +33,7 @@ private void WriteNode(FileNode node, StringBuilder sb, int depth, FormatContext
foreach (var child in node.Children) WriteNode(child, sb, depth + 1, context);
}
+ /// Formats node name: dir / suffix, hidden styling, collapsed placeholders.
private static string FormatNode(FileNode node, FormatContext context)
{
if (node.IsCollapsedPlaceholder)
diff --git a/src/FileTree.Core/Formatting/NodeNameStyler.cs b/src/FileTree.Core/Formatting/NodeNameStyler.cs
index 433cbfa..ae67c19 100644
--- a/src/FileTree.Core/Formatting/NodeNameStyler.cs
+++ b/src/FileTree.Core/Formatting/NodeNameStyler.cs
@@ -2,8 +2,17 @@
namespace FileTree.Core.Formatting;
+///
+/// Utility for styling node names: applies hidden file markers (prefix/suffix/minimal) based on options.
+/// Used by all tree formatters.
+///
internal static class NodeNameStyler
{
+ /// Applies hidden styling if enabled and node is hidden.
+ /// Base name.
+ /// Node metadata.
+ /// Context for style.
+ /// Styled name.
internal static string Apply(string name, FileNode node, FormatContext context)
{
if (string.IsNullOrEmpty(name))
@@ -19,6 +28,7 @@ internal static string Apply(string name, FileNode node, FormatContext context)
return ApplyHiddenStyle(name, context);
}
+ /// Applies the configured hidden style to name.
private static string ApplyHiddenStyle(string name, FormatContext context)
{
return context.Options.HiddenStyle switch
diff --git a/src/FileTree.Core/Formatting/TreeFormatterFactory.cs b/src/FileTree.Core/Formatting/TreeFormatterFactory.cs
index 4a7b36c..09cda47 100644
--- a/src/FileTree.Core/Formatting/TreeFormatterFactory.cs
+++ b/src/FileTree.Core/Formatting/TreeFormatterFactory.cs
@@ -2,8 +2,16 @@
namespace FileTree.Core.Formatting;
+///
+/// Factory for selecting concrete implementations based on .
+/// Simple switch, extensible for new formats.
+///
internal class TreeFormatterFactory
{
+ /// Creates formatter for the given format.
+ /// Desired output format.
+ /// Appropriate formatter instance.
+ /// Unknown format.
public ITreeFormatter Create(OutputFormat format)
{
return format switch
diff --git a/src/FileTree.Core/Formatting/UnicodeTreeFormatter.cs b/src/FileTree.Core/Formatting/UnicodeTreeFormatter.cs
index b44265e..095f164 100644
--- a/src/FileTree.Core/Formatting/UnicodeTreeFormatter.cs
+++ b/src/FileTree.Core/Formatting/UnicodeTreeFormatter.cs
@@ -3,8 +3,13 @@
namespace FileTree.Core.Formatting;
+///
+/// Unicode box-drawing tree formatter (│ ├ └ ─). Pretty terminal output.
+/// Similar structure to Ascii, different chars.
+///
internal class UnicodeTreeFormatter : ITreeFormatter
{
+ /// Formats tree using Unicode box characters. Root first, then children.
public string Format(FileNode root, FormatContext context)
{
var sb = new StringBuilder();
@@ -21,6 +26,7 @@ public string Format(FileNode root, FormatContext context)
return sb.ToString();
}
+ /// Formats node name, delegating to placeholder/styler.
private static string FormatNode(FileNode node, FormatContext context)
{
if (node.IsCollapsedPlaceholder)
diff --git a/src/FileTree.Core/GitIgnore/GitIgnoreParser.cs b/src/FileTree.Core/GitIgnore/GitIgnoreParser.cs
index 8086fe4..077ecfa 100644
--- a/src/FileTree.Core/GitIgnore/GitIgnoreParser.cs
+++ b/src/FileTree.Core/GitIgnore/GitIgnoreParser.cs
@@ -4,10 +4,16 @@
namespace FileTree.Core.GitIgnore
{
+ ///
+ /// Factory methods for from empty, lines, or .gitignore file.
+ /// Handles parsing, comment stripping, trimming.
+ ///
public static class GitIgnoreParser
{
+ /// Creates empty rules instance.
public static GitIgnoreRules Create() => new GitIgnoreRules();
+ /// Parses lines, ignores empty/comments, adds to new rules.
public static GitIgnoreRules FromLines(IEnumerable lines)
{
if (lines == null) throw new ArgumentNullException(nameof(lines));
@@ -23,6 +29,7 @@ public static GitIgnoreRules FromLines(IEnumerable lines)
return rules;
}
+ /// Loads from file path, reads lines then FromLines.
public static GitIgnoreRules FromFile(string gitIgnoreFilePath)
{
if (string.IsNullOrWhiteSpace(gitIgnoreFilePath)) throw new ArgumentNullException(nameof(gitIgnoreFilePath));
diff --git a/src/FileTree.Core/GitIgnore/GitIgnoreRules.cs b/src/FileTree.Core/GitIgnore/GitIgnoreRules.cs
index 8806f3f..4ca1ee7 100644
--- a/src/FileTree.Core/GitIgnore/GitIgnoreRules.cs
+++ b/src/FileTree.Core/GitIgnore/GitIgnoreRules.cs
@@ -5,15 +5,22 @@
namespace FileTree.Core.GitIgnore
{
+ ///
+ /// Wrapper around Ignore library for gitignore-style path matching.
+ /// Fluent Add rules, query IsIgnored/Filter.
+ ///
public class GitIgnoreRules
{
private readonly Ignore.Ignore _ignore;
+ /// Initializes empty matcher.
public GitIgnoreRules()
{
_ignore = new Ignore.Ignore();
}
+ /// Adds single rule, fluent.
+ /// Pattern.
public GitIgnoreRules Add(string rule)
{
if (rule == null) throw new ArgumentNullException(nameof(rule));
@@ -21,6 +28,7 @@ public GitIgnoreRules Add(string rule)
return this;
}
+ /// Adds rules enumerable, cleans empty/comments, fluent.
public GitIgnoreRules Add(IEnumerable rules)
{
if (rules == null) throw new ArgumentNullException(nameof(rules));
@@ -30,12 +38,14 @@ public GitIgnoreRules Add(IEnumerable rules)
return this;
}
+ /// Filters paths not ignored.
public IEnumerable Filter(IEnumerable paths)
{
if (paths == null) throw new ArgumentNullException(nameof(paths));
return _ignore.Filter(paths);
}
+ /// Tests if path ignored.
public bool IsIgnored(string path)
{
if (path == null) throw new ArgumentNullException(nameof(path));
diff --git a/src/FileTree.Core/Models/CollapseStyle.cs b/src/FileTree.Core/Models/CollapseStyle.cs
index 45375b8..f006bb2 100644
--- a/src/FileTree.Core/Models/CollapseStyle.cs
+++ b/src/FileTree.Core/Models/CollapseStyle.cs
@@ -2,7 +2,10 @@ namespace FileTree.Core.Models;
public enum CollapseStyle
{
+ /// ... placeholder.
Simple,
+ /// ... (5 more) with count.
Count,
+ /// ... [.cs x12] by extension.
ByExtension
}
\ No newline at end of file
diff --git a/src/FileTree.Core/Models/FileNode.cs b/src/FileTree.Core/Models/FileNode.cs
index 9ebac48..3c576dc 100644
--- a/src/FileTree.Core/Models/FileNode.cs
+++ b/src/FileTree.Core/Models/FileNode.cs
@@ -2,10 +2,20 @@
namespace FileTree.Core.Models;
+///
+/// Represents a node in the file tree.
+/// Leaf for files, container for dirs. Supports collapse placeholders.
+/// Immutable except children/collapsed props.
+///
public class FileNode
{
private readonly List _children = new();
+ /// Constructor.
+ /// Basename.
+ /// Absolute path.
+ /// Dir or file.
+ /// Hidden flag.
public FileNode(string name, string fullPath, bool isDirectory, bool isHidden = false)
{
Name = name;
@@ -14,22 +24,36 @@ public FileNode(string name, string fullPath, bool isDirectory, bool isHidden =
IsHidden = isHidden;
}
+ /// Basename (no path).
public string Name { get; }
+ /// Absolute path.
public string FullPath { get; }
+ /// True if directory.
public bool IsDirectory { get; }
+ /// True if hidden.
public bool IsHidden { get; }
+ /// Child nodes (read-only).
public IReadOnlyList Children => _children;
+ /// True if placeholder for collapsed children.
public bool IsCollapsedPlaceholder { get; set; }
+ /// Total hidden children count.
public int CollapsedCount { get; set; }
+ /// Hidden file children count.
public int CollapsedFileCount { get; set; }
+ /// Hidden folder children count.
public int CollapsedFolderCount { get; set; }
+ /// Extension hint for ByExtension collapse.
public string? CollapsedExtensionHint { get; set; }
+ /// Adds child (during scan).
+ /// Child node.
public void AddChild(FileNode child)
{
_children.Add(child);
}
+ /// Removes child (post-filter).
+ /// Child to remove.
public void RemoveChild(FileNode child)
{
_children.Remove(child);
diff --git a/src/FileTree.Core/Models/FileTreeOptions.cs b/src/FileTree.Core/Models/FileTreeOptions.cs
index 3fa0e80..f7923c6 100644
--- a/src/FileTree.Core/Models/FileTreeOptions.cs
+++ b/src/FileTree.Core/Models/FileTreeOptions.cs
@@ -1,19 +1,38 @@
namespace FileTree.Core.Models;
+///
+/// Core configuration for tree generation.
+/// Controls scanning limits, filtering, formatting, collapse.
+/// Defaults favor reasonable output; -1/-1 unlimited.
+///
public class FileTreeOptions
{
+ /// Max recursion depth. -1 unlimited (default).
public int MaxDepth { get; init; } = -1;
+ /// Max siblings per dir. -1 unlimited (default).
public int MaxWidth { get; init; } = -1;
+ /// Global node cap. -1 unlimited (default).
public int MaxNodes { get; init; } = -1;
+ /// Load/apply .gitignore rules from root. Default: false.
public bool UseGitIgnore { get; init; }
+ /// Exclude hidden files/dirs. Default: false.
public bool SkipHidden { get; init; }
+ /// Special style for hidden (if not skipped). Default: true.
public bool HighlightHiddenFiles { get; init; } = true;
+ /// Hidden marker style. Default: Prefix.
public HiddenStyle HiddenStyle { get; init; } = HiddenStyle.Prefix;
+ /// Tree render style (Ascii/Markdown/Unicode). Default: Ascii.
public OutputFormat Format { get; init; } = OutputFormat.Ascii;
+ /// Filtering config (rules, legacy).
public FilterOptions Filter { get; init; } = new();
+ /// Collapse dirs > N children. Null=auto.
public int? CollapseThreshold { get; init; }
+ /// First N children to show in collapsed. Default: 1.
public int CollapseKeepStart { get; init; } = 1;
+ /// Last N children to show in collapsed. Default: 1.
public int CollapseKeepEnd { get; init; } = 1;
+ /// Placeholder style. Default: Count.
public CollapseStyle CollapseStyle { get; init; } = CollapseStyle.Count;
+ /// Start collapsing from this depth (1=root children). Default: 1.
public int CollapseFrom { get; init; } = 1;
}
diff --git a/src/FileTree.Core/Models/FilterOptions.cs b/src/FileTree.Core/Models/FilterOptions.cs
index 199339f..5839c15 100644
--- a/src/FileTree.Core/Models/FilterOptions.cs
+++ b/src/FileTree.Core/Models/FilterOptions.cs
@@ -2,6 +2,10 @@
namespace FileTree.Core.Models;
+///
+/// Filtering configuration.
+/// Modern: RulesSource gitignore-style. Legacy: ext/name lists (deprecated).
+///
public class FilterOptions
{
///
diff --git a/src/FileTree.Core/Models/HiddenStyle.cs b/src/FileTree.Core/Models/HiddenStyle.cs
index 30ab137..1367b22 100644
--- a/src/FileTree.Core/Models/HiddenStyle.cs
+++ b/src/FileTree.Core/Models/HiddenStyle.cs
@@ -1,8 +1,15 @@
namespace FileTree.Core.Models;
+///
+/// Style for marking hidden nodes.
+///
public enum HiddenStyle
{
+ /// [hidden] prefix.
Prefix,
+ /// (hidden) suffix.
Suffix,
+ /// * minimal marker.
Minimal
}
+
diff --git a/src/FileTree.Core/Models/OutputFormat.cs b/src/FileTree.Core/Models/OutputFormat.cs
index 708ce5d..ec16ce6 100644
--- a/src/FileTree.Core/Models/OutputFormat.cs
+++ b/src/FileTree.Core/Models/OutputFormat.cs
@@ -1,8 +1,15 @@
namespace FileTree.Core.Models;
+///
+/// Tree rendering format.
+///
public enum OutputFormat
{
+ /// ASCII box chars (| / \ -).
Ascii,
+ /// Markdown tree list.
Markdown,
+ /// Unicode tree chars (│ ├ └ ─).
Unicode
+
}
\ No newline at end of file
diff --git a/src/FileTree.Core/Processing/CollapseProcessor.cs b/src/FileTree.Core/Processing/CollapseProcessor.cs
index 17389d3..05b6bb8 100644
--- a/src/FileTree.Core/Processing/CollapseProcessor.cs
+++ b/src/FileTree.Core/Processing/CollapseProcessor.cs
@@ -3,8 +3,14 @@
namespace FileTree.Core.Processing;
+///
+/// Default implementing tree collapse logic.
+/// Recursively clones tree, replacing large dirs with placeholders.
+/// Supports KeepStart/KeepEnd, from depth, different styles.
+///
internal sealed class CollapseProcessor : ITreeProcessor
{
+ /// Processes tree if threshold set, using recursive clone/collapse.
public FileNode Process(FileNode root, FileTreeOptions options)
{
if (root == null) throw new ArgumentNullException(nameof(root));
diff --git a/src/FileTree.Core/Processing/ITreeProcessor.cs b/src/FileTree.Core/Processing/ITreeProcessor.cs
index 5224351..09905e6 100644
--- a/src/FileTree.Core/Processing/ITreeProcessor.cs
+++ b/src/FileTree.Core/Processing/ITreeProcessor.cs
@@ -2,7 +2,15 @@
namespace FileTree.Core.Processing;
+///
+/// Post-scan tree processor pipeline interface.
+/// Transforms tree (e.g., collapse nodes, prune, etc.) based on options.
+///
internal interface ITreeProcessor
{
+ /// Processes/transforms the tree root.
+ /// Input tree.
+ /// Processing options (threshold etc.).
+ /// Processed tree (may mutate/replace nodes).
FileNode Process(FileNode root, FileTreeOptions options);
}
\ No newline at end of file
diff --git a/src/FileTree.Core/Scanning/FileScanner.cs b/src/FileTree.Core/Scanning/FileScanner.cs
index 0d4e3bc..e406254 100644
--- a/src/FileTree.Core/Scanning/FileScanner.cs
+++ b/src/FileTree.Core/Scanning/FileScanner.cs
@@ -9,10 +9,22 @@
namespace FileTree.Core.Scanning
{
- internal class FileScanner : IFileScanner
+///
+/// Implements tree scanning with limits, filtering, gitignore.
+/// Recursive, depth/node aware.
+///
+///
+/// Default implementation of . Handles recursive directory scanning with support for
+/// depth/width/node limits, .gitignore rules, custom filters (legacy/new), hidden files, empty folder pruning.
+/// Uses stack-based Enter/Exit for local .filetreeignore inheritance.
+///
+internal class FileScanner : IFileScanner
{
+ /// Total nodes scanned (for MaxNodes limit).
private int _nodeCount;
+ /// Whether to prune empty folders post-scan.
private bool _ignoreEmptyFolders;
+ /// Evaluator for inclusion rules (limits, filters, gitignore).
private ScanInclusionEvaluator? _inclusionEvaluator;
///
@@ -66,6 +78,14 @@ public FileNode Scan(string rootPath, FileTreeOptions options)
return rootNode;
}
+ ///
+ /// Recursively scans directory contents, applying inclusion rules and limits.
+ /// Builds child FileNode, recurses dirs, prunes empty if configured.
+ ///
+ /// Current directory.
+ /// Parent node to add children to.
+ /// Recursion depth.
+ /// Scan options.
private void PerformScan(DirectoryInfo dirInfo, FileNode parentNode, int currentDepth, FileTreeOptions options)
{
// MaxDepth and MaxNodes checks are now handled by ScanInclusionEvaluator,
diff --git a/src/FileTree.Core/Scanning/HiddenFileDetector.cs b/src/FileTree.Core/Scanning/HiddenFileDetector.cs
index c316da0..8e9232b 100644
--- a/src/FileTree.Core/Scanning/HiddenFileDetector.cs
+++ b/src/FileTree.Core/Scanning/HiddenFileDetector.cs
@@ -3,8 +3,25 @@
namespace FileTree.Core.Scanning
{
+ ///
+ /// Cross-platform detector for hidden files and directories.
+ /// On Windows, checks the Hidden file attribute.
+ /// On Unix-like systems, checks for dot-prefix in name (e.g., .git).
+ ///
internal static class HiddenFileDetector
{
+ ///
+ /// Determines if the specified file system item is hidden.
+ ///
+ /// The file or directory information to check.
+ ///
+ /// true if the item is hidden; otherwise, false.
+ ///
+ ///
+ /// Platform-specific logic:
+ /// - Windows: Checks .
+ /// - Other OS: Checks if name starts with '.'.
+ ///
internal static bool IsHidden(FileSystemInfo item)
{
if (OperatingSystem.IsWindows())
diff --git a/src/FileTree.Core/Scanning/IFileTreeScanner.cs b/src/FileTree.Core/Scanning/IFileTreeScanner.cs
index 53d8245..9814891 100644
--- a/src/FileTree.Core/Scanning/IFileTreeScanner.cs
+++ b/src/FileTree.Core/Scanning/IFileTreeScanner.cs
@@ -2,7 +2,20 @@
namespace FileTree.Core.Scanning;
+///
+/// Defines the scanning contract for building file tree structures from directories.
+/// Implementations handle recursion, limits, filtering during scan.
+/// Note: Named IFileScanner internally, but file is IFileTreeScanner.cs.
+///
internal interface IFileScanner
{
+ ///
+ /// Scans the directory tree starting from , building a hierarchy.
+ /// Applies scanning limits, hidden/gitignore checks based on .
+ ///
+ /// Absolute path to root directory.
+ /// Scan configuration (depth, width, filters).
+ /// Root representing the tree.
+ /// Access denied during scan.
FileNode Scan(string rootPath, FileTreeOptions options);
}
diff --git a/src/FileTree.Core/Scanning/ScanInclusionEvaluator.cs b/src/FileTree.Core/Scanning/ScanInclusionEvaluator.cs
index f69c6ec..24748bb 100644
--- a/src/FileTree.Core/Scanning/ScanInclusionEvaluator.cs
+++ b/src/FileTree.Core/Scanning/ScanInclusionEvaluator.cs
@@ -9,6 +9,11 @@
namespace FileTree.Core.Scanning
{
+ ///
+ /// Evaluates whether file system items should be included in the scan based on limits, filters, gitignore rules, hidden status, etc.
+ /// Supports hierarchical .filetreeignore files and legacy filter conversion.
+ /// Uses stack for local rules scope.
+ ///
internal class ScanInclusionEvaluator
{
private readonly GitIgnoreRules? _gitIgnoreRules;
@@ -19,6 +24,7 @@ internal class ScanInclusionEvaluator
private readonly List> _localRulesStack = new();
private readonly List _baseFilterRules;
private readonly bool _useLocalFilterFiles;
+ /// Name of local ignore file for directory-specific rules.
private const string LocalIgnoreFileName = ".filetreeignore";
///
@@ -28,6 +34,8 @@ internal class ScanInclusionEvaluator
/// File tree scanning options.
/// Optional .gitignore rules to apply.
/// Optional custom filter rules to apply.
+ /// Initializes evaluator with root path, options, and rules sources.
+ /// Combines gitignore, custom rules, legacy filters. Builds initial filter rules.
public ScanInclusionEvaluator(
string rootPath,
FileTreeOptions options,
@@ -110,6 +118,11 @@ public bool ShouldInclude(FileSystemInfo item, int currentDepth, int currentNode
return true;
}
+ ///
+ /// Loads .filetreeignore from dir if exists, pushes to stack, rebuilds rules.
+ ///
+ /// Directory to check for local ignore.
+ /// True if rules loaded/pushed.
public bool EnterDirectory(DirectoryInfo dirInfo)
{
if (!_useLocalFilterFiles)
@@ -134,6 +147,8 @@ public bool EnterDirectory(DirectoryInfo dirInfo)
return true;
}
+ /// Pops local rules from stack if previously pushed, rebuilds effective rules.
+ /// True if EnterDirectory loaded rules.
public void ExitDirectory(bool hadRules)
{
if (!hadRules)
diff --git a/src/FileTree.Core/Services/FileTreeService.cs b/src/FileTree.Core/Services/FileTreeService.cs
index c19e15e..c66a719 100644
--- a/src/FileTree.Core/Services/FileTreeService.cs
+++ b/src/FileTree.Core/Services/FileTreeService.cs
@@ -8,12 +8,20 @@
namespace FileTree.Core.Services;
+///
+/// Main service orchestrating tree generation: scan -> process -> format.
+/// DI-friendly ctor for testing.
+///
public class FileTreeService : IFileTreeService
{
private readonly IFileScanner _scanner;
private readonly TreeFormatterFactory _formatterFactory;
private readonly IReadOnlyList _processors;
+ /// DI ctor.
+ /// Tree scanner.
+ /// Formatter selector.
+ /// Optional processors (default collapse).
internal FileTreeService(
IFileScanner scanner,
TreeFormatterFactory formatterFactory,
@@ -24,11 +32,13 @@ internal FileTreeService(
_processors = processors?.ToList() ?? new List { new CollapseProcessor() };
}
+ /// Default ctor with built-in scanner/factory/collapse.
public FileTreeService() : this(new FileScanner(), new TreeFormatterFactory())
{
}
+
///
/// Generates a formatted file tree representation for the specified directory.
///
diff --git a/src/FileTree.Core/Utilities/PathValidationException.cs b/src/FileTree.Core/Utilities/PathValidationException.cs
index a7f78b0..ee5ff66 100644
--- a/src/FileTree.Core/Utilities/PathValidationException.cs
+++ b/src/FileTree.Core/Utilities/PathValidationException.cs
@@ -3,6 +3,10 @@
///
/// Custom exception for path validation errors in FileTree scanning.
///
+///
+/// Entry namespace for path validation errors.
+/// Contains PathErrorType enum and PathValidationException class.
+///
namespace FileTree.Core.Utilities
{
///
diff --git a/tests/FileTree.Core.Tests/Fixtures/TempDirectoryFixture.cs b/tests/FileTree.Core.Tests/Fixtures/TempDirectoryFixture.cs
index aeee3f6..005e2cb 100644
--- a/tests/FileTree.Core.Tests/Fixtures/TempDirectoryFixture.cs
+++ b/tests/FileTree.Core.Tests/Fixtures/TempDirectoryFixture.cs
@@ -3,6 +3,10 @@
namespace FileTree.Core.Tests.Fixtures
{
+ ///
+ /// IDisposable fixture for creating temporary directories/files for integration tests.
+ /// Cleans up on Dispose.
+///
public class TempDirectoryFixture : IDisposable
{
public string RootPath { get; }
diff --git a/tests/FileTree.Core.Tests/Fixtures/TestTreeFixture.cs b/tests/FileTree.Core.Tests/Fixtures/TestTreeFixture.cs
index 8912b1b..301e1e5 100644
--- a/tests/FileTree.Core.Tests/Fixtures/TestTreeFixture.cs
+++ b/tests/FileTree.Core.Tests/Fixtures/TestTreeFixture.cs
@@ -2,8 +2,14 @@
namespace FileTree.Core.Tests.Fixtures;
+///
+/// Fixture providing a standard test tree structure for formatter/scanner tests.
+/// Creates in-memory FileNode hierarchy with root/bin/src/docs/temp/config.
+///
public class TestTreeFixture
{
+ /// Creates the test tree with known structure for assertions.
+ /// Root FileNode.
public FileNode CreateTestTree()
{
// root/
diff --git a/tests/FileTree.Core.Tests/Scanning/FileScannerTests.cs b/tests/FileTree.Core.Tests/Scanning/FileScannerTests.cs
index cf746d0..dd44ed3 100644
--- a/tests/FileTree.Core.Tests/Scanning/FileScannerTests.cs
+++ b/tests/FileTree.Core.Tests/Scanning/FileScannerTests.cs
@@ -7,6 +7,10 @@
namespace FileTree.Tests.Scanning
{
+ ///
+ /// Tests for FileScanner.Scan: basic scan, limits, hidden, gitignore.
+ /// Uses TempDirectoryFixture implicitly via manual temp dir.
+ ///
public class FileScannerTests : IDisposable
{
private readonly string _tempRoot;
@@ -28,6 +32,7 @@ public void Dispose()
}
}
+ /// Default options builder with overrides for test variations.
private FileTreeOptions Options(FileTreeOptions overrides = null)
{
var baseOptions = new FileTreeOptions