Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions docs/docs/30-authoring/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ same as the [Minecraft give command](https://minecraft.wiki/w/Commands/give). Th
shown item name are retrieved from. `<ItemLink id="minecraft:stick" components="rarity=epic" />` will show the tooltip
using the epic rarity color, for example.

If the link targets an item from the same namespace as the page, an error will be shown if there's no page for that item.
You can opt out of this behavior by adding `optional={true}` or just `optional` to the link, or opt in for items of other mods by
adding `optional={false}`.

### Command Links

You can make links that run a command when clicked using `<CommandLink command="/command">text text</CommandLink>`.
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/guideme/compiler/tags/ItemLinkCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ public void compile(PageCompiler compiler, LytFlowParent parent, MdxJsxElementFi
var stack = itemAndId.getRight();

var linksTo = compiler.getIndex(ItemIndex.class).get(id);

// We'll error out for item-links to our own mod because we expect them to have a page
// while we don't have pages for Vanilla items or items from other mods.
if (linksTo == null && id.getNamespace().equals(compiler.getPageId().getNamespace())) {
// But authors can opt-in or out of this behavior.
boolean defaultOptional = !id.getNamespace().equals(compiler.getPageId().getNamespace());
var optional = MdxAttrs.getBoolean(compiler, parent, el, "optional", defaultOptional);
if (linksTo == null && !optional) {
parent.append(compiler.createErrorFlowContent("No page found for item " + id, el));
return;
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/guideme/compiler/tags/MdxAttrs.java
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,10 @@ public static boolean getBoolean(MdxJsxElementFields el, String name, boolean de
} else if (expressionValue.equals("false")) {
return false;
}
} else if (attribute.getStringValue() == null) {
// This allows for standalone attributes ("<Tag attr />") to be recognized as true
// but differentiates them from <Tag attr={null} />
return true;
}

throw new AttributeException(name, name + " should be {true} or {false}");
Expand Down
74 changes: 74 additions & 0 deletions src/test/java/guideme/compiler/tags/ItemLinkCompilerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package guideme.compiler.tags;

import static org.assertj.core.api.Assertions.assertThat;

import guideme.Guide;
import guideme.GuidePage;
import guideme.compiler.PageCompiler;
import guideme.compiler.TagCompiler;
import guideme.extensions.ExtensionCollection;
import guideme.internal.GuideME;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import net.minecraft.resources.Identifier;
import net.minecraft.server.MinecraftServer;
import net.neoforged.testframework.junit.EphemeralTestServerProvider;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(EphemeralTestServerProvider.class)
class ItemLinkCompilerTest {

public ItemLinkCompilerTest(MinecraftServer server) {
}

@Test
void testMissingTargetPageFromSameNamespace() throws IOException {
var page = compilePage("minecraft:test_page", """
<ItemLink id="minecraft:stick" />
""");

assertThat(page.document().getTextContent()).contains("No page found for item minecraft:stick");
}

@Test
void testMissingTargetPageFromSameNamespaceButOptional() throws IOException {
var page = compilePage("minecraft:test_page", """
<ItemLink id="minecraft:stick" optional />
""");

assertThat(page.document().getTextContent()).isEqualTo("Stick");
}

@Test
void testMissingTargetPageFromOtherNamespace() throws IOException {
var page = compilePage("giudeme:test_page", """
<ItemLink id="minecraft:stick" />
""");

assertThat(page.document().getTextContent()).isEqualTo("Stick");
}

@Test
void testMissingTargetPageFromOtherNamespaceButExplicitlyNonOptional() throws IOException {
var page = compilePage("giudeme:test_page", """
<ItemLink id="minecraft:stick" optional={false} />
""");

assertThat(page.document().getTextContent()).contains("No page found for item minecraft:stick");
}

private static GuidePage compilePage(String pageId, String content) throws IOException {
try (var in = new ByteArrayInputStream(content.getBytes())) {
var parsed = PageCompiler.parse(GuideME.MOD_ID, "en_us", Identifier.parse(pageId), in);
var testPages = Guide.builder(Identifier.fromNamespaceAndPath(GuideME.MOD_ID, "test"))
.watchDevelopmentSources(false)
.register(false)
.disableOpenHotkey()
.build();
return PageCompiler.compile(testPages, ExtensionCollection.builder()
.add(TagCompiler.EXTENSION_POINT, new ItemLinkCompiler())
.build(), parsed);
}
}
}
31 changes: 31 additions & 0 deletions src/test/java/guideme/compiler/tags/MdxAttrsTest.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package guideme.compiler.tags;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import guideme.compiler.PageCompiler;
import guideme.document.LytErrorSink;
import guideme.internal.GuideME;
import guideme.libs.mdast.mdx.model.MdxJsxAttribute;
import guideme.libs.mdast.mdx.model.MdxJsxAttributeNode;
import guideme.libs.mdast.mdx.model.MdxJsxElementFields;
import guideme.libs.mdast.mdx.model.MdxJsxTextElement;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
Expand Down Expand Up @@ -45,4 +51,29 @@ void testVector3() {
"");
MdxAttrs.getVector3(compiler, errorSink, el, "from", null);
}

@Test
void testBooleanAttrsWithExpression() throws Exception {
var parsedPage = PageCompiler.parse("ignored", "en_us", GuideME.makeId("test"),
new ByteArrayInputStream("<Tag attr={true} />".getBytes()));
var firstTag = parsedPage.getAstRoot().children().getFirst();
assertTrue(MdxAttrs.getBoolean((MdxJsxElementFields) firstTag, "attr", false));
}

@Test
void testBooleanAttrsWithoutValue() throws Exception {
var parsedPage = PageCompiler.parse("ignored", "en_us", GuideME.makeId("test"),
new ByteArrayInputStream("<Tag attr />".getBytes()));
var firstTag = parsedPage.getAstRoot().children().getFirst();
assertTrue(MdxAttrs.getBoolean((MdxJsxElementFields) firstTag, "attr", false));
}

@Test
void testBooleanAttrsWithNullExpressionValue() throws Exception {
var parsedPage = PageCompiler.parse("ignored", "en_us", GuideME.makeId("test"),
new ByteArrayInputStream("<Tag attr={null} />".getBytes()));
var firstTag = parsedPage.getAstRoot().children().getFirst();
var e = assertThrows(Exception.class, () -> MdxAttrs.getBoolean((MdxJsxElementFields) firstTag, "attr", false));
assertThat(e).hasMessage("attr should be {true} or {false}");
}
}
Loading