CTAN Comprehensive TeX Archive Network

Directory macros/latex/contrib/dirtreex

README.md

dirtreex

A self-contained package for rendering directory trees inside an optionally breakable, fully styleable frame. Built directly on TikZ and zref — no tcolorbox or classic dirtree underneath.

Features

  • Auto-tracked depth. Nest \dir, \file, and \archive as deeply as you want; the package derives the depth from the nesting — no per-entry depth argument.
  • Sharp or rounded elbows, configurable per tree or per entry via a single elbow radius key (0pt = sharp, any positive length = rounded arc).
  • Per-entry overrides for icons, line colour, line width, line style, and elbow radius.
  • Breakable frame. The border, background fill, and every pending │ column continue seamlessly across page boundaries.
  • CJK-friendly. Works out of the box with ctex + Lua.
  • Zero shell-escape, no external tools, no Python helpers.

Table of contents

  1. Requirements
  2. Installation
  3. Quick start
  4. Entries: \dir, \file, and \archive
  5. Verbatim names: \verbdir, \verbfile, and \verbarchive
  6. Environment options
  7. Cross-page behaviour
  8. Example gallery
  9. Compilation

10. Troubleshooting 11. Limitations 12. Project files 13. Contact 14. License


  1. Requirements
Component Notes
Lua, pdf or For CJK, use a suitable font setup, such as ctex with Lua.
extensions (\numexpr, \dimexpr, \ifcsname) Present in every modern engine (pdf, , Lua) and guaranteed by the package's format requirement (LaTeX2e 2020/10/01 or later).
TikZ 3.x The backgrounds library is loaded automatically.
xcolor Use colour names or expressions such as blue!50!black. Define HTML colours first, for example \definecolor{accent}{HTML}{336699}.
pgfkeys Option parsing.
zref-abspage Absolute page tracking; PGF positions distinguish standard two-column output.
Document commands ( kernel) \NewDocumentEnvironment / \NewDocumentCommand, plus the v (verbatim) argument type used by \verbdir / \verbfile / \verbarchive.

No -shell-escape, no external tools, no Python helper.


  1. Installation
  1. Copy dirtreex.sty into the same directory as your document, or install it under texmf-local/tex/latex/dirtreex/ and run mktexlsr ( Live) / the MiK equivalent.
  2. In your preamble:
   \usepackage{dirtreex}
   

  1. Quick start

The smallest possible document:

\documentclass{article}
\usepackage{dirtreex}

\begin{document}
\begin{dirtreex}
  \dir{project}{root}{
    \dir{src}{source}{
      \file{main.py}{entry point}
      \file{util.py}{helpers}
    }
    \file{README.md}{documentation}
  }
\end{dirtreex}
\end{document}

This produces a framed tree where each line is <name> … <comment> joined with a dot-leader, directory names receive a trailing /, and the elbow connectors (├, └, │) are laid out automatically.


  1. Entries: \dir, \file, and \archive
\dir    [<entry options>]{<name>}{<comment>}{<children>}
\file   [<entry options>]{<name>}{<comment>}
\archive[<entry options>]{<name>}{<comment>}{<children>}
  • <name> is typeset in \ttfamily. For \dir, a trailing / is appended automatically — write src, not src/. \file and \archive render the name as-is.
  • <comment> is typeset in \rmfamily and joined to <name> with a dot leader. Pass {} for no comment (the dot leader also disappears).
  • <children> is a run of further \dir / \file / \archive calls. \file has no children block. You do not supply the depth — the package tracks it from the nesting.
  • <entry options> are optional and described in §6.4.

\archive is a container entry: it accepts a children block, so its contents indent and connect inside the tree, and its name is rendered as-is (no path separator appended). Use it for archive-like containers (.tar.gz, .zip, .jar, …) or for any container whose name should not carry a trailing /. Set icon={...} (§6.4) to choose an icon for an individual entry. For automatic formatting by entry type, override \DirtreexFormatName (§4.4) and dispatch on the entry's t slot.

4.1 Multi-line comments

A comment may contain line breaks; the whole block is packed so its combined height stays rectangular, keeping the connector column straight:

