Skip to content

Commit 28929ce

Browse files
Refactor
- [x] Use JSpecify annotations - [x] Regroup all formatting work under single "LineFormatter" - [x] Builder with options (depth symbols, emoji, child limit)
1 parent efb63bf commit 28929ce

31 files changed

+588
-434
lines changed

ROADMAP.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
# Roadmap
22

33
## Next version
4-
[x] Use JSpecify annotations
5-
[ ] Builder with options (depth symbols, emoji, colors)
6-
[ ] Max depth option
7-
[ ] Unify dir-in-a-row into a single entry option
8-
[ } Regroup all formatting work under single "LineFormatter"
9-
[ ] Better "isLast" detection algorithm
10-
[ ] Unit tests, using @TempDir
4+
- [x] Use JSpecify annotations
5+
- [x] Regroup all formatting work under single "LineFormatter"
6+
- [x] Builder with options (depth symbols, emoji, child limit)
7+
- [ ] Max depth options
8+
- [ ] Better "isLast" detection algorithm
9+
- [ ] Unit tests, using @TempDir
10+
- [ ] Unify dir-in-a-row into a single entry option
1111

1212
## Other ideas
13-
[ ] Filtering
14-
[ ] Alternative implementation (using DirectoryStream instead of Files.walkFileTree)
13+
- [ ] Filtering
14+
- [ ] File attributes LineRenderer
15+
- [ ] Color options
16+
- [ ] Follow symlink option
17+
- [ ] Alternative implementation option, just for fun (using DirectoryStream instead of Files.walkFileTree)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter;
2+
3+
import java.nio.file.Path;
4+
import org.jspecify.annotations.NullMarked;
5+
6+
/**
7+
* Pretty prints a file tree, and returns the result as a String.
8+
*
9+
* @implNote Instances of this interface are not thread safe.
10+
*/
11+
@NullMarked
12+
public interface FileTreePrettyPrinter {
13+
14+
/**
15+
* Pretty prints the given path.
16+
*
17+
* @param path A directory or a file.
18+
*/
19+
String prettyPrint(Path path);
20+
21+
/**
22+
* Pretty prints the given path.
23+
*
24+
* @param path A directory or a file.
25+
*/
26+
default String prettyPrint(String path) {
27+
return prettyPrint(Path.of(path));
28+
}
29+
30+
/**
31+
* Get the options of this pretty printer.
32+
*/
33+
PrettyPrintOptions getOptions();
34+
35+
/**
36+
* Create a pretty printer with default options.
37+
*
38+
* @see PrettyPrintOptions#createDefault()
39+
*/
40+
static FileTreePrettyPrinter createDefault() {
41+
return builder().build();
42+
}
43+
44+
/**
45+
* Customize creation of a pretty printer through a builder.
46+
*
47+
* @see PrettyPrintOptions#createDefault()
48+
*/
49+
static FileTreePrettyPrinterBuilder builder() {
50+
return new FileTreePrettyPrinterBuilder();
51+
}
52+
53+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter;
2+
3+
import io.github.computerdaddyguy.jfiletreeprettyprinter.visitor.VisitingFileTreePrettyPrinter;
4+
import java.util.Objects;
5+
import java.util.function.Function;
6+
import org.jspecify.annotations.NullMarked;
7+
8+
@NullMarked
9+
public class FileTreePrettyPrinterBuilder {
10+
11+
private PrettyPrintOptions options = PrettyPrintOptions.createDefault();
12+
13+
public FileTreePrettyPrinter build() {
14+
return new VisitingFileTreePrettyPrinter(options);
15+
}
16+
17+
public FileTreePrettyPrinterBuilder withOptions(PrettyPrintOptions options) {
18+
this.options = Objects.requireNonNull(options, "options is null");
19+
return this;
20+
}
21+
22+
public FileTreePrettyPrinterBuilder customizeOptions(Function<PrettyPrintOptions, PrettyPrintOptions> optionsCustomizer) {
23+
Objects.requireNonNull(optionsCustomizer, "optionsCustomizer is null");
24+
var newOptions = optionsCustomizer.apply(this.options);
25+
this.options = Objects.requireNonNull(newOptions, "new options after customization is null");
26+
return this;
27+
}
28+
29+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter;
2+
3+
import io.github.computerdaddyguy.jfiletreeprettyprinter.visitor.RenderingOptions;
4+
import io.github.computerdaddyguy.jfiletreeprettyprinter.visitor.VisitingOptions;
5+
import io.github.computerdaddyguy.jfiletreeprettyprinter.visitor.RenderingOptions.DepthFormat;
6+
import java.nio.file.Path;
7+
import java.util.Objects;
8+
import java.util.function.Function;
9+
import org.jspecify.annotations.NullMarked;
10+
11+
@NullMarked
12+
public class PrettyPrintOptions implements VisitingOptions, RenderingOptions {
13+
14+
private Function<Path, Integer> childrenLimitFunction = p -> -1;
15+
16+
private DepthFormat depthFormat = DepthFormat.UNICODE_BOX_DRAWING;
17+
private boolean useEmojis = false;
18+
19+
public PrettyPrintOptions() {
20+
super();
21+
}
22+
23+
/**
24+
* Create new default options that can be customized using various {@code withXXX} methods.
25+
*/
26+
public static PrettyPrintOptions createDefault() {
27+
return new PrettyPrintOptions();
28+
}
29+
30+
// ----------------------------------------------
31+
32+
@Override
33+
public Function<Path, Integer> getChildrenLimitFunction() {
34+
return childrenLimitFunction;
35+
}
36+
37+
/**
38+
* Set a fixed limit to the number of visited children, per directory.
39+
* Default is no limit.
40+
*
41+
* @param childrenLimit Limit of visited children per directory, negative value means no limit.
42+
*/
43+
public PrettyPrintOptions withChildrenLimit(int childrenLimit) {
44+
return withChildrenLimitFunction(p -> childrenLimit);
45+
}
46+
47+
/**
48+
* Set a function that dynamically limits the number of visited children, depending on the parent directory.
49+
* Useful to avoid listing known-ahead huge directories (e.g. node's {@code node_modules}).
50+
* Default is no limit.
51+
*
52+
* @param childrenLimitFunction The dynamic limitation function, cannot be <code>null</code>. A negative computed value means no limit.
53+
*/
54+
public PrettyPrintOptions withChildrenLimitFunction(Function<Path, Integer> childrenLimitFunction) {
55+
this.childrenLimitFunction = Objects.requireNonNull(childrenLimitFunction, "childrenLimitFunction is null");
56+
return this;
57+
}
58+
59+
// ----------------------------------------------
60+
61+
@Override
62+
public DepthFormat depthFormat() {
63+
return depthFormat;
64+
}
65+
66+
/**
67+
* Sets the depth rendering format.
68+
* Default is {@link DepthFormat#UNICODE_BOX_DRAWING}.
69+
*
70+
* @param depthFormat The format to use, cannot be <code>null</code>.
71+
*/
72+
public PrettyPrintOptions withDepthFormat(DepthFormat depthFormat) {
73+
this.depthFormat = Objects.requireNonNull(depthFormat, "depthFormat is null");
74+
return this;
75+
}
76+
77+
// ----------------------------------------------
78+
79+
@Override
80+
public boolean useEmojis() {
81+
return useEmojis;
82+
}
83+
84+
/**
85+
* Whether or not use emojis in directory/filename rendering. Not all terminals supports emojis.
86+
* Default is {@code false}.
87+
*
88+
* @param useEmojis {@code true} to use emojis, {@code false} otherwise.
89+
*/
90+
public PrettyPrintOptions withEmojis(boolean useEmojis) {
91+
this.useEmojis = useEmojis;
92+
return this;
93+
}
94+
95+
}

src/main/java/io/github/computerdaddyguy/jfiletreeprinter/core/visitor/limit/ChildVisitCounter.java renamed to src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/visitor/ChildVisitCounter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.computerdaddyguy.jfiletreeprinter.core.visitor.limit;
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.visitor;
22

33
import java.io.IOException;
44
import java.io.UncheckedIOException;
@@ -12,7 +12,7 @@
1212
import org.jspecify.annotations.NullMarked;
1313

1414
@NullMarked
15-
public class ChildVisitCounter {
15+
class ChildVisitCounter {
1616

1717
private final Function<Path, Integer> childrenLimitFunction;
1818
private final List<ChildVisitCounterRecord> records;
@@ -52,7 +52,7 @@ public void exitCurrentDirectory() {
5252
records.removeLast();
5353
}
5454

55-
public Set<Path> notVisitedInCurrentDirCount() {
55+
public Set<Path> notVisitedInCurrentDir() {
5656
return records.isEmpty() ? Set.of() : records.getLast().notVisited();
5757
}
5858

src/main/java/io/github/computerdaddyguy/jfiletreeprinter/core/visitor/DefaultFileTreePrettyPrintVisitor.java renamed to src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/visitor/DefaultFileTreePrettyPrintVisitor.java

Lines changed: 17 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
package io.github.computerdaddyguy.jfiletreeprinter.core.visitor;
2-
3-
import io.github.computerdaddyguy.jfiletreeprinter.core.visitor.depth.Depth;
4-
import io.github.computerdaddyguy.jfiletreeprinter.core.visitor.depth.DepthFormatter;
5-
import io.github.computerdaddyguy.jfiletreeprinter.core.visitor.depth.DepthSymbol;
6-
import io.github.computerdaddyguy.jfiletreeprinter.core.visitor.filename.FileNameFormatter;
7-
import io.github.computerdaddyguy.jfiletreeprinter.core.visitor.limit.ChildVisitCounter;
8-
import io.github.computerdaddyguy.jfiletreeprinter.core.visitor.limit.ChildrenLimitConfig;
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.visitor;
2+
3+
import io.github.computerdaddyguy.jfiletreeprettyprinter.PrettyPrintOptions;
4+
import io.github.computerdaddyguy.jfiletreeprettyprinter.visitor.renderer.LineRenderer;
95
import java.io.File;
106
import java.io.IOException;
117
import java.nio.file.FileVisitResult;
@@ -17,47 +13,34 @@
1713
@NullMarked
1814
class DefaultFileTreePrettyPrintVisitor implements FileTreePrettyPrintVisitor {
1915

20-
private final DepthFormatter depthFormatter;
21-
private final ChildrenLimitConfig childrenLimitConfig;
22-
private final FileNameFormatter fileNameFormatter;
16+
private final LineRenderer lineRenderer;
2317

2418
private StringBuilder buff;
2519
private Depth depth;
2620
private ChildVisitCounter counter;
2721

28-
public DefaultFileTreePrettyPrintVisitor(
29-
DepthFormatter depthFormatter,
30-
ChildrenLimitConfig childrenLimitConfig, FileNameFormatter fileNameFormatter
31-
) {
22+
public DefaultFileTreePrettyPrintVisitor(PrettyPrintOptions options, LineRenderer lineRenderer) {
3223
super();
33-
this.depthFormatter = depthFormatter;
34-
this.childrenLimitConfig = childrenLimitConfig;
35-
this.fileNameFormatter = fileNameFormatter;
24+
this.lineRenderer = lineRenderer;
3625

37-
reset();
26+
this.buff = new StringBuilder();
27+
this.depth = new Depth();
28+
this.counter = new ChildVisitCounter(options.getChildrenLimitFunction());
3829
}
3930

4031
@Override
4132
public String getResult() {
4233
return buff.toString();
4334
}
4435

45-
@Override
46-
public void reset() {
47-
this.buff = new StringBuilder();
48-
this.depth = new Depth();
49-
this.counter = new ChildVisitCounter(childrenLimitConfig.getChildrenLimitFunction());
50-
}
51-
5236
@Override
5337
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
5438
// DEPTH
5539
updateDepth(dir);
56-
_appendBuff_depth();
57-
depth = depth.append(DepthSymbol.NON_LAST_FILE); // assume not last until proven otherwise
5840

5941
// FILE
60-
_appendBuff_path(fileNameFormatter.formatDirectoryBegin(dir, attrs));
42+
appendLine(lineRenderer.renderDirectoryBegin(depth, dir, attrs));
43+
depth = depth.append(DepthSymbol.NON_LAST_FILE); // assume not last until proven otherwise
6144

6245
// COUNTER
6346
counter.registerChildVisitInCurrentDir(dir);
@@ -76,10 +59,9 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
7659

7760
// DEPTH
7861
updateDepth(file);
79-
_appendBuff_depth();
8062

8163
// FILE
82-
_appendBuff_path(fileNameFormatter.formatFile(file, attrs));
64+
appendLine(lineRenderer.renderFile(depth, file, attrs));
8365

8466
return FileVisitResult.CONTINUE;
8567
}
@@ -88,11 +70,10 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
8870
public FileVisitResult visitFileFailed(Path file, @Nullable IOException exc) throws IOException {
8971
// DEPTH
9072
updateDepth(file);
91-
_appendBuff_depth();
9273

9374
// FILE
9475
if (exc != null) {
95-
_appendBuff_path(fileNameFormatter.formatFileException(file, exc));
76+
appendLine(lineRenderer.renderFileException(depth, file, exc));
9677
}
9778

9879
return FileVisitResult.CONTINUE;
@@ -102,7 +83,7 @@ public FileVisitResult visitFileFailed(Path file, @Nullable IOException exc) thr
10283
public FileVisitResult postVisitDirectory(Path dir, @Nullable IOException exc) throws IOException {
10384

10485
if (exc != null) {
105-
_appendBuff_path(fileNameFormatter.formatDirectoryException(dir, exc));
86+
appendLine(lineRenderer.renderDirectoryException(depth, dir, exc));
10687
}
10788

10889
// DEPTH
@@ -112,10 +93,7 @@ public FileVisitResult postVisitDirectory(Path dir, @Nullable IOException exc) t
11293
var limitReached = counter.exceedsCurrentLimit();
11394
if (limitReached) {
11495
depth = depth.append(DepthSymbol.LAST_FILE);
115-
_appendBuff_depth();
116-
_appendBuff_path(
117-
childrenLimitConfig.getFormatter().formatLimitReached(counter.notVisitedInCurrentDirCount())
118-
);
96+
appendLine(lineRenderer.renderLimitReached(depth, counter.notVisitedInCurrentDir()));
11997
depth = depth.pop();
12098
}
12199
counter.exitCurrentDirectory();
@@ -142,11 +120,7 @@ private boolean isLastChild(Path path) {
142120
return siblings != null && siblings[siblings.length - 1].toPath().equals(path);
143121
}
144122

145-
private void _appendBuff_depth() {
146-
buff.append(depthFormatter.format(depth));
147-
}
148-
149-
private void _appendBuff_path(@Nullable String str) {
123+
private void appendLine(@Nullable String str) {
150124
if (str != null) {
151125
buff.append(str).append('\n');
152126
}

src/main/java/io/github/computerdaddyguy/jfiletreeprinter/core/visitor/depth/Depth.java renamed to src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/visitor/Depth.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.computerdaddyguy.jfiletreeprinter.core.visitor.depth;
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.visitor;
22

33
import java.util.ArrayList;
44
import java.util.List;

src/main/java/io/github/computerdaddyguy/jfiletreeprinter/core/visitor/depth/DepthSymbol.java renamed to src/main/java/io/github/computerdaddyguy/jfiletreeprettyprinter/visitor/DepthSymbol.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.computerdaddyguy.jfiletreeprinter.core.visitor.depth;
1+
package io.github.computerdaddyguy.jfiletreeprettyprinter.visitor;
22

33
import org.jspecify.annotations.NullMarked;
44

0 commit comments

Comments
 (0)