Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6723148
Init commit
Efnilite Sep 14, 2025
4213806
add example
Efnilite Sep 15, 2025
aa11019
Move to new registration API
Efnilite Sep 15, 2025
d4c7b51
wait :(
Efnilite Sep 17, 2025
35655aa
Merge branch 'dev/feature' into feature/grouped-docs
Efnilite Sep 17, 2025
34bc926
Add location, fix merge
Efnilite Sep 17, 2025
5616181
Remove set from register
Efnilite Sep 17, 2025
6fe255e
Make CategoryImpl use classes instead of instances
Efnilite Sep 17, 2025
afd70a7
Add docs, bug fix
Efnilite Sep 17, 2025
1d926c8
Add multiple categories per module
Efnilite Sep 17, 2025
4b79f1f
Apply suggestion from @Absolutionism
Efnilite Sep 18, 2025
3369e17
Apply suggestion from @Absolutionism
Efnilite Sep 18, 2025
278377c
Apply suggestion from @Absolutionism
Efnilite Sep 18, 2025
2dd2203
Merge branch 'dev/feature' into feature/grouped-docs
Efnilite Sep 18, 2025
dce21d6
Merge remote-tracking branch 'origin/feature/grouped-docs' into featu…
Efnilite Sep 19, 2025
87caead
Update docs
Efnilite Sep 19, 2025
5b914e8
Update annotation
Efnilite Sep 19, 2025
5f7eded
Add subcategories
Efnilite Sep 19, 2025
9ab6833
update module registration code
Efnilite Sep 20, 2025
6254455
requested changes part 1
Efnilite Sep 20, 2025
310880f
requested changes part 2
Efnilite Sep 20, 2025
ec002bd
set isDefault in infoBuilder to false
Efnilite Sep 20, 2025
a211958
Merge branch 'dev/feature' into feature/grouped-docs
Efnilite Sep 20, 2025
35cf06b
move DamageSourceModule to implement Categorizable instead of AddonMo…
Efnilite Sep 21, 2025
efe7536
requested changes
Efnilite Sep 21, 2025
b33472e
Merge branch 'dev/feature' into feature/grouped-docs
Efnilite Sep 21, 2025
e43f09b
Merge remote-tracking branch 'origin/feature/grouped-docs' into featu…
Efnilite Sep 21, 2025
03a7f86
Update src/main/java/ch/njol/skript/doc/JSONGenerator.java
Efnilite Sep 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/main/java/ch/njol/skript/doc/Categorizable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ch.njol.skript.doc;

import org.jetbrains.annotations.NotNull;

import java.util.Set;

/**
* Represents anything that can be categorized.
*/
@FunctionalInterface
public interface Categorizable {

/**
* Returns the documentation categories which this object belongs to.
*
* @return The categories of this object.
*/
@NotNull Set<Category> categories();

}
90 changes: 90 additions & 0 deletions src/main/java/ch/njol/skript/doc/Category.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package ch.njol.skript.doc;

import com.google.common.base.Preconditions;
import org.jetbrains.annotations.NotNull;
import org.skriptlang.skript.addon.AddonModule;

import java.util.Set;

/**
* Represents a category a documented element can belong to.
*/
public interface Category {

Category ENTITIES = new CategoryImpl("Entities", "entity", "entities", "animal", "panda", "allay",
"zombie", "goat", "horse", "pig", "fish", "villager", "bee");
Category BREEDING = new CategoryImpl("Breeding");
Category PLAYERS = new CategoryImpl("Players", "player", "operator");
Category DAMAGE_SOURCES = new CategoryImpl("Damage Sources", "damage source");
Category BLOCKS = new CategoryImpl("Blocks", "block");
Category STRINGS = new CategoryImpl("Strings", "string", "text");
Category COMMANDS = new CategoryImpl("Commands", "command");
Category ITEMS = new CategoryImpl("Items", "item", "enchantment", "lore", "tooltip", "banner");
Category WORLDS = new CategoryImpl("Worlds", "world");
Category SCRIPTS = new CategoryImpl("Scripts", "script");
Category DISPLAYS = new CategoryImpl("Displays", "display");
Category TIME = new CategoryImpl("Time", "time", "unix");
Category UUIDS = new CategoryImpl("UUIDs", "uuid");
Category DATES = new CategoryImpl("Dates", "date");
Category LOCATIONS = new CategoryImpl("Locations", "location");
Category MATH = new CategoryImpl("Math", "angle", "degree", "radian",
"arithmetic", "nan", "round", "rounds", "root", "quaternion", "permutations",
"combinations", "numbers", "infinity", "exponential");
Category VECTORS = new CategoryImpl("Vectors", Category.MATH, "vector");

/**
* @return The display name of this category.
*/
@NotNull String name();

/**
* @return The parent category of this category.
*/
Category parent();

/**
* Adds a module to this category.
*
* @param module The module to add.
*/
void addModule(@NotNull Class<? extends AddonModule> module);

/**
* @return The modules that are represented by this category.
*/
@NotNull Set<Class<? extends AddonModule>> modules();

/**
* Creates a new category.
*
* @param name The name.
* @return The new category.
*/
static Category of(@NotNull String name) {
Preconditions.checkNotNull(name, "name cannot be null");

return new CategoryImpl(name);
}

/**
* Creates a new category.
*
* @param name The name.
* @param category The category.
* @return The new category.
*/
static Category of(@NotNull String name, @NotNull Category category) {
Preconditions.checkNotNull(name, "name cannot be null");
Preconditions.checkNotNull(category, "category cannot be null");

return new CategoryImpl(name, category);
}

/**
* @return All registered categories.
*/
static Set<Category> values() {
return CategoryImpl.getInstances();
}

}
65 changes: 65 additions & 0 deletions src/main/java/ch/njol/skript/doc/CategoryImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package ch.njol.skript.doc;

import com.google.common.base.Preconditions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import org.skriptlang.skript.addon.AddonModule;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

final class CategoryImpl implements Category {

private static final Set<Category> instances = new HashSet<>();
private final String name;
private final Category parent;
private final Set<String> keywords;
private final Set<Class<? extends AddonModule>> modules;

static Set<Category> getInstances() {
return instances;
}

CategoryImpl(String name, String... keywords) {
this(name, null, keywords);
}

CategoryImpl(String name, Category parent, String... keywords) {
instances.add(this);
this.name = name;
this.parent = parent;
this.keywords = Arrays.stream(keywords)
.map(String::toLowerCase)
.collect(Collectors.toUnmodifiableSet());
this.modules = new HashSet<>();
}

@Override
public @NotNull String name() {
return name;
}

@Override
public Category parent() {
return parent;
}

public @NotNull Set<String> keywords() {
return keywords;
}

@Override
public void addModule(@NotNull Class<? extends AddonModule> module) {
Preconditions.checkNotNull(module, "module cannot be null");

modules.add(module);
}

@Override
public @Unmodifiable @NotNull Set<Class<? extends AddonModule>> modules() {
return Set.copyOf(modules);
}

}
97 changes: 96 additions & 1 deletion src/main/java/ch/njol/skript/doc/JSONGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.skriptlang.skript.addon.AddonModule;
import org.skriptlang.skript.addon.SkriptAddon;
import org.skriptlang.skript.bukkit.registration.BukkitRegistryKeys;
import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos;
import org.skriptlang.skript.lang.structure.Structure;
import org.skriptlang.skript.registration.DefaultSyntaxInfos;
import org.skriptlang.skript.registration.SyntaxInfo;
import org.skriptlang.skript.registration.SyntaxOrigin;
import org.skriptlang.skript.registration.SyntaxOrigin.ModuleOrigin;
import org.skriptlang.skript.registration.SyntaxRegistry;

import java.io.File;
Expand Down Expand Up @@ -163,6 +166,9 @@ private static JsonObject generatedAnnotatedElement(SyntaxInfo<?> syntaxInfo) {
syntaxJsonObject.add("returns", getExpressionReturnTypes(expression));
}

syntaxJsonObject.add("categories", getCategoriesArray(syntaxInfo.origin(), name.value(),
description == null ? null : List.of(description.value()), syntaxInfo.patterns()));

return syntaxJsonObject;
}

