Back in my childhood while playing with a Win98 desktop, I've discovered the command `tree`.
For a child that knew nothing about computers properly, the amount of text displayed made me happy.
It was not only a bunch of text, it was a proper listing of dirs and subdirs.

On my path to gain some experience with C# I thought "Why not make my version of tree?".
Surely the world does not need another version tree, but it won't hurt either.
To make it more interesting I did most of it without a proper IDE.

Setting the scene

Tree does this:

$ tree # Tree is the current Directory
| |--Program.cs
| |--Program.csproj
| |--FileTree.cs
| |--Renderer.cs
| |--Tree.csproj

And the code is here.

But to write down my own initial direction:

Beginning a project

To C# pros or IDE users this might be cake, but starting a C# project can be REALLY alien depending from were you came.
Fortunately Tree is a simple project, so bootstrapping the project is like 35% of the job, which makes it somewhat enjoyable.

The `dotnet` command

`dotnet new console` gives you a Hello World, `dotnet build` and `dotnet run` do what you expect and the rest is history.

Looks like units of code in C# are grouped into projects (.csproj) and they can be either runnable (like console) or linkable (like library). The `.csproj` contains a XML describing your project and the references it needs.

Unlike Go or other friends, testing is itself a separate unit of code which uses a testing framework to test other units of code.

Nuget is used to resolve external dependencies and this is how it imports xUnit, which is the testing framework I'm using.

Because projects are either runnable or linkable and tests are a separate unit, we get our simple, but almost intuitive, project structure as seen here.

To skip the hassle of moving between project folders, solutions files that are usually managed by the IDE can be used to manage a reference to multiple projects with the `dotnet sln ...` collection of commands.

Laying the structure

The Tree project holds the logic to generate the model and render it, Tree.Test tests those functionalities and Program does the dirty part of processing user input and walking the file system.


The Node defines the base behavior needed to render the listing.

public abstract class
protected string

Node(string name)
Name = name;

public string
=> Name;

abstract public void
Add(Node node);

abstract public List<Node>

abstract public bool

Because the walking itself is handled inside the Program project, Directory and File classes are as simple as needed and not tied any SO walking.
The Directory class contains the method Directory With(Node node) which gives a DSL feel to it when writing listing tests.


The algorithm is mostly implemented here:

static void
Render(NodeKind kind, string name, In<string> stream, List<IdentKind> indentation)
for(int i = 0; i < indentation.Count-1; i++)
stream.Put("| ");
stream.Put(" ");

if(indentation.Count > 0)


The indentation list is used to handle the proper padding.
When rendering nodes inside the last node of a parent directory, its marked as final.
The "smart" bit is on ignoring the last indent of the list, this simplifies a bit the overall manipulation of the list.


The final bit is mostly discovering the APIs to list directories and parse arguments. Most of the code is derived from the structure of the main function:

static void
Main(string[] args)
Setup setup = new(args);
Node? node = null;

var error = setup.Error;
if(error is not null)

foreach(var path in setup.Paths)
node = Lookup(path, setup);

if(node is not null)
Render(node, setup);
throw new Exception(String.Format(
"Invalid path {0}", path));


I was not able to find an easy way to install the final console project into my environment, maybe there's a `dotnet install` of some sort, but copying the executable suffices my needs.

Go home.

☆Powered by Azure Blob