From 159412ca16d0710db99c14b2d961c05d419aee05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rub=C3=A9n?= <44579213+Rubenicos@users.noreply.github.com>
Date: Sun, 17 Sep 2023 09:02:22 -0600
Subject: [PATCH] Added support for emit and load yaml comments
---
.../yaml/YamlConfigurationLoader.java | 198 +++++++++++++++++-
1 file changed, 187 insertions(+), 11 deletions(-)
diff --git a/format/yaml/src/main/java/org/spongepowered/configurate/yaml/YamlConfigurationLoader.java b/format/yaml/src/main/java/org/spongepowered/configurate/yaml/YamlConfigurationLoader.java
index e68418492..d71ba7f52 100644
--- a/format/yaml/src/main/java/org/spongepowered/configurate/yaml/YamlConfigurationLoader.java
+++ b/format/yaml/src/main/java/org/spongepowered/configurate/yaml/YamlConfigurationLoader.java
@@ -20,23 +20,22 @@
import org.spongepowered.configurate.CommentedConfigurationNode;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.ConfigurationOptions;
-import org.spongepowered.configurate.loader.AbstractConfigurationLoader;
-import org.spongepowered.configurate.loader.CommentHandler;
-import org.spongepowered.configurate.loader.CommentHandlers;
-import org.spongepowered.configurate.loader.LoaderOptionSource;
+import org.spongepowered.configurate.loader.*;
import org.spongepowered.configurate.util.UnmodifiableCollections;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.comments.CommentLine;
+import org.yaml.snakeyaml.comments.CommentType;
import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.*;
import org.yaml.snakeyaml.representer.Representer;
import java.io.BufferedReader;
import java.io.Writer;
import java.math.BigInteger;
import java.sql.Timestamp;
-import java.util.Date;
-import java.util.Set;
+import java.util.*;
/**
* A loader for YAML-formatted configurations, using the SnakeYAML library for
@@ -70,8 +69,14 @@ public static Builder builder() {
*
*
This builder supports the following options:
*
+ * - <prefix>.yaml.pretty-printing
+ * - Equivalent to {@link #prettyPrinting(boolean)}
* - <prefix>.yaml.node-style
* - Equivalent to {@link #nodeStyle(NodeStyle)}
+ * - <prefix>.yaml.emit-comments
+ * - Equivalent to {@link #emitComments(boolean)}
+ * - <prefix>.yaml.load-comments
+ * - Equivalent to {@link #loadComments(boolean)}
*
*
* @since 4.0.0
@@ -79,6 +84,7 @@ public static Builder builder() {
public static final class Builder extends AbstractConfigurationLoader.Builder {
private final DumperOptions options = new DumperOptions();
private @Nullable NodeStyle style;
+ private boolean loadComments;
Builder() {
this.indent(4);
@@ -88,10 +94,13 @@ public static final class Builder extends AbstractConfigurationLoader.BuilderComments will always be loaded from files and
+ * stored in memory.
+ *
+ * @param emitComments whether to emit comments
+ * @return this builder
+ * @since 4.2.0
+ */
+ public Builder emitComments(final boolean emitComments) {
+ this.options.setProcessComments(emitComments);
+ return this;
+ }
+
+ /**
+ * Set whether comments should be loaded and stored in memory.
+ *
+ * @param loadComments whether to load comments
+ * @return this builder
+ * @since 4.2.0
+ */
+ public Builder loadComments(final boolean loadComments) {
+ this.loadComments = loadComments;
+ return this;
+ }
+
/**
* Gets the node style to be used by the resultant loader.
*
@@ -164,28 +212,124 @@ public YamlConfigurationLoader build() {
}
}
- private final ThreadLocal yaml;
+ private final ThreadLocal yaml;
private YamlConfigurationLoader(final Builder builder) {
super(builder, new CommentHandler[] {CommentHandlers.HASH});
final LoaderOptions loaderOpts = new LoaderOptions()
.setAcceptTabs(true)
- .setProcessComments(false);
+ .setProcessComments(builder.loadComments);
loaderOpts.setCodePointLimit(Integer.MAX_VALUE);
final DumperOptions opts = builder.options;
opts.setDefaultFlowStyle(NodeStyle.asSnakeYaml(builder.style));
- this.yaml = ThreadLocal.withInitial(() -> new Yaml(new Constructor(loaderOpts), new Representer(opts), opts, loaderOpts));
+ this.yaml = ThreadLocal.withInitial(() -> new PublicYaml(new PublicConstructor(loaderOpts), new Representer(opts), opts, loaderOpts));
}
@Override
protected void loadInternal(final CommentedConfigurationNode node, final BufferedReader reader) {
- node.raw(this.yaml.get().load(reader));
+ final PublicYaml yaml = this.yaml.get();
+ readYamlNode(yaml.getConstructor(), realNode(yaml.compose(reader)), node);
+ }
+
+ private static Node realNode(final Node yamlNode) {
+ if (yamlNode instanceof AnchorNode) {
+ return ((AnchorNode) yamlNode).getRealNode();
+ } else {
+ return yamlNode;
+ }
+ }
+
+ private static void readYamlNode(final PublicConstructor constructor, final Node yamlNode, final CommentedConfigurationNode node) {
+ readComment(yamlNode.getBlockComments(), node);
+ if (yamlNode instanceof MappingNode) {
+ if (((MappingNode) yamlNode).getValue().isEmpty()) {
+ node.raw(Collections.emptyMap());
+ } else {
+ for (NodeTuple tuple : ((MappingNode) yamlNode).getValue()) {
+ final ScalarNode keyNode = (ScalarNode) tuple.getKeyNode();
+ final CommentedConfigurationNode configNode = node.node(keyNode.getValue());
+
+ readComment(keyNode.getBlockComments(), configNode);
+ readYamlNode(constructor, realNode(tuple.getValueNode()), configNode);
+ }
+ }
+ } else if (yamlNode instanceof SequenceNode) {
+ if (((SequenceNode) yamlNode).getValue().isEmpty()) {
+ node.raw(Collections.emptyList());
+ } else {
+ for (Node o : ((SequenceNode) yamlNode).getValue()) {
+ readYamlNode(constructor, realNode(o), node.appendListNode());
+ }
+ }
+ } else {
+ node.raw(constructor.constructObject(yamlNode));
+ }
+ }
+
+ private static void readComment(final List list, final CommentedConfigurationNode node) {
+ if (list == null || node.comment() != null) {
+ return;
+ }
+ final StringJoiner comment = new StringJoiner(CONFIGURATE_LINE_SEPARATOR);
+ for (CommentLine line : list) {
+ String s;
+ if (line.getCommentType() == CommentType.BLANK_LINE) {
+ s = "";
+ } else {
+ s = line.getValue().replace("\r", "");
+ if (!s.isEmpty() && s.charAt(0) == ' ') {
+ s = s.substring(1);
+ }
+ }
+ comment.add(s);
+ }
+ node.comment(comment.toString());
}
@Override
protected void saveInternal(final ConfigurationNode node, final Writer writer) {
- this.yaml.get().dump(node.raw(), writer);
+ final PublicYaml yaml = this.yaml.get();
+ yaml.serialize(fromNode(yaml.getRepresenter(), node, true), writer);
+ }
+
+ private static Node fromNode(final Representer representer, final ConfigurationNode node, final boolean comment) {
+ final Node yamlNode;
+ if (node.isMap()) {
+ final List value = new ArrayList<>();
+ for (Map.Entry