Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

DirTree: A Simplified Directory Tree Tool

3.90/5 (6 votes)
29 Jun 2022CPOL 10.1K   165  
Console app to scan a VS directory and create a simplified directory tree
This is a quick and dirty Console app written in VS2019 and .NET 4.8 to scan a VS directory and create a simplified directory tree.

Introduction

This quick and dirty Console app can be used to produce a simplified tree representation similar to the DOS Tree command, but with an option to limited the tree depth.

Directories starting with '.' are excluded, and also other directories like bin and obj.

Below is an example tree from the open-source Budoco VS Code solution (Bug Tracker in ASP.NET Core available on GitHub).

Image 1

Using the Code

Command line usage:

DirTree [path] [depth]

If no path is given, the current directory is used.
Depth must be >= 0, default value is 2.

So without further ado, here is the source code:

C#
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;

namespace DirTree
{
    /// <summary>
    /// Console app to scan a VS directory and create a simplified directory tree.
    /// Command line usage: 
    ///   DirTree [path] [depth]
    /// If no path is given, the current directory is used.
    /// Depth must be >= 0, default value is 2.
    /// </summary>
    class Program
    {
        static string filter = "*.*";
        static string startPath;
        static List<string> excludeList = new List<string>();
        static int folderLevel;
        static int treeDepth = 2;
        static int folderLevelStart;
        static string lastFolder;
        static bool lastDirFlag;
        static int tabSize = 3;
        static StringBuilder sbTree;        // Optionally write to DirTree.txt

        static void Main(string[] args)
        {
            if (args.Length > 0)
            {
                startPath = args[0];

                if (args.Length > 1)
                {
                    treeDepth = int.Parse(args[1]);
                }
            }

            if (string.IsNullOrEmpty(startPath))
            {
                startPath = Environment.CurrentDirectory;
            }

            if (!startPath.EndsWith(@"\"))
            {
                startPath += @"\";
            }

            sbTree = new StringBuilder(50);
            sbTree.AppendLine(startPath);
            sbTree.AppendLine();

            // Add directories to exclude
            excludeList.Add("bin");
            excludeList.Add("obj");
            excludeList.Add("x64");
            excludeList.Add("x86");
            excludeList.Add("Properties");

            folderLevelStart = startPath.Count(x => x == '\\');
            var dirs = GetFoldersFiltered(startPath, filter);

            if (dirs.Length > 0)
            {
                lastFolder = dirs[dirs.Length - 1];
                ScanFolders(startPath);
                //File.WriteAllText("DirTree.txt", sbTree.ToString()); // Enable this 
                                                          // to write DirTree.txt
            }
            else
            {
                Console.WriteLine("No directories found.");
            }

            Console.ReadKey();
        }

        /// <summary>
        /// Get folders array, exclude folders with excludeList and 
        /// starting with '.' like .git .vs.
        /// </summary>
        /// <param name="path">The path</param>
        /// <param name="filter">Wildcard filter</param>
        /// <returns>A string array</returns>
        static string[] GetFoldersFiltered(string path, string filter)
        {
            var folders = Directory.GetDirectories(path, filter).ToList();

            for (int i = folders.Count - 1; i >= 0; i--)
            {
                string dirFullname = folders[i];
                string dirname = dirFullname.Replace(path, "");

                if (dirname.StartsWith(".") || excludeList.Contains(dirname))
                {
                    folders.RemoveAt(i);
                }
            }

            return folders.ToArray();
        }

        /// <summary>
        /// Recursively scan folders with a depth of <see cref="treeDepth"/>.
        /// </summary>
        /// <param name="path">The start path</param>
        /// <param name="continueLine">The folder level to continue vertical line
        /// </param>
        /// <param name="previous_indent">The previous indentation</param>
        static void ScanFolders(string path, int continueLine = -1, 
                                string previous_indent = "")
        {
            int i = 0;

            if (!path.EndsWith(@"\"))
            {
                path += @"\";
            }

            var dirs = GetFoldersFiltered(path, filter);
            int lastNode = dirs.Length - 1;

            while (i < dirs.Length)
            {
                string dirFullname = dirs[i];
                string dirname = dirFullname.Replace(path, "");
                string indent = "  ";
                string indent2 = string.Empty;

                if (dirFullname.StartsWith(lastFolder))
                {
                    lastDirFlag = true;
                }

                folderLevel = dirFullname.Count(x => x == '\\') - folderLevelStart;

                for (int j = 0; j < folderLevel; j++)
                {
                    if (lastDirFlag && continueLine >= j)
                    {
                        indent += " " + new string(' ', tabSize);
                    }
                    else
                    {
                        indent += "│" + new string(' ', tabSize);
                    }
                }

                if (previous_indent.Length > 0)
                {
                    indent = previous_indent + indent.Substring(previous_indent.Length);
                }

                if (i == lastNode)
                {
                    indent2 = "└" + new string('─', tabSize);
                    continueLine = folderLevel;
                }
                else
                {
                    indent2 = "├" + new string('─', tabSize);
                }

                Console.WriteLine($"{indent}{indent2}{dirname}");
                sbTree.AppendLine($"{indent}{indent2}{dirname}");

                if (folderLevel < treeDepth)
                {
                    if (i == lastNode)
                    {
                        indent += " " + new string(' ', tabSize);
                    }

                    // Recursive call
                    ScanFolders(dirFullname, continueLine, indent);
                }

                i++;
            }
        }
    }
}

History

  • 29th June, 2022: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)