\file[line color=purple]{Makefile}{
  build script\\
  (multi-line comment, stays aligned)
}

4.2 Empty subtrees and empty comments

Both are legal:

\dir{empty-dir}{}{}                % no comment, no children
\dir{has-comment}{with comment}{}  % comment but no children

4.3 Multiple trees on one page

Simply place several dirtreex environments back-to-back. Each environment has its own zref namespace, so their cross-page bookkeeping never collides.

4.4 Customising entry-name typesetting (\DirtreexFormatName)

\DirtreexFormatName is a public hook that controls how each entry name is typeset. The default formatter prints the optional icon with a nonbreaking 0.5em gap, then the stored name plus a trailing / for directories. An omitted or empty icon={} adds no prefix or gap.

Override it with \renewcommand to inject icons, prefixes, or styling. The hook receives the 1-based entry index as #1, and \DirtreexGetField{idx}{slot} reads any stored field for that index:

  • \DirtreexGetField{#1}{n} — the entry name (the first mandatory argument of \dir / \file / \archive).
  • \DirtreexGetField{#1}{t} — 1 for directories, 0 for files, 2 for archives.
  • \DirtreexGetField{#1}{i} — the raw icon code, empty when omitted; the name field stays unchanged.

A replacement hook controls the entire name, including icon rendering. Save and call the original formatter to retain its icon handling, gap, and directory slash when adding your own decoration.

Example — tag every directory with a [DIR] prefix while keeping the default formatter:

\let\OriginalDirtreexFormatName\DirtreexFormatName
\renewcommand{\DirtreexFormatName}[1]{%
  \ifnum\DirtreexGetField{#1}{t}=1 [DIR]\fi
  \OriginalDirtreexFormatName{#1}%
}

Example — three-way icon dispatch on t:

\renewcommand{\DirtreexFormatName}[1]{%
  \ifcase\DirtreexGetField{#1}{t}%
    [F]\space\DirtreexGetField{#1}{n}%             % t=0 file
  \or
    [D]\space\DirtreexGetField{#1}{n}/%            % t=1 directory
  \or
    [A]\space\DirtreexGetField{#1}{n}%             % t=2 archive
  \fi
}

Swap the bracketed tags for icons from your icon package of choice (\faFolder, \faFile, \faFileArchive, …) to get the same dispatch with glyphs.

Constraints. The replacement runs inside the row's name vbox under the body's font. It must respect the row's \hsize and leave the caller's \strut intact; anything that adds vertical material (for example a \parbox with its own depth) will pull the entry's connector out of alignment.

Stability. \DirtreexFormatName's single-argument convention and the \DirtreexGetField accessors for n (entry name), t (type flag), and i (icon code) are committed public surfaces — overrides built around them keep compiling across minor versions. Other slot keys exist internally but are not part of the public surface and may change.


  1. Verbatim names: \verbdir, \verbfile, and \verbarchive

\dir, \file, and \archive accept -tokenised name arguments — paths containing _, $, &, #, ^, ~, %, or \ need to be escaped (\_, \$, etc.) or wrapped in \detokenize{}. For paths where this is inconvenient, dirtreex provides three verbatim variants:

\verbdir    [<entry options>]|<path>|{<comment>}{<children>}
\verbfile   [<entry options>]|<path>|{<comment>}
\verbarchive[<entry options>]|<path>|{<comment>}{<children>}

Each verbatim variant pairs with the same-shape regular command: \verbdir mirrors \dir (children block, trailing /), \verbfile mirrors \file (no children), and \verbarchive mirrors \archive (children block, no trailing /).

The path uses the kernel's v (verbatim) argument type. It accepts two matching delimiters other than %, \, #, braces or a space, or a braced form such as \verbfile{path.txt}{comment}. ASCII letters retain their category; the usual special characters become literal characters, while spaces and tabs remain active tokens. Non-ASCII character categories depend on the engine: active in 8-bit engines, letter or other in Unicode engines.

Comment and children retain full catcodes — you can still write $x^2$, \textbf{...} and \\ inside the comment, and nested \dir / \file / \archive / \verbdir / \verbfile / \verbarchive inside the children block work normally.

5.1 Example

\begin{dirtreex}
  \verbdir|src/$ENV/data_v2|{environment-keyed data root}{%
    \verbfile|users_2026-01.parquet|{snapshot}%
    \verbfile|conf{prod}.yaml|{production config}%
  }
\end{dirtreex}

5.2 Restrictions

  • The chosen delimiter cannot appear inside the path. Pick one that doesn't collide:
  \verbfile|a/b|{...}        % | as delimiter
  \verbfile+a|b+{...}        % + as delimiter, | survives in the path
  \verbfile/a|b+c/{...}      % / as delimiter, | and + survive
  

Delimiter collisions corrupt SILENTLY. Writing \verbfile|foo|bar|{c} makes the verbatim scanner take |foo| as the name and b as the comment, leaving ar|{c} as stray tokens — no error fires. Pick a delimiter that does not appear in any of your paths.

  • The verbatim argument must be readable directly from source. If you wrap \verbfile|...| inside another macro that captures its argument as a token list (e.g., a user-side \NewDocumentCommand with an m or +m argument), the path is tokenised at the wrapper's call site — before \verbfile can re-catcode it — and the verbatim semantics are lost. The children block of \dir and \verbdir does not suffer from this: it is a live group, not a captured token list, so \verbfile|...| written inside \dir{...}{...}{ ... } works as expected.
  • Encoding. Under T1 / TU encoding (Lua default, or pdf with \usepackage[T1]{fontenc}), every cat-12 character renders as its literal glyph in monospace. If you target legacy OT1 encoding, the special characters _ and & may require explicit \char mapping — pick an encoding that supports them.

5.3 Where verbatim names are useful

  • Paths with parameter substitutions ($ENV, ${VAR}).
  • Windows paths (C:\Users\foo).
  • Configuration filenames (conf{prod}.yaml, app_$STAGE.toml).
  • Database identifiers (schema$ext.table_v2).
  • Log file patterns (err_$.log, out_%Y-%m-%d.log).

  1. Environment options

Options go inside [...] on \begin{dirtreex} and are parsed with pgfkeys. Spaces around = are allowed.

6.1 Top-level keys

Key Default Meaning
fontsize \small Size command applied to the tree body. Use any length-free size macro: \tiny, \small, \normalsize, …, \Large.
dotfill color inherited Colour of the leader between an entry name and its comment. Accepts an xcolor expression; inherits the current text colour when omitted.
dotfill command \dotfill code that fills the gap before a comment. Use \hrulefill for a solid rule, \hfill for a blank gap, or a custom leader with \hfill stretch.
line color black Default colour for connectors and cross-page extensions. Accepts any xcolor expression.
line width 0.4pt Default rule width for all connectors.
line style solid Dash pattern for every connector segment styled by this entry. Accepts any TikZ line-style token — solid, dashed, dotted, densely dashed, loosely dashed, densely dotted, loosely dotted, dash dot, dash dot dot, … — or a comma-separated TikZ option list such as dash pattern={on 3pt off 2pt},dash phase=1pt. solid keeps the primitive \vrule fast path; any other value routes that segment through TikZ. Propagates to the entry's own elbow, the upper-half of its own-depth pass-through column, and (for the next sibling at each active depth) the lower-half pass-through and full pure-pass-through trunks, plus the matching cross-page extension rules.
elbow radius 0pt Elbow geometry. 0pt (the default) gives sharp └/├ right angles; any positive length gives rounded arcs of that radius. Clamped against 0.5\baselineskip and the horizontal connector arm length for legibility.
box see §6.2 Frame settings.
pagebreak see §6.3 Page-break settings.

Enclose comma-separated line style options in braces. A macro may supply the list; conditional selection of key–value lists must finish before returning the selected keys. The stored keys are applied in normal TikZ order; per-entry styles retain capture-time expansion.

The dotfill options apply to all nonempty comments in that environment. The leader's colour and any declarations in its command are local to the leader; names and comments retain their own formatting. Explicit \\ continuations keep their right alignment, with a leader only before the first line.

\begin{dirtreex}[dotfill color=blue!60!black,
                dotfill command=\hrulefill]
  \dir{project}{root}{
    \file{main.py}{entry point}
    \file{LICENSE}{}
  }
\end{dirtreex}

Custom leader commands are evaluated in the tree's font at rendering time. Use \hfill stretch to preserve comment alignment, and keep the leader within the normal text height. See the comment-leader section of dirtreex_examples.tex for a custom dashed pattern.

6.2 The box family

Pass as a sub-list: box = { … }.

Subkey Default Meaning
true / false true Draw the frame at all. box=false leaves the tree bare.
corners 0pt Corner radius. A single value applies to all four corners; a four-value list is TL, TR, BR, BL.
border color black
border width 0.4pt
background color white Colour name or xcolor expression; define HTML colours first (see §1).
margin 6pt Padding between the border and the tree content. Single value = uniform; a four-value list is T, R, B, L (asymmetric padding is supported in both single and breakable output).

Example:

\begin{dirtreex}[
  box = {
    true, corners = 6pt, border color = purple,
    border width = 1pt, background color = purple!5,
    margin = {2pt, 20pt, 10pt, 4pt}
  }
]

6.3 The pagebreak family

Subkey Default Meaning
true / false true Allow the frame to break across pages. When false, tall trees spill past the page; they are not truncated.
box break at 0pt Vertical gap between the frame's torn edge and the page boundary. Single value applies to both sides of every break; two values = first-piece-bottom, next-piece-top.
tree break at 1em (0pt when box=false) Same, but applied to the tree's extension rules rather than to the border itself. Useful when the frame should reach the page edge while the internal tree lines pull back.

6.4 Per-entry overrides

All six entry commands accept icon={...} to print inline content before that entry's name using the default name formatter. Icons are specific to each entry: children and siblings start without an icon. The code is stored unexpanded and rendered in a local horizontal box, so ordinary font and colour declarations affect only the icon. A nonbreaking 0.5em gap follows a nonempty icon; omitting the option or using icon={} adds no gap.

\begin{dirtreex}
  \verbdir[icon={[D]}]|src_code|{sources}{
    \verbfile[icon={\textcolor{blue}{[F]}}]|main_app.py|{entry point}
    \file{LICENSE}{}
  }
\end{dirtreex}

Use a glyph command or a text-sized \includegraphics for an image icon. Keep its height and depth within the normal text row to preserve baseline and frame alignment; standard \raisebox can adjust its vertical position. The item-icon section in dirtreex_examples.tex shows images scaled to 0.9em high and lowered by 0.15em. Custom \DirtreexFormatName hooks control whether and how the icon is rendered (§4.4).

Any \dir, \file, \archive (or their verbatim siblings \verbdir / \verbfile / \verbarchive) can take the same line color, line width, line style, and elbow radius keys. They override the environment-level defaults for connector segments owned by that entry:

\dir[line color = blue]{src}{source}{
  \file[line color = red, line width = 1.5pt]{old.py}{deprecated}
  \file[line color = green!60!black, elbow radius = 0pt]{new.py}{sharp}
  \archive{vendor.zip}{bundled}{
    \file[line style = dashed]{numpy.py}{}
    \file[line style = dashed]{pandas.py}{}
  }
}

Each entry owns its own └-shape. The vertical line continuing down from that entry's arm toward the next sibling carries the next sibling's colour — so two siblings with different colours share a column that changes colour at arm level (sharp) or at arc-top (rounded).


  1. Cross-page behaviour

When a tree needs splitting, dirtreex emits pieces on consecutive pages or standard columns, accounting for current float reservations and rendered frame dimensions. Each piece contains a TikZ content node, with its frame and active │ columns drawn around it. Bare trees use the same splitting machinery without a frame.

The initial pass records absolute page numbers via zref-abspage and, in standard two-column mode, horizontal row origins via PGF. Later passes use these locations to distinguish a predecessor on the same piece from one across a page or column break. Recompile until the auxiliary files settle; the isolated regression cases settle within two passes, and the gallery with its table of contents within three.

Concretely:

  • Every piece after the first starts with a 0.5\baselineskip leader of empty space, so the first visible entry shows a │ above its elbow matching the inter-sibling spacing used on a single page.
  • Bottom extensions of every active column continue down to the first piece's bottom edge.
  • Top extensions re-appear in the second (and any subsequent) piece, matching the column colours and widths the previous piece ended with.
  • The closing piece retains the configured content width. If first-piece fitting consumes the entire tree, it produces a complete tree without an empty trailing closing piece.

  1. Example gallery

The snippets below are self-contained: drop any of them into a document with \usepackage{dirtreex} and they compile. For more worked examples of the supported options, see dirtreex_examples.tex in the repository root.

8.1 Defaults

\begin{dirtreex}
  \dir{project}{default settings}{
    \file{a.txt}{file A}
    \file{b.txt}{file B}
    \dir{sub}{subdirectory}{
      \file{c.txt}{file C}
    }
  }
\end{dirtreex}

8.2 Rounded elbows

\begin{dirtreex}[elbow radius = 3pt]
  \dir{round3}{rounded 3pt}{
    \file{a.txt}{}
    \dir{sub1}{subdir 1}{
      \file{b.txt}{}
      \dir{sub2}{subdir 2}{\file{c.txt}{}}
    }
    \file{d.txt}{}
  }
\end{dirtreex}

8.3 Per-branch colours

\begin{dirtreex}[
  elbow radius = 3pt,
  box = {
    true, border color = black,
    background color = white, margin = 6pt
  }]
  \dir{project}{}{
    \dir[line color = blue]{src}{blue: source code}{
      \file[line color = red]{old.py}{red: deprecated}
      \file[line color = green!60!black]{new.py}{green: new}
    }
    \dir[line color = orange]{docs}{orange: documentation}{
      \file{api.md}{}
    }
    \file[line color = purple]{Makefile}{purple}
  }
\end{dirtreex}

8.4 Thick lines

\begin{dirtreex}[
  line color = red!70!black, line width = 1pt,
  box = {
    true, border color = red!80!black,
    border width = 2pt, background color = red!5
  }]
  \dir{bold}{thick-line test}{
    \file{a.txt}{}
    \dir{sub}{}{\file{b.txt}{}}
  }
\end{dirtreex}

8.5 A tree that pages across

\begin{dirtreex}[
  elbow radius = 3pt,
  box = {
    true, border color = black,
    background color = white, margin = 6pt
  }]
  \dir{cross-page}{long tree}{
    \dir[line color = red]{red-branch}{}{
      \file{r1.txt}{}
      % ... add enough files to overflow
    }
    \dir[line color = blue]{blue-branch}{}{\file{b1.txt}{}}
  }
\end{dirtreex}

The border, background, and all active │ columns continue onto the next page; the first visible entry on that page shows a full │ stem above its elbow. Colours are carried across the page break seamlessly.

8.6 Asymmetric margin

\begin{dirtreex}[
  box = {margin = {2pt, 20pt, 10pt, 4pt},  % T, R, B, L
         border color = teal, background color = teal!5}]
  \dir{root}{}{
    \file{a}{}
    \file{b}{}
  }
\end{dirtreex}

  1. Compilation
lualatex -interaction=nonstopmode yourdoc.tex
lualatex -interaction=nonstopmode yourdoc.tex  # 2nd pass for zref
  • The first pass records absolute pages and any required column positions.
  • Subsequent passes read those locations to choose connector anchors and cross-break extensions.

For a fresh document, start with two passes and rerun if labels or the table of contents still change. The gallery requires three passes. A document with unchanged, settled auxiliary files generally needs only one.

The package emits Package dirtreex Warning: Rerun LaTeX... at \end{document} when required page or column data is missing. This is a rerun request, not a complete auxiliary-stability check; also heed 's changed-label warnings or use latexmk.


10. Troubleshooting

Symptom Likely cause Fix
First entry on a continuation page has no │ above its elbow Only one compile pass has run so far Run again. The Rerun LaTeX... warning at \end{document} is already asking you to.
Vertical columns stop halfway down the last piece Same cause — zref has not seen the break yet Run again.
CJK characters render as □ or are missing Missing glyphs or font setup Use a suitable Unicode engine and fonts, for example Lua with ctex.
! Package dirtreex Error: \dir used outside dirtreex environment (same for \file, \archive, and their verbatim siblings) A stray entry command that is not inside \begin{dirtreex}…\end{dirtreex} Wrap the call in a dirtreex environment.
! Package dirtreex Error: parsefour expects 1 or 4 comma-separated values (or parsetwo expects 1 or 2 …) corners, margin, box break at, or tree break at given with the wrong arity Pass either a single value or the full set (four for corners/margin, two for the break-at keys). Remember the enclosing braces: margin = {2pt, 4pt, 6pt, 8pt}, not margin = 2pt, 4pt, 6pt, 8pt.
Package dirtreex Warning: \dte@findanchor hit the recursion floor at entry N (once per env) The tree's first top-level entry has depth > 0, or an outer \begingroup shadowed \dte@depth and produced an inverted depth sequence Ensure the env body starts with a depth-0 \dir / \file / \archive (or \verbdir / \verbfile / \verbarchive). The connector for the affected entry is snapped to a cross-page-stub variant as a graceful fallback, so the build still succeeds — the warning is purely diagnostic.

11. Limitations

  • Nested directory/archive entries are supported; nested dirtreex environments and entry constructors invoked from rendering hooks are unsupported. Keep entry capture in the environment body.
  • Pagination assumes standard page/column and float accounting. Arbitrary replacement output routines or balancing-column packages are not qualified.
  • Entry formatting runs once per entry, but picture hooks can run during rejected fitting trials. Keep their global or immediate side effects in mind; grouping cannot roll them back.
  • A row or frame padding too large for any column can still produce a finite overflow diagnostic. Repeated unconsumed deferral stops at the twentieth attempt; reduce recurring paragraph/header material or clear pending floats.
  • Extremely small box.margin combined with a very large elbow radius crowds the corner; the internal clamp (connE ≤ 0.5\baselineskip) keeps things legible but will not rescue pathological values.
  • No built-in numbering or hyperlinks. Use icon={...} (§6.4) for per-entry icons and the \DirtreexFormatName hook (§4.4) for custom name formatting; wrap the tree in a minipage if you need decoration around the whole frame.

12. Project files

Path What it is
dirtreex.sty The package — a single self-contained file.
dirtreex_examples.tex Worked examples covering the full feature surface. Compile with lualatex dirtreex_examples.tex three times to settle tree locations and the table of contents.
assets/ PNG glyphs (directory.png, python.png, zip_file.png) used by the icon examples in dirtreex_examples.tex.
tests/ Three-engine development tests on GitHub; see test instructions and coverage.
README.md This documentation.
LICENSE The Project Public License 1.3c.

13. Contact

Bug reports, feature suggestions, and patches are welcome via either channel.


14. License

Released under the Project Public License, version 1.3c (LPPL-1.3c); see LICENSE for the full text. Maintenance status is author-maintained; the Current Maintainer is CloudCauldron (<w.yizheng@qq.com>).


15. About the Development

This project includes code generated with the assistance of AI tools. All such code has been reviewed and integrated by the maintainer.

Download the contents of this package in one zip archive (315.2k).

Dirtreex – Directory tree rendering for with

This package provides a self-contained tool for rendering directory trees in an optionally breakable, fully styleable frame. Built directly on and zref, it works natively without requiring tcolorbox, dirtree, shell escape, or external scripts.

Its main features include automatically tracked depth, so that \dir and \file commands can be nested naturally; breakable frames whose borders, background, and vertical connection lines continue across page boundaries; extensive customization with per-tree and per-entry settings for line colors, line widths, and elbow radii; and CJK-friendly operation with ctex and Lua.

PackageDirtreex
Bug tracker
Repository
Version1.2 2026-09-19
LicensesThe Project Public License 1.3c
MaintainerCloudCauldron
Contained inTeX Live as dirtreex
MiKTeX as dirtreex
TopicsTree
PGF
See alsodirtree
...
Guest Book Sitemap Contact Contact Author