Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.denizenscript.denizencore.scripts.containers.ScriptContainer;
import com.denizenscript.denizencore.scripts.queues.ScriptQueue;
import com.denizenscript.denizencore.tags.TagContext;
import com.denizenscript.denizencore.utilities.DefinitionProvider;

import java.io.File;

Expand Down Expand Up @@ -91,6 +92,8 @@ public interface DenizenImplementation {

void doFinalDebugOutput(String rawText);

void addFormatScriptDefinitions(DefinitionProvider provider, TagContext context);

String stripColor(String message);

void reloadConfig();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.denizenscript.denizencore.events.ScriptEvent;
import com.denizenscript.denizencore.objects.*;
import com.denizenscript.denizencore.scripts.containers.core.FormatScriptContainer;
import com.denizenscript.denizencore.tags.*;
import com.denizenscript.denizencore.tags.core.EscapeTagUtil;
import com.denizenscript.denizencore.utilities.*;
Expand Down Expand Up @@ -2286,7 +2287,7 @@ else if (elem.equals("false")) {
// Returns a BinaryTag holding 8 bytes of this integer number converted to binary format using big-endian 64-bit integer twos-complement encoding.
// @example
// # Narrates '00000000000000ff'
// - narrate <element[255].to_binary>
// - narrate <element[255].integer_to_binary>
// -->
tagProcessor.registerStaticTag(BinaryTag.class, "integer_to_binary", (attribute, object) -> {
ByteBuffer buffer = ByteBuffer.allocate(8);
Expand Down Expand Up @@ -2536,6 +2537,21 @@ else if (elem.equals("false")) {
tagProcessor.registerStaticTag(ElementTag.class, "unaccented", (attribute, object) -> {
return new ElementTag(UNACCENTED_PATTERN.matcher(Normalizer.normalize(object.asString(), Normalizer.Form.NFKD)).replaceAll(""), true);
});

// <--[tag]
// @attribute <ElementTag.format[<format_script>]>
// @returns ElementTag
// @group text manipulation
// @description
// Returns the text re-formatted according to a <@link language Format Script Containers>.
// -->
ElementTag.tagProcessor.registerTag(ElementTag.class, ScriptTag.class, "format", (attribute, object, format) -> {
if (!(format.getContainer() instanceof FormatScriptContainer formatScript) || formatScript.getFormatTag() == null) {
attribute.echoError("Script '" + format + "' is not a format script.");
return null;
}
return new ElementTag(formatScript.getFormattedText(object.asString(), attribute.context));
});
}

public static ObjectTagProcessor<ElementTag> tagProcessor = new ObjectTagProcessor<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ public MapTag(Map<StringHolder, ObjectTag> map) {
this.map = new LinkedHashMap<>(map);
}

public MapTag(MapTag mapTag) {
this(mapTag.map);
}

@Override
public MapTag duplicate() {
MapTag newMap = new MapTag();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,10 @@ public ScriptTag getScript() {
return internal.script;
}

public ScriptContainer getScriptContainer() {
return getScript() != null ? getScript().getContainer() : null;
}

public ScriptEntry setScript(String scriptName) {
internal.script = ScriptTag.valueOf(scriptName, CoreUtilities.basicContext);
return this;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.denizenscript.denizencore.scripts;

import com.denizenscript.denizencore.DenizenCore;
import com.denizenscript.denizencore.scripts.containers.ScriptContainer;
import com.denizenscript.denizencore.scripts.containers.core.FormatScriptContainer;
import com.denizenscript.denizencore.tags.ParseableTag;
import com.denizenscript.denizencore.tags.TagContext;
import com.denizenscript.denizencore.tags.TagManager;
import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.denizenscript.denizencore.utilities.YamlConfiguration;
import com.denizenscript.denizencore.utilities.debugging.Debug;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public record ScriptFormattingContext(Map<String, ParseableTag> formats, ParseableTag singleFormat) {

// <--[language]
// @name Script Logging Format
// @group Script Container System
// @description
// Script logging contexts provide the format certain commands will use for their texts. Most notably, this includes <@link command debug>.
// See specific command's documentation for information on which formats they use (for example, the 'debug' command supports a 'debug' format and an 'error' format).
// The formats are specified under a 'formats' key, and can be either a <@link language Format Script Containers> or a direct format with the same syntax as format scripts.
// When specifying a direct format, use the format name as the key; When specifying a format script, use '<format>_script' as the key (see example below).
// <code>
// my_project_task:
// type: task
// formats:
// # A direct format
// debug: [MyProject] <[text]>
// # A separate format script
// error_script: my_project_error
// script:
// - if <util.real_time_since_start.in_hours> > 20:
// # Will be formatted by the 'my_project_error' format script.
// - debug error "The system has been running for over 20 hours! Please restart!"
// - else:
// # Will print "[MyProject] The system does not need a restart yet."
// - debug "The system does not need a restart yet."
// </code>
// -->

public static final Set<String> FORMAT_TYPES = new HashSet<>();

public static String registerFormatType(String name) {
String nameLower = CoreUtilities.toLowerCase(name);
if (!FORMAT_TYPES.add(nameLower)) {
throw new IllegalArgumentException("Tried registering duplicate format type! format '" + name + "' already exists.");
}
return nameLower;
}

public static ScriptFormattingContext parseFromConfiguration(ScriptContainer script) {
YamlConfiguration formatsConfig = script.getConfigurationSection("formats");
if (formatsConfig == null) {
return null;
}
Map<String, ParseableTag> formats = new HashMap<>();
TagContext context = null;
for (String formatType : FORMAT_TYPES) {
String rawFormat = formatsConfig.getString(formatType);
if (rawFormat != null) {
if (context == null) {
context = DenizenCore.implementation.getTagContext(script);
}
formats.put(formatType, TagManager.parseTextToTag(rawFormat, context));
continue;
}
String formatScriptInput = formatsConfig.getString(formatType + "_script");
if (formatScriptInput == null) {
continue;
}
FormatScriptContainer formatScript = ScriptRegistry.getScriptContainerAs(formatScriptInput, FormatScriptContainer.class);
if (formatScript == null || formatScript.getFormatTag() == null) {
Debug.echoError(script, "Invalid format script '" + formatScriptInput + "' specified for format '" + formatType + "'.");
continue;
}
formats.put(formatType, formatScript.getFormatTag());
}
if (formats.isEmpty()) {
Debug.echoError(script, "Invalid formats config, must specify at least one valid format.");
return null;
}
return new ScriptFormattingContext(formats, null);
}

public boolean hasFormat(String formatType) {
return singleFormat != null || formats.containsKey(formatType);
}

public String formatOrNull(String formatType, String rawText, ScriptEntry entry) {
ParseableTag formatTag = singleFormat == null ? formats.get(formatType) : singleFormat;
return formatTag != null ? FormatScriptContainer.formatText(formatTag, rawText, entry.getContext(), entry.getScript()) : null;
}

public String format(String formatType, String rawText, ScriptEntry entry) {
String formatted = formatOrNull(formatType, rawText, entry);
return formatted != null ? formatted : rawText;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public static void _registerCoreTypes() {
_registerType("world", WorldScriptContainer.class);
_registerType("data", DataScriptContainer.class);
_registerType("yaml data", DataScriptContainer.class);
_registerType("format", FormatScriptContainer.class);
}

public static boolean containsScript(String id, Class scriptContainerType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
package com.denizenscript.denizencore.scripts.commands.core;

import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.objects.core.ScriptTag;
import com.denizenscript.denizencore.scripts.ScriptEntry;
import com.denizenscript.denizencore.scripts.ScriptFormattingContext;
import com.denizenscript.denizencore.scripts.commands.AbstractCommand;
import com.denizenscript.denizencore.scripts.commands.Holdable;
import com.denizenscript.denizencore.scripts.commands.generator.*;
import com.denizenscript.denizencore.scripts.containers.ScriptContainer;
import com.denizenscript.denizencore.scripts.containers.core.FormatScriptContainer;
import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.denizenscript.denizencore.utilities.debugging.Debug;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.scripts.ScriptEntry;
import com.denizenscript.denizencore.utilities.debugging.DebugSubmitter;

public class DebugCommand extends AbstractCommand implements Holdable {

public static final String DEBUG_FORMAT = ScriptFormattingContext.registerFormatType("debug");
public static final String ERROR_FORMAT = ScriptFormattingContext.registerFormatType("error");

public DebugCommand() {
setName("debug");
setSyntax("debug [<type>] [<message>] (name:<name>)");
setRequiredArguments(2, 3);
setSyntax("debug (<type>) [<message>] (name:<name>) (format:<format>)");
setRequiredArguments(1, 3);
isProcedural = true;
generateDebug = false;
autoCompile();
}

// <--[command]
// @Name Debug
// @Syntax debug [<type>] [<message>] (name:<name>)
// @Required 2
// @Syntax debug (<type>) [<message>] (name:<name>) (format:<format>)
// @Required 1
// @Maximum 3
// @Short Shows a debug message.
// @Group core
Expand All @@ -32,20 +39,20 @@ public DebugCommand() {
// @Description
// Use to quickly output debug information to console.
//
// Valid types include:
// Outputs plain text debug to the console by default, supporting the 'debug' format type (see <@link language Script Logging Formats>).
//
// Alternatively, specify one of the following debug types:
// DEBUG: standard hideable debug.
// HEADER: standard hideable debug inside a header line.
// FOOTER: a footer line.
// SPACER: a spacer line.
// LOG: global output, non-hideable.
// APPROVAL: "Okay!" output, non-hideable.
// ERROR: "Error!" output, non-hideable.
// ERROR: "Error!" output, non-hideable. Supports the 'error' format type, see <@link language Script Logging Formats>.
// REPORT: normally used to describe the arguments of a command, requires a name, hideable.
// EXCEPTION: outputs a full java stacktrace.
// RECORD: Use message 'start' to start recording, 'submit' to submit a recording, or 'cancel' to cancel a recording.
//
// TODO: Should [<type>] be required? Perhaps default to 'debug' mode?
//
// @Tags
// <entry[saveName].submitted> returns the submit link (if any).
//
Expand All @@ -55,7 +62,7 @@ public DebugCommand() {
//
// @Usage
// Use to add some information to help your own ability to read debug output from you script.
// - debug debug "Time is currently <[milliseconds].div[1000].round> seconds!"
// - debug "Time is currently <[milliseconds].div[1000].round> seconds!"
//
// @Usage
// Use to record a debug log of a certain script.
Expand All @@ -68,6 +75,7 @@ public DebugCommand() {
// -->

public enum DebugType {
OUTPUT,
DEBUG,
HEADER,
FOOTER,
Expand All @@ -88,42 +96,51 @@ public void addCustomTabCompletions(TabCompletionsBuilder tab) {

public static void autoExecute(ScriptEntry scriptEntry,
@ArgRaw @ArgLinear @ArgName("debug") String debug,
@ArgName("type") DebugType dbType,
@ArgPrefixed @ArgName("name") @ArgDefaultText("name") String name) {
@ArgName("type") @ArgDefaultText("output") DebugType dbType,
@ArgPrefixed @ArgName("name") @ArgDefaultNull String name,
@ArgName("format") @ArgPrefixed @ArgDefaultNull ScriptTag formatScript) {
ScriptFormattingContext formattingContext = null;
ScriptContainer scriptContainer = scriptEntry.getScriptContainer();
if (formatScript != null) {
if (!(formatScript.getContainer() instanceof FormatScriptContainer formatScriptContainer) || formatScriptContainer.getFormatTag() == null) {
Debug.echoError("Invalid 'format:' script specified: must be a format script container.");
return;
}
formattingContext = formatScriptContainer.getAsFormattingContext();
}
else if (scriptContainer != null) {
formattingContext = scriptContainer.getFormattingContext();
}
if (name == null) {
name = scriptContainer != null ? scriptContainer.getOriginalName() : "DebugCommand";
}
if (dbType != DebugType.RECORD) {
scriptEntry.setFinished(true);
}
switch (dbType) {
case DEBUG:
Debug.echoDebug(scriptEntry, debug);
break;
case HEADER:
Debug.echoDebug(scriptEntry, Debug.DebugElement.Header, debug);
break;
case FOOTER:
Debug.echoDebug(scriptEntry, Debug.DebugElement.Footer, debug);
break;
case SPACER:
Debug.echoDebug(scriptEntry, Debug.DebugElement.Spacer, debug);
break;
case LOG:
Debug.log(debug);
break;
case APPROVAL:
Debug.echoApproval(debug);
break;
case ERROR:
Debug.echoError(scriptEntry, debug);
break;
case REPORT:
case OUTPUT -> Debug.echoDebug(null, formattingContext == null ? debug : formattingContext.format(DEBUG_FORMAT, debug, scriptEntry));
case DEBUG -> Debug.echoDebug(scriptEntry, debug);
case HEADER -> Debug.echoDebug(scriptEntry, Debug.DebugElement.Header, debug);
case FOOTER -> Debug.echoDebug(scriptEntry, Debug.DebugElement.Footer, debug);
case SPACER -> Debug.echoDebug(scriptEntry, Debug.DebugElement.Spacer, debug);
case LOG -> Debug.log(name, debug);
case APPROVAL -> Debug.echoApproval(debug);
case ERROR -> {
String formatted = formattingContext != null ? formattingContext.formatOrNull(ERROR_FORMAT, debug, scriptEntry) : null;
if (formatted != null) {
Debug.echoDebug(null, formatted);
}
else {
Debug.echoError(scriptEntry, debug);
}
}
case REPORT -> {
if (scriptEntry.dbCallShouldDebug()) {
Debug.report(scriptEntry, name, debug);
}
break;
case EXCEPTION:
Debug.echoError(scriptEntry, new RuntimeException(debug));
break;
case RECORD:
}
case EXCEPTION -> Debug.echoError(scriptEntry, new RuntimeException(debug));
case RECORD -> {
String form = CoreUtilities.toLowerCase(debug);
switch (form) {
case "start":
Expand Down Expand Up @@ -156,7 +173,7 @@ else if (s.equals("disabled")) {
scriptEntry.setFinished(true);
break;
}
break;
}
}
}
}
Loading