aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuuz <6596629+Juuxel@users.noreply.github.com>2021-09-10 18:43:37 +0300
committerJuuz <6596629+Juuxel@users.noreply.github.com>2021-09-10 18:43:37 +0300
commit23406db80a4672c146aa1b720f403a6a5d4e464f (patch)
treec257f140b883118b9e21e234a958147fba5d9556
parent00a0bb5a4176c16be3fa61ad33713131a053168f (diff)
downloadLibGui-23406db80a4672c146aa1b720f403a6a5d4e464f.tar.gz
LibGui-23406db80a4672c146aa1b720f403a6a5d4e464f.tar.bz2
LibGui-23406db80a4672c146aa1b720f403a6a5d4e464f.zip
Add taglet for listing observable properties
Overkill? Yep. Still fancy? Absolutely!
-rw-r--r--build.gradle12
-rw-r--r--javadoc/build.gradle6
-rw-r--r--javadoc/src/main/java/io/github/cottonmc/cotton/gui/jd/PropertyTaglet.java174
-rw-r--r--settings.gradle1
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WButton.java3
-rw-r--r--src/main/java/io/github/cottonmc/cotton/gui/widget/WWidget.java5
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);