Add package and enclosing type fields to tag entries

This commit is contained in:
naofal.helal
2025-03-26 14:01:01 +03:00
parent c91343bf78
commit c67a91e22a
6 changed files with 172 additions and 42 deletions

View File

@@ -324,6 +324,7 @@ public class NoBuild {
fileManager.getJavaFileObjects(sourcePaths);
List<String> compilerOptions = new ArrayList<>();
compilerOptions.add("-Xlint:all");
compilerOptions.add("-d");
compilerOptions.add(classOutputPath);

View File

@@ -15,7 +15,7 @@ public class Jtags {
boolean absolutePaths = false;
boolean excludeNonPublic = false;
boolean excludeAnonymous = false;
boolean excludeStaticField = false;
List<Class<? extends TagField>> fields = new ArrayList<>();
}
public static void main(String[] args) {
@@ -40,11 +40,6 @@ public class Jtags {
options.absolutePaths = true;
break;
case "-no-static":
logger.config("Excluding 'file:' field in tags");
options.excludeStaticField = true;
break;
case "-no-non-public":
logger.config("Excluding non-public elements");
options.excludeNonPublic = true;
@@ -55,10 +50,47 @@ public class Jtags {
options.excludeAnonymous = true;
break;
case "-fields":
String fields =
switch (arguments.poll()) {
case null -> {
logger.severe("Expected argument after " + argument);
printUsage();
System.exit(1);
yield null;
}
case String s -> s;
};
options.fields =
fields
.chars()
.<Class<? extends TagField>>mapToObj(
it ->
(Class<? extends TagField>)
switch (it) {
case 's' -> TagField.StaticTag.class;
case 'p' -> TagField.Package.class;
case 't' -> TagField.EnclosingType.class;
default -> {
logger.severe("Unknown field: " + it);
printUsage();
System.exit(1);
yield null;
}
})
.toList();
logger.config(
() ->
"Including fields: "
+ String.join(
", ", options.fields.stream().map(it -> it.getSimpleName()).toList()));
break;
case "-lib":
logger.config("Third-party library mode");
options.absolutePaths = true;
options.excludeStaticField = true;
options.excludeNonPublic = true;
options.excludeAnonymous = true;
break;
@@ -83,6 +115,12 @@ public class Jtags {
}
}
if (options.sources.isEmpty()) {
logger.severe("No source files provided");
printUsage();
System.exit(1);
}
System.exit(run(options) ? 0 : 1);
}
@@ -96,16 +134,20 @@ public class Jtags {
static void printUsage() {
System.err.println(
"""
Usage: jtags [options] <sources...>
Options:
-o, -output <file> Write tags to specified <file>
-lib Treat sources as third-party libraries
(alias for -absolute -no-non-public -no-anonymous)
-absolute Use absolute paths for tag locations
-no-static Exclude the "file:" field from tags
-no-anonymous Exclude anonymous classes
-no-anonymous Exclude anonymous classes
-h, -help Show this message
""");
Usage: jtags [options] <sources...>
Options:
-o, -output <file> Write tags to specified <file>
-lib Treat sources as third-party libraries
(alias for -absolute -no-non-public -no-anonymous)
-no-anonymous Exclude anonymous classes
-no-non-public Exclude non-public elements
-absolute Use absolute paths for tag locations
-fields <fields> Fields to include in tag entries. Default: spt
Avaiblable fields are:
s static tag
p package
t enclosing type
-h, -help Show this message
""");
}
}

View File

@@ -2,12 +2,13 @@ package xyz.naofal.jtags;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.List;
public record Tag(TagKind kind, String name, Path location, String line, boolean isStatic)
public record Tag(TagKind kind, String name, Path location, String line, List<TagField> fields)
implements Comparable<Tag> {
public Tag(TagKind kind, String name, Path location, String line) {
this(kind, name, location, line, false);
this(kind, name, location, line, List.of());
}
@Override

View File

@@ -0,0 +1,9 @@
package xyz.naofal.jtags;
public sealed interface TagField {
public record StaticTag() implements TagField {}
public record Package(String p) implements TagField {}
public record EnclosingType(String type, TagKind kind) implements TagField {}
}

View File

@@ -1,6 +1,9 @@
package xyz.naofal.jtags;
import static xyz.naofal.jtags.JtagsLogger.logger;
import static xyz.naofal.jtags.TagField.EnclosingType;
import static xyz.naofal.jtags.TagField.Package;
import static xyz.naofal.jtags.TagField.StaticTag;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -60,8 +63,16 @@ public record TagsWriter(Options options) {
case ENUM_CONSTANT -> 'e';
case METHOD -> 'm';
});
if (!options().excludeStaticField && tag.isStatic()) {
writer.write("\tfile:");
for (TagField field : tag.fields()) {
writer.write('\t');
writer.write(
switch (field) {
case StaticTag() -> "file:";
case Package(var p) -> "package:" + p;
case EnclosingType(var t, var k) ->
k.name().toLowerCase() + ":" + (t.isEmpty() ? "(Anonymous)" : t);
});
}
writer.write('\n');

View File

@@ -9,7 +9,9 @@ import com.sun.source.tree.PackageTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePathScanner;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.PriorityQueue;
import javax.lang.model.element.Modifier;
import xyz.naofal.jtags.Jtags.Options;
@@ -46,10 +48,11 @@ public class TreeVisitor extends TreePathScanner<Void, TreeVisitorContext> {
@Override
public Void visitClass(ClassTree node, TreeVisitorContext p) {
if (options.excludeAnonymous && node.getSimpleName().isEmpty()) {
if (options.excludeNonPublic && !node.getModifiers().getFlags().contains(Modifier.PUBLIC)) {
return null;
}
if (options.excludeNonPublic && !node.getModifiers().getFlags().contains(Modifier.PUBLIC)) {
if (options.excludeAnonymous && node.getSimpleName().isEmpty()) {
return null;
}
@@ -57,23 +60,40 @@ public class TreeVisitor extends TreePathScanner<Void, TreeVisitorContext> {
return scan(node.getMembers(), p);
}
TagKind typeKind = getTypeKind(node);
List<TagField> fields = new ArrayList<>();
if (options.fields.contains(TagField.StaticTag.class)
&& node.getModifiers().getFlags().contains(Modifier.STATIC)) {
fields.add(new TagField.StaticTag());
}
if (options.fields.contains(TagField.Package.class)) {
for (var path : getCurrentPath().getParentPath()) {
if (path instanceof CompilationUnitTree compilationUnitTree) {
fields.add(
new TagField.Package(
Optional.ofNullable(compilationUnitTree.getPackageName())
.map(String::valueOf)
.orElse("")));
break;
}
}
}
if (options.fields.contains(TagField.EnclosingType.class)) {
for (var path : getCurrentPath().getParentPath()) {
if (path instanceof ClassTree classTree) {
fields.add(new TagField.EnclosingType(classTree.getSimpleName().toString(), typeKind));
break;
}
}
}
Tag tag =
new Tag(
switch (node.getKind()) {
case CLASS -> TagKind.CLASS;
case RECORD -> TagKind.RECORD;
case INTERFACE -> TagKind.INTERFACE;
case ENUM -> TagKind.ENUM;
case ANNOTATION_TYPE -> TagKind.ANNOTATION;
default -> {
logger.warning("Unknown class kind " + node.getKind());
yield TagKind.CLASS;
}
},
node.getSimpleName().toString(),
p.getLocation(),
p.getLine(node),
node.getModifiers().getFlags().contains(Modifier.STATIC));
typeKind, node.getSimpleName().toString(), p.getLocation(), p.getLine(node), fields);
logger.finer(() -> "Type: " + tag);
@@ -88,6 +108,24 @@ public class TreeVisitor extends TreePathScanner<Void, TreeVisitorContext> {
return null;
}
List<TagField> fields = new ArrayList<>();
if (options.fields.contains(TagField.StaticTag.class)
&& node.getModifiers().getFlags().contains(Modifier.STATIC)) {
fields.add(new TagField.StaticTag());
}
if (options.fields.contains(TagField.EnclosingType.class)) {
for (var path : getCurrentPath().getParentPath()) {
if (path instanceof ClassTree classTree) {
fields.add(
new TagField.EnclosingType(
classTree.getSimpleName().toString(), getTypeKind(classTree)));
break;
}
}
}
Tag tag =
new Tag(
switch (node.getKind()) {
@@ -104,7 +142,7 @@ public class TreeVisitor extends TreePathScanner<Void, TreeVisitorContext> {
.toString(),
p.getLocation(),
p.getLine(node),
node.getModifiers().getFlags().contains(Modifier.STATIC));
fields);
logger.finer(() -> "Method: " + tag);
@@ -125,9 +163,23 @@ public class TreeVisitor extends TreePathScanner<Void, TreeVisitorContext> {
return scan(node.getInitializer(), p);
}
TagKind enclosingTypeKind = getTypeKind(enclosingType);
List<TagField> fields = new ArrayList<>();
if (options.fields.contains(TagField.StaticTag.class)
&& node.getModifiers().getFlags().contains(Modifier.STATIC)) {
fields.add(new TagField.StaticTag());
}
if (options.fields.contains(TagField.EnclosingType.class)) {
fields.add(
new TagField.EnclosingType(enclosingType.getSimpleName().toString(), enclosingTypeKind));
}
Tag tag =
new Tag(
enclosingType.getKind() == Tree.Kind.ENUM
enclosingTypeKind == TagKind.ENUM
&& node.getModifiers()
.getFlags()
.containsAll(List.of(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL))
@@ -136,7 +188,7 @@ public class TreeVisitor extends TreePathScanner<Void, TreeVisitorContext> {
node.getName().toString(),
p.getLocation(),
p.getLine(node),
node.getModifiers().getFlags().contains(Modifier.STATIC));
fields);
logger.finer(() -> "Variable: " + tag);
@@ -144,4 +196,18 @@ public class TreeVisitor extends TreePathScanner<Void, TreeVisitorContext> {
return scan(node.getInitializer(), p);
}
private TagKind getTypeKind(ClassTree classTree) {
return switch (classTree.getKind()) {
case CLASS -> TagKind.CLASS;
case RECORD -> TagKind.RECORD;
case INTERFACE -> TagKind.INTERFACE;
case ENUM -> TagKind.ENUM;
case ANNOTATION_TYPE -> TagKind.ANNOTATION;
default -> {
logger.warning("Unknown class kind " + classTree.getKind());
yield TagKind.CLASS;
}
};
}
}