diff options
-rw-r--r-- | build.gradle | 12 | ||||
-rw-r--r-- | javadoc/build.gradle | 6 | ||||
-rw-r--r-- | javadoc/src/main/java/io/github/cottonmc/cotton/gui/jd/PropertyTaglet.java | 174 | ||||
-rw-r--r-- | settings.gradle | 1 | ||||
-rw-r--r-- | src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java | 3 | ||||
-rw-r--r-- | src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java | 5 |
6 files changed, 199 insertions, 2 deletions
diff --git a/build.gradle b/build.gradle index 9f04998..b0c39a2 100644 --- a/build.gradle +++ b/build.gradle @@ -16,6 +16,10 @@ archivesBaseName = project.archives_base_name version = "$project.mod_version+$project.minecraft_version" group = project.maven_group +configurations { + javadocClasspath +} + repositories { maven { url "https://server.bbkr.space/artifactory/libs-release" } /*maven { @@ -47,6 +51,8 @@ dependencies { modRuntime(modCompileOnly("com.terraformersmc:modmenu:$project.modmenu_version") { exclude group: 'net.fabricmc.fabric-api' }) + + javadocClasspath project(':javadoc') } processResources { @@ -79,9 +85,15 @@ checkstyle { toolVersion = '8.36.2' } +evaluationDependsOn(':javadoc') + javadoc { + dependsOn project(':javadoc').tasks.jar + options { links("https://maven.fabricmc.net/docs/yarn-$project.yarn_mappings") + taglets 'io.github.cottonmc.cotton.gui.jd.PropertyTaglet' + tagletPath project(':javadoc').tasks.jar.outputs.files.singleFile } exclude("**/impl/**") diff --git a/javadoc/build.gradle b/javadoc/build.gradle new file mode 100644 index 0000000..e8300bb --- /dev/null +++ b/javadoc/build.gradle @@ -0,0 +1,6 @@ +plugins { + id 'java' +} + +sourceCompatibility = rootProject.sourceCompatibility +targetCompatibility = rootProject.targetCompatibility diff --git a/javadoc/src/main/java/io/github/cottonmc/cotton/gui/jd/PropertyTaglet.java b/javadoc/src/main/java/io/github/cottonmc/cotton/gui/jd/PropertyTaglet.java new file mode 100644 index 0000000..163f1d6 --- /dev/null +++ b/javadoc/src/main/java/io/github/cottonmc/cotton/gui/jd/PropertyTaglet.java @@ -0,0 +1,174 @@ +package io.github.cottonmc.cotton.gui.jd; + +import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.TextTree; +import com.sun.source.util.DocTrees; +import com.sun.source.util.SimpleDocTreeVisitor; +import jdk.javadoc.doclet.Doclet; +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.doclet.Taglet; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeVisitor; +import javax.lang.model.util.ElementKindVisitor8; +import javax.lang.model.util.SimpleTypeVisitor8; + +public class PropertyTaglet implements Taglet { + private static final Pattern PROPERTY_METHOD = Pattern.compile("^(.+)Property$"); + private static final String OBSERVABLE_PROPERTY = "io.github.cottonmc.cotton.gui.widget.data.ObservableProperty"; + private DocTrees docTrees; + + @Override + public void init(DocletEnvironment env, Doclet doclet) { + docTrees = env.getDocTrees(); + } + + @Override + public Set<Location> getAllowedLocations() { + return Set.of(Location.TYPE); + } + + @Override + public boolean isInlineTag() { + return false; + } + + @Override + public String getName() { + return "properties"; + } + + @Override + public String toString(List<? extends DocTree> tags, Element element) { + if (!((element.getKind().isClass() || element.getKind().isInterface()) && element instanceof TypeElement type)) { + throw new IllegalArgumentException("Not a type: " + element); + } + + List<PropertyEntry> myEntries = scan(type); + StringBuilder builder = new StringBuilder(); + + if (!myEntries.isEmpty()) { + builder.append("<div class=\"caption\"><span>Properties</span></div>"); + builder.append("<div class=\"summary-table two-column-summary\">"); + builder.append("<div class=\"table-header col-first\">Property</div>"); + builder.append("<div class=\"table-header col-last\">Description</div>"); + + for (int i = 0; i < myEntries.size(); i++) { + PropertyEntry entry = myEntries.get(i); + String rowClass = (i % 2 == 0) ? "even-row-color" : "odd-row-color"; + builder.append("<div class=\"col-first ").append(rowClass).append("\"><code><span class=\"member-name-link\">"); + builder.append("<a href=\"#").append(entry.name).append("Property()\">").append(entry.name).append("</a>"); + builder.append("</span></code></div>"); + builder.append("<div class=\"col-last ").append(rowClass).append("\">").append(entry.doc).append("</div>"); + } + + builder.append("</div>"); + } + + Map<String, List<PropertyEntry>> inheritedProperties = new LinkedHashMap<>(); + scanParents(type, inheritedProperties); + + inheritedProperties.forEach((name, entries) -> { + if (!entries.isEmpty()) { + builder.append("<dt>Properties from <code>").append(name).append("</code></dt>"); + builder.append("<dd>"); + builder.append(entries.stream().map(entry -> "<code>" + entry.name + "</code>").collect(Collectors.joining(", "))); + builder.append("</dd>"); + } + }); + + return builder.toString(); + } + + private static String getClassName(TypeElement cl) { + return cl.getQualifiedName().toString(); + } + + private List<PropertyEntry> scan(TypeElement cl) { + // Java classes don't have LibGui properties (yet? 😳) + if (getClassName(cl).startsWith("java.")) return List.of(); + + return cl.getEnclosedElements().stream() + .filter(el -> el.getKind() == ElementKind.METHOD) + .map(el -> (ExecutableElement) el) + .filter(el -> el.getReturnType().accept(new ObservableTypeFilter(), null)) + .map(el -> { + Matcher matcher = PROPERTY_METHOD.matcher(el.getSimpleName()); + + if (matcher.matches()) { + String doc = docTrees.getDocCommentTree(el).getFirstSentence().stream() + .map(tree -> tree.accept(new SimpleDocTreeVisitor<String, Void>() { + @Override + public String visitText(TextTree node, Void o) { + return node.getBody(); + } + }, null)) + .filter(Objects::nonNull) + .findAny().orElse(""); + + return new PropertyEntry(matcher.group(1), doc); + } + + return null; + }) + .filter(Objects::nonNull) + .sorted() + .collect(Collectors.toList()); + } + + private void scanParents(TypeElement cl, Map<String, List<PropertyEntry>> inheritedProperties) { + TypeVisitor<Void, Void> typeVisitor = new SimpleTypeVisitor8<>() { + @Override + public Void visitDeclared(DeclaredType t, Void o) { + return t.asElement().accept(new ElementKindVisitor8<>() { + @Override + public Void visitType(TypeElement e, Object o) { + String fqn = e.getQualifiedName().toString(); + inheritedProperties.put(fqn, scan(e)); + scanParents(e, inheritedProperties); + return null; + } + }, null); + } + }; + + cl.getSuperclass().accept(typeVisitor, null); + cl.getInterfaces().forEach(itf -> itf.accept(typeVisitor, null)); + } + + private static final class ObservableTypeFilter extends SimpleTypeVisitor8<Boolean, Void> { + ObservableTypeFilter() { + super(false); + } + + @Override + public Boolean visitDeclared(DeclaredType t, Void v) { + Element type = t.asElement(); + + if (type.getKind() == ElementKind.CLASS) { + return ((TypeElement) type).getQualifiedName().contentEquals(OBSERVABLE_PROPERTY); + } + + return false; + } + } + + private record PropertyEntry(String name, String doc) implements Comparable<PropertyEntry> { + @Override + public int compareTo(PropertyEntry o) { + return name.compareTo(o.name); + } + } +} diff --git a/settings.gradle b/settings.gradle index 1dfe6ed..4e731fa 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,3 +15,4 @@ pluginManagement { rootProject.name = 'LibGui' include ':GuiTest' +include 'javadoc' diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java index 533e436..b845f41 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java @@ -20,6 +20,9 @@ import io.github.cottonmc.cotton.gui.widget.data.InputResult; import io.github.cottonmc.cotton.gui.widget.icon.Icon; import org.jetbrains.annotations.Nullable; +/** + * @properties + */ public class WButton extends WWidget { private static final Identifier DARK_WIDGETS_LOCATION = new Identifier("libgui", "textures/widget/dark_widgets.png"); diff --git a/src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java b/src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java index 8de8567..b300d43 100644 --- a/src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java +++ b/src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java @@ -1,7 +1,5 @@ package io.github.cottonmc.cotton.gui.widget; -import io.github.cottonmc.cotton.gui.widget.data.ObservableProperty; - import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; @@ -12,11 +10,14 @@ import net.minecraft.client.util.math.MatrixStack; import io.github.cottonmc.cotton.gui.GuiDescription; import io.github.cottonmc.cotton.gui.impl.VisualLogger; import io.github.cottonmc.cotton.gui.widget.data.InputResult; +import io.github.cottonmc.cotton.gui.widget.data.ObservableProperty; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; /** * The base class for all widgets. + * + * @properties */ public class WWidget { private static final VisualLogger LOGGER = new VisualLogger(WWidget.class); |