Compare commits

...

6 Commits

Author SHA1 Message Date
Moritz Hölting e55cec1739 update shulkerscript grammar 2025-09-29 14:11:49 +02:00
Moritz Hölting 1d88e4c0df start with first variable docs 2025-09-29 14:11:49 +02:00
Moritz Hölting 62406c9846 add 0.2.0 features from changelog to syntax, update dependencies for online playground 2025-09-29 14:11:49 +02:00
Moritz Hölting da7a8ddd71 automatically switch to linux/mac if detected 2025-09-29 14:11:49 +02:00
Moritz Hölting de85fce893 update dependencies 2025-09-29 14:11:49 +02:00
Moritz Hölting 47425d0ecd implement version badges rehype plugin 2025-09-29 14:11:44 +02:00
20 changed files with 3941 additions and 3267 deletions

View File

@ -6,6 +6,7 @@ import starlightUtils from "@lorenzo_lewis/starlight-utils";
import wasm from "vite-plugin-wasm";
import shikiConfig from "./src/utils/shiki";
import remarkVersionBadges from "./src/remark-plugins/version-badges";
const playgroundSidebarEntry = {
label: 'Playground',
@ -28,9 +29,9 @@ export default defineConfig({
},
favicon: '/favicon.ico',
description: 'A simple and powerful scripting language for Minecraft datapacks.',
social: {
email: 'mailto:shulkerscript@hoelting.dev',
},
social: [
{ icon: 'email', label: 'E-Mail', href: 'mailto:shulkerscript@hoelting.dev' },
],
tableOfContents: { minHeadingLevel: 1, maxHeadingLevel: 3 },
defaultLocale: 'root',
locales: {
@ -67,6 +68,9 @@ export default defineConfig({
Pagination: "./src/components/override/Pagination.astro",
SocialIcons: './src/components/override/SocialIcons.astro',
},
customCss: [
'./src/remark-plugins/version-badges.css',
],
sidebar: [
{
label: "leadingNavLinks",
@ -131,5 +135,10 @@ export default defineConfig({
plugins: [
wasm(),
],
}
},
markdown: {
remarkPlugins: [
remarkVersionBadges,
],
},
});

View File

@ -5,38 +5,42 @@
"scripts": {
"dev": "pnpm build-wasm && astro dev",
"start": "pnpm build-wasm && astro dev",
"build-wasm": "wasm-pack build --release --no-pack ./src/wasm/webcompiler -- --features wee_alloc",
"build-wasm": "wasm-pack build --release --no-pack ./src/wasm/webcompiler -- --features lol_alloc",
"build": "pnpm build-wasm && astro check && astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/check": "^0.9.3",
"@astrojs/react": "^3.6.0",
"@astrojs/starlight": "^0.28.2",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@lorenzo_lewis/starlight-utils": "^0.1.1",
"@monaco-editor/react": "^4.6.0",
"@mui/icons-material": "^6.1.1",
"@mui/material": "^6.1.1",
"@shikijs/monaco": "^1.7.0",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"astro": "^4.15.9",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.2.1",
"sharp": "^0.33.5",
"shiki": "^1.7.0",
"starlight-links-validator": "^0.12.1",
"tm-themes": "^1.4.3",
"typescript": "^5.4.5",
"use-immer": "^0.10.0",
"vite-plugin-wasm": "^3.3.0"
"@astrojs/check": "^0.9.4",
"@astrojs/react": "^4.3.0",
"@astrojs/starlight": "^0.36.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@lorenzo_lewis/starlight-utils": "^0.3.2",
"@monaco-editor/react": "^4.7.0",
"@mui/icons-material": "^7.3.0",
"@mui/material": "^7.3.0",
"@shikijs/monaco": "^3.9.2",
"@types/mdast": "^4.0.4",
"@types/react": "^19.1.9",
"@types/react-dom": "^19.1.7",
"@types/unist": "^3.0.3",
"astro": "^5.12.8",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-icons": "^5.5.0",
"sharp": "^0.34.3",
"shiki": "^3.9.2",
"starlight-links-validator": "^0.18.0",
"tm-themes": "^1.10.7",
"typescript": "^5.9.2",
"unified": "^11.0.5",
"unist-util-visit": "^5.0.0",
"use-immer": "^0.11.0",
"vite-plugin-wasm": "^3.5.0"
},
"devDependencies": {
"sass": "^1.77.6"
"sass": "^1.90.0"
},
"packageManager": "pnpm@9.7.0"
"packageManager": "pnpm@10.17.1"
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
<script defer>
const isLinux = /Linux/.test(window.navigator.platform) || ["Macintosh", "MacIntel", "MacPOC", "Mac68K"].indexOf(window.navigator.platform) !== -1;
if (isLinux) {
(async () => {
function waitForElements(selector) {
return new Promise(resolve => {
if (document.querySelectorAll(selector).length > 0) {
return resolve(document.querySelectorAll(selector));
}
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelectorAll(selector));
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
waitForElements("starlight-tabs a").then(() => {
setTimeout(() => {
document.querySelectorAll("starlight-tabs a").values().filter(el => el.innerText === "Linux / macOS").forEach(el => el.click());
}, 1000);
});
})()
}
</script>

View File

@ -1,7 +1,7 @@
import { useEffect, useState } from "react";
import { useMonaco, type Monaco } from "@monaco-editor/react";
import { useImmer, type Updater } from "use-immer";
import ThemeProvider from "@mui/material/styles/ThemeProvider";
import { ThemeProvider } from "@mui/material/styles";
import ErrorDisplay from "./playground/ErrorDisplay";
import FileView from "./playground/FileView";
import Editor from "./playground/Editor";

View File

@ -1,10 +1,10 @@
import type { File } from "@utils/playground";
import MonacoEditor, { useMonaco } from "@monaco-editor/react";
import { getHighlighter, type Highlighter } from "shiki";
import { createHighlighter, type Highlighter } from "shiki";
import { shikiToMonaco } from "@shikijs/monaco";
import { useEffect, useState } from "react";
import darkPlus from "tm-themes/themes/dark-plus.json";
import lightPlus from "tm-themes/themes/light-plus.json";
import darkPlus from "tm-themes/themes/dark-plus.json" with { type: 'json' };
import lightPlus from "tm-themes/themes/light-plus.json" with { type: 'json' };
import { shulkerscriptGrammar } from "@utils/shulkerscript-grammar";
import { mcfunctionGrammar } from "@utils/mcfunction-grammar";
@ -24,7 +24,7 @@ export default function Editor({
useEffect(() => {
if (monaco) {
if (highlighter == null) {
getHighlighter({
createHighlighter({
themes: [darkPlus as any, lightPlus],
langs: ["toml", shulkerscriptGrammar, mcfunctionGrammar],
}).then((highlighter) => {

View File

@ -3,10 +3,10 @@ title: Getting Started
description: Get started with Shulkerscript
---
import { Steps, FileTree, Tabs, TabItem } from '@astrojs/starlight/components';
import OsTabSwitcher from '../../../components/OsTabSwitcher.astro';
## Installation
To get started with Shulkerscript, you need to install the Shulkerscript CLI.
You can either [download](#download-from-github) the latest release from the GitHub releases page or [build it from source](#building-from-source).
@ -15,6 +15,7 @@ If you want to try out Shulkerscript without installing anything, you can use th
:::
### Quickinstall script *(recommended)*
<OsTabSwitcher />
<Steps>
1. <Tabs>
<TabItem label="Windows" icon="seti:windows">
@ -47,7 +48,7 @@ As a fallback it will build the CLI from source if Rust is installed.
1. Make sure you have [cargo-binstall](https://github.com/cargo-bins/cargo-binstall) installed. If not, follow the [installation instructions](https://github.com/cargo-bins/cargo-binstall?tab=readme-ov-file#installation).
2. Run
```bash
cargo-binstall --git https://github.com/moritz-hoelting/shulkerscript-cli shulkerscript-cli
cargo-binstall --locked --git https://github.com/moritz-hoelting/shulkerscript-cli shulkerscript-cli
```
3. Test the installation by running
```bash
@ -75,7 +76,7 @@ This method takes the longest. If you have no reason to build from source, you m
1. Make sure you have [Rust and Cargo](https://rustup.rs) installed.
2. Install the CLI by running
```bash
cargo install --git https://github.com/moritz-hoelting/shulkerscript-cli
cargo install --locked --git https://github.com/moritz-hoelting/shulkerscript-cli
```
3. Test the installation by running
```bash
@ -152,6 +153,6 @@ Only functions annotated with `#[tick]`, `#[load]`, `#[deobfuscate]` or called f
```bash
shulkerscript build --zip
```
This will create a ZIP archive containing the compiled project.
This will create a ZIP archive in the `dist` directory containing the compiled project.
3. You can now distribute the archive to your users.
</Steps>

View File

@ -5,7 +5,7 @@ description: Learn the syntax of Shulkerscript
## Comments
Single-line comments start with `//` and continue until the end of the line.
Multiline comments start with `/*` and end with `*/`.
Multi-line comments start with `/*` and end with `*/`.
```shulkerscript
// This is a single line comment
@ -29,13 +29,15 @@ They start with a `/` and are followed by the command.
This will result in `say Hello, world!` being included in the `.mcfunction` file.
:::note
Literal commands are just syntactic sugar for the [`run`](#run) keyword.
If you want to use [function arguments](#parameters), take a look at [macro strings](#macro-strings) in combination with the [`run`](#run) keyword, as literal commands are just syntactic sugar.
:::
## Functions
!changed[0.2.0]
Functions are blocks of code that can be executed.
They start with `fn` followed by the name of the function, parenthesis and a block of code.
Optionally they can be preceeded by annotations. When a function has the `pub` keyword in front of it, it will be accessible from other files.
They start with `fn` followed by the name of the function, [arguments](../../reference/functions/#arguments) in parentheses and a block of code.
Optionally they can be preceded by annotations. When a function has the `pub` keyword in front of it, it will be accessible from other files.
```shulkerscript title="src/main.shu"
#[tick]
fn main() {
@ -43,15 +45,48 @@ fn main() {
}
#[deobfuscate]
pub fn hello() {
pub fn hello(macro name) {
/say I can be called from other files!
run `say Hello $(name)`;
}
```
This code defines a function called `main` that will be executed every tick.
:::note
Shulkerscript always requires at least one function annotated with `tick`, `load` or `deobfuscate`.
Otherwise no `.mcfunction` files will be generated.
Otherwise, no `.mcfunction` files will be generated.
:::
### Function calls
Functions can be called by using their name followed by parenthesis.
Arguments can be specified in the parenthesis separated by commas.
```shulkerscript
#[tick]
fn main() {
hello("World");
}
fn hello(macro name) {
run `say Hello, $(name)!`;
}
```
### Return
!since[0.2.0]
To early end a function execution or to return a value, the `return` command was added in Minecraft 1.20.
Similarly, the `return` statement is available in Shulkerscript.
```shulkerscript
#[deobfuscate = "returns_value"]
fn returnsValue() {
return 5;
}
```
:::caution[Important]
This should be always preferred over using the raw `/return` command.
Special handling is done to make returning work in combination with conditionals, groups, etc.
:::
### Annotations
@ -63,22 +98,73 @@ Currently, the following annotations are supported:
- `#[deobfuscate]`: The function will keep the original name in the output (path of the `.shu`-file followed by the function name).
- `#[deobfuscate = "path/to/function"]`: The function will be named as specified in the argument.
### Function calls
Functions can be called by using their name followed by parenthesis.
### Parameters
Functions can be defined with parameters by specifying them in the parenthesis. When calling the function, the arguments have to be passed in the same order.
```shulkerscript
#[tick]
fn main() {
hello();
fn hello(macro greeting, macro name) {
run `say $(greeting), $(name)!`;
}
fn hello() {
/say Hello, world!
#[load]
fn main() {
hello("Hello", "world");
}
```
Keep in mind that if a function has parameters, they cannot be used with the annotations `#[tick]` or `#[load]`, as arguments cannot be provided in that case.
:::caution[Important]
Due to limitations in the way Minecraft macros work, together with the handling of complex statements like groups and if-else statements, special care has to be taken when using the escapement character `\` in the arguments.
This happens because Minecraft inserts the macro text literally into the slot, thereby losing one level of escaping when it is passed to another function. If you absolutely need to use the `\` character in your arguments, compile the datapack, observe how and where it is passed and adjust the escaping accordingly.
:::
### Provided functions
!since[0.2.0]
Shulkerscript provides some internal functions.
These can be called like user-defined functions, but have access to the compilation process and therefore greater possibilities.
| Function | Description |
| -------- | ----------- |
| `print` | Allows to print a string or macro string to the chat. Variables in the macro string are substituted at runtime. |
Example:
```shulkerscript
#[load]
fn load() {
int x = 5;
print(`Value of x=$(x)`);
}
```
## Template Strings
!since[0.2.0]
To use variables inside of strings, template strings can be used.
Instead of normal quotation marks `"`, the backtick `` ` `` is used for this type of string.
Inside a template string, `$(VAR_NAME)` can be used to place the value of the variable at that position in the string.
When wanting to use it in a regular command, use the run syntax.
```shulkerscript
fn macroFunction(macro name) {
run `say Hello $(name)`;
}
```
:::tip
You can use template strings in most places inside functions where you would use a string.
This includes execute blocks and conditional statements.
:::
## Imports
Functions from other files can be imported by using the `from`-`import` syntax.
Functions and global variables from other files can be imported by using the `from`-`import` syntax.
```shulkerscript title="src/main.shu"
namespace "foo";
@ -108,10 +194,12 @@ from "./foo" import bar, baz;
```
## Tags
!changed[0.2.0]
In Minecraft, tags are used to group multiple items, blocks, entities, etc. together.
In Shulkerscript, tags can be defined right in the code, where they are needed.
```shulkerscript
tag "foo" of "block" [
tag<"block"> "foo" [
"minecraft:stone",
"minecraft:dirt"
]
@ -121,7 +209,7 @@ This will result in a tag of type `block` with the name `foo` containing the blo
If you want the tag to replace, instead of append to the existing tag, you can use the `replace` keyword.
```shulkerscript
tag "foo" of "block" replace [
tag<"block"> "foo" replace [
"minecraft:stone",
"minecraft:dirt"
]
@ -139,12 +227,12 @@ the types:
But you can also use custom types, refer to [this page](https://minecraft.wiki/w/Tag) for more information.
:::tip
`of "[type]"` can be omitted and will default to `"function"`.
`<"[type]">` can be omitted and will default to `"function"`.
:::
## Conditional Statements
Conditional statements are used to execute code based on a condition.
They start with `if` followed by a condition in parenthesis and a block of code.
They start with `if` followed by a condition in parentheses and a block of code.
Optionally they can be followed by an `else` block.
```shulkerscript
if ("block ~ ~-1 ~ minecraft:stone") {
@ -158,7 +246,7 @@ To learn more about how to combine or negate conditions, refer to the [if-else s
## Execute Blocks
Execute blocks are used to execute a block of code in a specific context.
They consist of the keyword you would pass to the `/execute` command followed the argument as a string in parenthesis and a block of code.
They consist of the keyword you would pass to the `/execute` command followed the argument as a string in parentheses and a block of code.
```shulkerscript
as ("@a") { // execute as all players
/say Hello, world!
@ -177,7 +265,7 @@ positioned ("0 0 0"), in ("minecraft:overworld") {
```
:::tip[Did you know?]
[Conditionals](#conditional-statements) are also implemented as execute blocks.Therefore you can chain them together with other execute blocks. Keep in mind that an if-else statement can only be used as the last execute block in a chain.
[Conditionals](#conditional-statements) are also implemented as execute blocks. Therefore, you can chain them together with other execute blocks. Keep in mind that an if-else statement can only be used as the last execute block in a chain.
:::
### Supported Execute Blocks
@ -212,6 +300,36 @@ group {
}
```
## Variables
!since[0.2.0]
Variables can be used to store values.
There are different types available, and they differ in how they are stored,
what data can be stored in them and how they can be used.
| Keyword | Storage method | Usage |
| ------- | --------------- | ----- |
| `int` | Scoreboard | Single integer values |
| `bool` | Data Storage | Single boolean values |
| `val` | Compiler memory | Can store any data type, but has to be known at compile time |
| `int[NUMBER]` | Scoreboard | Array of `NUMBER` integers |
| `bool[NUMBER]` | Data Storage | Array of `NUMBER` booleans |
| `int[]` | Scoreboard | Map of integers |
| `bool[]` | Entity tag | Map of booleans (keys have to be valid entities in the world) |
```shulkerscript
int x = 5;
bool y = true;
int z = x + 2;
int[2] arr;
arr[0] = 1;
arr[1] = 2;
```
Read more in the [reference](../../reference/variables).
## Run
The `run` keyword is used to evaluate the following expression and include the resulting command in the output.
```shulkerscript

View File

@ -9,11 +9,25 @@ sidebar:
Shulkerscript supports writing Lua code directly in your scripts. This allows you to use Lua's powerful features and libraries to extend the functionality of your scripts.
The Lua code is embedded in the Shulkerscript code using the `lua` keyword. In the future, you will be able to pass arguments to the Lua code, but for now, you can only write Lua code without arguments.
The Lua code is embedded in the Shulkerscript code using the `lua` keyword. You can pass variables from Shulkerscript to Lua, and the Lua code can return values that can be used in Shulkerscript.
```shulkerscript
int a = 5;
int b = lua(a) {
-- Lua code goes here
print("The objective is: " .. a.objective);
print("The target is: " .. a.target);
return 10;
};
```
:::caution
The Lua code is run during **compilation**, not during **execution** of the Shulkerscript. This means that the Lua code can only access the variables that are available at compile time, e.g. therefore receiving the objective and target of an `int` variable, but not the actual value that is only available during execution.
:::
Your Lua code should return a string value when used in combination with the run keyword. This string returned will be included in the output.
## Syntax
```shulkerscript
run lua() {
-- Lua code goes here
@ -21,17 +35,52 @@ run lua() {
};
```
## Globals
The following globals are available in the Lua environment:
- `shu_location`: The relative filepath of the script being executed
## Inputs
The variables from Shulkerscript can be passed to lua by putting the variable name in the parentheses of the `lua` keyword. The variable will be available in the Lua code as a global variable with the same name.
Depending on the type of the variable, the Lua code will receive a different type of value. The following table shows the mapping of Shulkerscript types to Lua types:
| Shulkerscript Type | Lua Type |
| ------------------------ | ------------------------------------------- |
| int | table { objective: string, target: string } |
| bool | table { storage: string, path: string } |
| macro function parameter | string "$(macro)$ |
```shulkerscript
fn greet(name) {
run `say Greeting $(name) from Lua:`;
run lua(name) {
return {
value="Hello, " .. name .. "!",
contains_macro=true
}
};
}
```
:::note
When returning a value that contains a macro, instead of just returning the value, you have to return a table with the value and a boolean that indicates that the value contains a macro.
:::
## Shulkerscript Module
The Lua environment has access to a few globals that are provided by Shulkerscript. These globals are available in the `shulkerscript` table. The following values are available in the `shulkerscript` table:
- `version`: The version of Shulkerscript that is being used in string format.
- `file_path`: The path to the file where the Lua code is written.
- `start_line`: The line number where the Lua code starts.
- `end_line`: The line number where the Lua code ends.
- `start_column`: The column number where the Lua code starts.
- `end_column`: The column number where the Lua code ends.
After variables are introduced in Shulkerscript, it is planned to make them available in the Lua environment as well.
:::note
:::tip[Feedback Requested]
If you can think of any other globals that should be available in the Lua environment, please let us know! This could include:
- variables with values (like `shu_location`)
- variables with values (like `version`)
- functions that calculate/modify values (general utility things not provided by Lua or specific to Shulkerscript)
- functions that interact with the Shulkerscript environment (flags to set for the compiler to alter the build process)
Please either write a mail or open an issue on GitHub. The links can be found in the upper right corner of the page.
:::

View File

@ -12,9 +12,12 @@ name = "shulkerpack"
# The description of the datapack
description = "I created this datapack with Shulkerscript"
# The pack format of the datapack (https://minecraft.wiki/w/Data_pack#Pack_format)
pack_format = 26
pack_format = 81
# The version of the datapack (currently not used)
version = "0.1.0"
# Optional set the main namespace of the datapack
# Otherwise derived from the name
main-namespace = "shulkerpack"
[compiler] # optional
# path to the folder to use as a datapack template

View File

@ -0,0 +1,56 @@
---
title: Variables
description: Different types and use-cases of variables
---
Variables are used to store data to be used later. There are different types of variables for numbers and booleans.
In Minecraft, these variables are stored in scoreboards, tags, and NBT storages, depending on the type of data. Shulkerscript provides a way to work with these variables in a more abstract way.
## Integer Variables
Integer variables are used to store whole numbers. They can be created using the `int` keyword.
```shulkerscript
// Create an integer variable called myInt
int myInt = 42;
// Create an array of integers
int manyInts[5] = [1, 2, 3, 4, 5];
// Access the third element of the array (indexing starts at 0)
manyInts[2] = 42;
// Create an entity-integer map/scoreboard
int myScore[];
// Set the score of the nearest player to 42
myScore["@p"] = 42;
```
### Operations
You can perform operations on integer variables, such as addition, subtraction, multiplication, and division.
```shulkerscript
int a = 5;
int b = 3;
int c = a + b; // c is now 8
int d = a - b; // d is now 2
int e = a * b; // e is now 15
int f = a / b; // f is now 1, integer division
int g = a % b; // g is now 2, remainder of the division
```
## Boolean Variables
Boolean variables are used to store true or false values. They can be created using the `bool` keyword.
```shulkerscript
// Create a boolean variable called myBool
bool myBool = true;
// Create an array of booleans
bool manyBools[5] = [true, false, true, false, true];
// Create an entity-boolean map/tag and set the value of the nearest player to true at the same time
bool myScore["@p"] = true;
```

View File

@ -0,0 +1,55 @@
.version-badges {
display: flex;
gap: 0.5rem;
margin: 0.5em 0 !important;
margin-bottom: 0 !important;
flex-wrap: wrap;
}
.version-badges + p {
margin-top: 0.5rem !important;
}
.version-badges span {
font-size: 0.75rem;
padding: 2px 6px;
border-radius: 4px;
white-space: nowrap;
border: 1px solid transparent;
}
html[data-theme='light'] .version-badges .since-badge {
background-color: #e0f7fa;
color: #00796b;
border-color: #b2ebf2;
}
html[data-theme='light'] .version-badges .changed-badge {
background-color: #fff8e1;
color: #795548;
border-color: #ffe082;
}
html[data-theme='light'] .version-badges .deprecated-badge {
background-color: #ffebee;
color: #b71c1c;
border-color: #ffcdd2;
}
html[data-theme='dark'] .version-badges .since-badge {
background-color: #004d40;
color: #b2dfdb;
border-color: #00695c;
}
html[data-theme='dark'] .version-badges .changed-badge {
background-color: #3e2723;
color: #ffe0b2;
border-color: #6d5037;
}
html[data-theme='dark'] .version-badges .deprecated-badge {
background-color: #4a0000;
color: #ffcdd2;
border-color: #b71c1c;
}

View File

@ -0,0 +1,64 @@
import { visit } from 'unist-util-visit';
import type { Plugin } from 'unified';
import type { Parent } from 'unist';
import type { Paragraph } from 'mdast';
const INLINE_REGEX = /!(since|changed|deprecated)\[([^\]]+)\]/g;
const remarkVersionBadges: Plugin = () => {
return (tree) => {
visit(tree, 'paragraph', (node, index, parent) => {
const paragraph = node as Paragraph;
const parentNode = parent as Parent;
if (!paragraph.children || paragraph.children.length !== 1) return;
const child = paragraph.children[0];
if(child.type !== 'text') return;
const value = child.value;
const matches = [...value.matchAll(INLINE_REGEX)];
if(matches.length === 0) return;
const isOnlyBadges = matches.reduce((allMatched, match) => {
return allMatched && match[0] === value.slice(match.index!, match.index! + match[0].length)
}, true);
if (!isOnlyBadges) return;
const badges: string[] = matches.map(([_, type, version]) => {
let label = '';
let className = '';
let title = '';
switch(type) {
case 'since':
label = `Since v${version}`;
className = 'since-badge';
title = `This feature was added in v${version}`;
break;
case 'changed':
label = `Changed in v${version}`;
className = 'changed-badge';
title = `This feature was changed in v${version}`;
break;
case 'deprecated':
label = `Deprecated since v${version}`;
className = 'deprecated-badge'
title = `This feature is deprecated since v${version}`;
break;
}
return `<span class="${className}" title="${title}">${label}</span>`;
});
const htmlNode = {
type: 'html',
value: `<div class="version-badges">${badges.join('')}</div>`,
} as const;
parentNode.children.splice(index!, 1, htmlNode);
});
}
}
export default remarkVersionBadges;

View File

@ -1,12 +1,13 @@
import { shulkerscriptGrammar } from "./shulkerscript-grammar";
import { mcfunctionGrammar } from "./mcfunction-grammar";
import { mcscriptGrammar } from "./mcscript-grammar";
import shulkerscriptGrammar from "./shulkerscript-grammar.json" with { type: 'json' };
const config = {
langs: [
shulkerscriptGrammar,
mcfunctionGrammar,
mcscriptGrammar,
"lua",
],
};

View File

@ -0,0 +1,206 @@
{
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
"name": "shulkerscript",
"aliases": ["shu"],
"displayName": "Shulkerscript",
"fileTypes": ["shu"],
"scopeName": "source.shulkerscript",
"patterns": [
{ "include": "#comments" },
{ "include": "#commandLiteral" },
{ "include": "#luaBlock" },
{ "include": "#declarationKeywords" },
{ "include": "#flowKeywords" },
{ "include": "#generalKeywords" },
{ "include": "#executeKeywords" },
{ "include": "#types" },
{ "include": "#functions" },
{ "include": "#strings" },
{ "include": "#numbers" },
{ "include": "#annotations" },
{ "include": "#operators" },
{ "include": "#punctuation" }
],
"repository": {
"comments": {
"patterns": [
{
"name": "comment.line.doc.shulkerscript",
"match": "///.*$"
},
{
"name": "comment.line.double-slash.shulkerscript",
"match": "//.*$"
},
{
"name": "comment.block.shulkerscript",
"begin": "/\\*",
"end": "\\*/"
}
]
},
"declarationKeywords": {
"patterns": [
{
"name": "keyword.declaration.shulkerscript",
"match": "\\b(namespace|fn|pub|val|tag)\\b"
}
]
},
"flowKeywords": {
"patterns": [
{
"name": "keyword.control.flow.shulkerscript",
"match": "\\b(if|else|return|group)\\b"
}
]
},
"generalKeywords": {
"patterns": [
{
"name": "keyword.control.shulkerscript",
"match": "\\b(from|import|replace|run)\\b"
}
]
},
"executeKeywords": {
"patterns": [
{
"name": "keyword.control.execute.shulkerscript",
"match": "\\b(align|anchored|as|asat|at|facing|in|on|positioned|rotated|store|summon)\\b"
}
]
},
"types": {
"patterns": [
{
"name": "storage.type.shulkerscript",
"match": "\\b(int|bool|macro)\\b"
}
]
},
"functions": {
"patterns": [
{
"name": "entity.name.function.shulkerscript",
"match": "\\b([A-Za-z_][A-Za-z0-9_]*)\\s*(?=\\()"
}
]
},
"strings": {
"patterns": [
{
"name": "string.quoted.double.shulkerscript",
"begin": "\"",
"end": "\"",
"patterns": [
{
"name": "constant.character.escape.shulkerscript",
"match": "\\\\."
}
]
},
{
"name": "string.interpolated.shulkerscript",
"begin": "`",
"end": "`",
"patterns": [
{
"name": "variable.interpolation.shulkerscript",
"begin": "\\$\\(",
"end": "\\)",
"beginCaptures": {
"0": {
"name": "punctuation.interpolation.begin.shulkerscript"
}
},
"endCaptures": {
"0": {
"name": "punctuation.interpolation.end.shulkerscript"
}
},
"patterns": [
{ "include": "$self" },
{
"name": "variable.interpolation.shulkerscript",
"match": ".+?"
}
]
}
]
}
]
},
"numbers": {
"patterns": [
{
"name": "constant.numeric.shulkerscript",
"match": "\\b[0-9]+\\b"
}
]
},
"annotations": {
"patterns": [
{
"name": "meta.annotation.shulkerscript",
"begin": "#\\[",
"end": "\\]",
"patterns": [
{ "include": "#declarationKeywords" },
{ "include": "#flowKeywords" },
{ "include": "#generalKeywords" },
{ "include": "#strings" },
{ "include": "#numbers" }
]
}
]
},
"operators": {
"patterns": [
{
"name": "keyword.operator.shulkerscript",
"match": "(\\+|\\-|\\*|\\/|%|==|!=|<=|>=|<|>|&&|\\|\\||=|!)"
}
]
},
"punctuation": {
"patterns": [
{
"name": "punctuation.separator.shulkerscript",
"match": "[,;]"
},
{
"name": "punctuation.brackets.shulkerscript",
"match": "[\\[\\]\\(\\)\\{\\}]"
},
{
"name": "punctuation.accessor.shulkerscript",
"match": "\\."
}
]
},
"commandLiteral": {
"name": "string.commandliteral.shulkerscript",
"match": "^\\s*(/\\w+)(.*)$",
"captures": {
"1": {
"name": "keyword.other.commandliteral.shulkerscript"
},
"2": {
"name": "string.command.shulkerscript"
}
}
},
"luaBlock": {
"name": "meta.embedded.lua.shulkerscript",
"begin": "(lua)\\s*\\(\\s*((\\w+\\s*,\\s*)*\\w+?\\s*)?\\)\\s*\\{",
"end": "\\}",
"beginCaptures": {
"1": {
"name": "keyword.control.lua.shulkerscript"
}
},
"patterns": [{ "include": "source.lua" }]
}
}
}

View File

@ -1,205 +1,5 @@
import type { LanguageInput } from "shiki";
export const shulkerscriptGrammar: LanguageInput = {
name: "shulkerscript",
aliases: ["shu"],
displayName: "Shulkerscript",
fileTypes: ["shu"],
scopeName: "source.shulkerscript",
patterns: [
{
include: "#namespaceKeyword",
},
{
include: "#functionContents",
},
{
include: "#functionDeclaration",
},
{
include: "#functionAnnotation",
},
{
include: "#importStatement",
},
{
include: "#tagDeclaration",
},
],
repository: {
$base: {},
$self: {},
// Groupings
functionContents: {
patterns: [
{ include: "#docComment" },
{ include: "#lineComment" },
{ include: "#blockComment" },
{ include: "#commandLiteral" },
{ include: "#lua" },
{ include: "#groupBlock" },
{ include: "#stringLiteral" },
{ include: "#binaryOperator" },
{ include: "#executeKeyword" },
{ include: "#elseKeyword" },
{ include: "#runKeyword" },
{ include: "#functionCall" },
],
},
import grammar from "./shulkerscript-grammar.json" with { type: 'json' };
// Components
lineComment: {
name: "comment.line.shulkerscript",
match: "//.*$",
},
blockComment: {
name: "comment.block.shulkerscript",
begin: "/\\*",
end: "\\*/",
},
docComment: {
name: "comment.documentation.shulkerscript",
match: "///.*$",
},
namespaceKeyword: {
name: "keyword.other.namespace.shulkerscript",
match: "\\bnamespace\\b",
},
functionDeclaration: {
begin: "^\\s*(pub\\s)?(fn)\\s+(\\w+)\\(\\s*\\)\\s*{",
end: "}",
captures: {
1: {
name: "keyword.control.public.shulkerscript",
},
2: {
name: "keyword.control.function.shulkerscript",
},
3: {
name: "entity.name.function.shulkerscript",
},
},
patterns: [{ include: "#functionContents" }],
},
functionAnnotation: {
name: "entity.annotation.function.shulkerscript",
match: '(#\\[)(\\w+)(?:\\s*=\\s*"[^"]+")?(\\])',
captures: {
1: {
name: "keyword.operator.annotation.shulkerscript",
},
2: {
name: "entity.annotation.function.shulkerscript",
},
3: {
name: "keyword.operator.annotation.shulkerscript",
},
},
},
functionCall: {
match: "(\\w+)\\s*\\(\\s*(?:\\w+\\s*)?\\)",
captures: {
1: {
name: "entity.name.function.shulkerscript",
},
},
},
tagDeclaration: {
begin: '^\\s*(tag)\\s+("\\w+")\\s+(?:(of)\\s+("\\w+")\\s+)?(?:(replace)\\s+)?\\[',
end: "]",
patterns: [{ include: "#stringLiteral" }],
captures: {
1: {
name: "keyword.control.tag.shulkerscript",
},
2: {
name: "string.quoted.double.shulkerscript",
},
3: {
name: "keyword.control.of.shulkerscript",
},
4: {
name: "string.quoted.double.shulkerscript",
},
5: {
name: "keyword.control.replace.shulkerscript",
},
},
},
binaryOperator: {
name: "punctuation.operator.binary.shulkerscript",
match: "(&&|\\|\\|)",
},
executeKeyword: {
name: "keyword.control.execute.shulkerscript",
match: "\\b(if|align|as|asat|at|facing|in|on|positioned|rotated|store|summon)\\b",
},
elseKeyword: {
name: "keyword.control.else.shulkerscript",
match: "\\belse\\b",
},
runKeyword: {
name: "keyword.control.run.shulkerscript",
match: "\\brun\\b",
},
commandLiteral: {
name: "string.commandliteral.shulkerscript",
match: "^\\s*(/\\w+)(.*)$",
captures: {
1: {
name: "keyword.other.commandliteral.shulkerscript",
},
2: {
name: "string.command.shulkerscript",
},
},
},
stringLiteral: {
name: "string.quoted.double.shulkerscript",
begin: '"',
end: '"',
patterns: [
{
name: "constant.character.escape.shulkerscript",
match: "\\\\.",
},
],
},
lua: {
name: "entity.lua.shulkerscript",
begin: "(lua)\\s*\\(\\s*\\)\\s*{",
end: "}",
beginCaptures: {
1: {
name: "keyword.control.lua.shulkerscript",
},
},
patterns: [{ include: "source.lua" }],
},
groupBlock: {
name: "entity.group.shulkerscript",
begin: "^\\s*(group)\\s*{",
end: "}",
captures: {
1: {
name: "keyword.control.group.shulkerscript",
},
2: {
name: "entity.name.group.shulkerscript",
},
},
patterns: [{ include: "#functionContents" }],
},
importStatement: {
name: "keyword.import.other.shulkerscript",
begin: "from\\s",
end: "import\\s(\\w+(?:\\s?,\\s?(?:\\w+))*)\\s?,?\\s?",
captures: {
1: {
name: "entity.name.function.shulkerscript",
},
},
patterns: [{ include: "#stringLiteral" }],
},
},
};
export const shulkerscriptGrammar: LanguageInput = grammar;

File diff suppressed because it is too large Load Diff

View File

@ -10,21 +10,25 @@ crate-type = ["cdylib"]
opt-level = "z"
[features]
wee_alloc = ["dep:wee_alloc"]
lol_alloc = ["dep:lol_alloc"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ansi-to-html = "0.2.1"
anyhow = "1.0.86"
ansi-to-html = "0.2.2"
anyhow = "1.0.99"
base64 = "0.22.1"
cfg-if = "1.0.0"
colored = "2.1.0"
cfg-if = "1.0.1"
colored = "3.0.0"
console_error_panic_hook = "0.1.7"
serde = "1.0"
lol_alloc = { version = "0.4.1", optional = true }
serde = "1.0.219"
serde-wasm-bindgen = "0.6.5"
shulkerscript = { version = "0.1.0", default-features = false, features = ["serde", "shulkerbox"] }
toml = "0.8.19"
wasm-bindgen = "0.2.93"
wee_alloc = { version = "0.4.5", optional = true }
zip = { version = "2.1.3", default-features = false, features = ["deflate"] }
# shulkerscript = { version = "0.1.0", default-features = false, features = ["serde", "shulkerbox"] }
shulkerscript = { git = "https://github.com/moritz-hoelting/shulkerscript-lang.git", default-features = false, features = ["serde", "shulkerbox"], rev = "d988a10d9dee222d16ec2b30da202fb8a9a1f051" }
toml = "0.9.5"
wasm-bindgen = "0.2.100"
zip = { version = "4.3.0", default-features = false, features = ["deflate"] }
[package.metadata.wasm-pack.profile.release]
wasm-opt = false

View File

@ -19,9 +19,9 @@ mod fs;
mod pack_toml;
cfg_if::cfg_if! {
if #[cfg(feature = "wee_alloc")] {
if #[cfg(feature = "lol_alloc")] {
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
static ALLOC: lol_alloc::LockedAllocator<lol_alloc::FreeListAllocator> = lol_alloc::LockedAllocator::new(lol_alloc::FreeListAllocator::new());
}
}
@ -103,7 +103,7 @@ fn _compile(root_dir: &VFolder) -> Result<VFolder> {
};
let res = pack_format.and_then(|pack_format| {
shulkerscript::compile(&printer, root_dir, pack_format, &get_script_paths(root_dir))
shulkerscript::compile(&printer, root_dir, "main", pack_format, &get_script_paths(root_dir))
.map_err(|e| e.into())
});

View File

@ -6,7 +6,8 @@
"@*": ["src/*"]
},
"jsx": "react-jsx",
"jsxImportSource": "react"
"jsxImportSource": "react",
"resolveJsonModule": true
},
"exclude": ["dist", "node_modules"]
}