Skip to content
41 changes: 41 additions & 0 deletions src/main/java/ch/njol/skript/doc/JSONGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.bukkit.event.block.BlockCanBuildEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.skriptlang.skript.lang.experiment.Experiment;
import org.skriptlang.skript.lang.structure.Structure;
import org.skriptlang.skript.lang.structure.StructureInfo;

Expand Down Expand Up @@ -366,6 +367,44 @@ private static JsonArray generateFunctionArray(Iterator<JavaFunction<?>> functio
return syntaxArray;
}

/**
* Generates a JsonArray with all data for each {@link Experiment}.
*
* @return a JsonArray containing the documentation JsonObjects for each experiment
*/
private static JsonArray generateExperiments() {
JsonArray array = new JsonArray();

for (Experiment experiment : Skript.experiments().registered()) {
JsonObject object = new JsonObject();

object.addProperty("id", experiment.codeName());

if (experiment.displayName().isEmpty()) {
object.addProperty("name", (String) null);
} else {
object.addProperty("name", experiment.displayName());
}

if (experiment.description().isEmpty()) {
object.add("description", null);
} else {
JsonArray description = new JsonArray();
for (String part : experiment.description()) {
description.add(part);
}
object.add("description", description);
}

object.addProperty("pattern", experiment.pattern().toString());
object.addProperty("phase", experiment.phase().name().toLowerCase(Locale.ENGLISH));

array.add(object);
}

return array;
}

