A lightweight and flexible Java library with a native CLI to pretty-print directory structures - ideal for documentation, project overviews, or CLI tools.
JFileTreePrettyPrint project structure, using JFileTreePrettyPrint
Supports various options to customize the directories scanning and rendering:
| Option | Description | Supported in CLI | Supported in Library API |
|---|---|---|---|
filter - dir |
Include/exclude specific directories | ✅ Yes | ✅ Yes |
filter - file |
Include/exclude specific files | ✅ Yes | ✅ Yes |
sorting |
Custom sorting of files/directories | ❌ No | ✅ Yes |
emojis |
Display emojis as 📂 folders and 📄files icons | ✅ Yes | ✅ Yes |
emoji - custom mapping |
Custom emoji mapping | ❌ No | ✅ Yes |
compactDirectories |
Compact single-child directories into one line | ✅ Yes | ✅ Yes |
childLimit - static |
Static limit number of displayed children per directory | ✅ Yes | ✅ Yes |
childLimit - dynamic |
Dynamic limit number of displayed children per directory | ✅ Yes | ✅ Yes |
maxDepth |
Maximum directory depth to display | ✅ Yes | ✅ Yes |
lineExtensions |
Append custom strings/comments after matching paths | ✅ Yes | ✅ Yes |
treeFormat |
Custom symbols for branches and indentation | ❌ No | ✅ Yes |
external options file |
Use options defined in an external file | ✅ Yes | ❌ No |
debug |
Print debug information to console | ✅ Yes (--debug) |
❌ No |
Unlike a plain recursive Files.walk(), this library:
- 📂 Prints visually appealing directory trees.
- 🎨 Allows rich customization (filters, sorts, emojis, compacting, tree style).
- 🙅 Is dependency-free (on runtime) and compatible with Java 21+.
- ⚙️ Choose between Java lib or Native CLI implementation.
- See 🆕CHANGELOG.md for a list of released versions and detailed changes.
- See 🗺️ROADMAP.md to discover planned features and upcoming improvements.
- This project is licensed under the Apache License 2.0. See ⚖️LICENSE for details.
- For any questions or feedback please open an issue on this repository, as detailed in 🤝CONTRIBUTING.md.
- Java 21 or later
- No runtime dependencies!
For Maven, import this dependency to your pom.xml:
<dependency>
<groupId>io.github.202132.xyzputerdaddyguy</groupId>
<artifactId>jfiletreeprettyprinter-core</artifactId>
<version>0.2.0</version>
</dependency>For Gradle:
implementation "io.github.202132.xyzputerdaddyguy:jfiletreeprettyprinter-core:0.2.0"// Example: BasicUsage.java
var printer = FileTreePrettyPrinter.createDefault(); // Create a printer with default options
var tree = printer.prettyPrint("src/example/resources/base"); // Pretty print the target folder
System.out.println(tree); // Admire the result!Result:
base/
├─ businessPlan.pdf
├─ businessProject.pdf
├─ cars/
│ ├─ Ferrari.doc
│ └─ Porsche.doc
├─ diyIdeas.docx
├─ giftIdeas.txt
└─ images/
├─ funnyCat.gif
├─ holidays/
│ ├─ meAtTheBeach.jpeg
│ └─ meAtTheZoo.jpeg
└─ landscape.jpeg
Tip
All code below is availabe in jfiletreeprettyprinter-examples submodule.
Files and directories can be selectively included or excluded using a custom PathMatcher.
Filtering applies independently to files and directories. Files are filtered only if their parent directory passes the directory filter. If none of a directory’s children match, the directory is still displayed.
The PathMatchers class provides several ready-to-use methods for creating and combining common matchers.
// Example: Filtering.java
var excludeDirWithNoJavaFiles = PathMatchers.not(PathMatchers.hasNameEndingWith("no_java_file"));
var hasJavaExtension = PathMatchers.hasExtension("java");
var prettyPrinter = FileTreePrettyPrinter.builder()
.customizeOptions(
options -> options
.filterDirectories(excludeDirWithNoJavaFiles)
.filterFiles(hasJavaExtension)
)
.build();filtering/
├─ dir_with_java_files/
│ ├─ file_B.java
│ └─ file_E.java
├─ dir_with_nested_java_files/
│ └─ nested_dir_with_java_files/
│ ├─ file_G.java
│ └─ file_J.java
└─ file_A.java
Files and directories can be sorted using a custom comparator (default is alphabetical order).
If the provided comparator considers two paths equal (i.e., returns 0), an alphabetical comparator is applied as a tie-breaker to ensure consistent results across all systems.
The PathSorts class provides a set of basic, ready-to-use comparators, as well as a builder for creating your own tailor-made sort.
// Example: Sorting.java
var prettyPrinter = FileTreePrettyPrinter.builder()
.customizeOptions(options -> options.sort(PathSorts.DIRECTORY_FIRST))
.build();sorting/
├─ c_dir/
│ └─ c_file
├─ d_dir/
│ ├─ d_b_dir/
│ │ └─ d_b_file
│ └─ d_a_file
├─ a_file
├─ b_file
├─ x_file
└─ y_file
You can choose to use default built-in emojis, or define your own emoji mapping.
Folders use the 📂 emoji, and files will have an emoji depending on their name or extension (when applicable).
Define your own emoji mappings with the EmojiMapping class!
// Example: Emojis.java
var prettyPrinter = FileTreePrettyPrinter.builder()
.customizeOptions(options -> options.withDefaultEmojis()) // or withEmojis(EmojiMapping) for custom mapping
.build();// Run Emojis.java example for the full list of emoji mappings
📂 emojis/
├─ 📦 file.zip
├─ 🐳 Dockerfile
├─ 🤵 Jenkinsfile
├─ ☕ file.java
├─ 📖 readme
├─ ⚙️ file.ini
├─ 📊 file.xlsx
├─ 📃 file.docx
├─ 📕 file.pdf
├─ 🎵 file.mp3
├─ 🖼️ file.jpeg
└─ 🎬 file.avi
You can set a fixed limit to the number of children displayed for each directory. Each directory and file that pass the filter (if set) counts for one.
// Example: ChildLimitStatic.java
var prettyPrinter = FileTreePrettyPrinter.builder()
.customizeOptions(options -> options.withChildLimit(3))
.build();child_limit_static/
├─ file_0_1
├─ folder_1/
│ ├─ file_1_1
│ ├─ file_1_2
│ ├─ file_1_3
│ └─ ...
├─ folder_2/
│ ├─ file_2_1
│ ├─ file_2_2
│ ├─ file_2_3
│ └─ ...
└─ ...
Or you can also set a limitation function, to dynamically choose the number of children displayed in each directory.
This avoids cluttering the result with known large folders (e.g. node_modules) while continuing to pretty-print other folders normally.
Use the ChildLimits class to help you build the limit function that fits your needs.
// Example: ChildLimitDynamic.java
var childLimit = ChildLimits.builder()
.setDefault(ChildLimits.UNLIMITED) // Unlimited children by default
.add(PathMatchers.hasName("node_modules"), 0) // Do NOT print any children in "node_modules" folder
.build();
var prettyPrinter = FileTreePrettyPrinter.builder()
.customizeOptions(options -> options.withChildLimit(childLimit))
.build();child_limit_dynamic/
├─ file_0_1
├─ folder_1/
│ ├─ file_1_1
│ ├─ file_1_2
│ ├─ file_1_3
│ ├─ file_1_4
│ └─ file_1_5
└─ node_modules/
└─ ...
You can extend each displayed path with additional information by providing a custom Function<Path, String>.
This is useful to annotate your tree with comments, display file sizes, or add domain-specific notes.
The function receives the current path and returns an optional string to append (empty string is authorized).
If the function returns null, nothing is added.
Use the LineExtensions class to help you build line extension functions.
// Example: LineExtension.java
var printedPath = Path.of("src/example/resources/line_extension");
Function<Path, String> lineExtension = LineExtensions.builder()
.add(PathMatchers.hasRelativePathMatchingGlob(printedPath, "src/main/java/api"), "\t\t\t// All API code: controllers, etc.")
.add(PathMatchers.hasRelativePathMatchingGlob(printedPath, "src/main/java/domain"), "\t\t\t// All domain code: value objects, etc.")
.add(PathMatchers.hasRelativePathMatchingGlob(printedPath, "src/main/java/infra"), "\t\t\t// All infra code: database, email service, etc.")
.add(PathMatchers.hasNameMatchingGlob("*.properties"), "\t// Config file")
.build();
var prettyPrinter = FileTreePrettyPrinter.builder()
.customizeOptions(options -> options.withLineExtension(lineExtension))
.build();line_extension/
└─ src/
└─ main/
├─ java/
│ ├─ api/ // All API code: controllers, etc.
│ │ └─ Controller.java
│ ├─ domain/ // All domain code: value objects, etc.
│ │ └─ ValueObject.java
│ └─ infra/ // All infra code: database, email service, etc.
│ └─ Repository.java
└─ resources/
└─ application.properties // Config file
Directory chains with a single child directory are fully expanded by default, but you can inline them into a single tree entry.
// Example: CompactDirectories.java
var prettyPrinter = FileTreePrettyPrinter.builder()
.customizeOptions(options -> options.withCompactDirectories(true))
.build();single_directory_child/
├─ file1
├─ file2
└─ this/is/single/directory/child/
├─ file1
├─ file2
└─ file3
You can customize the default max depth (default is 20).
// Example: MaxDepth.java
var prettyPrinter = FileTreePrettyPrinter.builder()
.customizeOptions(options -> options.withMaxDepth(3))
.build();max_depth/
└─ level1/
├─ file1#1
├─ file1#2
└─ level2/
├─ file2#1
├─ file2#2
└─ level3/
└─ ... (max depth reached)
Choose between different built-in tree formats, or create your own.
The default is UNICODE_BOX_DRAWING, supported by all terminals, but you can also switch to use CLASSIC_ASCII.
// Example: FileTreeFormat.java
var prettyPrinter = FileTreePrettyPrinter.builder()
.customizeOptions(options -> options.withTreeFormat(TreeFormats.CLASSIC_ASCII))
.build();tree_format/
|-- file_1
|-- file_2
`-- subFolder/
|-- subFile_1
`-- subFile_2
You can download the latest CLI release directly from https://github.com/ComputerDaddyGuy/JFileTreePrettyPrinter/releases/latest.
Choose the archive for your platform (Windows, Linux, or macOS), download it, and unzip it anywhere on your system.
Note
If desired, add the executable’s folder to your system’s PATH variable to run it from any directory.
To pretty-print a folder and its contents, simply run:
$ jfiletreeprettyprinter <folderName>Example:
$ jfiletreeprettyprinter jfiletreeprettyprinter-examples/src/main/resources/base/
base/
├─ businessPlan.pdf
├─ businessProject.pdf
├─ cars/
│ ├─ Ferrari.doc
│ └─ Porsche.doc
├─ diyIdeas.docx
├─ giftIdeas.txt
└─ images/
├─ funnyCat.gif
├─ holidays/
│ ├─ meAtTheBeach.jpeg
│ └─ meAtTheZoo.jpeg
└─ landscape.jpegTo get an overview of the CLI’s capabilities and available options:
$ jfiletreeprettyprinter --help
Usage: prettyprint [-dhV] [-o] [<target>]
Pretty-prints directory structure
[<target>] The path to pretty print
-d, --debug debug mode
-h, --help Show this help message and exit.
-o, --options the options file
-V, --version Print version information and exit.If the tree symbols appear as garbled characters (e.g., Ôöé instead of ├─), your console is likely not using UTF-8 encoding.
Set UTF-8 Encoding
# Windows
> chcp 65001
# Linux (bash, zsh)
$ export LANG=en_US.UTF-8
# macOS (bash, zsh)
$ export LC_CTYPE=UTF-8
The native CLI supports custom options through an external JSON or YAML file provided with the --options (or -o) argument.
This options file must comply with the CLI options file schema.
Tip
YAML is fully supported since it’s a superset of JSON, offering a more readable syntax.
$ jfiletreeprettyprinter myFolder --options myOptions.jsonIf no options file is explicitly provided as argument, the CLI automatically searches for one in the following order:
- Inside the target path:
A.prettyprintfile located within the directory being printed. - Beside the executable:
A.prettyprintfile in the same folder as the native executable. - In the user’s home directory:
A.prettyprintfile located in the user’s home folder. - Fallback:
If no configuration file is found, default options are used — equivalent toPrettyPrintOptions.createDefault().
Tip
Using a .prettyprint file allows each project or directory to define its own display style — no need to pass extra parameters each time.
Below is an example configuration file that demonstrates commonly used options. It should be mostly self-explanatory:
emojis: true
maxDepth: 2
compactDirectories: true
childLimit:
type: static
limit: 3
lineExtensions:
- extension: " // Main entry point"
matcher:
type: name
glob: "FileTreePrettyPrinter.java"
filter:
dir:
type: name
glob: "src/main/**"
file:
type: name
glob: "*.java"While the CLI offers broad configuration flexibility, not all features from the Java API are currently available.
Refer to the ⚙️ Options Compatibility Matrix at the top of this README for a complete overview.
Path Matcher Support
The external options file supports a limited subset of matchers for defining filtering and selection logic:
| Matcher type | Description | Example | Equivalent in PathMatchers Library API |
|---|---|---|---|
name |
Matches folder or file name using a glob pattern | *.java |
hasNameMatchingGlob(String glob) |
path |
Matches a relative path (from the printed root) using a glob pattern | src/main/java/** |
hasRelativePathMatchingGlob(Path ref, String glob) |
allOf, anyOf, noneOf |
Composite matchers combining multiple matchers | n/a | allOf(...), anyOf(...), noneOf(...) |
Note
The term glob refers to the pattern syntax supported by FileSystem.getPathMatcher(String).
Made with ❤️ by ComputerDaddyGuy