Skip to content

Commit

Permalink
Documentation annotations improvements (Prototype). (#7470)
Browse files Browse the repository at this point in the history
* Add example annotation.

* Support new annotation in documentation generators.

* Update src/main/java/ch/njol/skript/doc/Example.java
  • Loading branch information
Moderocky authored Feb 28, 2025
1 parent 913a7d8 commit 20b8f2a
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 8 deletions.
7 changes: 6 additions & 1 deletion src/main/java/ch/njol/skript/doc/Documentation.java
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,12 @@ private static void insertSyntaxElement(final PrintWriter pw, final SyntaxElemen
Class<?> elementClass = info.getElementClass();
if (elementClass.getAnnotation(NoDoc.class) != null)
return;
if (elementClass.getAnnotation(Name.class) == null || elementClass.getAnnotation(Description.class) == null || elementClass.getAnnotation(Examples.class) == null || elementClass.getAnnotation(Since.class) == null) {
if (elementClass.getAnnotation(Name.class) == null
|| elementClass.getAnnotation(Description.class) == null
|| (!elementClass.isAnnotationPresent(Examples.class)
&& !elementClass.isAnnotationPresent(Example.class)
&& !elementClass.isAnnotationPresent(Example.Examples.class))
|| elementClass.getAnnotation(Since.class) == null) {
Skript.warning("" + elementClass.getSimpleName() + " is missing information");
return;
}
Expand Down
50 changes: 50 additions & 0 deletions src/main/java/ch/njol/skript/doc/Example.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package ch.njol.skript.doc;

import java.lang.annotation.*;

/**
* An example to be used in documentation for the annotated element.
* Multiple example annotations can be stacked on a single syntax element.
* <p>
* Each annotation should include a single example.
* This can be used instead of the existing {@link ch.njol.skript.doc.Examples} annotation.
* <p>
* <b>Multi-line examples</b> should use multi-line strings.
* Note that whitespace and quotes do not need to be escaped in this mode.
* The indentation will start from the least-indented line (and most IDEs provide a guideline to show this).
* <pre>{@code
* @Example("set player's health to 1")
* @Example("""
* if player's health is greater than 10:
* send "Wow you're really healthy!"
* """)
* @Example("""
* # sets the player's health to 1
* set player's health to 1""")
* public class MyExpression extends ... {
* }
* }</pre>
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Example.Examples.class)
@Documented
public @interface Example {

String value();

boolean inTrigger() default true; // todo needed?

/**
* The internal container annotation for multiple examples.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface Examples {

Example[] value();

}

}
29 changes: 25 additions & 4 deletions src/main/java/ch/njol/skript/doc/HTMLGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.block.BlockCanBuildEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.skriptlang.skript.lang.entry.EntryData;
import org.skriptlang.skript.lang.entry.EntryValidator;
Expand Down Expand Up @@ -442,10 +443,7 @@ private String generateAnnotated(String descTemp, SyntaxElementInfo<?> info, @Nu
.replace("\\", "\\\\").replace("\"", "\\\"").replace("\t", " "));

// Examples
Examples examples = c.getAnnotation(Examples.class);
desc = desc.replace("${element.examples}", Joiner.on("<br>").join(getDefaultIfNullOrEmpty((examples != null ? Documentation.escapeHTML(examples.value()) : null), "Missing examples.")));
desc = desc.replace("${element.examples-safe}", Joiner.on("<br>").join(getDefaultIfNullOrEmpty((examples != null ? Documentation.escapeHTML(examples.value()) : null), "Missing examples."))
.replace("\\", "\\\\").replace("\"", "\\\"").replace("\t", " "));
desc = extractExamples(desc, c);

// Documentation ID
desc = desc.replace("${element.id}", DocumentationIdProvider.getId(info));
Expand Down Expand Up @@ -546,6 +544,29 @@ private String generateAnnotated(String descTemp, SyntaxElementInfo<?> info, @Nu
return desc;
}

private @NotNull String extractExamples(String desc, Class<?> syntax) {
if (syntax.isAnnotationPresent(Example.class)) {
Example examples = syntax.getAnnotation(Example.class);
return this.addExamples(desc, examples.value());
} else if (syntax.isAnnotationPresent(Example.Examples.class)) {
Example.Examples examples = syntax.getAnnotation(Example.Examples.class);
return this.addExamples(desc, Arrays.stream(examples.value())
.map(Example::value).toArray(String[]::new));
} else if (syntax.isAnnotationPresent(Examples.class)) {
Examples examples = syntax.getAnnotation(Examples.class);
return this.addExamples(desc, examples.value());
} else {
return this.addExamples(desc, (String[]) null);
}
}

private @NotNull String addExamples(String desc, String @Nullable ... examples) {
desc = desc.replace("${element.examples}", Joiner.on("<br>").join(getDefaultIfNullOrEmpty((examples != null ? Documentation.escapeHTML(examples) : null), "Missing examples.")));
desc = desc.replace("${element.examples-safe}", Joiner.on("<br>").join(getDefaultIfNullOrEmpty((examples != null ? Documentation.escapeHTML(examples) : null), "Missing examples."))
.replace("\\", "\\\\").replace("\"", "\\\"").replace("\t", " "));
return desc;
}

private String generateEvent(String descTemp, SkriptEventInfo<?> info, @Nullable String page) {
Class<?> c = info.getElementClass();
String desc;
Expand Down
17 changes: 14 additions & 3 deletions src/main/java/ch/njol/skript/doc/JSONGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.skriptlang.skript.lang.structure.Structure;
import org.skriptlang.skript.lang.structure.StructureInfo;
Expand All @@ -21,6 +22,7 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Objects;
import java.util.stream.Stream;
Expand All @@ -39,7 +41,7 @@ public JSONGenerator(File templateDir, File outputDir) {
* @param strings the String array to convert
* @return the JsonArray containing the Strings
*/
private static @Nullable JsonArray convertToJsonArray(String @Nullable [] strings) {
private static @Nullable JsonArray convertToJsonArray(String @Nullable ... strings) {
if (strings == null)
return null;
JsonArray jsonArray = new JsonArray();
Expand Down Expand Up @@ -73,9 +75,18 @@ public JSONGenerator(File templateDir, File outputDir) {
syntaxJsonObject.add("description", new JsonArray());
}

Examples examplesAnnotation = syntaxClass.getAnnotation(Examples.class);
if (examplesAnnotation != null) {
if (syntaxClass.isAnnotationPresent(Examples.class)) {
@NotNull Examples examplesAnnotation = syntaxClass.getAnnotation(Examples.class);
syntaxJsonObject.add("examples", convertToJsonArray(examplesAnnotation.value()));
} else if (syntaxClass.isAnnotationPresent(Example.Examples.class)) {
// If there are multiple examples, they get containerised
@NotNull Example.Examples examplesAnnotation = syntaxClass.getAnnotation(Example.Examples.class);
syntaxJsonObject.add("examples", convertToJsonArray(Arrays.stream(examplesAnnotation.value())
.map(Example::value).toArray(String[]::new)));
} else if (syntaxClass.isAnnotationPresent(Example.class)) {
// If the user adds just one example, it isn't containerised
@NotNull Example example = syntaxClass.getAnnotation(Example.class);
syntaxJsonObject.add("examples", convertToJsonArray(example.value()));
} else {
syntaxJsonObject.add("examples", new JsonArray());
}
Expand Down

0 comments on commit 20b8f2a

Please sign in to comment.