/**
* Cleans the provided patterns
*
Expand Down Expand Up @@ -416,6 +455,8 @@ public void generate() {

jsonDocs.add("functions", generateFunctionArray(Functions.getJavaFunctions().iterator()));

jsonDocs.add("experiments", generateExperiments());

saveDocs(outputDir.toPath().resolve("docs.json"), jsonDocs);
}

Expand Down
178 changes: 169 additions & 9 deletions src/main/java/ch/njol/skript/registrations/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,178 @@
import ch.njol.skript.SkriptAddon;
import ch.njol.skript.patterns.PatternCompiler;
import ch.njol.skript.patterns.SkriptPattern;
import com.google.common.base.Preconditions;
import org.jetbrains.annotations.NotNull;
import org.skriptlang.skript.lang.experiment.Experiment;
import org.skriptlang.skript.lang.experiment.ExperimentRegistry;
import org.skriptlang.skript.lang.experiment.LifeCycle;

import java.util.Collection;
import java.util.List;

/**
* Experimental feature toggles as provided by Skript itself.
*/
public enum Feature implements Experiment {
EXAMPLES("examples", LifeCycle.STABLE),
QUEUES("queues", LifeCycle.EXPERIMENTAL),
FOR_EACH_LOOPS("for loop", LifeCycle.EXPERIMENTAL, "for [each] loop[s]"),
SCRIPT_REFLECTION("reflection", LifeCycle.EXPERIMENTAL, "[script] reflection"),
CATCH_ERRORS("catch runtime errors", LifeCycle.EXPERIMENTAL, "error catching [section]"),
TYPE_HINTS("type hints", LifeCycle.EXPERIMENTAL, "[local variable] type hints"),
DAMAGE_SOURCE("damage source", LifeCycle.EXPERIMENTAL, "damage source[s]")
;

EXAMPLES("examples",
"Examples",
"""
A section used to provide examples inside code.

```
example:
kick the player due to "you are not allowed here!"
```
""",
LifeCycle.STABLE),
QUEUES("queues",
"Queues",
"""
A collection that removes elements whenever they are requested.

This is useful for processing tasks or keeping track of things that need to happen only once.

```
set {queue} to a new queue of "hello" and "world"

broadcast the first element of {queue}
# "hello" is now removed

broadcast the first element of {queue}
# "world" is now removed

# queue is empty
```

```
set {queue} to a new queue of all players

set {player 1} to a random element out of {queue}\s
set {player 2} to a random element out of {queue}
# players 1 and 2 are guaranteed to be distinct
```

Queues can be looped over like a regular list.
""",
LifeCycle.EXPERIMENTAL),
FOR_EACH_LOOPS("for loop",
"For Loops",
"""
A new kind of loop syntax that stores the loop index and value in variables for convenience.

This can be used to avoid confusion when nesting multiple loops inside each other.

```
for {_index}, {_value} in {my list::*}:
broadcast "%{_index}%: %{_value}%"
```

```
for each {_player} in all players:
send "Hello %{_player}%!" to {_player}
```

All existing loop features are also available in this section.
""",
LifeCycle.EXPERIMENTAL,
"for [each] loop[s]"),
SCRIPT_REFLECTION("reflection",
"Script Reflection",
"""
This feature includes:

- The ability to reference a script in code.
- Finding and running functions by name.
- Reading configuration files and values.
""",
LifeCycle.EXPERIMENTAL,
"[script] reflection"),
CATCH_ERRORS("catch runtime errors",
"Runtime Error Catching",
"""
A new catch runtime errors section allows you to catch and \
suppress runtime errors within it and access them later with \
the last caught runtime errors.

```
catch runtime errors:
...
set worldborder center of {_border} to {_my unsafe location}
...
if last caught runtime errors contains "Your location can't have a NaN value as one of its components":
set worldborder center of {_border} to location(0, 0, 0)
```
""",
LifeCycle.EXPERIMENTAL,
"error catching [section]"),
TYPE_HINTS("type hints",
"Type Hints",
"""
Local variable type hints enable Skript to understand \
what kind of values your local variables will hold at parse time. \
Consider the following example:

```
set {_a} to 5
set {_b} to "some string"
... do stuff ...
set {_c} to {_a} in lowercase # oops i used the wrong variable
```

Previously, the code above would parse without issue. \
However, Skript now understands that when it is used, \
{_a} could only be a number (and not a text). \
Thus, the code above would now error with a message about mismatched types.

Please note that this feature is currently only supported by simple local variables. \
A simple local variable is one whose name does not contain any expressions:

```
{_var} # can use type hints
{_var::%player's name%} # can't use type hints
```
""",
LifeCycle.EXPERIMENTAL,
"[local variable] type hints"),
DAMAGE_SOURCE("damage source",
"Damage Sources",
"""
Damage sources are a more advanced and detailed version of damage causes. \
Damage sources include information such as the type of damage, \
the location where the damage originated from, the entity that \
directly caused the damage, and more.

Below is an example of what damaging using custom damage sources looks like:

```
damage all players by 5 using a custom damage source:
set the damage type to magic
set the causing entity to {_player}
set the direct entity to {_arrow}
set the damage location to location(0, 0, 10)
```

For more details about the syntax, visit damage source on our documentation website.
""",
LifeCycle.EXPERIMENTAL,
"damage source[s]");

private final String displayName;
private final String codeName;
private final String description;
private final LifeCycle phase;
private final SkriptPattern compiledPattern;

Feature(String codeName, LifeCycle phase, String... patterns) {
Feature(@NotNull String codeName, @NotNull String displayName,
@NotNull String description, @NotNull LifeCycle phase,
String... patterns) {
Preconditions.checkNotNull(codeName, "codeName cannot be null");
Preconditions.checkNotNull(displayName, "displayName cannot be null");
Preconditions.checkNotNull(description, "description cannot be null");

this.displayName = displayName;
this.description = description.strip();
this.codeName = codeName;
this.phase = phase;
this.compiledPattern = switch (patterns.length) {
Expand All @@ -40,6 +190,16 @@ public static void registerAll(SkriptAddon addon, ExperimentRegistry manager) {
}
}

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

@Override
public @NotNull Collection<String> description() {
return List.of(description);
}

@Override
public String codeName() {
return codeName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ public SkriptPattern pattern() {
}

static {
registerAll(Skript.getAddonInstance(), Skript.experiments());
if (!TestMode.GEN_DOCS) {
registerAll(Skript.getAddonInstance(), Skript.experiments());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import ch.njol.skript.patterns.SkriptPattern;
import ch.njol.skript.registrations.Feature;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
Expand Down Expand Up @@ -56,6 +59,20 @@ default boolean isKnown() {
*/
SkriptPattern pattern();

/**
* @return The display name for this experiment.
*/
default @NotNull String displayName() {
return codeName();
}

/**
* @return The description for this experiment.
*/
default @NotNull Collection<String> description() {
return List.of();
}

/**
* @return Whether the usage pattern of this experiment matches the input text
*/
Expand Down