Drawing a directory listing a la the tree command in TikZ

Good day everyone -

Is there a fast and efficient way to draw directory tree structures (aka depth-indented listings) like the unix's tree command, that is, is there a way to draw the following using TikZ?

root
|
|--- dir1
|     |
|     |-- a1
|     |-- a2
|--- dir2
|--- dir3


I've checked tikz-tree and tikz-qtree, but the packages always want to grow the tree balanced and not down then to the right.

Thanks.

-
If TikZ is not really obligatory, then try dirtree package. –  przemoc Jul 20 '11 at 22:38
Using TikZ is tricky here: you can get part of the way by using grow with three children = one child at (2em,-2em) and two children at (2em,-2em) and (2em, -4em) and edge from parent to draw perpendicular lines. But as the manual says, tree layout occurs without reasoning about the size of the children. tikz-qtree handles reasoning about the size of the children, and the grow'=right option will grow trees rightwards, but balanced instead of bottom-heavy. So some variant of tikz-qtree's code should work...but I don't understand its code well enough to write that variant. –  Ben Lerner Jul 21 '11 at 0:53

I just came across this thanks to the duplicate at Vertical trees with TikZ. The following code seems to work, though I will admit to not testing it fully and making up the numbers so that it just works.

Here's the code:

\documentclass[border=10]{standalone}
\usepackage{tikz}

\makeatletter
\newcount\dirtree@lvl
\newcount\dirtree@plvl
\newcount\dirtree@clvl
\def\dirtree@growth{%
\ifnum\tikznumberofcurrentchild=1\relax
\expandafter\xdef\csname dirtree@p@\the\dirtree@plvl\endcsname{\the\dirtree@lvl}
\fi
\dirtree@clvl=\dirtree@lvl
\pgf@xa=1cm\relax
\pgf@ya=-1cm\relax
\pgf@ya=\dirtree@clvl\pgf@ya
\pgftransformshift{\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
\ifnum\tikznumberofcurrentchild=\tikznumberofchildren
\fi
}

\tikzset{
dirtree/.style={
growth function=\dirtree@growth,
every node/.style={anchor=north},
every child node/.style={anchor=west},
edge from parent path={(\tikzparentnode\tikzparentanchor) |- (\tikzchildnode\tikzchildanchor)}
}
}
\makeatother
\begin{document}
\begin{tikzpicture}[dirtree]
\node {toplevel}
child { node {Foo}
child { node {foo} }
child { node {bar} }
child { node {baz} }
}
child { node {Bar}
child { node {foo} }
child { node {foo} }
child { node {foo} }
child { node {foo} }
child { node {bar} }
child { node {baz} }
}
child { node {Baz}
child { node {foo} }
child { node {bar} }
child { node {baz} }
};
\end{tikzpicture}
\end{document}


We define a new growth function. The idea is that we have a global counter that keeps track of how many nodes we've processed so far and position the children according to this. The main problem is that the way trees are built: the origin for each branch is the parent node. So we want to apply an absolute transformation (the total height from the root) but have to take into account the relative transformation first. This takes a bit of bookkeeping, but once that's figured out then the rest is ... plain sailing.

The edges are the easy bit - we simply define them to be |-, ie vertical and then horizontal.

-
I’ve added node anchors for better appearance with longer text. There’s also another solution on texample.net, now: texample.net/tikz/examples/filesystem-tree. –  Speravir Dec 28 '11 at 17:54
@Speravir Good addition, thanks. I also agree with your comment on the texample example! I'm not known for the elegance of my code here. If you can think of any further improvements to make it easier to customise, then feel free to edit the answer (or, if you feel you've done a lot of improving, then add another answer with the improved code). –  Loop Space Dec 28 '11 at 20:09
Worth noting the modifications from tex.stackexchange.com/q/89129/86 for better arranging the horizontal alignment. –  Loop Space Jan 4 '13 at 11:21

Here are two concepts doing it with TikZ. The first one doesn't draw enough lines, the second one draws too many. I guess one needs counters for each level. Those counters would be increased if the same level occurs again and draw a line to it, while they would reset when a higher level is encounterd, e.g. like one could principally do with

\usepackage{chngcntr}
\counterwithin{leveltwo}{levelone}


Probably I'll get to it this week, but don't count on it. Here's what I have so far:

\documentclass[parskip,14pt]{scrartcl}
\usepackage[margin=15mm]{geometry}
\usepackage{tikz}

\newcounter{treeline}

