From 8484ce622313efc876ce7677cf7b8d0c88364d41 Mon Sep 17 00:00:00 2001 From: Samuel SCHNEGG Date: Fri, 26 Sep 2025 23:10:05 +0200 Subject: [PATCH 1/4] Get rid of String buffer --- .../renderer/DefaultTreeEntryRenderer.java | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/DefaultTreeEntryRenderer.java b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/DefaultTreeEntryRenderer.java index a9f23a2..8cfe1dd 100644 --- a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/DefaultTreeEntryRenderer.java +++ b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/DefaultTreeEntryRenderer.java @@ -26,60 +26,60 @@ public DefaultTreeEntryRenderer(RenderingOptions options) { @Override public String renderTree(TreeEntry entry) { - var depth = Depth.createNewEmpty(); - var buff = new StringBuilder(); - renderTree(entry, depth, buff); - return buff.toString(); + return renderTree(entry, Depth.createNewEmpty()); } - public void renderTree(TreeEntry entry, Depth depth, StringBuilder buff) { - switch (entry) { - case TreeEntry.DirectoryEntry dirEntry -> renderDirectory(buff, depth, dirEntry, List.of(dirEntry.getDir())); - case TreeEntry.FileEntry fileEntry -> renderFile(buff, depth, fileEntry); - case TreeEntry.SkippedChildrenEntry skippedChildrenEntry -> renderSkippedChildrenEntry(buff, depth, skippedChildrenEntry); - case TreeEntry.MaxDepthReachEntry maxDepthReachEntry -> renderMaxDepthReachEntry(buff, depth, maxDepthReachEntry); - } + private String renderTree(TreeEntry entry, Depth depth) { + return switch (entry) { + case TreeEntry.DirectoryEntry dirEntry -> renderDirectory(depth, dirEntry, List.of(dirEntry.getDir())); + case TreeEntry.FileEntry fileEntry -> renderFile(depth, fileEntry); + case TreeEntry.SkippedChildrenEntry skippedChildrenEntry -> renderSkippedChildrenEntry(depth, skippedChildrenEntry); + case TreeEntry.MaxDepthReachEntry maxDepthReachEntry -> renderMaxDepthReachEntry(depth, maxDepthReachEntry); + }; } - private void renderDirectory(StringBuilder buff, Depth depth, DirectoryEntry dirEntry, List compactPaths) { + private String renderDirectory(Depth depth, DirectoryEntry dirEntry, List compactPaths) { if (options.areCompactDirectoriesUsed() && dirEntry.getEntries().size() == 1 && dirEntry.getEntries().get(0) instanceof DirectoryEntry childDirEntry) { var newCompactPaths = new ArrayList<>(compactPaths); newCompactPaths.add(childDirEntry.getDir()); - renderDirectory(buff, depth, childDirEntry, newCompactPaths); - return; + return renderDirectory(depth, childDirEntry, newCompactPaths); } - buff.append(lineRenderer.renderDirectoryBegin(depth, dirEntry, compactPaths)); + var line = lineRenderer.renderDirectoryBegin(depth, dirEntry, compactPaths); var childIt = dirEntry.getEntries().iterator(); if (childIt.hasNext()) { - buff.append('\n'); + line += "\n"; } + var childLines = ""; + while (childIt.hasNext()) { var childEntry = childIt.next(); var childDepth = depth.append(childIt.hasNext() ? DepthSymbol.NON_LAST_FILE : DepthSymbol.LAST_FILE); - renderTree(childEntry, childDepth, buff); + childLines += renderTree(childEntry, childDepth); if (childIt.hasNext()) { - buff.append('\n'); + childLines += "\n"; } } + + return line + childLines; } - private void renderFile(StringBuilder buff, Depth depth, FileEntry fileEntry) { - buff.append(lineRenderer.renderFile(depth, fileEntry)); + private String renderFile(Depth depth, FileEntry fileEntry) { + return lineRenderer.renderFile(depth, fileEntry); } - private void renderSkippedChildrenEntry(StringBuilder buff, Depth depth, SkippedChildrenEntry skippedChildrenEntry) { - buff.append(lineRenderer.renderChildLimitReached(depth, skippedChildrenEntry)); + private String renderSkippedChildrenEntry(Depth depth, SkippedChildrenEntry skippedChildrenEntry) { + return lineRenderer.renderChildLimitReached(depth, skippedChildrenEntry); } - private void renderMaxDepthReachEntry(StringBuilder buff, Depth depth, MaxDepthReachEntry maxDepthReachEntry) { - buff.append(lineRenderer.renderMaxDepthReached(depth, maxDepthReachEntry)); + private String renderMaxDepthReachEntry(Depth depth, MaxDepthReachEntry maxDepthReachEntry) { + return lineRenderer.renderMaxDepthReached(depth, maxDepthReachEntry); } } From ebf91e6c9b54fa8a8d245e5a3d48996805238f8a Mon Sep 17 00:00:00 2001 From: Samuel SCHNEGG Date: Sat, 27 Sep 2025 00:33:05 +0200 Subject: [PATCH 2/4] Line extension --- CHANGELOG.md | 11 +- README.md | 45 +++++ ROADMAP.md | 18 +- .../example/LineExtension.java | 33 +++ .../src/main/java/api/Controller.java | 0 .../src/main/java/domain/ValueObject.java | 0 .../src/main/java/infra/Repository.java | 0 .../src/main/resources/application.properties | 0 .../PrettyPrintOptions.java | 30 ++- .../renderer/DefaultTreeEntryRenderer.java | 24 ++- .../renderer/RenderingOptions.java | 8 + .../renderer/depth/Depth.java | 4 + .../CompactDirectoriesTest.java | 24 ++- .../LineExtensionTest.java | 191 ++++++++++++++++++ .../jfiletreeprettyprinter/MaxDepthTest.java | 5 +- 15 files changed, 366 insertions(+), 27 deletions(-) create mode 100644 src/example/java/io/github/computerdaddyguy/jfiletreeprettyprinter/example/LineExtension.java create mode 100644 src/example/resources/line_extension/src/main/java/api/Controller.java create mode 100644 src/example/resources/line_extension/src/main/java/domain/ValueObject.java create mode 100644 src/example/resources/line_extension/src/main/java/infra/Repository.java create mode 100644 src/example/resources/line_extension/src/main/resources/application.properties create mode 100644 src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/LineExtensionTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c3c345..566d712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,15 +5,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +--- ## [0.0.4] - Unreleased +### Added +- Line extension: append any String after a path (comment, file details, etc.) + ### Changed - Filtering: moved to options +- Compact dirs: root dir is now never compacted ### Removed - Error handling: exceptions are now thrown instead of being handled by the renderer - +--- ## [0.0.3] - 2025-09-21 ### Added @@ -25,14 +30,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Sorting: option method `withFileSort` renamed in `sort` - Child limit: various renaming and refactor - +--- ## [0.0.2] - 2025-09-16 ### Added - Option: sorting files and directories - 39 new default files & extension mappings for emojis - +--- ## [0.0.1] - 2025-09-14 ### Added diff --git a/README.md b/README.md index 5408cbf..26aa5cf 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ - Limit displayed children (fixed value or dynamically) - Compact directory chains - Maximum depth +- Custom line extension (comment, file details, etc.) - Various styles for tree rendering > [!CAUTION] @@ -90,6 +91,7 @@ base/ * [Max depth](#max-depth) * [Sorting](#sorting) * [Filtering](#filtering) +* [Line extension](#line-extension) ## Tree format Choose between different tree formats. @@ -296,6 +298,49 @@ filtering/ └─ file_A.java ``` +## Line extension +You can extend each displayed path with additional information by providing a custom `Function`. +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. +If the function returns `null`, nothing is added. + +```java +// Example: LineExtension.java +Function lineExtension = path -> { + if (PathUtils.isDirectory(path) && PathUtils.hasName(path, "api")) { + return "\t\t\t// All API code: controllers, etc."; + } + if (PathUtils.isDirectory(path) && PathUtils.hasName(path, "domain")) { + return "\t\t\t// All domain code: value objects, etc."; + } + if (PathUtils.isDirectory(path) && PathUtils.hasName(path, "infra")) { + return "\t\t\t// All infra code: database, email service, etc."; + } + if (PathUtils.isFile(path) && PathUtils.hasName(path, "application.properties")) { + return "\t// Config file"; + } + return null; +}; +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 +``` + # Changelog See [CHANGELOG.md](CHANGELOG.md) for released versions. diff --git a/ROADMAP.md b/ROADMAP.md index 4a1be02..f7834b3 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -20,18 +20,14 @@ ## To do - [x] Use Github wiki to document options instead of readme - [x] Jacoco coverage report -- [ ] Option: Line extension (=additional text after the file name) -- [ ] Option: Filename decorator +- [x] Option: Line extension (=additional text after the file name) -## Backlog +## Backlog / To analyze / To implement if requested +- [ ] More `PathPredicates` functions! - [ ] Option: custom tree format - [ ] Option: custom emojis -- [ ] Option: color - [ ] Refactor unit tests (custom assert?) -- [ ] More `PathPredicates` functions! - -## Abandoned -These ideas will likely not been implemented because they do not align with JFileTreePrettyPrint vision: -- File attributes LineRenderer (size, author, createAt, etc.) -- Print optional legend for symlink/other file types symbols (at the end of the tree) -- Follow symlink option \ No newline at end of file +- [ ] Option: color +- [ ] Option: Filename decorator +- [ ] Option: Follow symlink +- [ ] Advanced line extension function (file size, author, timestamps, etc. ) diff --git a/src/example/java/io/github/computerdaddyguy/jfiletreeprettyprinter/example/LineExtension.java b/src/example/java/io/github/computerdaddyguy/jfiletreeprettyprinter/example/LineExtension.java new file mode 100644 index 0000000..99795d2 --- /dev/null +++ b/src/example/java/io/github/computerdaddyguy/jfiletreeprettyprinter/example/LineExtension.java @@ -0,0 +1,33 @@ +package io.github.computerdaddyguy.jfiletreeprettyprinter.example; + +import io.github.computerdaddyguy.jfiletreeprettyprinter.FileTreePrettyPrinter; +import io.github.computerdaddyguy.jfiletreeprettyprinter.PathUtils; +import java.nio.file.Path; +import java.util.function.Function; + +public class LineExtension { + + public static void main(String[] args) { + Function lineExtension = path -> { + if (PathUtils.isDirectory(path) && PathUtils.hasName(path, "api")) { + return "\t\t\t// All API code: controllers, etc."; + } + if (PathUtils.isDirectory(path) && PathUtils.hasName(path, "domain")) { + return "\t\t\t// All domain code: value objects, etc."; + } + if (PathUtils.isDirectory(path) && PathUtils.hasName(path, "infra")) { + return "\t\t\t// All infra code: database, email service, etc."; + } + if (PathUtils.isFile(path) && PathUtils.hasName(path, "application.properties")) { + return "\t// Config file"; + } + return null; + }; + var prettyPrinter = FileTreePrettyPrinter.builder() + .customizeOptions(options -> options.withLineExtension(lineExtension)) + .build(); + var tree = prettyPrinter.prettyPrint("src/example/resources/line_extension"); + System.out.println(tree); + } + +} diff --git a/src/example/resources/line_extension/src/main/java/api/Controller.java b/src/example/resources/line_extension/src/main/java/api/Controller.java new file mode 100644 index 0000000..e69de29 diff --git a/src/example/resources/line_extension/src/main/java/domain/ValueObject.java b/src/example/resources/line_extension/src/main/java/domain/ValueObject.java new file mode 100644 index 0000000..e69de29 diff --git a/src/example/resources/line_extension/src/main/java/infra/Repository.java b/src/example/resources/line_extension/src/main/java/infra/Repository.java new file mode 100644 index 0000000..e69de29 diff --git a/src/example/resources/line_extension/src/main/resources/application.properties b/src/example/resources/line_extension/src/main/resources/application.properties new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/PrettyPrintOptions.java b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/PrettyPrintOptions.java index 1b4bdba..809120b 100644 --- a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/PrettyPrintOptions.java +++ b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/PrettyPrintOptions.java @@ -5,6 +5,7 @@ import java.nio.file.Path; import java.util.Comparator; import java.util.Objects; +import java.util.function.Function; import java.util.function.Predicate; import java.util.function.ToIntFunction; import org.jspecify.annotations.NullMarked; @@ -313,9 +314,36 @@ public Predicate pathFilter() { * @param filter The filter, null to disable filtering */ - public PrettyPrintOptions filter(Predicate filter) { + public PrettyPrintOptions filter(@Nullable Predicate filter) { this.pathFilter = filter; return this; } + // ---------- Line extension ---------- + + @Nullable + private Function lineExtension; + + @Override + public Function getLineExtension() { + return lineExtension; + } + + /** + * Sets a custom line extension function that appends additional text to each + * printed line, allowing you to customize the display of files or directories. + *

+ * Typical use cases include adding comments, showing file sizes, or displaying metadata. + *

+ * The function receives the current {@link Path} displayed on the line + * and returns an optional string to be appended. + * If the function returns {@code null}, nothing is added. + * + * @param lineExtension the custom line extension function, or {@code null} to disable + */ + public PrettyPrintOptions withLineExtension(@Nullable Function lineExtension) { + this.lineExtension = lineExtension; + return this; + } + } diff --git a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/DefaultTreeEntryRenderer.java b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/DefaultTreeEntryRenderer.java index 8cfe1dd..cfed0f1 100644 --- a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/DefaultTreeEntryRenderer.java +++ b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/DefaultTreeEntryRenderer.java @@ -41,14 +41,20 @@ private String renderTree(TreeEntry entry, Depth depth) { private String renderDirectory(Depth depth, DirectoryEntry dirEntry, List compactPaths) { if (options.areCompactDirectoriesUsed() + && !depth.isRoot() && dirEntry.getEntries().size() == 1 && dirEntry.getEntries().get(0) instanceof DirectoryEntry childDirEntry) { - var newCompactPaths = new ArrayList<>(compactPaths); - newCompactPaths.add(childDirEntry.getDir()); - return renderDirectory(depth, childDirEntry, newCompactPaths); + + var extension = computeLineExtension(dirEntry.getDir()); + if (extension.isEmpty()) { + var newCompactPaths = new ArrayList<>(compactPaths); + newCompactPaths.add(childDirEntry.getDir()); + return renderDirectory(depth, childDirEntry, newCompactPaths); + } } var line = lineRenderer.renderDirectoryBegin(depth, dirEntry, compactPaths); + line += computeLineExtension(dirEntry.getDir()); var childIt = dirEntry.getEntries().iterator(); @@ -70,8 +76,18 @@ private String renderDirectory(Depth depth, DirectoryEntry dirEntry, List return line + childLines; } + private String computeLineExtension(Path path) { + if (options.getLineExtension() == null) { + return ""; + } + var extension = options.getLineExtension().apply(path); + return extension == null ? "" : extension; + } + private String renderFile(Depth depth, FileEntry fileEntry) { - return lineRenderer.renderFile(depth, fileEntry); + var line = lineRenderer.renderFile(depth, fileEntry); + line += computeLineExtension(fileEntry.getFile()); + return line; } private String renderSkippedChildrenEntry(Depth depth, SkippedChildrenEntry skippedChildrenEntry) { diff --git a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/RenderingOptions.java b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/RenderingOptions.java index 77d1368..54c02bc 100644 --- a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/RenderingOptions.java +++ b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/RenderingOptions.java @@ -1,6 +1,8 @@ package io.github.computerdaddyguy.jfiletreeprettyprinter.renderer; import io.github.computerdaddyguy.jfiletreeprettyprinter.PrettyPrintOptions.TreeFormat; +import java.nio.file.Path; +import java.util.function.Function; import org.jspecify.annotations.NullMarked; @NullMarked @@ -24,4 +26,10 @@ public interface RenderingOptions { */ TreeFormat getTreeFormat(); + /** + * The line extension function. + * @return + */ + Function getLineExtension(); + } diff --git a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/depth/Depth.java b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/depth/Depth.java index a05f59a..acc004a 100644 --- a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/depth/Depth.java +++ b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/depth/Depth.java @@ -26,6 +26,10 @@ public int getSize() { return symbols.size(); } + public boolean isRoot() { + return getSize() == 0; + } + public Depth append(DepthSymbol symbol) { var newList = new ArrayList(symbols); newList.add(symbol); diff --git a/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/CompactDirectoriesTest.java b/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/CompactDirectoriesTest.java index 7702fc2..4440790 100644 --- a/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/CompactDirectoriesTest.java +++ b/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/CompactDirectoriesTest.java @@ -44,7 +44,8 @@ void dirWithOneDir() { .getPath(); var result = printer.prettyPrint(path); var expected = """ - targetPath/dir1/"""; + targetPath/ + └─ dir1/"""; assertThat(result).isEqualTo(expected); } @@ -63,18 +64,29 @@ void dirWithOneDirAndOneFile() { } @Test - void dirWithTwoDirs() { + void complex() { + // @formatter:off var path = FileStructureCreator .forTargetPath(root) - .createDirectory("dir1") - .createDirectory("dir2") + .createAndEnterDirectory("dirA") + .createAndEnterDirectory("dirB") + .createAndEnterDirectory("dirC") + .createFiles("file", 3) + .up() + .up() .up() + .createDirectory("dirX") .getPath(); + // @formatter:on + var result = printer.prettyPrint(path); var expected = """ targetPath/ - ├─ dir1/ - └─ dir2/"""; + ├─ dirA/dirB/dirC/ + │ ├─ file1 + │ ├─ file2 + │ └─ file3 + └─ dirX/"""; assertThat(result).isEqualTo(expected); } diff --git a/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/LineExtensionTest.java b/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/LineExtensionTest.java new file mode 100644 index 0000000..99f7500 --- /dev/null +++ b/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/LineExtensionTest.java @@ -0,0 +1,191 @@ +package io.github.computerdaddyguy.jfiletreeprettyprinter; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.computerdaddyguy.jfiletreeprettyprinter.util.FileStructureCreator; +import io.github.computerdaddyguy.jfiletreeprettyprinter.util.FileStructures; +import java.nio.file.Path; +import java.util.function.Function; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class LineExtensionTest { + + @TempDir + private Path root; + + @Test + void emptyDir() { + var printer = FileTreePrettyPrinter.builder() + .customizeOptions(options -> options.withLineExtension(p -> " /* YES ! */")) + .build(); + + var path = FileStructures.emptyDirectory(root); + var result = printer.prettyPrint(path); + var expected = "targetPath/ /* YES ! */"; + assertThat(result).isEqualTo(expected); + } + + @Test + void example_dir_match() { + + Function lineExtension = path -> { + if (PathUtils.isDirectory(path) && PathUtils.hasName(path, "api")) { + return "\t\t\t// All API code: controllers, etc."; + } + if (PathUtils.isDirectory(path) && PathUtils.hasName(path, "domain")) { + return "\t\t\t// All domain code: value objects, etc."; + } + if (PathUtils.isDirectory(path) && PathUtils.hasName(path, "infra")) { + return "\t\t\t// All infra code: database, email service, etc."; + } + if (PathUtils.isFile(path) && PathUtils.hasName(path, "application.properties")) { + return "\t// Config file"; + } + return null; + }; + + var printer = FileTreePrettyPrinter.builder() + .customizeOptions(options -> options.withLineExtension(lineExtension)) + .build(); + + var result = printer.prettyPrint("src/example/resources/line_extension"); + var expected = """ + 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"""; + assertThat(result).isEqualTo(expected); + } + + @Test + void compact_dir_first_dir() { + + // @formatter:off + var path = FileStructureCreator + .forTargetPath(root) + .createAndEnterDirectory("dirA") + .createAndEnterDirectory("dirB") + .createAndEnterDirectory("dirC") + .createFiles("file", 3) + .up() + .up() + .up() + .createDirectory("dirX") + .getPath(); + // @formatter:on + + Function lineExtension = p -> { + if (PathUtils.hasName(p, "dirA")) { + return " // 1"; + } + return null; + }; + + var printer = FileTreePrettyPrinter.builder() + .customizeOptions(options -> options.withLineExtension(lineExtension)) + .customizeOptions(options -> options.withCompactDirectories(true)) + .build(); + + var result = printer.prettyPrint(path); + var expected = """ + targetPath/ + ├─ dirA/ // 1 + │ └─ dirB/dirC/ + │ ├─ file1 + │ ├─ file2 + │ └─ file3 + └─ dirX/"""; + assertThat(result).isEqualTo(expected); + } + + @Test + void compact_dir_middle_dir() { + + // @formatter:off + var path = FileStructureCreator + .forTargetPath(root) + .createAndEnterDirectory("dirA") + .createAndEnterDirectory("dirB") + .createAndEnterDirectory("dirC") + .createFiles("file", 3) + .up() + .up() + .up() + .createDirectory("dirX") + .getPath(); + // @formatter:on + + Function lineExtension = p -> { + if (PathUtils.hasName(p, "dirB")) { + return " // 2"; + } + return null; + }; + + var printer = FileTreePrettyPrinter.builder() + .customizeOptions(options -> options.withLineExtension(lineExtension)) + .customizeOptions(options -> options.withCompactDirectories(true)) + .build(); + + var result = printer.prettyPrint(path); + var expected = """ + targetPath/ + ├─ dirA/dirB/ // 2 + │ └─ dirC/ + │ ├─ file1 + │ ├─ file2 + │ └─ file3 + └─ dirX/"""; + assertThat(result).isEqualTo(expected); + } + + @Test + void compact_dir_last_dir() { + + // @formatter:off + var path = FileStructureCreator + .forTargetPath(root) + .createAndEnterDirectory("dirA") + .createAndEnterDirectory("dirB") + .createAndEnterDirectory("dirC") + .createFiles("file", 3) + .up() + .up() + .up() + .createDirectory("dirX") + .getPath(); + // @formatter:on + + Function lineExtension = p -> { + if (PathUtils.hasName(p, "dirC")) { + return " // 3"; + } + return null; + }; + + var printer = FileTreePrettyPrinter.builder() + .customizeOptions(options -> options.withLineExtension(lineExtension)) + .customizeOptions(options -> options.withCompactDirectories(true)) + .build(); + + var result = printer.prettyPrint(path); + var expected = """ + targetPath/ + ├─ dirA/dirB/dirC/ // 3 + │ ├─ file1 + │ ├─ file2 + │ └─ file3 + └─ dirX/"""; + assertThat(result).isEqualTo(expected); + } + +} diff --git a/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/MaxDepthTest.java b/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/MaxDepthTest.java index 8091461..31aa450 100644 --- a/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/MaxDepthTest.java +++ b/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/MaxDepthTest.java @@ -60,8 +60,9 @@ void withCompactDirectories() { var result = printer.prettyPrint(path); var expected = """ - targetPath/level1/level2/ - └─ ... (max depth reached)"""; + targetPath/ + └─ level1/level2/ + └─ ... (max depth reached)"""; assertThat(result).isEqualTo(expected); } From 7078f6d51f6f741a8731cffb3cbb43c127ff7081 Mon Sep 17 00:00:00 2001 From: Samuel SCHNEGG Date: Sat, 27 Sep 2025 00:41:11 +0200 Subject: [PATCH 3/4] Fix sonar --- .../jfiletreeprettyprinter/PrettyPrintOptions.java | 1 + .../renderer/DefaultTreeEntryRenderer.java | 8 ++++---- .../jfiletreeprettyprinter/renderer/LineRenderer.java | 5 ----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/PrettyPrintOptions.java b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/PrettyPrintOptions.java index 809120b..1850ca5 100644 --- a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/PrettyPrintOptions.java +++ b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/PrettyPrintOptions.java @@ -325,6 +325,7 @@ public PrettyPrintOptions filter(@Nullable Predicate filter) { private Function lineExtension; @Override + @Nullable public Function getLineExtension() { return lineExtension; } diff --git a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/DefaultTreeEntryRenderer.java b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/DefaultTreeEntryRenderer.java index cfed0f1..a51eed9 100644 --- a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/DefaultTreeEntryRenderer.java +++ b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/DefaultTreeEntryRenderer.java @@ -62,18 +62,18 @@ private String renderDirectory(Depth depth, DirectoryEntry dirEntry, List line += "\n"; } - var childLines = ""; + var childLines = new StringBuilder(); while (childIt.hasNext()) { var childEntry = childIt.next(); var childDepth = depth.append(childIt.hasNext() ? DepthSymbol.NON_LAST_FILE : DepthSymbol.LAST_FILE); - childLines += renderTree(childEntry, childDepth); + childLines.append(renderTree(childEntry, childDepth)); if (childIt.hasNext()) { - childLines += "\n"; + childLines.append('\n'); } } - return line + childLines; + return line + childLines.toString(); } private String computeLineExtension(Path path) { diff --git a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/LineRenderer.java b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/LineRenderer.java index 1b105fc..f59258f 100644 --- a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/LineRenderer.java +++ b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/LineRenderer.java @@ -10,21 +10,16 @@ import java.nio.file.Path; import java.util.List; import org.jspecify.annotations.NullMarked; -import org.jspecify.annotations.Nullable; @NullMarked interface LineRenderer { - @Nullable String renderDirectoryBegin(Depth depth, DirectoryEntry dirEntry, List dirs); - @Nullable String renderFile(Depth depth, FileEntry fileEntry); - @Nullable String renderChildLimitReached(Depth depth, SkippedChildrenEntry skippedChildrenEntry); - @Nullable String renderMaxDepthReached(Depth depth, MaxDepthReachEntry maxDepthReachEntry); /** From 98677bc10a76601a12b9e3854600acca9d721f4c Mon Sep 17 00:00:00 2001 From: Samuel SCHNEGG Date: Sat, 27 Sep 2025 00:45:31 +0200 Subject: [PATCH 4/4] Fix sonar --- .../renderer/RenderingOptions.java | 2 + .../LineExtensionTest.java | 90 ++++++------------- 2 files changed, 31 insertions(+), 61 deletions(-) diff --git a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/RenderingOptions.java b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/RenderingOptions.java index 54c02bc..9fc63a4 100644 --- a/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/RenderingOptions.java +++ b/src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/renderer/RenderingOptions.java @@ -4,6 +4,7 @@ import java.nio.file.Path; import java.util.function.Function; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; @NullMarked public interface RenderingOptions { @@ -30,6 +31,7 @@ public interface RenderingOptions { * The line extension function. * @return */ + @Nullable Function getLineExtension(); } diff --git a/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/LineExtensionTest.java b/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/LineExtensionTest.java index 99f7500..029ae29 100644 --- a/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/LineExtensionTest.java +++ b/src/test/java/io/github/computerdaddyguy/jfiletreeprettyprinter/LineExtensionTest.java @@ -69,20 +69,6 @@ void example_dir_match() { @Test void compact_dir_first_dir() { - // @formatter:off - var path = FileStructureCreator - .forTargetPath(root) - .createAndEnterDirectory("dirA") - .createAndEnterDirectory("dirB") - .createAndEnterDirectory("dirC") - .createFiles("file", 3) - .up() - .up() - .up() - .createDirectory("dirX") - .getPath(); - // @formatter:on - Function lineExtension = p -> { if (PathUtils.hasName(p, "dirA")) { return " // 1"; @@ -90,12 +76,6 @@ void compact_dir_first_dir() { return null; }; - var printer = FileTreePrettyPrinter.builder() - .customizeOptions(options -> options.withLineExtension(lineExtension)) - .customizeOptions(options -> options.withCompactDirectories(true)) - .build(); - - var result = printer.prettyPrint(path); var expected = """ targetPath/ ├─ dirA/ // 1 @@ -104,26 +84,13 @@ void compact_dir_first_dir() { │ ├─ file2 │ └─ file3 └─ dirX/"""; - assertThat(result).isEqualTo(expected); + + compact_dir(lineExtension, expected); } @Test void compact_dir_middle_dir() { - // @formatter:off - var path = FileStructureCreator - .forTargetPath(root) - .createAndEnterDirectory("dirA") - .createAndEnterDirectory("dirB") - .createAndEnterDirectory("dirC") - .createFiles("file", 3) - .up() - .up() - .up() - .createDirectory("dirX") - .getPath(); - // @formatter:on - Function lineExtension = p -> { if (PathUtils.hasName(p, "dirB")) { return " // 2"; @@ -131,12 +98,6 @@ void compact_dir_middle_dir() { return null; }; - var printer = FileTreePrettyPrinter.builder() - .customizeOptions(options -> options.withLineExtension(lineExtension)) - .customizeOptions(options -> options.withCompactDirectories(true)) - .build(); - - var result = printer.prettyPrint(path); var expected = """ targetPath/ ├─ dirA/dirB/ // 2 @@ -145,46 +106,53 @@ void compact_dir_middle_dir() { │ ├─ file2 │ └─ file3 └─ dirX/"""; - assertThat(result).isEqualTo(expected); + + compact_dir(lineExtension, expected); } @Test void compact_dir_last_dir() { + Function lineExtension = p -> { + if (PathUtils.hasName(p, "dirC")) { + return " // 3"; + } + return null; + }; + + var expected = """ + targetPath/ + ├─ dirA/dirB/dirC/ // 3 + │ ├─ file1 + │ ├─ file2 + │ └─ file3 + └─ dirX/"""; + + compact_dir(lineExtension, expected); + } + + private void compact_dir(Function lineExtension, String expected) { + // @formatter:off var path = FileStructureCreator .forTargetPath(root) .createAndEnterDirectory("dirA") - .createAndEnterDirectory("dirB") - .createAndEnterDirectory("dirC") - .createFiles("file", 3) - .up() - .up() + .createAndEnterDirectory("dirB") + .createAndEnterDirectory("dirC") + .createFiles("file", 3) + .up() + .up() .up() .createDirectory("dirX") .getPath(); // @formatter:on - Function lineExtension = p -> { - if (PathUtils.hasName(p, "dirC")) { - return " // 3"; - } - return null; - }; - var printer = FileTreePrettyPrinter.builder() .customizeOptions(options -> options.withLineExtension(lineExtension)) .customizeOptions(options -> options.withCompactDirectories(true)) .build(); var result = printer.prettyPrint(path); - var expected = """ - targetPath/ - ├─ dirA/dirB/dirC/ // 3 - │ ├─ file1 - │ ├─ file2 - │ └─ file3 - └─ dirX/"""; assertThat(result).isEqualTo(expected); }