Expand Down Expand Up @@ -224,7 +230,7 @@ private static JsonObject generateEventElement(BukkitSyntaxInfos.Event<?> info)
syntaxJsonObject.add("requirements", convertToJsonArray(info.requiredPlugins().toArray(new String[0])));
syntaxJsonObject.add("examples", convertToJsonArray(info.examples().toArray(new String[0])));
syntaxJsonObject.add("eventValues", getEventValues(info));
syntaxJsonObject.add("keywords", convertToJsonArray(info.keywords().toArray(new String[0])));
syntaxJsonObject.add("categories", getCategoriesArray(info.origin(), info.name(), info.description(), info.patterns()));

return syntaxJsonObject;
}
Expand Down Expand Up @@ -370,6 +376,9 @@ private static JsonObject generateClassInfoElement(ClassInfo<?> classInfo) {
syntaxJsonObject.add("description", convertToJsonArray(classInfo.getDescription()));
syntaxJsonObject.add("requirements", convertToJsonArray(classInfo.getRequiredPlugins()));
syntaxJsonObject.add("examples", convertToJsonArray(classInfo.getExamples()));
syntaxJsonObject.add("categories", getCategoriesArray(null,
Objects.requireNonNullElse(classInfo.getDocName(), classInfo.getCodeName()),
List.of(classInfo.getDescription()), null));

return syntaxJsonObject;
}
Expand Down Expand Up @@ -408,6 +417,7 @@ private static JsonObject generateFunctionElement(JavaFunction<?> function) {

String functionSignature = function.getSignature().toString(false, false);
functionJsonObject.add("patterns", convertToJsonArray(functionSignature));
functionJsonObject.add("categories", getCategoriesArray(null, function.getName(), List.of(function.getDescription()), null));
return functionJsonObject;
}

Expand Down Expand Up @@ -459,6 +469,91 @@ private static JsonArray cleanPatterns(String... strings) {
return convertToJsonArray(strings);
}