\newcommand{\treeroot}[1]{% Title
\node[above] at (0,0) {#1};%
\setcounter{treeline}{0}
}

\newcommand{\treeentry}[2]{% Title, Level
\draw[->] (#2-1,-\value{treeline}/2) -- (#2-1,-\value{treeline}/2-0.5) -- (#2+0.5,-\value{treeline}/2-0.5) node[right] {#1};
\stepcounter{treeline}
}

\newcommand{\altentry}[2]{% Title, Level
\draw[->] (#2-1,-\value{treeline}/2) -- (#2-1,-\value{treeline}/2-0.5) -- (#2+0.5,-\value{treeline}/2-0.5) node[right] {#1};
\foreach \x in {1,...,#2}
{   \draw (\x-1,-\value{treeline}/2) -- (\x-1,-\value{treeline}/2-0.5);
}
\stepcounter{treeline}
}

\begin{document}

\begin{tikzpicture}
\treeroot{$\sqrt{of\ all\ evil}$}
\treeentry{Bla}{1}
\treeentry{Bla}{2}
\treeentry{Bla}{3}
\treeentry{Bla}{2}
\treeentry{Bla}{2}
\treeentry{Bla}{3}
\treeentry{Bla}{3}
\treeentry{Bla}{3}
\treeentry{Bla}{3}
\treeentry{Bla}{3}
\treeentry{Bla}{4}
\treeentry{Bla}{4}
\treeentry{Bla}{4}
\treeentry{Bla}{4}
\treeentry{Bla}{4}
\treeentry{Bla}{4}
\treeentry{Bla}{1}
\treeentry{Bla}{1}
\treeentry{Bla}{2}
\treeentry{Bla}{3}
\treeentry{Bla}{2}
\treeentry{Bla}{2}
\treeentry{Bla}{3}
\treeentry{Bla}{1}
\end{tikzpicture}
\hspace{2cm}
\begin{tikzpicture}
\treeroot{$\sqrt{of\ all\ evil}$}
\altentry{Bla}{1}
\altentry{Bla}{2}
\altentry{Bla}{3}
\altentry{Bla}{2}
\altentry{Bla}{2}
\altentry{Bla}{3}
\altentry{Bla}{3}
\altentry{Bla}{3}
\altentry{Bla}{3}
\altentry{Bla}{3}
\altentry{Bla}{4}
\altentry{Bla}{4}
\altentry{Bla}{4}
\altentry{Bla}{4}
\altentry{Bla}{4}
\altentry{Bla}{4}
\altentry{Bla}{1}
\altentry{Bla}{1}
\altentry{Bla}{2}
\altentry{Bla}{3}
\altentry{Bla}{2}
\altentry{Bla}{2}
\altentry{Bla}{3}
\altentry{Bla}{1}
\end{tikzpicture}

\end{document}


And the result:

-

Another example using the tikz trees library:

\documentclass{standalone}

\usepackage{tikz}
\usetikzlibrary{trees}

\usepackage{xcolor}

\begin{document}

\tikzstyle{every node}=[thick,anchor=west, rounded corners, font={\scriptsize\ttfamily}, inner sep=2.5pt]
\tikzstyle{selected}=[draw=blue,fill=blue!10]
\tikzstyle{root}=[selected, fill=blue!30]

\begin{tikzpicture}[%
scale=.7,
grow via three points={one child at (0.5,-0.65) and
two children at (0.5,-0.65) and (0.5,-1.1)},
edge from parent path={(\tikzparentnode.south) |- (\tikzchildnode.west)}]
\node [root] {src}
child { node [selected] {asf}
child { node [selected] {avr32}
child { node [selected] {drivers}
child { node [selected] {canif}
child { node {canif.h}}
child { node {canif.c}}
}
}
}
}
child { node at (0,-3.5) [selected] {net}
child { node [selected] {can}
child { node {can.h}}
child { node {can\_mob.h}}
child { node {can\_mob.c}}
child { node {can\_port.h}}
child { node {can\_port.c}}
child { node {can\_trcv.h}}
child { node {can\_trcv.c}}
}
}
child { node at (0,-8) {\dots}};
\end{tikzpicture}

\end{document}


-

Here's a forest solution which defines a new style dir tree which can be used to typeset trees in this format.

\documentclass[tikz, border=5pt, multi]{standalone}
\usepackage{forest}
\usetikzlibrary{arrows.meta}
\forestset{
dir tree/.style={
for tree={
parent anchor=south west,
child anchor=west,
anchor=mid west,
inner ysep=1pt,
grow'=0,
align=left,
edge path={
\noexpand\path [draw, \forestoption{edge}] (!u.parent anchor) ++(1em,0) |- (.child anchor)\forestoption{edge label};
},
font=\sffamily,
if n children=0{}{
delay={
prepend={[,phantom, calign with current]}
}
},
fit=band,
before computing xy={
l=2em
}
},
}
}
\begin{document}
\begin{forest}
dir tree
[root
[bin
]
[etc
[X11]
[cron.d]
[default]
]
[local
[bin]
[etc]
[texlive
[2014
[bin]
[texmf-config]
[texmf-dist]
[texmf-var]
]
[texmf-local]
]
]
[usr
[bin]
[include]
[share]
]
[var
[cache]
[log]
[spool]
[tmp]
]
]
\end{forest}
\end{document}


-