/**
* Returns the category name of an element given its origin, name, description and patterns.
*
* <p>Attempts to find a category by using pattern first.
* If this has no results, then it will use the name and description.
* </p>
*
* @param origin The origin of this element.
* @param name The name of this element.
* @param description The description of this element.
* @param patterns The patterns of this element.
* @return The category name, or null if none is found.
*/
private static @Nullable JsonArray getCategoriesArray(
@Nullable SyntaxOrigin origin, @NotNull String name,
@Nullable Collection<String> description, @Nullable Collection<String> patterns
) {
JsonArray categories = new JsonArray();
if (origin instanceof ModuleOrigin elementOrigin) {
Class<?> moduleClass = elementOrigin.module().getClass();
for (Category category : Category.values()) {
if (category.modules().contains(moduleClass)) {
categories.add(getCategoryJson(category));
}
}
return categories;
}

if (patterns == null) patterns = List.of();
JsonArray first = getCategories(String.join("", patterns));
if (!first.isEmpty()) {
return first;
}
if (description == null) description = List.of();

JsonArray second = getCategories(name + String.join("", description) + String.join("", patterns));
if (!second.isEmpty()) {
return second;
}
return null;
}

/**
* Attempts to find the categories based on the input.
*
* @param input The input.
* @return The categories, or null if none are found.
*/
private static @NotNull JsonArray getCategories(@NotNull String input) {
String lower = input.toLowerCase();
JsonArray options = new JsonArray();
for (Category category : Category.values()) {
if (!(category instanceof CategoryImpl impl)) {
break;
}

for (String keyword : impl.keywords()) {
if (lower.contains(keyword)) {
options.add(getCategoryJson(category));
break;
}
}
}
Comment on lines +528 to +534
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was randomly going through the wip updated docs page again and noticed "category" checked it out saw that timespan and beacon effect are classified as apart of the "Display" category.

I decided to go check into this, this is caused by the usage guessing assignments with contains beacon effect contains display and timespan contains displayed.

Is this behavior planned to stay or do you plan to eventually move away from automatic assignments.
imo if something like this is to stay it should be checked against something more reliable

  • Modules, which is already done with Categorizable
  • Syntax patterns and not descriptions/names %display% vs. display
image

Copy link
Member

@sovdeeth sovdeeth Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think excluding the description/examples, searching with \b word boundaries, and specifically looking for types in the syntax patterns would be good improvements.
On the docs side, I'd like to see these as selectable categories (think shopping websites) rather than exclusive lists of syntaxes. One syntax may naturally belong to multiple categories, and having a way to select various categories for display I think would improve the UI.


return options;
}

/**
* Transforms a category into a json object.
* @param category The category.
* @return The transformed category.
*/
private static JsonObject getCategoryJson(@NotNull Category category) {
JsonObject object = new JsonObject();

object.addProperty("name", category.name());
if (category.parent() != null) {
object.addProperty("parent", category.parent().name());
} else {
object.add("parent", null);
}

return object;
}

/**
* Gets the json object representing the addon.
*
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/org/skriptlang/skript/addon/AddonModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import org.jetbrains.annotations.ApiStatus;
import org.skriptlang.skript.Skript;
import org.skriptlang.skript.registration.SyntaxInfo;
import org.skriptlang.skript.registration.SyntaxOrigin;
import org.skriptlang.skript.registration.SyntaxRegistry;
import org.skriptlang.skript.registration.SyntaxRegistry.Key;

/**
* A module is a component of a {@link SkriptAddon} used for registering syntax and other {@link Skript} components.
Expand Down Expand Up @@ -44,4 +48,48 @@ default boolean canLoad(SkriptAddon addon) {
return true;
}

/**
* Registers syntax such that it belongs to the current module.
*
* @param addon The addon this module belongs to.
* @param registry The registry to add this syntax to.
* @param cls The syntax info.
* @param <I> The type of syntax.
*/
default <I extends SyntaxInfo<?>> void register(SkriptAddon addon, Key<I> registry, I cls) {
register(addon, addon.syntaxRegistry(), registry, cls);
}

/**
* Registers syntax such that it belongs to the current module.
*
* @param addon The addon this module belongs to.
* @param registry The registry to add this syntax to.
* @param classes The syntax infos.
* @param <I> The type of syntax.
*/
@SuppressWarnings("unchecked")
default <I extends SyntaxInfo<?>> void register(SkriptAddon addon, Key<I> registry, I... classes) {
SyntaxRegistry syntaxRegistry = addon.syntaxRegistry();
for (I cls : classes) {
register(addon, syntaxRegistry, registry, cls);
}
}

/**
* Registers syntax such that it belongs to the current module.
*
* @param addon The addon this module belongs to.
* @param syntaxRegistry The syntax registry.
* @param registry The registry to add this syntax to.
* @param cls The syntax info.
* @param <I> The type of syntax.
*/
default <I extends SyntaxInfo<?>> void register(SkriptAddon addon, SyntaxRegistry syntaxRegistry, Key<I> registry, I cls) {
//noinspection unchecked
syntaxRegistry.register(registry, (I) cls.toBuilder()
.origin(SyntaxOrigin.of(addon, this))
.build());
}

}
Loading