├── icon.png ├── .gitignore ├── imgs └── preview.png ├── tests ├── comprehensive │ ├── no_newline.mcfunction │ ├── uuid.mcfunction │ ├── parsers │ │ ├── message │ │ │ ├── invalid.mcfunction │ │ │ └── valid.mcfunction │ │ ├── entity_anchor │ │ │ ├── valid.mcfunction │ │ │ └── invalid.mcfunction │ │ ├── swizzle │ │ │ ├── invalid.mcfunction │ │ │ └── valid.mcfunction │ │ ├── block_position.mcfunction │ │ ├── dimension │ │ │ ├── invalid.mcfunction │ │ │ └── valid.mcfunction │ │ ├── int_range │ │ │ ├── invalid.mcfunction │ │ │ └── valid.mcfunction │ │ ├── rotation │ │ │ ├── invalid.mcfunction │ │ │ └── valid.mcfunction │ │ ├── block_position │ │ │ ├── valid.mcfunction │ │ │ └── invalid.mcfunction │ │ └── position │ │ │ ├── invalid.mcfunction │ │ │ └── valid.mcfunction │ ├── root_redirect.mcfunction │ ├── README.md │ ├── json.mcfunction │ ├── nbt_list.mcfunction │ ├── greedy_commands.mcfunction │ ├── fakeplayer.mcfunction │ ├── misc.mcfunction │ ├── number.mcfunction │ ├── operation.mcfunction │ ├── commands.mcfunction │ ├── block_predicate.mcfunction │ ├── strings.mcfunction │ ├── nbt_compound.mcfunction │ ├── resource_location.mcfunction │ ├── nbt_path │ │ ├── valid.mcfunction │ │ └── invalid.mcfunction │ ├── range.mcfunction │ ├── showcase.mcfunction │ ├── comments.mcfunction │ ├── commands │ │ └── effect.mcfunction │ ├── target_selectors │ │ ├── invalid.mcfunction │ │ └── valid.mcfunction │ ├── text_components │ │ ├── invalid.mcfunction │ │ └── valid.mcfunction │ ├── everything.mcfunction │ └── selector.mcfunction ├── jmc.mcfunction ├── macros.mcfunction ├── mcbuild.mcfunction ├── demo.mcfunction ├── vanilla.mcfunction └── bolt.mcfunction ├── .vscode └── launch.json ├── mcfunction.tmPreferences ├── language-configuration.json ├── LICENSE ├── package.json ├── .github ├── ISSUE_TEMPLATE │ ├── feature-request.yaml │ └── bug-report.yaml └── workflows │ └── publish.yml ├── CHANGELOG.md ├── README.md ├── mcfunction.tmLanguage.yaml ├── mcfunction.tmLanguage.json └── mcfunction.tmLanguage /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MinecraftCommands/syntax-mcfunction/HEAD/icon.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | node_modules/ 4 | *.vsix 5 | 6 | .vscode/* 7 | !.vscode/launch.json 8 | -------------------------------------------------------------------------------- /imgs/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MinecraftCommands/syntax-mcfunction/HEAD/imgs/preview.png -------------------------------------------------------------------------------- /tests/comprehensive/no_newline.mcfunction: -------------------------------------------------------------------------------- 1 | # no newline at the end of this file 2 | function my.pack:my/folder/hello_world -------------------------------------------------------------------------------- /tests/comprehensive/uuid.mcfunction: -------------------------------------------------------------------------------- 1 | execute as f7a39418-72ca-4bf2-bc7e-ba9df67a4707 run 2 | execute as 0-0-0-0-0 run 3 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/message/invalid.mcfunction: -------------------------------------------------------------------------------- 1 | say hello@e[world 2 | say hello@e[sort]world 3 | say hello@e[sort=]world 4 | -------------------------------------------------------------------------------- /tests/comprehensive/root_redirect.mcfunction: -------------------------------------------------------------------------------- 1 | execute as @a run 2 | execute as @a run say hello 3 | execute as running_player say hello 4 | -------------------------------------------------------------------------------- /tests/comprehensive/README.md: -------------------------------------------------------------------------------- 1 | # Comprehensive Test Suite 2 | > *Originally from [language-mcfunction](https://github.com/Arcensoth/language-mcfunction/tree/main/tests) 3 | -------------------------------------------------------------------------------- /tests/comprehensive/json.mcfunction: -------------------------------------------------------------------------------- 1 | tellraw @a {"text": "hello world", "color": "blue"} 2 | tellraw @a [{"text": "hello", "color": "blue"}, {"text": "world", "color": "blue"}] 3 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/message/valid.mcfunction: -------------------------------------------------------------------------------- 1 | say hello 2 | say hello world 3 | say hello @e world 4 | say hello@eworld 5 | say hello@e[tag=foo]world 6 | say @@e[tag=x,tag=!x]e 7 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/entity_anchor/valid.mcfunction: -------------------------------------------------------------------------------- 1 | execute anchored eyes run say hello 2 | execute anchored feet run say hello 3 | 4 | execute anchored eyes anchored feet run say hello 5 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/swizzle/invalid.mcfunction: -------------------------------------------------------------------------------- 1 | # non-executable 2 | execute align y 3 | execute align xz 4 | execute align xyz 5 | 6 | # not real axes 7 | execute align a run say hello 8 | execute align abc run say hello 9 | -------------------------------------------------------------------------------- /tests/comprehensive/nbt_list.mcfunction: -------------------------------------------------------------------------------- 1 | # test array types 2 | give @s minecraft:stone{foo: [B; 123b, 123b]} 3 | give @s minecraft:stone{foo: [I; 123, 456]} 4 | give @s minecraft:stone{foo: [L; 123L, 456L]} 5 | give @s minecraft:stone{foo: [foo; 123L, 456L]} 6 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/entity_anchor/invalid.mcfunction: -------------------------------------------------------------------------------- 1 | # non-executable 2 | execute anchored eyes 3 | execute anchored feet 4 | 5 | # not real anchors 6 | execute anchored foo run say hello 7 | execute anchored bar run say hello 8 | execute anchored eyes anchored nose run say hello 9 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/swizzle/valid.mcfunction: -------------------------------------------------------------------------------- 1 | execute align x run say hello 2 | execute align y run say hello 3 | execute align z run say hello 4 | execute align xy run say hello 5 | execute align xz run say hello 6 | execute align yz run say hello 7 | execute align xyz run say hello 8 | 9 | execute align x align y align z run say hello 10 | -------------------------------------------------------------------------------- /tests/comprehensive/greedy_commands.mcfunction: -------------------------------------------------------------------------------- 1 | say hello [world] how are.you @s today? 2 | execute as @a run say hello [world] how are.you @s today? 3 | execute as uh_oh_say at @s run say hello [world] how are.you @s today? 4 | execute as say_uh_oh at @s run say hello [world] how are.you @s today? 5 | say hi hello whats up my friend \ 6 | this is still apart of the say command 7 | # but not this 8 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/block_position.mcfunction: -------------------------------------------------------------------------------- 1 | execute if blocks 0 0 0 1 2 3 -1 -2 -3 all run say hi 2 | execute if blocks ~1 ~2 ~3 ~-1 ~-2 ~-3 ~0 ~0 ~0 all run say hi 3 | execute if blocks ^1 ^2 ^3 ^-1 ^-2 ^-3 ^0 ^0 ^0 all run say hi 4 | 5 | # invalid, mixed coords 6 | execute if blocks 1 ^2 ^3 ^-1 ^-2 ^-3 ^0 ^0 ^0 all run say hi 7 | execute if blocks ~1 ^2 ^3 ^-1 ^-2 ^-3 ^0 ^0 ^0 all run say hi 8 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/dimension/invalid.mcfunction: -------------------------------------------------------------------------------- 1 | # non-executable 2 | execute in minecraft:overworld 3 | execute in minecraft:the_nether 4 | execute in minecraft:the_end 5 | 6 | # bad resource location 7 | execute in :overworld run say hello 8 | execute in minecraft: run say hello 9 | execute in : run say hello 10 | 11 | # no slashes 12 | execute in over/world run say hello 13 | execute in minecraft:over/world run say hello 14 | -------------------------------------------------------------------------------- /tests/comprehensive/fakeplayer.mcfunction: -------------------------------------------------------------------------------- 1 | scoreboard notfake 2 | scoreboard not_fake 3 | scoreboard unquoted.string 4 | scoreboard #fakeplayer 5 | scoreboard #fake.player 6 | scoreboard #fake_player 7 | scoreboard $fakeplayer 8 | scoreboard %fakeplayer 9 | scoreboard .fakeplayer 10 | scoreboard "quoted string" 11 | 12 | # cursed blame SnaveSutit 13 | scoreboard #fakeplayer-1y-1z- 14 | scoreboard #-200 15 | scoreboard %fakeplayer-1.20%% 16 | scoreboard .-30 17 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/int_range/invalid.mcfunction: -------------------------------------------------------------------------------- 1 | # just ellipsis 2 | execute if score @s foo matches .. run say hello 3 | 4 | # decimals 5 | execute if score @s foo matches 0.5 run say hello 6 | execute if score @s foo matches 0.5.. run say hello 7 | execute if score @s foo matches ..0.5 run say hello 8 | execute if score @s foo matches -0.5..0.5 run say hello 9 | 10 | # technically invalid, but hard to detect with regex 11 | execute if score @s foo matches 1..-1 run say hello 12 | -------------------------------------------------------------------------------- /tests/comprehensive/misc.mcfunction: -------------------------------------------------------------------------------- 1 | # root redirect 2 | execute as @a at @s run 3 | execute as @a at @s run 4 | execute as @a at @s run say 5 | execute as @a at @s run say hello 6 | 7 | # booleans 8 | effect give @s minecraft:night_vision 999999 1 true 9 | effect give @s minecraft:night_vision 999999 1 false 10 | 11 | # crazy whitespace 12 | execute 13 | execute 14 | execute 15 | execute 16 | execute 17 | execute 18 | 19 | # non-literal characters 20 | execute. 21 | execute_ 22 | execute- 23 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/dimension/valid.mcfunction: -------------------------------------------------------------------------------- 1 | # without namespace 2 | execute in overworld run say hello 3 | execute in the_nether run say hello 4 | execute in the_end run say hello 5 | 6 | # with namespace 7 | execute in minecraft:overworld run say hello 8 | execute in minecraft:the_nether run say hello 9 | execute in minecraft:the_end run say hello 10 | 11 | # nested 12 | execute in minecraft:overworld in minecraft:the_nether run say hello 13 | 14 | # custom dimension 15 | execute in minecraft:the_aether run say hello 16 | -------------------------------------------------------------------------------- /tests/jmc.mcfunction: -------------------------------------------------------------------------------- 1 | class folder_one.folder_two 2 | { 3 | function folder_three.function_name() { 4 | say "Code example 1"; 5 | say "Code example 2"; 6 | } 7 | new file_type(folder_name.json_file_name) { // See JSON Files page for this feature 8 | JSON_CONTENT 9 | } 10 | } 11 | 12 | folder_one.folder_two.folder_three.function_name(); 13 | 14 | if ($deathCount>5 ) { 15 | say "More than 5 death!"; 16 | } else if ($deathCount matches 2..3 ) { 17 | say "Between 2 to 3 death!"; 18 | } else if ($deathCount) { 19 | say "At least 1 death"; 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /tests/comprehensive/parsers/int_range/valid.mcfunction: -------------------------------------------------------------------------------- 1 | # exact numbers 2 | execute if score @s foo matches -1 run say hello 3 | execute if score @s foo matches 0 run say hello 4 | execute if score @s foo matches 1 run say hello 5 | 6 | # minimum 7 | execute if score @s foo matches 1.. run say hello 8 | execute if score @s foo matches 0.. run say hello 9 | execute if score @s foo matches -1.. run say hello 10 | 11 | # maximum 12 | execute if score @s foo matches ..1 run say hello 13 | execute if score @s foo matches ..0 run say hello 14 | execute if score @s foo matches ..-1 run say hello 15 | 16 | # min and max 17 | execute if score @s foo matches -1..1 run say hello 18 | -------------------------------------------------------------------------------- /mcfunction.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | mcfunction 7 | scope 8 | source.mcfunction 9 | settings 10 | 11 | shellVariables 12 | 13 | 14 | name 15 | TM_COMMENT_START 16 | value 17 | # 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/comprehensive/number.mcfunction: -------------------------------------------------------------------------------- 1 | # test valid number 2 | execute if score @a temp matches 1 run 3 | say 123 4 | say 1.0 5 | say 0.1 6 | say 123.456 7 | say .1 8 | say .789 9 | 10 | # test valid number in selectors 11 | execute as @a[distance=1] run 12 | execute as @a[distance=123] run 13 | execute as @a[distance=1.0] run 14 | execute as @a[distance=0.1] run 15 | execute as @a[distance=123.456] run 16 | execute as @a[distance=.1] run 17 | execute as @a[distance=.789] run 18 | 19 | # test valid negative numbers 20 | execute if score @a temp matches -1 run 21 | 22 | # test invalid number 23 | say . 24 | say 1. 25 | 26 | # test invalid number in selectors 27 | execute as @a[distance=.] run 28 | execute as @a[distance=1.] run 29 | -------------------------------------------------------------------------------- /tests/comprehensive/operation.mcfunction: -------------------------------------------------------------------------------- 1 | # test scoreboard players operation 2 | scoreboard players operation @s foo %= @s bar 3 | scoreboard players operation @s foo *= @s bar 4 | scoreboard players operation @s foo += @s bar 5 | scoreboard players operation @s foo -= @s bar 6 | scoreboard players operation @s foo /= @s bar 7 | scoreboard players operation @s foo < @s bar 8 | scoreboard players operation @s foo = @s bar 9 | scoreboard players operation @s foo > @s bar 10 | scoreboard players operation @s foo >< @s bar 11 | 12 | # test execute if score 13 | execute if score @s foo < @s bar run 14 | execute if score @s foo <= @s bar run 15 | execute if score @s foo = @s bar run 16 | execute if score @s foo > @s bar run 17 | execute if score @s foo >= @s bar run 18 | -------------------------------------------------------------------------------- /language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "#", 5 | }, 6 | // symbols used as brackets 7 | "brackets": [ 8 | ["{", "}"], 9 | ["[", "]"], 10 | ["(", ")"] 11 | ], 12 | // symbols that are auto closed when typing 13 | "autoClosingPairs": [ 14 | ["{", "}"], 15 | ["[", "]"], 16 | ["(", ")"], 17 | ["\"", "\""], 18 | ["'", "'"] 19 | ], 20 | // symbols that can be used to surround a selection 21 | "surroundingPairs": [ 22 | ["{", "}"], 23 | ["[", "]"], 24 | ["(", ")"], 25 | ["\"", "\""], 26 | ["'", "'"] 27 | ] 28 | } -------------------------------------------------------------------------------- /tests/comprehensive/parsers/rotation/invalid.mcfunction: -------------------------------------------------------------------------------- 1 | # bad decimals with absolutes 2 | execute rotated 10 . run say hello 3 | execute rotated 10 5. run say hello 4 | execute rotated 10 - run say hello 5 | execute rotated 10 -. run say hello 6 | execute rotated 10 -5. run say hello 7 | 8 | # bad decimals with relatives 9 | execute rotated ~10 ~. run say hello 10 | execute rotated ~10 ~5. run say hello 11 | execute rotated ~10 ~- run say hello 12 | execute rotated ~10 ~-. run say hello 13 | execute rotated ~10 ~-5. run say hello 14 | 15 | # can't use locals with rotation 16 | execute rotated ^10 ^ run say hello 17 | execute rotated ^10 ^10 run say hello 18 | execute rotated ^10 ^0.5 run say hello 19 | execute rotated ^10 ^.5 run say hello 20 | execute rotated ^10 ^-10 run say hello 21 | execute rotated ^10 ^-0.5 run say hello 22 | execute rotated ^10 ^-.5 run say hello 23 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/block_position/valid.mcfunction: -------------------------------------------------------------------------------- 1 | # all absolutes 2 | execute if block 10 0 -10 minecraft:stone run say hello 3 | execute if block 10 10 -10 minecraft:stone run say hello 4 | execute if block 10 -10 -10 minecraft:stone run say hello 5 | 6 | # absolutes mixed with relatives 7 | execute if block 10 ~ -10 minecraft:stone run say hello 8 | execute if block 10 ~10 -10 minecraft:stone run say hello 9 | execute if block 10 ~-10 -10 minecraft:stone run say hello 10 | 11 | # all relatives 12 | execute if block ~10 ~ ~-10 minecraft:stone run say hello 13 | execute if block ~10 ~10 ~-10 minecraft:stone run say hello 14 | execute if block ~10 ~-10 ~-10 minecraft:stone run say hello 15 | 16 | # all locals (can't be mixed) 17 | execute if block ^10 ^ ^-10 minecraft:stone run say hello 18 | execute if block ^10 ^10 ^-10 minecraft:stone run say hello 19 | execute if block ^10 ^-10 ^-10 minecraft:stone run say hello 20 | -------------------------------------------------------------------------------- /tests/macros.mcfunction: -------------------------------------------------------------------------------- 1 | #> Borrowed from slicedlime 2 | # src: https://github.com/slicedlime/examples/ 3 | 4 | scoreboard players operation @a result \ 5 | += @e[type=marker,limit=1,tag=source] value 6 | 7 | # Run the command in $(command) (eval) 8 | $$(command) 9 | 10 | # Concatenate $(string1) and $(string2), save that to $(path) in storage $(id) 11 | $data modify storage $(id) $(path) set value "with random $(string1) stuff $(string2)" 12 | 13 | # Set the time to $(time) 14 | $time set $(time) 15 | 16 | # this doesn't look perfect, but i can't really fix it 17 | $data modify \ 18 | storage $(id) $(path) set \ 19 | value "$(string1)$(string2)" 20 | 21 | $data modify 22 | storage $(id) $(path) set 23 | value "$(string1)$(string2)" 24 | 25 | random value 2..45 26 | 27 | scoreboard players set #me not_comment 3 # hello world 28 | give @a #all_diamonds 29 | 30 | execute summon marker run mud:register {command: "function a:target", setup: ""} 31 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/rotation/valid.mcfunction: -------------------------------------------------------------------------------- 1 | # all absolutes 2 | execute rotated 10 0 run say hello 3 | execute rotated 10 10 run say hello 4 | execute rotated 10 0.5 run say hello 5 | execute rotated 10 .5 run say hello 6 | execute rotated 10 -10 run say hello 7 | execute rotated 10 -0.5 run say hello 8 | execute rotated 10 -.5 run say hello 9 | 10 | # absolutes mixed with relatives 11 | execute rotated 10 ~ run say hello 12 | execute rotated 10 ~10 run say hello 13 | execute rotated 10 ~0.5 run say hello 14 | execute rotated 10 ~.5 run say hello 15 | execute rotated 10 ~-10 run say hello 16 | execute rotated 10 ~-0.5 run say hello 17 | execute rotated 10 ~-.5 run say hello 18 | 19 | # all relatives 20 | execute rotated ~10 ~ run say hello 21 | execute rotated ~10 ~10 run say hello 22 | execute rotated ~10 ~0.5 run say hello 23 | execute rotated ~10 ~.5 run say hello 24 | execute rotated ~10 ~-10 run say hello 25 | execute rotated ~10 ~-0.5 run say hello 26 | execute rotated ~10 ~-.5 run say hello 27 | -------------------------------------------------------------------------------- /tests/mcbuild.mcfunction: -------------------------------------------------------------------------------- 1 | dir a { 2 | function test { 3 | 4 | } 5 | 6 | function target { 7 | scoreboard players set #x v 1 8 | } 9 | 10 | function setup { 11 | data merge storage a:test {} 12 | LOOP(1000,i){ 13 | execute summon marker run mud:register {command:"function tests:a/target",setup:""} 14 | summon marker 0 0 0 {Tags:["a.i"]} 15 | } 16 | } 17 | 18 | function cleanup { 19 | data remove storage a:test {} 20 | kill @e[type=marker] 21 | } 22 | } 23 | 24 | dir b { 25 | 26 | function test { 27 | execute @e[tag=b.test] run function tests:b/target 28 | } 29 | 30 | function target { 31 | scoreboard players set #x v 1 32 | } 33 | 34 | function setup { 35 | LOOP(1000,i) { 36 | summon marker 0 0 0 {Tags:["b.test"]} 37 | summon marker 0 0 0 {Tags:["b.i"]} 38 | } 39 | } 40 | 41 | function cleanup { 42 | data remove storage b:test {} 43 | kill @e[type=marker] 44 | } 45 | } -------------------------------------------------------------------------------- /tests/comprehensive/commands.mcfunction: -------------------------------------------------------------------------------- 1 | execute 2 | execute foo 3 | execute as 4 | execute as foo 5 | execute as @s 6 | execute as @s foo 7 | execute as @s execute 8 | execute as @s execute as 9 | execute as @s execute as @s 10 | execute as @s as 11 | execute as @s as foo 12 | execute as @s as @s 13 | execute run 14 | execute run foo 15 | execute run execute 16 | execute run execute foo 17 | execute run execute run 18 | execute as @s run 19 | execute as @s run foo 20 | execute as @s run as 21 | execute as @s run as @s 22 | execute as @s run execute 23 | execute as @s run execute foo 24 | execute as @s run execute as @s run 25 | 26 | advancement 27 | advancement foo 28 | advancement grant 29 | advancement grant foo 30 | advancement grant targets 31 | advancement grant targets foo 32 | advancement grant targets everything 33 | 34 | execute as targets 35 | execute as targets 36 | execute as targets run 37 | execute as targets run 38 | execute as targets run execute 39 | execute as targets run execute 40 | execute as targets run execute as 41 | execute as targets run execute as 42 | execute as targets run execute as targets 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2023 MinecraftCommands 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "syntax-mcfunction", 3 | "publisher": "MinecraftCommands", 4 | "displayName": "syntax-mcfunction", 5 | "description": "Syntax highlighting for mcfunction files (Last Updated: 1.21)", 6 | "icon": "icon.png", 7 | "author": "MinecraftCommands", 8 | "version": "1.0.1", 9 | "license": "MIT", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/MinecraftCommands/syntax-mcfunction" 13 | }, 14 | "engines": { 15 | "vscode": "^1.81.0" 16 | }, 17 | "categories": [ 18 | "Programming Languages" 19 | ], 20 | "contributes": { 21 | "languages": [ 22 | { 23 | "id": "mcfunction", 24 | "aliases": [ 25 | "mcfunction", 26 | "mcf", 27 | "MCF" 28 | ], 29 | "extensions": [ 30 | ".mcfunction" 31 | ], 32 | "configuration": "./language-configuration.json" 33 | } 34 | ], 35 | "grammars": [ 36 | { 37 | "language": "mcfunction", 38 | "scopeName": "source.mcfunction", 39 | "path": "./mcfunction.tmLanguage.json" 40 | } 41 | ] 42 | }, 43 | "devDependencies": { 44 | "js-yaml": "^4.1.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/position/invalid.mcfunction: -------------------------------------------------------------------------------- 1 | # bad decimals with absolutes 2 | execute positioned 10 . -10 run say hello 3 | execute positioned 10 5. -10 run say hello 4 | execute positioned 10 - -10 run say hello 5 | execute positioned 10 -. -10 run say hello 6 | execute positioned 10 -5. -10 run say hello 7 | 8 | # bad decimals with relatives 9 | execute positioned ~10 ~. ~-10 run say hello 10 | execute positioned ~10 ~5. ~-10 run say hello 11 | execute positioned ~10 ~- ~-10 run say hello 12 | execute positioned ~10 ~-. ~-10 run say hello 13 | execute positioned ~10 ~-5. ~-10 run say hello 14 | 15 | # bad decimals with locals 16 | execute positioned ^10 ^. ^-10 run say hello 17 | execute positioned ^10 ^5. ^-10 run say hello 18 | execute positioned ^10 ^- ^-10 run say hello 19 | execute positioned ^10 ^-. ^-10 run say hello 20 | execute positioned ^10 ^-5. ^-10 run say hello 21 | 22 | # bad mixed locals 23 | execute positioned 1 2 ^3 run say hello 24 | execute positioned 1 ^2 3 run say hello 25 | execute positioned ^1 2 3 run say hello 26 | execute positioned ~1 ~2 ^3 run say hello 27 | execute positioned ~1 ^2 ~3 run say hello 28 | execute positioned ^1 ~2 ~3 run say hello 29 | 30 | # not a real operator 31 | execute positioned %1 %2 %3 run say hello 32 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/block_position/invalid.mcfunction: -------------------------------------------------------------------------------- 1 | # bad decimals with absolutes 2 | execute if block 10 0.5 -10 minecraft:stone run say hello 3 | execute if block 10 .5 -10 minecraft:stone run say hello 4 | execute if block 10 -0.5 -10 minecraft:stone run say hello 5 | execute if block 10 -.5 -10 minecraft:stone run say hello 6 | 7 | # bad decimals with relatives 8 | execute if block ~10 ~0.5 ~-10 minecraft:stone run say hello 9 | execute if block ~10 ~.5 ~-10 minecraft:stone run say hello 10 | execute if block ~10 ~-0.5 ~-10 minecraft:stone run say hello 11 | execute if block ~10 ~-.5 ~-10 minecraft:stone run say hello 12 | 13 | # bad decimals with locals 14 | execute if block ^10 ^0.5 ^-10 minecraft:stone run say hello 15 | execute if block ^10 ^.5 ^-10 minecraft:stone run say hello 16 | execute if block ^10 ^-0.5 ^-10 minecraft:stone run say hello 17 | execute if block ^10 ^-.5 ^-10 minecraft:stone run say hello 18 | 19 | # bad mixed locals 20 | execute if block 1 2 ^3 minecraft:stone run say hello 21 | execute if block 1 ^2 3 minecraft:stone run say hello 22 | execute if block ^1 2 3 minecraft:stone run say hello 23 | execute if block ~1 ~2 ^3 minecraft:stone run say hello 24 | execute if block ~1 ^2 ~3 minecraft:stone run say hello 25 | execute if block ^1 ~2 ~3 minecraft:stone run say hello 26 | 27 | # not a real operator 28 | execute if block %1 %2 %3 minecraft:stone run say hello 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yaml: -------------------------------------------------------------------------------- 1 | name: "💡 Feature Request" 2 | description: Create a ticket for a new feature request 3 | title: "Request: " 4 | labels: ["feature-request"] 5 | 6 | body: 7 | - type: textarea 8 | id: summary 9 | attributes: 10 | label: "Summary" 11 | description: Provide a brief explanation of your feature request 12 | placeholder: Describe in a few lines your feature request 13 | validations: 14 | required: true 15 | 16 | - type: textarea 17 | id: basic_example 18 | attributes: 19 | label: "Basic Example" 20 | description: Indicate here some basic examples of your feature. 21 | placeholder: A few specific words about your feature request. 22 | validations: 23 | required: true 24 | 25 | - type: dropdown 26 | id: platform 27 | attributes: 28 | label: Is this specific to a text editor? 29 | multiple: true 30 | options: 31 | - VSCode 32 | - Sublime Text 33 | - Other (please specify above) 34 | validations: 35 | required: false 36 | 37 | - type: textarea 38 | id: other-info 39 | attributes: 40 | label: "Other Info" 41 | description: Any drawbacks or other details of your feature request? 42 | placeholder: Other thoughts you think might be **Important** 43 | render: bash 44 | validations: 45 | required: false 46 | -------------------------------------------------------------------------------- /tests/comprehensive/block_predicate.mcfunction: -------------------------------------------------------------------------------- 1 | # test argument types 2 | execute if block ~ ~ ~ minecraft:oak_log[axis=x] run 3 | execute if block ~ ~ ~ minecraft:oak_leaves[distance=5] run 4 | execute if block ~ ~ ~ minecraft:oak_leaves[persistent=true] run 5 | execute if block ~ ~ ~ minecraft:oak_leaves[persistent=false] run 6 | execute if block ~ ~ ~ minecraft:oak_leaves[persistent = false] run 7 | 8 | # test multiple arguments 9 | execute if block ~ ~ ~ minecraft:oak_leaves[distance=5,persistent=true] run 10 | 11 | # test tagged variant 12 | execute if block ~ ~ ~ #minecraft:leaves[distance=5] run 13 | execute if block ~ ~ ~ #minecraft:leaves[distance=5,persistent=true] run 14 | execute if block ~ ~ ~ #minecraft:leaves[distance=5, persistent=true] run 15 | 16 | # test with nbt 17 | setblock ~ ~ ~ mypack:foo{foo:bar} destroy 18 | setblock ~ ~ ~ mypack:foo{foo: bar} destroy 19 | setblock ~ ~ ~ mypack:foo[facing=up]{foo: bar} destroy 20 | setblock ~ ~ ~ mypack:foo[facing = up]{foo: bar} destroy 21 | 22 | # test without namespace 23 | setblock ~ ~ ~ foo{foo:bar} destroy 24 | setblock ~ ~ ~ foo[facing=up]{foo: bar} destroy 25 | setblock ~ ~ ~ foo[facing=up] destroy 26 | setblock ~ ~ ~ foo[ facing = up ]{foo: bar} destroy 27 | setblock ~ ~ ~ foo[ facing = up ]{foo: bar} destroy 28 | 29 | # invalid 30 | setblock ~ ~ ~ mypack:foo[facing = up]foo destroy 31 | setblock ~ ~ ~ mypack:foo[facing = up]{foo: bar}foo destroy 32 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v0.6.0 4 | 5 | - Added `##` as a valid block starter 6 | - Fixed comments ordering so that both inline AND commented commands work 7 | 8 | ## v0.5.0 9 | 10 | - Literals with suffixes (b, B, l, L) now highlight properly 11 | - Literals inside properties (specifically `true`/`false`) highlight better 12 | - Resource Names within properties highlight better 13 | - This causes false-positives via `minecraft:block{key:value}` to highlight incorrectly 14 | - Added `-`, `.` as a valid special fakeplayer character 15 | - These characters will only highlight within valid fakeplayer starting characters (`#%$.`) 16 | - Adjusted some scope names to match `language-mcfunction` better 17 | - Fixed sublime support (hopefully) 18 | - Improved some testing 19 | 20 | ## v0.4.x 21 | 22 | - Fiddling around w/ VSCode Publishing 23 | 24 | ## v0.3.1 25 | 26 | - Update name to `syntax-mcfunction` 27 | - Move to `MinecraftCommands` organization 28 | 29 | ## v0.3.0 30 | 31 | - Added support for Sublime Text 32 | - Reverted commenting changes from last release 33 | - This caused issues when you commented out normal code 34 | - Slightly restructured GA + some naming 35 | 36 | ## v0.2.0 37 | 38 | Refactor and additions to qualify for a `mcfunction` language 39 | 40 | - Added explicit macro support 41 | - Loosened restrictions on special commenting 42 | - Fixed various bugs 43 | 44 | ## v0.1.0 45 | 46 | Initial release for the `bolt` community with major support for multiline commands 47 | -------------------------------------------------------------------------------- /tests/comprehensive/strings.mcfunction: -------------------------------------------------------------------------------- 1 | # literal 2 | # without contenxt, there is no way to differentiate 3 | # literal arguments from literal subcommands 4 | tag @s add mytag 5 | 6 | # literal 7 | tag @s add my_tag 8 | 9 | # literal 10 | say hello 11 | 12 | # literals 13 | say hello world 14 | 15 | # unquoted string 16 | # could be confused with an nbt path 17 | # but nbt paths typically have at least one capital letter 18 | tag @s add my.tag 19 | 20 | # unquoted string 21 | tag @s add my-tag 22 | 23 | # maybe nbt path 24 | data get entity @s My.Tag 25 | 26 | # definitely nbt path 27 | data get entity @s My.Tag[0] 28 | 29 | # quoted string 30 | say "hello world" 31 | say "hello ' world" 32 | say 'hello world' 33 | say 'hello " world' 34 | 35 | # quoted string escaped 36 | say "hello \" world" 37 | say 'hello \' world' 38 | 39 | # player names 40 | scoreboard players reset $mypack:some.fakeplayer 41 | scoreboard players reset $mypack.core:another.fakeplayer 42 | 43 | # quoted string with trailing characters 44 | say "oh oh"bad 45 | say 'oh oh'bad 46 | 47 | # quoted string with terminal backslash 48 | say "uh oh\ 49 | say 'uh oh\ 50 | 51 | # quoted string unbounded 52 | say "uh oh 53 | # this should be a comment, otherwise 54 | # the quoted string is probably leaking 55 | 56 | say 'uh oh 57 | # same here 58 | 59 | tellraw @s ["test test test\ 60 | test test test test test test\ 61 | test test", "test test"] 62 | 63 | tellraw @s ['test test test\ 64 | test test test test test test\ 65 | test test', 'test test'] 66 | -------------------------------------------------------------------------------- /tests/demo.mcfunction: -------------------------------------------------------------------------------- 1 | #> Block Comment Header 2 | # Normal comment - Example of some commands 3 | # @returns $my.variable at #resource:location 4 | 5 | # However, we don't get @returns highlighting here 6 | 7 | setblock ~ ~ ~ minecraft:dispenser[facing=up]{Items: [{id: "minecraft:diamond", Count: 1}]} 8 | $data modify storage $(id) $(path) set value "with random $(string1) stuff $(string2)" 9 | tellraw @a [{"text": "hello", "color": "blue"}, {"text": "world", "color": "blue"}] 10 | random roll 2..45 # neat command they added 11 | give @a #minecraft:log # plenty of wood to capture 12 | 13 | # multiline 14 | execute \ 15 | as @a \ 16 | at @s \ 17 | if entity @s[distance=..0.6] \ 18 | say bar 19 | 20 | # bolt 21 | infinite_invisibility = { 22 | Id: 14, 23 | Duration: 999999, 24 | Amplifier: 1, 25 | ShowParticles: false, 26 | } 27 | 28 | def summon_chicken_army(n): 29 | for i in range(n): 30 | summon chicken ~i ~ ~ { 31 | Tags: [f"quack{i}"], 32 | IsChickenJockey: true, 33 | Passengers: [{ 34 | id: zombie, 35 | IsBaby: true, 36 | ActiveEffects: [infinite_invisibility] 37 | }] 38 | } 39 | 40 | say Go forth, my minions! 41 | summon_chicken_army(16) 42 | 43 | # mcbuild 44 | LOOP(1000,i){ 45 | execute summon marker run mud:register {command:"function tests:a/target",setup:""} 46 | summon marker 0 0 0 {Tags:["a.i"]} 47 | } 48 | -------------------------------------------------------------------------------- /tests/comprehensive/parsers/position/valid.mcfunction: -------------------------------------------------------------------------------- 1 | # all absolutes 2 | execute positioned 10 0 -10 run say hello 3 | execute positioned 10 10 -10 run say hello 4 | execute positioned 10 0.5 -10 run say hello 5 | execute positioned 10 .5 -10 run say hello 6 | execute positioned 10 -10 -10 run say hello 7 | execute positioned 10 -0.5 -10 run say hello 8 | execute positioned 10 -.5 -10 run say hello 9 | 10 | # absolutes mixed with relatives 11 | execute positioned 10 ~ -10 run say hello 12 | execute positioned 10 ~10 -10 run say hello 13 | execute positioned 10 ~0.5 -10 run say hello 14 | execute positioned 10 ~.5 -10 run say hello 15 | execute positioned 10 ~-10 -10 run say hello 16 | execute positioned 10 ~-0.5 -10 run say hello 17 | execute positioned 10 ~-.5 -10 run say hello 18 | 19 | # all relatives 20 | execute positioned ~10 ~ ~-10 run say hello 21 | execute positioned ~10 ~10 ~-10 run say hello 22 | execute positioned ~10 ~0.5 ~-10 run say hello 23 | execute positioned ~10 ~.5 ~-10 run say hello 24 | execute positioned ~10 ~-10 ~-10 run say hello 25 | execute positioned ~10 ~-0.5 ~-10 run say hello 26 | execute positioned ~10 ~-.5 ~-10 run say hello 27 | 28 | # all locals (can't be mixed) 29 | execute positioned ^10 ^ ^-10 run say hello 30 | execute positioned ^10 ^10 ^-10 run say hello 31 | execute positioned ^10 ^0.5 ^-10 run say hello 32 | execute positioned ^10 ^.5 ^-10 run say hello 33 | execute positioned ^10 ^-10 ^-10 run say hello 34 | execute positioned ^10 ^-0.5 ^-10 run say hello 35 | execute positioned ^10 ^-.5 ^-10 run say hello 36 | -------------------------------------------------------------------------------- /tests/comprehensive/nbt_compound.mcfunction: -------------------------------------------------------------------------------- 1 | # test compounds 2 | data modify block ~ ~ ~ RecordItem.tag.custom set value { foo: true } 3 | data modify block ~ ~ ~ RecordItem.tag.custom set value { foo: true, bar: 1234, baz: "hello world" } 4 | data modify block ~ ~ ~ RecordItem.tag.custom set value { foo: { bar: true } } 5 | data modify block ~ ~ ~ RecordItem.tag.custom set value { foo: { bar: true, baz: 1234 } } 6 | data modify block ~ ~ ~ RecordItem.tag.custom set value { foo: { bar: true, baz: 1234, fiz: "hello world" } } 7 | 8 | # test lists 9 | data modify block ~ ~ ~ RecordItem.tag.custom set value [ 1 ] 10 | data modify block ~ ~ ~ RecordItem.tag.custom set value [ 1, 2, 3 ] 11 | data modify block ~ ~ ~ RecordItem.tag.custom set value [ a, "hello world", b ] 12 | data modify block ~ ~ ~ RecordItem.tag.custom set value [ [1], [2.1, 2.5, 2.9], [3] ] 13 | 14 | # test combos 15 | data modify block ~ ~ ~ RecordItem.tag.custom set value { foo: [ 1 ] } 16 | data modify block ~ ~ ~ RecordItem.tag.custom set value { foo: [ 1, 2, 3 ] } 17 | data modify block ~ ~ ~ RecordItem.tag.custom set value { foo: [ a, "hello world", b ] } 18 | data modify block ~ ~ ~ RecordItem.tag.custom set value { foo: [ { foo: true }, { bar: 1234 }, { baz: "hello world" } ] } 19 | data modify block ~ ~ ~ RecordItem.tag.custom set value [ { foo: true }, { bar: 1234 }, { baz: "hello world" } ] 20 | 21 | # edge case keys 22 | execute unless data block 0 0 0 this.block.is.something{foo_bar: true} 23 | execute unless data block 0 0 0 this.block.is.something{Foo.Bar: true} 24 | execute unless data block 0 0 0 this.block.is.something{foo-bar: true} 25 | execute unless data block 0 0 0 this.block.is.something[{foo.bar: true}] 26 | -------------------------------------------------------------------------------- /tests/comprehensive/resource_location.mcfunction: -------------------------------------------------------------------------------- 1 | # valid, with namespace 2 | function mypack:foo 3 | function mypack:foo/bar 4 | function mypack:foo/bar/baz 5 | function #mypack:foo 6 | function #mypack:foo/bar 7 | function #mypack:foo/bar/baz 8 | 9 | # valid, without namespace 10 | function foo 11 | function foo/bar 12 | function foo/bar/baz 13 | function #foo 14 | function #foo/bar 15 | function #foo/bar/baz 16 | 17 | # valid, with trailing command 18 | execute if block ~ ~ ~ mypack:foo run say hi 19 | execute if block ~ ~ ~ mypack:foo/bar run say hi 20 | execute if block ~ ~ ~ mypack:foo/bar/baz run say hi 21 | execute if block ~ ~ ~ #mypack:foo run say hi 22 | execute if block ~ ~ ~ #mypack:foo/bar run say hi 23 | execute if block ~ ~ ~ #mypack:foo/bar/baz run say hi 24 | 25 | # invalid 26 | function mypack: 27 | function :foo 28 | function #mypack: 29 | function #:foo 30 | function mypack:/ 31 | function mypack:foo/ 32 | function mypack:/foo 33 | function #mypack:/ 34 | function #mypack:foo/ 35 | function #mypack:/foo 36 | 37 | # invalid, with trailing command 38 | execute if block ~ ~ ~ mypack: run say hi 39 | execute if block ~ ~ ~ :foo run say hi 40 | execute if block ~ ~ ~ mypack:/ run say hi 41 | execute if block ~ ~ ~ mypack:foo/ run say hi 42 | execute if block ~ ~ ~ mypack:/foo run say hi 43 | execute if block ~ ~ ~ mypack:/ run say hi 44 | execute if block ~ ~ ~ mypack:foo/ run say hi 45 | execute if block ~ ~ ~ mypack:/foo run say hi 46 | execute if block ~ ~ ~ #mypack:/ run say hi 47 | execute if block ~ ~ ~ #mypack:foo/ run say hi 48 | execute if block ~ ~ ~ #mypack:/foo run say hi 49 | 50 | function gm4_boots_of_ostara:flippers_merge 51 | function gm4_boots_of_ostara:flippers/merge 52 | function gm_boots_of_ostara:flippers_merge 53 | -------------------------------------------------------------------------------- /tests/comprehensive/nbt_path/valid.mcfunction: -------------------------------------------------------------------------------- 1 | # basic property access 2 | data modify entity @s SelectedItem set value true 3 | data modify entity @s SelectedItem. set value true 4 | data modify entity @s SelectedItem.tag set value true 5 | data modify entity @s SelectedItem.tag. set value true 6 | data modify entity @s SelectedItem.tag.display set value true 7 | 8 | # list access 9 | data modify entity @s Inventory[] set value true 10 | data modify entity @s Inventory[0] set value true 11 | data modify entity @s Inventory[-1] set value true 12 | data modify entity @s Inventory[].tag set value true 13 | 14 | # list access with compound 15 | data modify entity @s Inventory[{}] set value true 16 | data modify entity @s Inventory[{Count: 64}] set value true 17 | data modify entity @s Inventory[{id: "minecraft:diamond"}] set value true 18 | 19 | # adjacent list access 20 | data modify entity @s Item.tag.foo[][] set value true 21 | data modify entity @s Item.tag.foo[0][] set value true 22 | data modify entity @s Item.tag.foo[][0] set value true 23 | data modify entity @s Item.tag.foo[0][0] set value true 24 | data modify entity @s Item.tag.foo[][][] set value true 25 | data modify entity @s Item.tag.foo[][0][] set value true 26 | data modify entity @s Item.tag.foo[][{}][] set value true 27 | 28 | # compound access 29 | data get entity @s Inventory[].tag{custom: true}.display.Name 30 | 31 | # quoted keys 32 | data modify entity @s Item.tag."my_quoted_key" 33 | data modify entity @s Item.tag."my_quoted_key" 34 | data modify entity @s Item.tag."my_quoted_key" set value true 35 | data modify entity @s Item.tag."my_quoted_key".foo set value true 36 | data modify entity @s Item.tag."my quoted key" 37 | data modify entity @s Item.tag."my quoted key".foo 38 | data modify entity @s Item.tag."my quoted key" set value true 39 | data modify entity @s Item.tag."my quoted key".foo set value true 40 | -------------------------------------------------------------------------------- /tests/comprehensive/range.mcfunction: -------------------------------------------------------------------------------- 1 | # test valid range with integers 2 | execute if score @a temp matches 10.. run 3 | execute if score @a temp matches ..20 run 4 | execute if score @a temp matches 10..20 run 5 | 6 | # test valid range with integers in selectors 7 | execute as @a[distance=10..] run 8 | execute as @a[distance=..20] run 9 | execute as @a[distance=11..19] run 10 | 11 | # test valid range with negative integers 12 | execute if score @a temp matches -1.. run 13 | execute if score @a temp matches ..-1 run 14 | execute if score @a temp matches -2..-1 run 15 | execute if score @a temp matches -1..1 run 16 | 17 | # test valid range with negative integers in selectors 18 | execute as @a[x_rotation=-1..] run 19 | execute as @a[x_rotation=..-1] run 20 | execute as @a[x_rotation=-2..-1] run 21 | execute as @a[x_rotation=-1..1] run 22 | 23 | # test valid range with decimals in selectors 24 | execute as @a[distance=0.1..] run 25 | execute as @a[distance=0.2..0.8] run 26 | execute as @a[distance=..0.9] run 27 | execute as @a[distance=.1..] run 28 | execute as @a[distance=.2..0.8] run 29 | execute as @a[distance=0.2...8] run 30 | execute as @a[distance=.2...8] run 31 | execute as @a[distance=...9] run 32 | 33 | # test valid range with negative decimals in selectors 34 | execute as @a[x_rotation=-0.1..] run 35 | execute as @a[x_rotation=-.1..] run 36 | execute as @a[x_rotation=..-0.1] run 37 | execute as @a[x_rotation=..-.1] run 38 | execute as @a[x_rotation=-0.2..-0.1] run 39 | execute as @a[x_rotation=-.2..-.1] run 40 | execute as @a[x_rotation=-0.1..0.1] run 41 | execute as @a[x_rotation=-.1...1] run 42 | 43 | # test valid mixed range in selectors 44 | execute as @a[x_rotation=0..0.2] run 45 | execute as @a[x_rotation=0...2] run 46 | execute as @a[distance=0.2..1] run 47 | execute as @a[distance=.2..1] run 48 | 49 | # test invalid range 50 | execute if score @a temp matches .. run 51 | 52 | # test invalid range in selectors 53 | execute as @a[distance=..] run 54 | -------------------------------------------------------------------------------- /tests/comprehensive/nbt_path/invalid.mcfunction: -------------------------------------------------------------------------------- 1 | # multi dots 2 | data modify entity @s SelectedItem..tag set value true 3 | data modify entity @s SelectedItem...tag set value true 4 | data modify entity @s SelectedItem....tag set value true 5 | data modify entity @s SelectedItem..tag.custom set value true 6 | data modify entity @s SelectedItem..tag..custom set value true 7 | 8 | # list access 9 | data modify entity @s Inventory[ 0] set value true 10 | data modify entity @s Inventory[0 ] set value true 11 | data modify entity @s Inventory[ 0 ] set value true 12 | data modify entity @s Inventory[0.5] set value true 13 | data modify entity @s Inventory[true] set value true 14 | data modify entity @s Inventory[1, 2] set value true 15 | 16 | # trailing bracket data 17 | data modify entity @s Inventory[0]tag set value true 18 | data modify entity @s Inventory[{}]tag set value true 19 | data modify entity @s Inventory{}tag set value true 20 | 21 | # list access with compound 22 | data modify entity @s Inventory[ {} ] set value true 23 | data modify entity @s Inventory[x{}] set value true 24 | data modify entity @s Inventory[{}x] set value true 25 | data modify entity @s Inventory[x{}x] set value true 26 | data modify entity @s Inventory[ {Count: 64} ] set value true 27 | 28 | # test list access leak 29 | data get entity @s Inventory[ 30 | data get entity @s Inventory[set value true 31 | data get entity @s Inventory[ set value true 32 | say hello 33 | 34 | # test compound access leak 35 | data get entity @s Inventory[].tag{custom: true 36 | data get entity @s Inventory[].tag{set value true 37 | data get entity @s Inventory[].tag{ set value true 38 | say hello 39 | 40 | # test inner list leak 41 | data get entity @s Inventory[[ 42 | say hello 43 | 44 | # test inner compound leak 45 | data get entity @s Inventory[{ 46 | say hello 47 | 48 | # test trailing text after quoted nbt path key 49 | data modify entity @s Item.tag."my_quoted_key"foo 50 | data modify entity @s Item.tag."my_quoted_key"foo set value true 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yaml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug Report 2 | description: File a bug report related to syntax highlighting 3 | title: "Bug: " 4 | labels: ["bug"] 5 | 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thank you for taking the time to fill out a bug report. 11 | 12 | > **Important** 13 | > Remember to check that you are on the latest version of the extension (or indicate pre-release if applicable). 14 | 15 | - type: dropdown 16 | id: type-of-issue 17 | attributes: 18 | label: | 19 | What type of issues are you dealing with? 20 | description: | 21 | > **Note** 22 | > Coloring inconsistencies w/ `language-mcfunction` are not always considered bugs and may not be fixed 23 | multiple: true 24 | options: 25 | - Incorrect highlighting 26 | - Scope leak (breaking highlighting later in the file) 27 | - Coloring or scope inconsistency 28 | - Alternate syntax related (likely "Won't Fix") 29 | - Other (please specify) 30 | 31 | - type: textarea 32 | id: what-happened 33 | attributes: 34 | label: What happened? 35 | description: | 36 | What did you expect to happen? What happened instead? 37 | Include screenshots to explain your problem visually. 38 | placeholder: Tell us what you see! 39 | validations: 40 | required: true 41 | 42 | - type: dropdown 43 | id: platform 44 | attributes: 45 | label: Which text editors are you seeing the problem on? 46 | multiple: true 47 | options: 48 | - VSCode 49 | - Sublime Text 50 | - Other (please specify above) 51 | validations: 52 | required: true 53 | 54 | - type: textarea 55 | id: code-snippets 56 | attributes: 57 | label: Include a code snippet 58 | description: Put the exact code snippet used to produce the bug 59 | placeholder: | 60 | say my buggy command 61 | value: | 62 | execute as @a run say buggy command executed 63 | render: mcfunction 64 | validations: 65 | required: false 66 | -------------------------------------------------------------------------------- /tests/comprehensive/showcase.mcfunction: -------------------------------------------------------------------------------- 1 | #> Raycasting 2 | # Casts a ray from starting position up to 128 blocks away. 3 | 4 | function mypack:raycast/loop 5 | function #mypack:hooks/raycast/begin 6 | effect give @s minecraft:night_vision 999999 1 true 7 | execute if score @a temp matches 10..20 8 | execute positioned 10 ~10 -10 9 | execute positioned 10 ^0.5 -10 10 | execute if block ~ ~ ~ minecraft:oak_leaves[persistent=true] 11 | execute if block ~ ~ ~ #minecraft:leaves[distance=5,persistent=false] 12 | setblock ~ ~ ~ minecraft:dispenser[facing=up]{Items: [{id: "minecraft:diamond", Count: 1}]} 13 | tag @s add my.tag 14 | datapack enable "hello world" 15 | datapack enable "escape \" me" 16 | datapack enable 'single quotes' 17 | give @s minecraft:diamond_sword{display: {Name: '"My Custom Sword"'}} 18 | execute as f7a39418-72ca-4bf2-bc7e-ba9df67a4707 run say hello 19 | execute as 0-0-0-0-0 run say goodbye 20 | execute as @a[sort=nearest] 21 | execute as @a[gamemode=!creative] 22 | execute as @a[tag=foo,tag=bar,tag=!baz] 23 | execute as @a[distance=100] 24 | execute as @a[distance=1..19] 25 | execute as @a[distance=...001] 26 | execute as @a[type=minecraft:bat] 27 | execute as @a[type=!#minecraft:skeletons,type=!minecraft:zombie] 28 | execute as @a[name="Custom Name"] as @s 29 | execute as @e[nbt={ PortalCooldown: 0 }] 30 | execute as @e[nbt={ Item: {id: "minecraft:diamond", Count: 64 } }] 31 | execute if score @s foo < @s bar 32 | scoreboard players operation @s foo %= @s bar 33 | data get entity @s SelectedItem.tag.display.Name 34 | data get entity @s Inventory[0] 35 | data get entity @s Inventory[{id: "minecraft:diamond"}].Count 36 | data get entity @s Inventory[].tag{custom: true}.display.Name 37 | data merge entity @s { foo: true, bar: 1234 } 38 | data modify block ~ ~ ~ RecordItem.tag set value { messages: [hi, bye] } 39 | data modify block ~ ~ ~ RecordItem.tag.messages append value [ { message: "hello world" } ] 40 | tellraw @a {"text": "hello world", "color": "blue"} 41 | tellraw @a [{"text": "hello", "color": "blue"}, {"text": "world", "color": "blue"}] 42 | execute as @a[scores={myscore=1..}] 43 | execute as @a[scores={foo=10, bar=1..5, baz=..0}] 44 | execute as @a[advancements={mypack:some/advancement=true}] 45 | execute as @a[advancements={mypack:another/advancement={foo=true, bar=false}}] 46 | 47 | -------------------------------------------------------------------------------- /tests/comprehensive/comments.mcfunction: -------------------------------------------------------------------------------- 1 | #> Raycasting 2 | # 3 | # Casts a ray from starting position along a configurable number 4 | # of blocks with a confugrable accuracy, counting the number of 5 | # entities hit by the ray along the way. 6 | # 7 | # @params 8 | # $mypack.raycast.distance param 9 | # The number of blocks to cast forward. 10 | # $mypack.raycast.precision param 11 | # The ratio of block precision to a full block. 12 | # 13 | # @returns 14 | # $mypack.raycast.result return 15 | # The number of entities hit by the ray. 16 | 17 | # @test no longer part of the block comment 18 | execute as @s run say this should not be a comment 19 | 20 | execute as @s run say this should also not be a comment 21 | 22 | # Non-highlighted block 23 | # @hello this block is not highlighted 24 | # because the first line doesn't have a prefix 25 | 26 | #! Another block 27 | # @hello this is another block 28 | # and this is the end 29 | 30 | ## Another block 31 | # @hello this is another block 32 | # and this is the end 33 | 34 | execute as @s run say this should be a command 35 | 36 | #> One block 37 | #> Two block 38 | 39 | # @hello world 40 | #> Red block 41 | # @hello world 42 | #> Blue block 43 | # @hello world 44 | 45 | #> An indented block 46 | # @hello does this still work? 47 | # hopefully it does 48 | execute as @s run say goodbye world 49 | 50 | #> Yet another block 51 | # @except this time 52 | # we have very strange indents 53 | execute as @s run say goodbye world 54 | execute as @s run say goodbye world 55 | execute as @s run say goodbye world 56 | 57 | #> Yet another block 58 | # @yeah another one 59 | # blah blah blah 60 | execute as @s run say this should also be a command 61 | execute as @s run say this should also also be a command 62 | 63 | execute as @s run say this should also also also be a command 64 | 65 | ## An alternate block comment prefix 66 | # a 67 | # b 68 | # c 69 | 70 | ### With multiple characters 71 | 72 | ########## Really long one 73 | 74 | #~ Another alternate prefix 75 | 76 | #! And another one 77 | 78 | #@ And another one 79 | 80 | #$ And another one 81 | 82 | #% And another one 83 | 84 | #^ And another one 85 | 86 | #* And another one 87 | 88 | ## This should be a block comment 89 | 90 | #execute as @a run say hi 91 | # execute as @a run say hi 92 | #> execute as @a run say hi 93 | 94 | execute as @a run say hi # tacossss 95 | execute if block #minecraft:stone 96 | 97 | -------------------------------------------------------------------------------- /tests/comprehensive/commands/effect.mcfunction: -------------------------------------------------------------------------------- 1 | effect 2 | effect clear 3 | effect clear targets 4 | effect clear targets effect 5 | effect give 6 | effect give targets 7 | effect give targets effect 8 | effect give targets effect seconds 9 | effect give targets effect seconds amplifier 10 | effect give targets effect seconds amplifier hideParticles 11 | 12 | # effect 13 | effect 14 | effect 15 | effect foo 16 | 17 | # effect clear 18 | effect clear 19 | effect clear s 20 | effect clear @ 21 | effect clear @s 22 | effect clear @s 23 | effect clear @s foo 24 | effect clear @s minecraft:foo 25 | effect clear @s strength 26 | effect clear @s minecraft:strength 27 | effect clear @s minecraft:strength 28 | effect give @e[tag=foo] minecraft:strength 29 | effect give Arcensoth minecraft:strength 30 | effect give f7a39418-72ca-4bf2-bc7e-ba9df67a4707 minecraft:strength 31 | effect give @s minecraft:strength/foo 32 | 33 | # effect give 34 | effect give 35 | effect give 36 | effect give s 37 | effect give @ 38 | effect give @s 39 | effect give @s 40 | effect give @s foo 41 | effect give @s minecraft:foo 42 | effect give @s strength 43 | effect give @s minecraft:strength 44 | effect give @e[tag=foo] minecraft:strength 45 | effect give Arcensoth minecraft:strength 46 | effect give f7a39418-72ca-4bf2-bc7e-ba9df67a4707 minecraft:strength 47 | effect give @s minecraft:strength/foo 48 | effect give @s minecraft:strength 49 | effect give @s minecraft:strength 1 50 | effect give @s minecraft:strength foo 51 | effect give @s minecraft:strength 0 52 | effect give @s minecraft:strength -1 53 | effect give @s minecraft:strength 1 54 | effect give @s minecraft:strength 999999 55 | effect give @s minecraft:strength 999999 56 | effect give @s minecraft:strength 999999 foo 57 | effect give @s minecraft:strength 999999 0 58 | effect give @s minecraft:strength 999999 -1 59 | effect give @s minecraft:strength 999999 1 60 | effect give @s minecraft:strength 999999 127 61 | effect give @s minecraft:strength 999999 127 62 | effect give @s minecraft:strength 999999 127 t 63 | effect give @s minecraft:strength 999999 127 true 64 | effect give @s minecraft:strength 999999 127 false 65 | effect give @s minecraft:strength 999999 127 true 66 | effect give @s minecraft:strength 999999 127 true 67 | effect give @s minecraft:strength 999999 127 true uhoh 68 | 69 | effect give @s minecraft:strength 70 | effect give @s minecraft:strength 71 | effect give @s minecraft:strength 72 | effect give @s minecraft:strength 73 | effect give @s minecraft:strength 74 | effect give @s minecraft:strength 75 | effect give @s minecraft:strength 999999 76 | -------------------------------------------------------------------------------- /tests/comprehensive/target_selectors/invalid.mcfunction: -------------------------------------------------------------------------------- 1 | execute as @s[ 2 | execute as @s[ 3 | execute as @s[sort=nearest, 4 | execute as @s[sort=nearest, 5 | execute as @s[ run say hello 6 | execute as @s[]run execute as @s run say hello 7 | execute as @s[ ]run execute as @s run say hello 8 | execute as @s [] run execute as @s run say hello 9 | execute as @s [ ] run execute as @s run say hello 10 | execute as @s[nearest] run execute as @s run say hello 11 | execute as @s[nearest ] run execute as @s run say hello 12 | execute as @s[ nearest] run execute as @s run say hello 13 | execute as @s[ nearest ] run execute as @s run say hello 14 | execute as @s[,] run execute as @s run say hello 15 | execute as @s[ ,] run execute as @s run say hello 16 | execute as @s[, ] run execute as @s run say hello 17 | execute as @s[ , ] run execute as @s run say hello 18 | execute as @s[,sort=nearest] run execute as @s run say hello 19 | execute as @s[ ,sort=nearest] run execute as @s run say hello 20 | execute as @s[ ,sort=nearest] run execute as @s run say hello 21 | execute as @s[sort,sort=nearest] run execute as @s run say hello 22 | execute as @s[sort ,sort=nearest] run execute as @s run say hello 23 | execute as @s[sort=,sort=nearest] run execute as @s run say hello 24 | execute as @s[sort=nearest,] run execute as @s run say hello 25 | execute as @s[sort=nearest, ] run execute as @s run say hello 26 | execute as @s[sort=nearest ,, sort=bar] run execute as @s run say hello 27 | execute as @s[sort=nearest , , sort=bar] run execute as @s run say hello 28 | execute as @s[sort=nearest,sort] run execute as @s run say hello 29 | execute as @s[sort=nearest,sort=] run execute as @s run say hello 30 | execute as @s[sort=nearest,sort,sort=bar] run execute as @s run say hello 31 | execute as @s[sort=nearest,sort=,sort=bar] run execute as @s run say hello 32 | 33 | execute as @e[sort] run 34 | execute as @e[sort=] run 35 | execute as @e[sort=foo] run 36 | execute as @e[sort=nearestfoo] run 37 | execute as @e[sort=foonearest] run 38 | execute as @e[sort=!nearest] run 39 | 40 | execute as @e[limit] run 41 | execute as @e[limit=] run 42 | execute as @e[limit=foo] run 43 | execute as @e[limit=nearest] run 44 | execute as @e[limit=0.1] run 45 | execute as @e[limit=-1] run 46 | execute as @e[limit=!1] run 47 | 48 | execute as @e["quoted_key=foo] run say hi 49 | execute as @e[quoted_key"=foo] run say hi 50 | execute as @e["quoted_key""=foo] run say hi 51 | execute as @e["quoted_key"bar=foo] run say hi 52 | execute as @e[bar"quoted_key"=foo] run say hi 53 | 54 | execute as @e['quoted_key=foo] run say hi 55 | execute as @e[quoted_key'=foo] run say hi 56 | execute as @e['quoted_key''=foo] run say hi 57 | execute as @e['quoted_key'bar=foo] run say hi 58 | execute as @e[bar'quoted_key'=foo] run say hi 59 | -------------------------------------------------------------------------------- /tests/comprehensive/text_components/invalid.mcfunction: -------------------------------------------------------------------------------- 1 | tellraw @s xtrue 2 | tellraw @s xtrue, 3 | tellraw @s true, 4 | tellraw @s [true,] 5 | tellraw @s [xtrue,] 6 | tellraw @s truex 7 | tellraw @s xtruex 8 | 9 | tellraw @s {"bold": foo} 10 | tellraw @s {"bold": "true"} 11 | 12 | tellraw @s {"color": "foo"} 13 | 14 | tellraw @s {"keybind": "foo"} 15 | 16 | tellraw @s { "extra" : foo } 17 | tellraw @s { "extra" : foo, "text": "hello" } 18 | tellraw @s { "extra" : "{}" } 19 | tellraw @s { "extra" : "{ a : [ { b : c} ] }" } 20 | tellraw @s { "extra" : "{ a : " " } 21 | tellraw @s { "extra" : "{ a : \" " } 22 | tellraw @s { "extra" : "[]" } 23 | tellraw @s { "extra" : "[ { a : b } ] " } 24 | tellraw @s { "extra" : "[ { a : \" } ] " } 25 | tellraw @s { "extra" : "[ { a : " } ] " } 26 | 27 | tellraw @s { "text": "hello", "extra" : "{}" } 28 | tellraw @s { "extra" : "{}", "text": "hello" } 29 | tellraw @s [{ "extra" : "{}" }, { "extra" : "{}" }] 30 | 31 | tellraw @s { "unknown" : "{}" } 32 | tellraw @s { "unknown" : "[]" } 33 | tellraw @s { "extra" : [ { "unknown" : "foo" } ] } 34 | tellraw @s { "extra" : [ { "unknown" : "{}" } ] } 35 | tellraw @s { "extra" : [ { "unknown" : "[]" } ] } 36 | tellraw @s { "foo" : { "bar" : "{}" } } 37 | tellraw @s { "foo" : { "bar" : "[]" } } 38 | tellraw @s { "foo" : [ { "bar" : "{}" } ] } 39 | tellraw @s { "foo" : [ { "bar" : "[]" } ] } 40 | 41 | tellraw @s {"text"} 42 | tellraw @s {"text 43 | tellraw @s {"text" 44 | tellraw @s {"text": 45 | tellraw @s {"text":} 46 | tellraw @s {"text":} 47 | tellraw @s {"text": } 48 | tellraw @s {"text": } 49 | tellraw @s {"text": } 50 | tellraw @s {"text":foo} 51 | tellraw @s {"text": foo} 52 | tellraw @s {"text": foo} 53 | tellraw @s {"text": foo} 54 | tellraw @s {"text": foo } 55 | tellraw @s {"text" foo} 56 | tellraw @s {"text" foo } 57 | tellraw @s {"text": 0} 58 | tellraw @s {"text": true} 59 | tellraw @s {"text": hello world} 60 | tellraw @s {"translate": true} 61 | tellraw @s {"translate": hello world} 62 | tellraw @s {"translate": true, "text"} 63 | tellraw @s {"translate": true, "text":} 64 | tellraw @s {"translate": true, "text": true} 65 | tellraw @s {"translate": true, "text": "hello"} 66 | tellraw @s {"translate": two words, "text": "hello"} 67 | tellraw @s {"translate": "two words", "text": uh oh} 68 | tellraw @s {"translate": true, "text": hello world} 69 | tellraw @s {"translate": true, "unknown": true} 70 | 71 | tellraw @s {"unknown": "unknown text component key"} 72 | tellraw @s {"selector": "not a target"} 73 | tellraw @s {"selector": " @e"} 74 | tellraw @s {"selector": "@e "} 75 | tellraw @s {"selector": " @e "} 76 | tellraw @s {"selector": "@e["} 77 | tellraw @s {"selector": "@e]"} 78 | tellraw @s {"selector": "@e[sort"} 79 | tellraw @s {"selector": "@e[sort]"} 80 | tellraw @s {"selector": "@e[sort="} 81 | tellraw @s {"selector": "@e[sort=foo"} 82 | tellraw @s {"selector": "@e[sort=foo]"} 83 | tellraw @s {"selector": "@e[sort=nearest"} 84 | tellraw @s {"selector": " @e[sort=nearest]"} 85 | tellraw @s {"selector": "@e[sort=nearest] "} 86 | tellraw @s {"selector": " @e[sort=nearest] "} 87 | tellraw @s {"selector": "@e[sort=nearest,"} 88 | tellraw @s {"selector": "@e[sort=nearest,]"} 89 | tellraw @s [{"extra": "x"}] 90 | tellraw @s [{"extra": {"foo": [0, [1, 2], 3]}}] 91 | 92 | tellraw @s {"block": "", "nbt": "RecordItem"} 93 | tellraw @s {"block": "no", "nbt": "RecordItem"} 94 | tellraw @s {"block": true, "nbt": "RecordItem"} 95 | tellraw @s {"block": 0, "nbt": "RecordItem"} 96 | tellraw @s {"block": 1 2 3, "nbt": "RecordItem"} 97 | tellraw @s {"block": "10 20", "nbt": "RecordItem"} 98 | tellraw @s {"block": "^10 20 30", "nbt": "RecordItem"} 99 | tellraw @s {"block": "10 ^20 30", "nbt": "RecordItem"} 100 | tellraw @s {"block": "10 20 ^30", "nbt": "RecordItem"} 101 | 102 | tellraw @s {"text": 3.14x} 103 | 104 | tellraw @s {"text": "hello", "nby": "RecordItem.tag.foo", "text": "world"} 105 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | pull_request: 5 | branches: [main] 6 | paths: 7 | - "mcfunction.*" 8 | push: 9 | tags: ["v*.*.*"] 10 | 11 | jobs: 12 | ci: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | # --------------------------------------------- 17 | # ---- gather repo ----- 18 | # --------------------------------------------- 19 | - uses: actions/checkout@v3 20 | with: 21 | fetch-depth: 0 22 | 23 | # Fetch all branches and tags 24 | - name: Fetch all branches and tags 25 | run: git fetch --all 26 | 27 | - name: Checkout main branch 28 | run: git checkout main 29 | 30 | # --------------------------------------------- 31 | # ---- setup deps ----- 32 | # --------------------------------------------- 33 | - name: Use Node.js ${{ matrix.node-version }} 34 | uses: actions/setup-node@v3 35 | with: 36 | node-version: 20.x 37 | cache: npm 38 | 39 | - name: Install npm stuff 40 | run: | 41 | npm install -g @vscode/vsce 42 | npm install -g js-yaml 43 | npm install -g ovsx 44 | npm install -g plist2 45 | 46 | - name: "Setup jq" 47 | uses: dcarbone/install-jq-action@v2 48 | with: 49 | version: 1.7 50 | force: true 51 | 52 | # --------------------------------------------- 53 | # ---- generate files ----- 54 | # --------------------------------------------- 55 | - name: Export YAML -> JSON -> PLIST 56 | run: | 57 | js-yaml mcfunction.tmLanguage.yaml > mcfunction.tmLanguage.json 58 | plist2 mcfunction.tmLanguage.json mcfunction.tmLanguage 59 | 60 | - id: version 61 | name: Version 62 | run: | 63 | VERSION=$(echo ${{ github.ref_name }} | sed 's/^v//') 64 | echo "v=$VERSION" >> "$GITHUB_OUTPUT" 65 | echo "ext=syntax-mcfunction-$VERSION.vsix" >> "$GITHUB_OUTPUT" 66 | 67 | # --------------------------------------------- 68 | # ---- push files ----- 69 | # --------------------------------------------- 70 | - name: Push JSON to repo for sublime (if not matched) 71 | if: github.ref == 'refs/heads/main' 72 | run: | 73 | git config --global user.name "github-actions" 74 | git config --global user.email "action@github.com" 75 | git add mcfunction.tmLanguage.json mcfunction.tmLanguage 76 | git commit -m "🤖 Generate JSON/PLIST" 77 | git push origin main 78 | continue-on-error: true 79 | 80 | - name: Push version number to repo 81 | if: startsWith(github.ref, 'refs/tags/') 82 | run: | 83 | jq '.version = "${{ steps.version.outputs.v }}"' package.json > package.json.tmp 84 | mv package.json.tmp package.json 85 | git config --global user.name "github-actions" 86 | git config --global user.email "action@github.com" 87 | git add package.json 88 | git commit -m "🎉 Release ${{ steps.version.outputs.v }}" 89 | git push origin main 90 | 91 | # --------------------------------------------- 92 | # ---- release ----- 93 | # --------------------------------------------- 94 | - id: package 95 | name: Package VSCode Ext 96 | run: vsce package 97 | 98 | - name: Archive production artifacts 99 | uses: actions/upload-artifact@v3 100 | if: "!startsWith(github.ref, 'refs/tags/')" 101 | with: 102 | name: dist-without-markdown 103 | path: | 104 | *.vsix 105 | mcfunction.tmLanguage.yaml 106 | mcfunction.tmLanguage.json 107 | 108 | - name: Release 109 | uses: softprops/action-gh-release@v1 110 | if: startsWith(github.ref, 'refs/tags/') 111 | with: 112 | files: | 113 | *.vsix 114 | mcfunction.tmLanguage.yaml 115 | mcfunction.tmLanguage.json 116 | 117 | - name: Release to vsce 118 | if: startsWith(github.ref, 'refs/tags/') 119 | run: vsce publish 120 | env: 121 | VSCE_PAT: ${{ secrets.VSCE_PAT }} 122 | 123 | - name: Release to openvsix 124 | if: startsWith(github.ref, 'refs/tags/') 125 | run: npx ovsx publish ${{ steps.version.outputs.ext }} -p ${{ secrets.OVSX }} 126 | -------------------------------------------------------------------------------- /tests/comprehensive/target_selectors/valid.mcfunction: -------------------------------------------------------------------------------- 1 | execute as @s run execute as @s run say hello 2 | execute as 0-0-0-0-0 run execute as @s run say hello 3 | execute as f7a39418-72ca-4bf2-bc7e-ba9df67a4707 run execute as @s run say hello 4 | execute as Arcensoth run execute as @s run say hello 5 | execute as some_guy run execute as @s run say hello 6 | execute as @s[] run execute as @s run say hello 7 | execute as @s[ ] run execute as @s run say hello 8 | execute as @s[tag=foo] run execute as @s run say hello 9 | execute as @s[tag=!foo] run execute as @s run say hello 10 | execute as @s[ tag = foo ] run execute as @s run say hello 11 | execute as @s[ tag = !foo ] run execute as @s run say hello 12 | execute as @s[ tag = ! foo ] run execute as @s run say hello 13 | execute as @s[tag=foo,tag=bar] run execute as @s run say hello 14 | execute as @s[ tag=foo,tag=bar] run execute as @s run say hello 15 | execute as @s[tag=foo,tag=bar ] run execute as @s run say hello 16 | execute as @s[tag=foo ,tag=bar] run execute as @s run say hello 17 | execute as @s[tag=foo, tag=bar] run execute as @s run say hello 18 | execute as @s[tag=foo , tag=bar] run execute as @s run say hello 19 | execute as @s[ tag = foo , tag = bar ] run execute as @s run say hello 20 | execute as @s[tag = foo ,tag = bar ] run execute as @s run say hello 21 | execute as @s[tag = foo , tag = bar ] run execute as @s run say hello 22 | execute as @s[ tag = foo ,tag = bar ] run execute as @s run say hello 23 | execute as @s[ tag = foo , tag = bar ] run execute as @s run say hello 24 | execute as @s[tag = foo ,tag = bar ] run execute as @s run say hello 25 | execute as @s[tag = foo,tag = bar ] run execute as @s run say hello 26 | execute as @s[tag = foo, tag = bar ] run execute as @s run say hello 27 | execute as @s[ tag = foo , tag = bar ] run execute as @s run say hello 28 | execute as @s[tag = foo , tag = bar] run execute as @s run say hello 29 | execute as @s[ tag = foo , tag = bar , tag = baz ] run execute as @s run say hello 30 | execute as @s[nbt={custom: true}] as @s run say hello 31 | execute as @s[nbt={foo: {bar: []}}] as @s run say hello 32 | execute as @s[nbt={foo: {bar: [{baz: {}}]}}] as @s run say hello 33 | execute as @s[nbt=!{custom: true}] as @s run say hello 34 | execute as @e[tag=foo, sort=nearest, nbt={custom: true}] run execute as @s run execute as @s run say hello 35 | 36 | execute as @e[sort=nearest] run say hello 37 | execute as @e[ sort = nearest ] run say hello 38 | execute as @e[ sort = nearest ] run say hello 39 | 40 | execute as @e[limit=1] run say hello 41 | execute as @e[limit=01] run say hello 42 | execute as @e[ limit = 1 ] run say hello 43 | execute as @e[ limit = 1 ] run say hello 44 | 45 | execute as @e[team=] run say hi 46 | execute as @e[team=!] run say hi 47 | execute as @e[team=foo] run say hi 48 | execute as @e[team=!foo] run say hi 49 | execute as @e[team=foo.bar] run say hi 50 | execute as @e[team=!foo.bar] run say hi 51 | 52 | execute as @e[tag=] run say hi 53 | execute as @e[tag=!] run say hi 54 | execute as @e[tag=foo] run say hi 55 | execute as @e[tag=!foo] run say hi 56 | execute as @e[tag=foo.bar] run say hi 57 | execute as @e[tag=!foo.bar] run say hi 58 | 59 | execute as @e[name=] run say hi 60 | execute as @e[name=!] run say hi 61 | execute as @e[name=foo] run say hi 62 | execute as @e[name=!foo] run say hi 63 | execute as @e[name=""] run say hi 64 | execute as @e[name="foo bar"] run say hi 65 | execute as @e[name=!"foo bar"] run say hi 66 | execute as @e[name="foo \" bar"] run say hi 67 | execute as @e[name="foo ' bar"] run say hi 68 | execute as @e[name=''] run say hi 69 | execute as @e[name='foo bar'] run say hi 70 | execute as @e[name=!'foo bar'] run say hi 71 | execute as @e[name='foo \' bar'] run say hi 72 | execute as @e[name='foo " bar'] run say hi 73 | 74 | # double quoted keys 75 | execute as @e["quoted_key"=foo] run say hi 76 | execute as @e["quoted key"=foo] run say hi 77 | execute as @e[foo=bar,"quoted key"=foo] run say hi 78 | execute as @e["quoted key"=foo,foo=bar] run say hi 79 | execute as @e[foo=bar,"quoted key"=foo,foo=bar] run say hi 80 | execute as @e[foo=bar, "quoted key"=foo, foo=bar] run say hi 81 | execute as @e["quoted key"=foo, "another quoted key"=foo] run say hi 82 | execute as @e["quoted 'x key"=foo] run say hi 83 | execute as @e["quoted 'x' key"=foo] run say hi 84 | 85 | # single quoted keys 86 | execute as @e['quoted_key'=foo] run say hi 87 | execute as @e['quoted key'=foo] run say hi 88 | execute as @e[foo=bar,'quoted key'=foo] run say hi 89 | execute as @e['quoted key'=foo,foo=bar] run say hi 90 | execute as @e[foo=bar,'quoted key'=foo,foo=bar] run say hi 91 | execute as @e[foo=bar, 'quoted key'=foo, foo=bar] run say hi 92 | execute as @e['quoted key'=foo, 'another quoted key'=foo] run say hi 93 | execute as @e['quoted "x key'=foo] run say hi 94 | execute as @e['quoted "x" key'=foo] run say hi 95 | -------------------------------------------------------------------------------- /tests/comprehensive/everything.mcfunction: -------------------------------------------------------------------------------- 1 | #> Raycasting 2 | # 3 | # Casts a ray from starting position along a configurable number 4 | # of blocks with a confugrable accuracy, counting the number of 5 | # entities hit by the ray along the way. 6 | # 7 | # @params 8 | # $mypack.raycast.distance param 9 | # The number of blocks to cast forward. 10 | # $mypack.raycast.precision param 11 | # The ratio of block precision to a full block. 12 | # 13 | # @returns 14 | # $mypack.raycast.result return 15 | # The number of entities hit by the ray. 16 | 17 | function #mypack:hooks/raycast/begin 18 | 19 | function mypack:raycast/loop 20 | 21 | function #mypack:hooks/raycast/end 22 | 23 | # this is a comment 24 | say hello world 25 | 26 | # indented comment 27 | say hello indent 28 | 29 | effect give @s minecraft:night_vision 999999 1 true 30 | effect give @s minecraft:night_vision 999999 1 false 31 | 32 | teleport 1 2 3 33 | teleport 100.5 80 -100.5 34 | 35 | execute if score @a temp matches 10.. run 36 | execute if score @a temp matches ..20 run 37 | execute if score @a temp matches 10..20 run 38 | 39 | execute positioned 10 ~ -10 run 40 | execute positioned 10 ~10 -10 run 41 | execute positioned 10 ~0.5 -10 run 42 | execute positioned 10 ~.5 -10 run 43 | execute positioned 10 ~-10 -10 run 44 | execute positioned 10 ~-0.5 -10 run 45 | execute positioned 10 ~-.5 -10 run 46 | 47 | execute positioned 10 ^ -10 run 48 | execute positioned 10 ^10 -10 run 49 | execute positioned 10 ^0.5 -10 run 50 | execute positioned 10 ^.5 -10 run 51 | execute positioned 10 ^-10 -10 run 52 | execute positioned 10 ^-0.5 -10 run 53 | execute positioned 10 ^-.5 -10 run 54 | 55 | function mypack:foo 56 | function mypack:foo/bar 57 | function mypack:foo/bar/baz 58 | function #mypack:foo 59 | function #mypack:foo/bar 60 | function #mypack:foo/bar/baz 61 | 62 | execute if block ~ ~ ~ minecraft:oak_log[axis=x] run 63 | execute if block ~ ~ ~ minecraft:oak_leaves[distance=5] run 64 | execute if block ~ ~ ~ minecraft:oak_leaves[persistent=true] run 65 | execute if block ~ ~ ~ minecraft:oak_leaves[persistent=false] run 66 | execute if block ~ ~ ~ minecraft:oak_leaves[distance=5,persistent=true] run 67 | execute if block ~ ~ ~ #minecraft:leaves[distance=5] run 68 | execute if block ~ ~ ~ #minecraft:leaves[distance=5,persistent=true] run 69 | setblock ~ ~ ~ mypack:foo{foo:bar} destroy 70 | setblock ~ ~ ~ mypack:foo{foo: bar} destroy 71 | setblock ~ ~ ~ mypack:foo[facing=up]{foo: bar} destroy 72 | setblock ~ ~ ~ mypack:foo[facing = up]{foo: bar} destroy 73 | setblock ~ ~ ~ minecraft:dispenser[facing=up]{Items: [{id: "minecraft:diamond", Count: 1}]} 74 | 75 | tag @s add my.tag 76 | 77 | datapack enable "hello world" 78 | datapack enable "escape \" me" 79 | datapack enable 'hello world' 80 | datapack enable 'escape \' me' 81 | 82 | execute as f7a39418-72ca-4bf2-bc7e-ba9df67a4707 run 83 | execute as 0-0-0-0-0 run 84 | 85 | execute store result score #fakeplayer 86 | execute store result score #fake.player 87 | execute store result score #fake_player 88 | execute store result score $fakeplayer 89 | execute store result score %fakeplayer 90 | 91 | execute as @b 92 | execute as @a 93 | 94 | execute as @a[tag=foo] 95 | execute as @a[tag=!foo] 96 | 97 | execute as @a[sort=nearest] run 98 | execute as @a[gamemode=survival] run 99 | execute as @a[gamemode=!creative] run 100 | execute as @a[tag=foo,tag=bar,tag=!baz] run 101 | 102 | execute as @a[distance=15] 103 | execute as @a[distance=1.5] 104 | execute as @a[distance=.5] 105 | execute as @a[distance=-.25] 106 | 107 | execute as @a[distance=100] 108 | execute as @a[distance=..10] 109 | execute as @a[distance=11..19] 110 | execute as @a[distance=20..] 111 | execute as @a[distance=0.5] 112 | execute as @a[distance=..0.1] 113 | execute as @a[distance=0.2..0.8] 114 | execute as @a[distance=0.9..] 115 | 116 | execute as @a[type=minecraft:bat] run 117 | execute as @a[type=!minecraft:cow,type=!minecraft:pig] run 118 | execute as @a[type=#minecraft:skeletons] run 119 | execute as @a[type=!#minecraft:skeletons,type=!minecraft:zombie] run 120 | 121 | execute as @a[tag=my.tag] run 122 | 123 | execute as @a[name="hello world"] as @s run 124 | execute as @a[name="escape \" me"] as @s run 125 | execute as @a[name="how, about, commas ?"] as @s run 126 | execute as @a[name="and [braces] ?"] as @s run 127 | 128 | execute as @e[nbt={ PortalCooldown: 0 }] run 129 | execute as @e[nbt={ Item: {id: "minecraft:diamond", Count: 64 } }] run 130 | 131 | execute if score @s foo < @s bar run 132 | execute if score @s foo <= @s bar run 133 | execute if score @s foo = @s bar run 134 | execute if score @s foo > @s bar run 135 | execute if score @s foo >= @s bar run 136 | 137 | data get entity @s SelectedItem.tag.display.Name 138 | data get entity @s Inventory[0] 139 | data get entity @s Inventory[{id: "minecraft:diamond"}].Count 140 | data get entity @s Inventory[].tag{custom: true}.display.Name 141 | 142 | data merge entity @s { foo: true, bar: 1234 } 143 | data modify block ~ ~ ~ RecordItem.tag set value { messages: [hi, bye] } 144 | data modify block ~ ~ ~ RecordItem.tag.messages append value [ { message: "hello world" } ] 145 | 146 | tellraw @a {"text": "hello world", "color": "blue"} 147 | tellraw @a [{"text": "hello", "color": "blue"}, {"text": "world", "color": "blue"}] 148 | 149 | execute as @a[scores={myscore=10}] run 150 | execute as @a[scores={myscore=10..12}] run 151 | execute as @a[scores={foo=10, bar=1..5, baz=..0}] run 152 | 153 | execute as @a[advancements={minecraft:story/form_obsidian=true}] run 154 | execute as @a[advancements={minecraft:story/obtain_armor={iron_helmet=true}}] run 155 | execute as @a[advancements={minecraft:story/obtain_armor={iron_helmet=true, gold_helmet=false}}] run 156 | execute as @a[advancements={minecraft:story/form_obsidian=true, minecraft:story/follow_ender_eye=true}] run 157 | execute as @a[advancements={minecraft:story/form_obsidian={foo=true, bar=false},minecraft:story/follow_ender_eye={foo=false, bar=true}}] run 158 | 159 | give @s diamond_sword{display: {Name: '"My Custom Sword"'}} 160 | give @s minecraft:diamond_sword{display: {Name: '"My Custom Sword"'}} 161 | 162 | execute if score @s foo < @s bar run say execute if score @s foo < @s bar run say 163 | execute if score @s foo < @s bar run say hello @e[tag=baz, sort=nearest, limit=1] how are you? 164 | -------------------------------------------------------------------------------- /tests/comprehensive/text_components/valid.mcfunction: -------------------------------------------------------------------------------- 1 | # See: https://github.com/skylinerw/guides/blob/master/java/text%20component.md 2 | 3 | # "text" and escaping 4 | tellraw @s {"text": "hello world"} 5 | tellraw @s {"text": "hello \"escaped\" world"} 6 | tellraw @s {"text": "hello \" world"} 7 | tellraw @s {"text": "hello ' world"} 8 | tellraw @s {"text": "hello\nworld"} 9 | tellraw @s {"text": true} 10 | tellraw @s {"text": 123} 11 | tellraw @s {"text": -321} 12 | tellraw @s {"text": 3.14} 13 | 14 | # "bold", "italic", "underline", and "obfuscated" 15 | tellraw @s {"bold": false} 16 | tellraw @s {"text": "this is bold", "bold": true} 17 | tellraw @s {"italic": false} 18 | tellraw @s {"text": "this is italic", "italic": true} 19 | tellraw @s {"underlined": false} 20 | tellraw @s {"text": "this is underlined", "underlined": true} 21 | tellraw @s {"strikethrough": false} 22 | tellraw @s {"text": "this is strikethrough", "strikethrough": true} 23 | tellraw @s {"obfuscated": false} 24 | tellraw @s {"text": "this is obfuscated", "obfuscated": true} 25 | 26 | # "color" 27 | tellraw @s {"color": "black"} 28 | tellraw @s {"color": "dark_blue"} 29 | tellraw @s {"color": "dark_green"} 30 | tellraw @s {"color": "dark_aqua"} 31 | tellraw @s {"color": "dark_red"} 32 | tellraw @s {"color": "dark_purple"} 33 | tellraw @s {"color": "gold"} 34 | tellraw @s {"color": "gray"} 35 | tellraw @s {"color": "dark_gray"} 36 | tellraw @s {"color": "blue"} 37 | tellraw @s {"color": "green"} 38 | tellraw @s {"color": "aqua"} 39 | tellraw @s {"color": "red"} 40 | tellraw @s {"color": "light_purple"} 41 | tellraw @s {"color": "yellow"} 42 | tellraw @s {"color": "white"} 43 | 44 | # "extra" 45 | tellraw @s {"extra": []} 46 | tellraw @s {"extra": ["hello"]} 47 | tellraw @s {"extra": ["hello", "world"]} 48 | tellraw @s {"extra": [{"text": "hello world"}]} 49 | tellraw @s {"extra": [{"text": "hello \" world"}]} 50 | tellraw @s {"extra": [{"extra": ["nested"]}]} 51 | tellraw @s {"extra": [{"extra": [{"extra": ["nested again"]}]}]} 52 | 53 | # "translate" and "with" 54 | tellraw @s {"translate": "gui.toTitle"} 55 | tellraw @s {"translate": "Text inserted here"} 56 | tellraw @s {"translate": "commands.generic.entity.invalidType", "with": ["Creeper"]} 57 | tellraw @s {"translate": "Insert a %s here.", "with": ["STRING"]} 58 | tellraw @s {"translate": "custom.key", "with": ["STRING1", "STRING2"]} 59 | tellraw @s {"translate": "Nearest player: %s", "with": [{"selector": "@p"}]} 60 | 61 | # "keybind" 62 | tellraw @s {"keybind": "key.drop"} 63 | 64 | # "selector" 65 | tellraw @s {"selector": ""} 66 | tellraw @s {"selector": "Arcensoth"} 67 | tellraw @s {"selector": "f7a39418-72ca-4bf2-bc7e-ba9df67a4707"} 68 | tellraw @s {"selector": "@s"} 69 | tellraw @s {"selector": "@e[sort=nearest, limit=1]"} 70 | 71 | # "insertion" 72 | tellraw @s {"insertion": "hello world"} 73 | tellraw @s {"insertion": "/execute as @a run say hello @e[sort=nearest, limit=1]"} 74 | 75 | # "score" 76 | tellraw @s {"score": {"name": "@p", "objective": "TEST"}} 77 | tellraw @s {"score": {"name": "*", "objective": "TEST"}} 78 | tellraw @s {"score": {"name": "@p", "objective": "TEST", "value": 123}} 79 | tellraw @s {"score": {"name": "@p", "objective": "TEST", "value": -123}} 80 | tellraw @s {"score": {"name": "@p", "objective": "TEST", "value": 3.14}} 81 | tellraw @s {"score": {"name": "@p", "objective": "TEST", "value": true}} 82 | tellraw @s {"score": {"name": "@p", "objective": "TEST", "value": "hello world"}} 83 | tellraw @s {"score": {"name": "@e[sort=nearest, limit=1]", "objective": "TEST", "value": "hello"}} 84 | tellraw @s {"score": {"name": "#temp", "objective": "TEST"}} 85 | tellraw @s {"score": {"name": "$mypack.calc", "objective": "TEST"}} 86 | 87 | # "clickEvent" 88 | tellraw @s {"text": "Click", "clickEvent": {"action": "open_url", "value": "http://google.com"}} 89 | tellraw @s {"text": "Click", "clickEvent": {"action": "run_command", "value": "/say Must be OP'd to run this command"}} 90 | tellraw @s {"text": "Click", "clickEvent": {"action": "suggest_command", "value": "Text replaced"}} 91 | 92 | # "hoverEvent" 93 | tellraw @s {"text": "Hover", "hoverEvent": {"action": "show_text", "value": "Basic string"}} 94 | tellraw @s {"text": "Hover", "hoverEvent": {"action": "show_text", "value": ["", { "text": "Text\n", "color": "green", "underlined": true}, "component"]}} 95 | tellraw @s {"text": "Hover", "hoverEvent": {"action": "show_entity", "value": "{name: \"Skylinerw\", type: \"Creeper\", id: \"00000000-0000-0000-0000-000000000000\"}"}} 96 | tellraw @s {"text": "Hover", "hoverEvent": {"action": "show_entity", "value": "{name: \"Skylinerw\", id: \"Not a valid UUID\"}"}} 97 | tellraw @s {"text": "Hover", "hoverEvent": {"action": "show_item", "value": "{id: \"minecraft:stone\", Count: 1b, tag: {display: {Lore: [\"\\\"Lore line 1\\\"\", \"\\\"Lore line 2\\\"\"]}}}"}} 98 | tellraw @s {"text": "Hover", "hoverEvent": {"action": "show_item", "value": "{id: 'minecraft:stone', Count: 1b, tag: {display: {Lore: ['\"Lore line 1\"', '\"Lore line 2\"']}}}"}} 99 | 100 | # "nbt", "entity", "block", and "interpret" 101 | tellraw @s {"entity": "@s", "nbt": "SelectedItem"} 102 | tellraw @s {"entity": "@s", "nbt": "SelectedItem.tag"} 103 | tellraw @s {"entity": "@e[sort=nearest, limit=1]", "nbt": "SelectedItem.tag.display"} 104 | tellraw @s {"entity": "@s", "nbt": "SelectedItem.tag.display.Name", "interpret": true} 105 | tellraw @s {"block": "~ ~-1 ~", "nbt": "RecordItem"} 106 | tellraw @s {"block": "~ ~-1 ~", "nbt": "RecordItem.tag"} 107 | tellraw @s {"block": "~ ~-1 ~", "nbt": "RecordItem.tag.display"} 108 | tellraw @s {"block": "10 20 30", "nbt": "RecordItem"} 109 | tellraw @s {"block": "10 ~20 30", "nbt": "RecordItem"} 110 | tellraw @s {"block": "10 ~ 30", "nbt": "RecordItem"} 111 | tellraw @s {"block": "~10 20 ~30", "nbt": "RecordItem"} 112 | tellraw @s {"block": "~10 ~20 ~30", "nbt": "RecordItem"} 113 | tellraw @s {"block": "~ ~ ~", "nbt": "RecordItem"} 114 | tellraw @s {"block": "^10 ^20 ^30", "nbt": "RecordItem"} 115 | tellraw @s {"block": "^10 ^ ^30", "nbt": "RecordItem"} 116 | 117 | # nested lists 118 | tellraw @s ["Hello ", {"selector": "@e[sort=nearest, limit=1]"}, ", how are you?"] 119 | tellraw @s ["Hello ", {"selector": "@e[sort=nearest, limit=1]"}, ", how are you?"] 120 | tellraw @s ["a", ["b", {"text": "c"}, ["d"], "e"]] 121 | tellraw @s ["a", ["b", {"text": "c"}, ["d"], "e"], {"text":"f", "extra": [{"text": "g"}]}] 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | logo 2 | 3 | # syntax-mcfunction 4 | 5 |

6 | A syntax definition for Minecraft Commands. 7 |
8 | Give this repo a ⭐ if you like it! 9 |

10 | 11 | ![demo](https://raw.githubusercontent.com/MinecraftCommands/syntax-mcfunction/main/imgs/preview.png) 12 | 13 | This project contains syntax definitions for Minecraft commands (`.mcfunction`) using the textmate grammer spec. It is a general, feature complete language spec built via generic syntatical components that describe the language (aka, mimimal hardcoded command support). This means it should support future game versions, many modded commands, and even custom language compilers such as [bolt](https://github.com/mcbeet/bolt) and [jmc](https://jmc.wingedseal.com/). 14 | 15 | > [!Warning] 16 | > This syntax does **not** contain any error highlighting meaning invalid commands will highlight correctly. If you are looking error validation and auto-complete for vanilla commands, we recommend the [Spyglass](https://spyglassmc.com/) project for a language-server that builds upon this simple grammar! 17 | 18 | ## Installing 19 | 20 | ### [VSCode Extension](https://marketplace.visualstudio.com/items?itemName=MinecraftCommands.syntax-mcfunction) 21 | 22 | Install the extension from the [marketplace](https://marketplace.visualstudio.com/items?itemName=MinecraftCommands.syntax-mcfunction) or by searching for `syntax-mcfunction` in the extensions tab. 23 | 24 | ### [open-vsix Extension](https://open-vsx.org/extension/MinecraftCommands/syntax-mcfunction) 25 | 26 | Follow these [instructions](https://github.com/eclipse/openvsx/wiki/Using-Open-VSX-in-VS-Code) if you are looking on using the open-vsix version of this project in VSCode or any of it's forks. 27 | 28 | ### Sublime Text 29 | 30 | It is recommended you use [Package Control](https://packagecontrol.io/) to manage the package: 31 | 32 | 1. [Install Package Control](https://packagecontrol.io/installation) if it is not already present. 33 | 2. Run the `Package Control: Add Repository` [command](https://packagecontrol.io/docs/usage) and enter `https://github.com/MinecraftCommands/syntax-mcfunction.git` to add the repository as a package. 34 | 3. Run the `Package Control: Install Package` and search for `syntax-mcfunction` to install it as you would a normal package. 35 | 36 |
If you already had language-mcfunction 37 | 38 | > **Note** 39 | > You might want to remove Arc's language-mcfunction if you have it installed. You can do that via `Package Control: Remove Repository` and selecting https://github.com/Arcensoth/language-mcfunction then `Package Control: Remove Package` and selecting `language-mcfunction` from 2021. 40 | 41 |
42 | 43 | This will keep the package updated with the repository automatically. 44 | 45 | Otherwise you can clone the repository into user packages (e.g. `%appdata%\Sublime Text 3\Packages`) and update it manually. 46 | 47 | ## `language-mcfunction` 48 | 49 | While this grammar was created from scratch, much of it's work was adapted from the original [`language-mcfunction`](https://github.com/arcensoth/language-mcfunction) grammar. One of the original ideas for `language-mcfunction` was to develop a general, simpler version for `mcfunction`. 50 | 51 | > [!Note] 52 | > Actually, an earlier prototype of this grammar was going to be used as a base for a `language-mcfunction` rewrite. 53 | 54 | There are some key differences to note between this syntax and the original: 55 | - `language-mcfunction` is designed to operate on singular lines. If one line is broken, it does not affect lines after it and commands **cannot** span across multiple lines at all. 56 | - This was the core issue that caused me to develop `syntax-mcfunction` (even before Minecraft 1.20.2 introduced multi-line commands) as I needed a grammar that had multi-line support. 57 | - `language-mcfunction` contains specific commands validation and reports errors for invalid commands 58 | - `syntax-mcfunction` differs in philosophy here. Textmate grammars are not well-equipped to actually validate commands — that's more of a role for language servers. 59 | - This project also generally highlights commands as it allows for easier maintenance and is better for platforms such as Github where multiple command extensions / modded commands might reuse the `.mcfunction` extension. 60 | 61 | Due to these, coloring and highlighting will vary in a couple of places when comparing these grammars. While the goal is to match `language-mcfunction` as close as possible, there will never be a true 1:1 match in highlighting as the entire design of the grammar differs. 62 | 63 | ## Contributing 64 | 65 | We are happy to accept any PRs just make sure to make a cooresponding issue first to track it. When making a PR, make sure the branch of your fork is **not** `main` and also make sure maintainers are allowed to edit your PR. This is helpful for me making small edits to the PR before merging. 66 | 67 | We only require edits to the `yaml` file as the `json` and `plist` files are generated. You can use the npm package, `js-yaml`, to generate the `json` file after editting the `yaml` file. 68 | 69 | ```bash 70 | npx js-yaml mcfunction.tmLanguage.yaml > mcfunction.tmLanguage.json 71 | ``` 72 | 73 | ### Making Releases 74 | 75 | Releases are semi-automated in this repo. Once your PR is merged into the `main` branch, the maintainer will create a new tag to trigger our Github CI to produce a new version. 76 | 77 | ```bash 78 | git tag -a v1.2.3 79 | git push --tags 80 | ``` 81 | 82 | This will generate the json and plist variations of the grammar, create and push a `.vsix` extension to the VSCode and open-vsix marketplaces, and create a release in the [releases](https://github.com/MinecraftCommands/syntax-mcfunction/releases) tab. 83 | 84 | The maintainer will then need to manually fill in the body of the release to add in the changelog and links. 85 | 86 | ## Acknowledgements 87 | 88 | This repo is entirely dedicated to our beloved community member and friend, [Arcensoth](https://github.com/Arcensoth). His work on the original [language-mcfunction](https://github.com/Arcensoth/language-mcfunction) and overall contributions to the Minecraft Commands community was invaluable and he will be missed. 89 | 90 | > Rest in Peace, Arcensoth 91 | -------------------------------------------------------------------------------- /tests/vanilla.mcfunction: -------------------------------------------------------------------------------- 1 | #> Example of base game commands 2 | # 3 | # Casts a ray from starting position along a configurable number 4 | # of blocks with a confugrable accuracy, counting the number of 5 | # entities hit by the ray along the way. 6 | # 7 | # @params 8 | # $mypack.raycast.distance param 9 | # The number of blocks to cast forward. 10 | # $mypack.raycast.precision param 11 | # The ratio of block precision to a full block. 12 | # 13 | # @returns 14 | # $mypack.raycast.result return 15 | # The number of entities hit by the ray. 16 | # Caching handled by storage:location/over/here 17 | 18 | function #mypack:hooks/raycast/begin 19 | 20 | function mypack:raycast/loop 21 | 22 | function #mypack:hooks/raycast/end 23 | 24 | # this is a comment 25 | say hello world 26 | 27 | # indented comment 28 | say hello indent 29 | 30 | effect give @s minecraft:night_vision 999999 1 true 31 | effect give @s minecraft:night_vision 999999 1 false 32 | 33 | teleport 1 2 3 34 | teleport 100.5 80 -100.5 35 | 36 | execute if score @a temp matches 10.. run 37 | execute if score @a temp matches ..20 run 38 | execute if score @a temp matches 10..20 run 39 | 40 | execute positioned 10 ~ -10 run 41 | execute positioned 10 ~10 -10 run 42 | execute positioned 10 ~0.5 -10 run 43 | execute positioned 10 ~.5 -10 run 44 | execute positioned 10 ~-10 -10 run 45 | execute positioned 10 ~-0.5 -10 run 46 | execute positioned 10 ~-.5 -10 run 47 | 48 | execute positioned 10 ^ -10 run 49 | execute positioned 10 ^10 -10 run 50 | execute positioned 10 ^0.5 -10 run 51 | execute positioned 10 ^.5 -10 run 52 | execute positioned 10 ^-10 -10 run 53 | execute positioned 10 ^-0.5 -10 run 54 | execute positioned 10 ^-.5 -10 run 55 | 56 | function mypack:foo 57 | function mypack:foo/bar 58 | function mypack:foo/bar/baz 59 | function #mypack:foo 60 | function #mypack:foo/bar 61 | function #mypack:foo/bar/baz 62 | 63 | execute if block ~ ~ ~ minecraft:oak_log[axis=x] run 64 | execute if block ~ ~ ~ minecraft:oak_leaves[distance=5] run 65 | execute if block ~ ~ ~ minecraft:oak_leaves[persistent=true] run 66 | execute if block ~ ~ ~ minecraft:oak_leaves[persistent=false] run 67 | execute if block ~ ~ ~ minecraft:oak_leaves[distance=5,persistent=true] run 68 | execute if block ~ ~ ~ #minecraft:leaves[distance=5] run 69 | execute if block ~ ~ ~ #minecraft:leaves[distance=5,persistent=true] run 70 | setblock ~ ~ ~ mypack:foo{foo:bar} destroy 71 | setblock ~ ~ ~ mypack:foo{foo: bar} destroy 72 | setblock ~ ~ ~ mypack:foo[facing=up]{foo: bar} destroy 73 | setblock ~ ~ ~ mypack:foo[facing = up]{foo: bar} destroy 74 | setblock ~ ~ ~ minecraft:dispenser[facing=up]{Items: [{id: "minecraft:diamond", Count: 1}]} 75 | 76 | tag @s add my.tag 77 | 78 | datapack enable "hello world" 79 | datapack enable "escape \" me" 80 | datapack enable 'hello world' 81 | datapack enable 'escape \' me' 82 | 83 | execute as f7a39418-72ca-4bf2-bc7e-ba9df67a4707 run 84 | execute as 0-0-0-0-0 run 85 | 86 | execute store result score #fakeplayer 87 | execute store result score #fake.player 88 | execute store result score #fake_player 89 | execute store result score $fakeplayer 90 | execute store result score %fakeplayer 91 | 92 | execute as @b 93 | execute as @a 94 | 95 | execute as @a[tag=foo] 96 | execute as @a[tag=!foo] 97 | 98 | execute as @a[sort=nearest] run 99 | execute as @a[gamemode=survival] run 100 | execute as @a[gamemode=!creative] run 101 | execute as @a[tag=foo,tag=bar,tag=!baz] run 102 | 103 | execute as @a[distance=15] 104 | execute as @a[distance=1.5] 105 | execute as @a[distance=.5] 106 | execute as @a[distance=-.25] 107 | 108 | execute as @a[distance=100] 109 | execute as @a[distance=..10] 110 | execute as @a[distance=11..19] 111 | execute as @a[distance=20..] 112 | execute as @a[distance=0.5] 113 | execute as @a[distance=..0.1] 114 | execute as @a[distance=0.2..0.8] 115 | execute as @a[distance=0.9..] 116 | 117 | execute as @a[type=minecraft:bat] run 118 | execute as @a[type=!minecraft:cow,type=!minecraft:pig] run 119 | execute as @a[type=#minecraft:skeletons] run 120 | execute as @a[type=!#minecraft:skeletons,type=!minecraft:zombie] run 121 | 122 | execute as @a[tag=my.tag] run 123 | 124 | execute as @a[name="hello world"] as @s run 125 | execute as @a[name="escape \" me"] as @s run 126 | execute as @a[name="how, about, commas ?"] as @s run 127 | execute as @a[name="and [braces] ?"] as @s run 128 | 129 | execute as @e[nbt={ PortalCooldown: 0 }] run 130 | execute as @e[nbt={ Item: {id: "minecraft:diamond", Count: 64 } }] run 131 | 132 | execute if score @s foo < @s bar run 133 | execute if score @s foo <= @s bar run 134 | execute if score @s foo = @s bar run 135 | execute if score @s foo > @s bar run 136 | execute if score @s foo >= @s bar run 137 | 138 | data get entity @s SelectedItem.tag.display.Name 139 | data get entity @s Inventory[0] 140 | data get entity @s Inventory[{id: "minecraft:diamond"}].Count 141 | data get entity @s Inventory[].tag{custom: true}.display.Name 142 | 143 | data merge entity @s { foo: true, bar: 1234 } 144 | data modify block ~ ~ ~ RecordItem.tag set value { messages: [hi, bye] } 145 | data modify block ~ ~ ~ RecordItem.tag.messages append value [ { message: "hello world" } ] 146 | 147 | tellraw @a {"text": "hello world", "color": "blue"} 148 | tellraw @a [{"text": "hello", "color": "blue"}, {"text": "world", "color": "blue"}] 149 | 150 | execute as @a[scores={myscore=10}] run 151 | execute as @a[scores={myscore=10..12}] run 152 | execute as @a[scores={foo=10, bar=1..5, baz=..0}] run 153 | 154 | execute as @a[advancements={minecraft:story/form_obsidian=true}] run 155 | execute as @a[advancements={minecraft:story/obtain_armor={iron_helmet=true}}] run 156 | execute as @a[advancements={minecraft:story/obtain_armor={iron_helmet=true, gold_helmet=false}}] run 157 | execute as @a[advancements={minecraft:story/form_obsidian=true, minecraft:story/follow_ender_eye=true}] run 158 | execute as @a[advancements={minecraft:story/form_obsidian={foo=true, bar=false},minecraft:story/follow_ender_eye={foo=false, bar=true}}] run 159 | 160 | give @s diamond_sword{display: {Name: '"My Custom Sword"'}} 161 | give @s minecraft:diamond_sword{display: {Name: '"My Custom Sword"'}} 162 | 163 | execute if score @s foo < @s bar run say execute if score @s foo < @s bar run say 164 | execute if score @s foo < @s bar run say hello @e[tag=baz, sort=nearest, limit=1] how are you? 165 | 166 | scoreboard players operation @a result \ 167 | += @e[type=marker,limit=1,tag=source] value 168 | $data modify storage $(id) $(path) set value "with random $(string1) stuff $(string2)" 169 | 170 | give @n armor_stand[entity_data={id:"minecraft:armor_stand", Tags:[taco_sauce]}] 171 | 172 | say -1L -1l -1S -1s -1 0 1 1b 1s 1S 1l 1L 173 | 174 | say "Something" Something 175 | 176 | give @s taco[minecraft:unbreaking] 177 | -------------------------------------------------------------------------------- /tests/comprehensive/selector.mcfunction: -------------------------------------------------------------------------------- 1 | # test valid 2 | execute as @b 3 | execute as @a 4 | execute as @a[] 5 | execute as @a[tag=foo] 6 | execute as @a[tag=!foo] 7 | 8 | # test valid with trailing command 9 | execute as @a run 10 | execute as @a[] run 11 | execute as @a[tag=foo] run 12 | execute as @a[tag=!foo] run 13 | 14 | # test valid with mixed selectors 15 | execute as @a as @a run 16 | execute as @a as @a[sort=nearest] run 17 | execute as @a[sort=nearest] as @a run 18 | execute as @a[sort=nearest] as @a[sort=nearest] run 19 | teleport @a @s 20 | 21 | # test valid multiple arguments 22 | execute as @a[sort=nearest,limit=1] run 23 | execute as @a[sort=nearest,limit=1] run 24 | execute as @a[sort=nearest,limit=1,distance=0] run 25 | execute as @a[sort=nearest,tag=!foo,tag=bar] run 26 | 27 | # test valid whitespace around arguments 28 | 29 | execute as @a[sort=nearest] run 30 | execute as @a[ sort=nearest] run 31 | execute as @a[sort=nearest ] run 32 | execute as @a[ sort=nearest ] run 33 | 34 | execute as @a[sort=nearest,limit=1] run 35 | execute as @a[ sort=nearest,limit=1] run 36 | execute as @a[sort=nearest,limit=1 ] run 37 | execute as @a[ sort=nearest,limit=1 ] run 38 | 39 | execute as @a[sort=nearest, limit=1] run 40 | execute as @a[ sort=nearest, limit=1] run 41 | execute as @a[sort=nearest, limit=1 ] run 42 | execute as @a[ sort=nearest, limit=1 ] run 43 | 44 | execute as @a[sort=nearest ,limit=1] run 45 | execute as @a[ sort=nearest ,limit=1] run 46 | execute as @a[sort=nearest ,limit=1 ] run 47 | execute as @a[ sort=nearest ,limit=1 ] run 48 | 49 | execute as @a[sort=nearest , limit=1] run 50 | execute as @a[ sort=nearest , limit=1] run 51 | execute as @a[sort=nearest , limit=1 ] run 52 | execute as @a[ sort=nearest , limit=1 ] run 53 | 54 | execute as @a[ sort =nearest , limit=1 ] run 55 | execute as @a[ sort= nearest , limit=1 ] run 56 | execute as @a[ sort = nearest , limit=1 ] run 57 | 58 | execute as @a[ tag = foo , limit = 1 ] run 59 | execute as @a[ tag = ! foo , limit = 1 ] run 60 | 61 | # test valid arguments in separate selectors 62 | execute as @a[sort=nearest] as @s[tag=foo] run 63 | execute as @a[sort=nearest,limit=1] as @a[tag=foo] run 64 | execute as @a[sort=nearest] as @a[tag=foo,tag=bar] run 65 | execute as @a[sort=nearest,limit=1] as @a[tag=foo,tag=bar] run 66 | 67 | # test valid resource location argument 68 | execute as @a[type=minecraft:bat] run 69 | execute as @a[ type = minecraft:bat ] run 70 | execute as @a[ type = ! minecraft:bat ] run 71 | execute as @a[type=minecraft:bat,tag=foo] run 72 | execute as @a[tag=foo,type=minecraft:bat] run 73 | execute as @a[tag=foo,type=minecraft:bat,tag=foo] run 74 | 75 | # test valid tagged resource location argument 76 | execute as @a[type=#minecraft:skeletons] run 77 | execute as @a[ type = #minecraft:skeletons ] run 78 | execute as @a[ type = ! #minecraft:skeletons ] run 79 | execute as @a[type=#minecraft:skeletons,tag=foo] run 80 | execute as @a[tag=foo,type=#minecraft:skeletons] run 81 | execute as @a[tag=foo,type=#minecraft:skeletons,tag=foo] run 82 | 83 | # test valid range argument 84 | execute as @a[distance=..10] 85 | execute as @a[distance=11..19] 86 | execute as @a[distance=20..] 87 | execute as @a[distance=0.5] 88 | execute as @a[distance=..0.1] 89 | execute as @a[distance=0.2..0.8] 90 | execute as @a[distance=0.9..] 91 | execute as @a[tag=foo,distance=1..,tag=bar] 92 | execute as @a[ tag = foo , distance = 1.. , tag = bar ] 93 | 94 | # test valid number argument 95 | execute as @a[distance=15] 96 | execute as @a[distance=1.5] 97 | execute as @a[distance=.5] 98 | execute as @a[distance=-.25] 99 | execute as @a[tag=foo,distance=-.25,tag=bar] 100 | execute as @a[ tag = foo , distance = -.25 , tag = bar ] 101 | 102 | # test valid boolean argument (were it to exist) 103 | execute as @a[somekey=true] 104 | execute as @a[somekey=false] 105 | execute as @a[ somekey = true ] 106 | execute as @a[tag=foo,somekey=true,tag=bar] 107 | execute as @a[ tag = foo , somekey = true , tag = bar ] 108 | 109 | # test valid literal argument 110 | execute as @a[sort=nearest] run 111 | execute as @a[tag=foo,sort=nearest,tag=bar] run 112 | execute as @a[ tag = foo , sort = nearest , tag = bar ] run 113 | 114 | # test valid unquoted string argument 115 | execute as @a[tag=my.custom.tag] run 116 | execute as @a[tag=foo,tag=my.custom.tag,tag=bar] run 117 | execute as @a[ tag = foo , tag = my.custom.tag , tag = bar ] run 118 | 119 | # test valid quoted string argument 120 | execute as @a[tag=foo,name="[",tag=bar] as @s run 121 | execute as @a[tag=foo,name="]",tag=bar] as @s run 122 | execute as @a[tag=foo,name="[]",tag=bar] as @s run 123 | execute as @a[tag=foo,name="[[]]",tag=bar] as @s run 124 | execute as @a[tag=foo,name="foo, bar"] as @s run 125 | execute as @a[tag=foo,name="escaped \" quote"] as @s run 126 | execute as @a[ tag = foo , name = " lots of space " , tag = bar ] as @s run 127 | 128 | # test valid single quoted string argument 129 | execute as @a[tag=foo,name='[',tag=bar] as @s run 130 | execute as @a[tag=foo,name=']',tag=bar] as @s run 131 | execute as @a[tag=foo,name='[]',tag=bar] as @s run 132 | execute as @a[tag=foo,name='[[]]',tag=bar] as @s run 133 | execute as @a[tag=foo,name='foo, bar'] as @s run 134 | execute as @a[tag=foo,name='escaped \' quote'] as @s run 135 | execute as @a[ tag = foo , name = ' lots of space ' , tag = bar ] as @s run 136 | 137 | # test valid nbt argument 138 | execute as @e[nbt={PortalCooldown:0}] run 139 | execute as @e[nbt={ PortalCooldown : 0 }] run 140 | execute as @e[ nbt = { PortalCooldown : 0 } ] run 141 | execute as @e[nbt={Item:{id:"minecraft:diamond",Count:64}}] run 142 | execute as @e[nbt={ Item : { id : "minecraft:diamond", Count : 64 } }] run 143 | execute as @e[ nbt = { Item : { id : "minecraft:diamond" , Count : 64 } } ] run 144 | 145 | # test valid scores argument 146 | execute as @a[scores={myscore=10}] run 147 | execute as @a[scores={myscore=10..12}] run 148 | execute as @a[scores={myscore=5..}] run 149 | execute as @a[scores={myscore=..15}] run 150 | execute as @a[scores={foo=10,bar=1..5}] run 151 | execute as @a[scores={foo=10,bar=1..5,baz=..0}] run 152 | execute as @a[ scores = {foo=10,bar=1..5,baz=..0} ] run 153 | execute as @a[ scores = { foo = 10 , bar = 1..5 , baz = ..0 } ] run 154 | execute as @a[scores={my.score=10}] run 155 | execute as @a[scores={myScore=10}] run 156 | 157 | # test valid advancements argument 158 | execute as @a[advancements={minecraft:story/form_obsidian=true}] run 159 | execute as @a[advancements={minecraft:story/form_obsidian=false}] run 160 | execute as @a[advancements={minecraft:story/obtain_armor={iron_helmet=true}}] run 161 | execute as @a[advancements={minecraft:story/obtain_armor={iron_helmet=true, gold_helmet=false}}] run 162 | execute as @a[ advancements = { minecraft:story/obtain_armor = { iron_helmet = true , gold_helmet = false } } ] run 163 | execute as @a[advancements={minecraft:story/follow_ender_eye=true}] run 164 | execute as @a[advancements={minecraft:story/form_obsidian=true,minecraft:story/follow_ender_eye=true}] run 165 | execute as @a[advancements={minecraft:story/form_obsidian=true,minecraft:story/follow_ender_eye=true,minecraft:story/obtain_armor=true}] run 166 | execute as @a[advancements={minecraft:story/form_obsidian={foo=true, bar=false},minecraft:story/follow_ender_eye={foo=false, bar=true}}] run 167 | execute as @a[ advancements = {minecraft:story/follow_ender_eye=true} ] run 168 | execute as @a[ advancements = { minecraft:story/follow_ender_eye = true } ] run 169 | execute as @a[ advancements = { minecraft:story/form_obsidian = true , minecraft:story/follow_ender_eye = true , minecraft:story/obtain_armor = true } ] run 170 | 171 | # test invalid edge cases 172 | execute as @a[,] as @s run 173 | execute as @a[,,] as @s run 174 | execute as @a[,tag=foo] as @s run 175 | execute as @a[tag=foo,] as @s run 176 | execute as @a[,tag=foo,] as @s run 177 | execute as @a[tag,=foo] as @s run 178 | execute as @a[tag=,foo] as @s run 179 | execute as @a[tag,=,foo] as @s run 180 | execute as @a[,tag,=,foo,] as @s run 181 | execute as @a[tag=foo,name="[[]]"],tag=bar] as @s run 182 | 183 | # test invalid 184 | execute as @ 185 | execute as @A 186 | execute as @ab 187 | execute as @0 188 | execute as @_ 189 | execute as @a[ 190 | execute as @a] 191 | 192 | # test invalid with trailing command 193 | execute as @ run 194 | execute as @a[ run 195 | execute as @a] run 196 | 197 | # test invalid basic arguments 198 | execute as @a[sort] run 199 | execute as @a[sort=] run 200 | execute as @a[=nearest] run 201 | 202 | # test invalid quoted string argument 203 | execute as @a[name=my name] run 204 | execute as @a[name="my name] run 205 | execute as @a[name=my name"] run 206 | ]] 207 | 208 | # test invalid resource location argument 209 | execute as @a[type=mypack:] run 210 | execute as @a[type=:foo] run 211 | execute as @a[type=#mypack:] run 212 | execute as @a[type=#:foo] run 213 | ] 214 | 215 | # test invalid edge cases 216 | execute as @a[gamemode=survival,]] run 217 | -------------------------------------------------------------------------------- /mcfunction.tmLanguage.yaml: -------------------------------------------------------------------------------- 1 | name: Syntax Mcfunction 2 | 3 | scopeName: source.mcfunction 4 | uuid: 8918dadd-8ebe-42d9-b1e9-0489ab228d9d 5 | 6 | fileTypes: 7 | - mcfunction 8 | - bolt 9 | 10 | patterns: 11 | - include: "#root" 12 | 13 | repository: 14 | root: 15 | patterns: 16 | - include: "#literals" 17 | - include: "#comments" 18 | - include: "#say" 19 | - include: "#names" # we do names before inline since inline comments can eat 20 | - include: "#comments_inline" 21 | - include: "#subcommands" 22 | - include: "#property" 23 | - include: "#operators" # ideally not so low but this eats into fakeplayers 24 | - include: "#selectors" 25 | 26 | # Comments have two styles 27 | # 1. Basic `# comment`, single line 28 | # 2. `#>`, etc. block comments 29 | # 30 | # We provide a subset of command highlighting within comments to enable 31 | # docstring-esque type highlighting 32 | comments: 33 | patterns: 34 | - applyEndPatternLast: 1 35 | begin: ^\s*(#[>!#])(.+)$ 36 | beginCaptures: 37 | "1": 38 | name: comment.block.mcfunction 39 | "2": 40 | name: markup.bold.mcfunction 41 | captures: 42 | "0": 43 | name: comment.block.mcfunction 44 | end: ^(?!#) 45 | name: meta.comments 46 | patterns: 47 | - include: "#comments_block" 48 | - captures: 49 | "0": 50 | name: comment.line.mcfunction 51 | match: '^\s*#.*$' 52 | name: meta.comments 53 | 54 | # `execute if entity @a run tellraw "hello" # cool command here` 55 | # ^^^^^^^^^^^^^^^^^^^ 56 | comments_inline: 57 | patterns: 58 | - captures: 59 | "0": 60 | name: comment.line.mcfunction 61 | match: "#.*$" 62 | name: meta.comments 63 | 64 | comments_block: 65 | patterns: 66 | - applyEndPatternLast: 1 67 | begin: ^\s*#[>!] 68 | captures: 69 | "0": 70 | name: comment.block.mcfunction 71 | end: $ 72 | name: meta.comments_block 73 | patterns: 74 | - include: "#comments_block_emphasized" 75 | - applyEndPatternLast: 1 76 | begin: ^\s*# 77 | captures: 78 | "0": 79 | name: comment.block.mcfunction 80 | end: $ 81 | name: meta.comments_block 82 | patterns: 83 | - include: "#comments_block_normal" 84 | 85 | # Header comments (just bolded) 86 | # `#> This function does cool things` 87 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 88 | comments_block_emphasized: 89 | patterns: 90 | - include: "#comments_block_special" 91 | - captures: 92 | "0": 93 | name: markup.bold.mcfunction 94 | match: \S+ 95 | name: meta.comments_block_emphasized 96 | 97 | #> Normal comments within a comment block 98 | comments_block_normal: 99 | patterns: 100 | - include: "#comments_block_special" 101 | - captures: 102 | "0": 103 | name: comment.block.mcfunction 104 | match: \S+ 105 | name: meta.comments_block_normal 106 | - include: "#whitespace" 107 | 108 | # Special highlighting for resource locations and params 109 | # `# Taco Bell @rx97` 110 | # ^^^^^ 111 | # `# Caching handled by storage:location/over/here` 112 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^ 113 | # `# $mypack.raycast.result return` 114 | # ^^^^^^^^^^^^^^^^^^^^^^ 115 | comments_block_special: 116 | patterns: 117 | - captures: 118 | "0": 119 | name: markup.heading.mcfunction 120 | match: '@\S+' 121 | name: meta.comments_block_special 122 | - include: "#resource-name" 123 | - captures: 124 | "0": 125 | name: variable.other.mcfunction 126 | match: "[#%$][A-Za-z0-9_.#%$]+" 127 | name: meta.comments_block_special 128 | 129 | # Literals encompasses numbers, booleans, UUIDs, and strings 130 | literals: 131 | patterns: 132 | - captures: 133 | "0": 134 | name: constant.numeric.boolean.mcfunction 135 | match: \b(true|false|True|False)\b 136 | name: meta.literals 137 | - captures: 138 | "0": 139 | name: variable.uuid.mcfunction 140 | match: \b[0-9a-fA-F]+(?:-[0-9a-fA-F]+){4}\b 141 | name: meta.names 142 | - captures: 143 | "0": 144 | name: constant.numeric.float.mcfunction 145 | match: '[+-]?\d*\.?\d+([eE]?[+-]?\d+)?[df]?\b' 146 | name: meta.literals 147 | - captures: 148 | "0": 149 | name: constant.numeric.integer.mcfunction 150 | match: '[+-]?\d+(b|B|L|l|s|S)?\b' 151 | name: meta.literals 152 | 153 | # yea this should be operator, but i need this to exist before #names 154 | # otherwise `..0.5` will be a valid name 155 | - captures: 156 | "0": 157 | name: variable.other.mcfunction 158 | match: \.\. 159 | name: meta.ellipse.literals 160 | 161 | # Strings will encompass multiple lines meaning an unclosed quote will 162 | # eat extra tokens. 163 | - applyEndPatternLast: 1 164 | begin: '"' 165 | beginCaptures: 166 | "0": 167 | name: punctuation.definition.string.begin.mcfunction 168 | end: '"' 169 | endCaptures: 170 | "0": 171 | name: punctuation.definition.string.end.mcfunction 172 | name: string.quoted.double.mcfunction 173 | patterns: 174 | - include: "#literals_string-double" 175 | - applyEndPatternLast: 1 176 | begin: "'" 177 | beginCaptures: 178 | "0": 179 | name: punctuation.definition.string.begin.mcfunction 180 | end: "'" 181 | endCaptures: 182 | "0": 183 | name: punctuation.definition.string.begin.mcfunction 184 | name: string.quoted.single.mcfunction 185 | patterns: 186 | - include: "#literals_string-single" 187 | 188 | # Simplistic highlighting for basic subcommands. 189 | subcommands: 190 | patterns: 191 | - captures: 192 | "0": 193 | name: entity.name.class.mcfunction 194 | match: "[a-z_]+" # "[A-Za-z_]+" 195 | name: meta.literals 196 | 197 | # Strings also have to account for `\`s esp, solo at the end of a line 198 | literals_string-double: 199 | patterns: 200 | - captures: 201 | "0": 202 | name: constant.character.escape.mcfunction 203 | match: \\. 204 | name: meta.literals_string-double 205 | - captures: 206 | "0": 207 | name: constant.character.escape.mcfunction 208 | match: \\ 209 | name: meta.literals_string-double 210 | - include: "#macro-name" 211 | - captures: 212 | "0": 213 | name: string.quoted.double.mcfunction 214 | match: '[^\\"]' 215 | name: meta.literals_string-double 216 | 217 | literals_string-single: 218 | patterns: 219 | - captures: 220 | "0": 221 | name: constant.character.escape.mcfunction 222 | match: \\. 223 | name: meta.literals_string-single 224 | - captures: 225 | "0": 226 | name: constant.character.escape.mcfunction 227 | match: \\ 228 | name: meta.literals_string-double 229 | - include: "#macro-name" 230 | - captures: 231 | "0": 232 | name: string.quoted.single.mcfunction 233 | match: '[^\\'']' 234 | name: meta.literals_string-single 235 | 236 | # We need to hardcode `say` to avoid greedy `'`s to break formatting 237 | # ex: `say Bob's uncle was pretty cool`, this would break commands that follow 238 | say: 239 | patterns: 240 | - begin: ^(\s*)(say) 241 | beginCaptures: 242 | "1": 243 | name: whitespace.mcfunction 244 | "2": 245 | name: keyword.control.flow.mcfunction 246 | end: \n 247 | name: meta.say.mcfunction 248 | patterns: 249 | - captures: 250 | "0": 251 | name: constant.character.escape.mcfunction 252 | match: \\\s*\n 253 | meta: meta.say.backslash.mcfunction 254 | - include: "#literals_string-double" 255 | - include: "#literals_string-single" 256 | - begin: (run)(\s+)(say) 257 | beginCaptures: 258 | "1": 259 | name: entity.name.mcfunction 260 | "2": 261 | name: whitespace.mcfunction 262 | "3": 263 | name: keyword.control.flow.mcfunction 264 | end: \n 265 | name: meta.say.mcfunction 266 | patterns: 267 | - captures: 268 | "0": 269 | name: constant.character.escape.mcfunction 270 | match: \\\s*\n 271 | meta: meta.say.backslash.mcfunction 272 | - include: "#literals_string-double" 273 | - include: "#literals_string-single" 274 | 275 | # Names are a very generic term in this grammar. They specially highlight the 276 | # beginning of a line (and a word after `run`), handle macro commands, and 277 | # fakeplayers. 278 | names: 279 | patterns: 280 | - captures: 281 | "1": 282 | name: whitespace.mcfunction 283 | "2": 284 | name: keyword.control.flow.mcfunction 285 | match: ^(\s*)([a-z_]+)(?=\s) 286 | name: meta.names 287 | - captures: 288 | "1": 289 | name: whitespace.mcfunction 290 | "2": 291 | name: markup.italic.mcfunction 292 | "3": 293 | name: whitespace.mcfunction 294 | "4": 295 | name: keyword.control.flow.mcfunction 296 | match: ^(\s*)(\$)( ?)([a-z_]*) 297 | name: meta.names 298 | - captures: 299 | "1": 300 | name: entity.name.mcfunction 301 | "2": 302 | name: whitespace.mcfunction 303 | "3": 304 | name: keyword.control.flow.mcfunction 305 | match: (run)(\s+)([a-z_]+) 306 | name: meta.names 307 | - include: "#resource-name" 308 | - captures: 309 | "0": 310 | name: entity.name.mcfunction 311 | match: '[A-Za-z]+(?=\W)' 312 | name: meta.names 313 | - captures: 314 | "0": 315 | name: string.unquoted.mcfunction 316 | match: "[A-Za-z_][A-Za-z0-9_.#%$]*" 317 | name: meta.names 318 | - include: "#macro-name" 319 | - captures: 320 | "0": 321 | name: variable.other.mcfunction 322 | match: '([#%$]|((?<=\s)\.))[A-Za-z0-9_.#%$\-]+' 323 | name: meta.names 324 | 325 | # These are for macro-names defined within a macro-line (including strings). 326 | # TODO: only activate this if the beginning of the line starts with `$` 327 | macro-name: 328 | patterns: 329 | - captures: 330 | "1": 331 | name: punctuation.definition.template-expression.begin.mcfunction 332 | "2": 333 | name: variable.other.mcfunction 334 | "3": 335 | name: punctuation.definition.template-expression.end.mcfunction 336 | match: '(\$\()([A-Za-z0-9_]*)(\))' 337 | name: meta.macro-name 338 | 339 | # General operators (including beyond mcfunction) 340 | operators: 341 | patterns: 342 | - captures: 343 | "0": 344 | name: constant.numeric.mcfunction 345 | match: "[~^]" 346 | name: meta.operators 347 | - captures: 348 | "0": 349 | name: keyword.operator.mcfunction 350 | match: '[\-%?!+*<>\\/|&=.:,;]' 351 | name: meta.operators 352 | 353 | # Properties are a subset of definitions that exist within `{}`, `[]`, or `()`s. They are a 354 | # generic formula for highlighting key-value pairs, but are also capable of doing a good job 355 | # for generic scoping (such as in mcbuild), JSON, or even python/js esque expressions. They 356 | # can be nested as many times as needed as long as the brackets match! 357 | # 358 | # Unfortuntely, there's a bit of limitations based on how strict it is with mcfunction 359 | # vs how accepting it is of foreign syntax. For example `[key:val]` and `[key: val]` 360 | # highlight very differently as either a resource-name or a key/value. 361 | # 362 | # In the future, we could attempt a stricter property key-value scheme and then fall-back 363 | # to a generic, more forgiving highlighting if the strict one fails. This would be complex 364 | # though maybe needing some lookaheads, etc which could be not worth. 365 | property: 366 | patterns: 367 | - applyEndPatternLast: 1 368 | begin: \{ 369 | captures: 370 | "0": 371 | name: punctuation.mcfunction 372 | end: \} 373 | name: meta.property.curly 374 | patterns: 375 | - include: "#resource-name" 376 | - include: "#literals" 377 | - include: "#property_key" 378 | - include: "#operators" 379 | - include: "#property_value" 380 | - include: $self 381 | - applyEndPatternLast: 1 382 | begin: \[ 383 | captures: 384 | "0": 385 | name: variable.other.mcfunction 386 | end: \] 387 | name: meta.property.square 388 | patterns: 389 | - include: "#resource-name" 390 | - include: "#literals" 391 | - include: "#property_key" 392 | - include: "#operators" 393 | - include: "#property_value" 394 | - include: $self 395 | - applyEndPatternLast: 1 396 | begin: \( 397 | captures: 398 | "0": 399 | name: punctuation.mcfunction 400 | end: \) 401 | name: meta.property.paren 402 | patterns: 403 | - include: "#resource-name" 404 | - include: "#literals" 405 | - include: "#property_key" 406 | - include: "#operators" 407 | - include: "#property_value" 408 | - include: $self 409 | 410 | property_key: 411 | patterns: 412 | - captures: 413 | "0": 414 | name: variable.other.mcfunction 415 | match: '#?[a-z_][a-z_\.\-]*\:[a-z0-9_\.\-/]+(?=\s*\=:)' 416 | name: meta.property_key 417 | - captures: 418 | "0": 419 | name: variable.other.mcfunction 420 | match: '#?[a-z_][a-z0-9_\.\-/]+' 421 | name: meta.property_key 422 | - captures: 423 | "0": 424 | name: variable.other.mcfunction 425 | match: '[A-Za-z_]+[A-Za-z_\-\+]*' 426 | name: meta.property_key 427 | 428 | property_value: 429 | patterns: 430 | - captures: 431 | "0": 432 | name: string.unquoted.mcfunction 433 | match: '#?[a-z_][a-z_\.\-]*\:[a-z0-9_\.\-/]+' 434 | name: meta.property_value 435 | - captures: 436 | "0": 437 | name: string.unquoted.mcfunction 438 | match: '#?[a-z_][a-z0-9_\.\-/]+' 439 | name: meta.property_value 440 | 441 | resource-name: 442 | patterns: 443 | - captures: 444 | "0": 445 | name: entity.name.function.mcfunction 446 | match: "#?[a-z_][a-z0-9_.-]*:[a-z0-9_./-]+" 447 | name: meta.resource-name 448 | - captures: 449 | "0": 450 | name: entity.name.function.mcfunction 451 | match: '#?[a-z0-9_\.\-]+\/[a-z0-9_\.\-\/]+' 452 | name: meta.resource-name 453 | 454 | # Selectors consume more than 1 char after an `@` symbol to allow for fancier 455 | # `@param` style highlighting like python decorators or even modded selectors. 456 | selectors: 457 | patterns: 458 | - captures: 459 | "0": 460 | name: support.class.mcfunction 461 | match: "@[a-z]+" 462 | name: meta.selectors 463 | -------------------------------------------------------------------------------- /tests/bolt.mcfunction: -------------------------------------------------------------------------------- 1 | #> Example of 3rd party commands (bolt) 2 | 3 | say hello 4 | say this is a function file augmented with mecha 5 | 6 | # a = {1, 2, 3} 7 | 8 | if score @s tmp matches 1.. as @a: 9 | for feature in [ 10 | "multiline", 11 | "nesting", 12 | "implicit execute", 13 | "relative location", 14 | ]: 15 | say (feature + " is automatically enabled") 16 | message = "abc" * 5 17 | say message.upper() 18 | 19 | def you_can_make_functions(): 20 | say you_can_make_functions.__name__.replace("_", " ").capitalize() 21 | tellraw @a [ 22 | "", 23 | {"text": "yep"}, 24 | ] 25 | return "and return values" 26 | 27 | say you_can_make_functions() 28 | 29 | def fib(n): 30 | if n <= 1: 31 | return n 32 | return fib(n - 1) + fib(n - 2) 33 | 34 | say fib(7) 35 | say fib(8) 36 | say fib(9) 37 | 38 | def default_params_are_neat( 39 | number, 40 | result=fib(number), 41 | thing={ 42 | "number": number, 43 | "result": result, 44 | }, 45 | ): 46 | say Unlike in python default params are evaluated when the function is called 47 | say number 48 | say result 49 | say thing 50 | 51 | default_params_are_neat(12) 52 | 53 | say this is basically a custom subset of python 54 | say functions are first-class objects just like in python 55 | 56 | my_functions = [] 57 | 58 | for i in "abc": 59 | def functions_in_loop(value): 60 | def yo(): 61 | say value.upper() 62 | return yo 63 | my_functions.append(functions_in_loop(i * 3)) 64 | 65 | my_functions[0]() 66 | my_functions[1]() 67 | my_functions[2]() 68 | 69 | should_break = False 70 | 71 | while True: 72 | if should_break: 73 | break 74 | say just once 75 | should_break = True 76 | 77 | something = '{"text": "Hello", "bold": true}' 78 | as @a at @s if block ~ ~-1 ~ #wool give @s stone{ 79 | display: { 80 | Name: something 81 | } 82 | } 83 | 84 | with_tuples = ((((())))) + (1,2,3) + (4,) 85 | say with_tuples 86 | say (with_tuples) 87 | say (with_tuples,) 88 | a = f'\\' 89 | # say f"f-strings {"work".upper()!r} too {a}" 90 | # say f"{{{f"{{{f"{{{7:08}}}\\"}}}\""}}}" 91 | 92 | x = 8 93 | if score @s tmp matches (x, None) say wat 94 | if score @s tmp matches f"{x}.." say wat 95 | 96 | 97 | def copy_items(type, count): 98 | for i in range(count): 99 | item replace entity @a f"{type}.{i}" from entity @s f"{type}.{i}" 100 | 101 | copy_items('hotbar', 9) 102 | copy_items('inventory', 26) 103 | 104 | def f(): 105 | yield 1 106 | yield 2 107 | yield 3 108 | 109 | for i in f(): 110 | say i 111 | 112 | def wow(ok): 113 | yield from ok() 114 | yield from ok() 115 | 116 | say list(wow(f)) 117 | 118 | say ctx.generate.id("hello") 119 | 120 | import math 121 | say math.cos(math.pi) 122 | 123 | import math as m 124 | say (math is m) 125 | 126 | from ./thing import do_stuff 127 | 128 | say do_stuff(1, math.pi) 129 | 130 | import ./thing as thing 131 | 132 | say (thing.do_stuff is do_stuff) 133 | 134 | for i in range(6): 135 | if i == 1: 136 | say 1 137 | elif i == 2: 138 | say 2 139 | elif i == 3: 140 | say 3 141 | else: 142 | say other 143 | say done 144 | 145 | execute 146 | as @a # For each "player", 147 | at @s # start at their feet. 148 | anchored eyes # Looking through their eyes, 149 | facing 0 0 0 # face perfectly at the target 150 | anchored feet # (go back to the feet) 151 | positioned ^ ^ ^1 # and move one block forward. 152 | rotated as @s # Face the direction the player 153 | # is actually facing, 154 | positioned ^ ^ ^-1 # and move one block back. 155 | if entity @s[distance=..0.6] function ./abc: 156 | say foo 157 | say bar 158 | 159 | baz = "demo:xyz" 160 | if predicate foo:bar function baz: 161 | say foo 162 | say bar 163 | 164 | for node in generate_tree("abcdefghijklmnopqrstuvwxyz0123456789"): 165 | append function node.parent: 166 | if node.partition(5): 167 | if score @s thingy matches node.range function node.children 168 | else: 169 | if score @s thingy matches node.range say node.value 170 | 171 | data modify entity @e[type=armor_stand,limit=1] NoBasePlate set value 1b 172 | 173 | from ./thing import call_recursive 174 | call_recursive() 175 | 176 | def try_coordinates(): 177 | a = 1 178 | setblock a 2 3 stone 179 | a = "1 2 3" 180 | setblock a stone 181 | a = 1 182 | if block ^ f"^{a}" ^ #planks say 42 183 | if block ("~", "~", "~") #planks say 42 184 | 185 | try_coordinates() 186 | 187 | keyword_arguments = dict(foo=1, bar=2, **{"thing": 42}) 188 | say keyword_arguments 189 | 190 | for node in generate_tree(range(8), name="small_tree"): 191 | append function node.parent: 192 | if node.partition(): 193 | if score @s thingy matches node.range function node.children 194 | else: 195 | if score @s thingy matches node.range say node.value 196 | 197 | def try_unpacking(): 198 | a = [1, *"abc", 2] 199 | b = dict(zip(a, a)) 200 | c = {**b, "b": "thing"} 201 | say c 202 | 203 | try_unpacking() 204 | 205 | def try_set_item(): 206 | try_set_item.data = {} 207 | try_set_item.data[1] = {} 208 | try_set_item.data[1][2] = "foo" 209 | say try_set_item.data 210 | 211 | try_set_item() 212 | 213 | for node in generate_tree(range(10, 20), key=int): 214 | append function node.parent: 215 | if node.partition(): 216 | if score @s thingy matches node.range function node.children 217 | else: 218 | if score @s thingy matches node.range say node.value 219 | 220 | scoreboard objectives setdisplay list some_score_name 221 | color = "red" 222 | scoreboard objectives setdisplay f"sidebar.team.{color}" some_score_name 223 | 224 | weapon = {1: "weapon.offhand", 2: "weapon.mainhand"} 225 | item replace entity @s weapon.offhand from entity @s weapon.mainhand 226 | my_weapon = weapon 227 | item replace entity @s my_weapon.1 from entity @s my_weapon.2 228 | 229 | numbers = list(range(12)) 230 | say numbers[:] 231 | say numbers[3:] 232 | say numbers[:9] 233 | say numbers[3:9] 234 | say numbers[::] 235 | say numbers[3::] 236 | say numbers[:9:] 237 | say numbers[3:9:] 238 | say numbers[::-1] 239 | say numbers[9::-1] 240 | say numbers[:3:-1] 241 | say numbers[9:3:-1] 242 | 243 | execute at @s if block ~ ~ ~ #minecraft:beds: 244 | teleport @s ~ ~0.56250 ~ 245 | 246 | del numbers[3:] 247 | say numbers 248 | 249 | for loop, value in loop_info("abcd"): 250 | say f"==[{loop.current}]==" 251 | say (value == loop.current) 252 | say loop.before 253 | say loop.after 254 | say loop.first 255 | say loop.last 256 | say loop.cycle("foo", "bar") 257 | 258 | from ./thing import raw 259 | 260 | raw(f"say hello{'!' * 5}") 261 | 262 | execute function demo:bbb: 263 | say 1 264 | append function demo:bbb: 265 | say 2 266 | prepend function demo:bbb: 267 | say 0 268 | 269 | mykey1 = "foo" 270 | 271 | if data storage some:path/to/storage f"some.{mykey1}.path" 272 | if data storage some:path/to/storage f"some.{mykey1}.path" run say hi 273 | if data storage some:path/to/storage f'some.{mykey1}.path{{my: "compound"}}' 274 | if data storage some:path/to/storage f'some.{mykey1}.path{{my: "compound"}}' run say hi 275 | 276 | mykey2 = "bar" 277 | 278 | mypath1 = f"some.{mykey2}.path" 279 | if data storage some:path/to/storage (mypath1) 280 | if data storage some:path/to/storage (mypath1) run say hi 281 | 282 | mypath2 = f'some.{mykey2}.path{{my: "compound"}}' 283 | if data storage some:path/to/storage (mypath2) 284 | if data storage some:path/to/storage (mypath2) run say hi 285 | 286 | mypath3 = f'some.{mykey2}.path[{{my: "subscript"}}]' 287 | if data storage some:path/to/storage (mypath3) 288 | if data storage some:path/to/storage (mypath3) run say hi 289 | 290 | myindex = 42 291 | 292 | if data storage some:path/to/storage f'some.{mykey1}.path{{my: "compound"}}.stuff[{myindex}].beep.{mykey2}[{{my: "subscript"}}].boop' run say hi 293 | 294 | mypath4 = "foo.bar" 295 | if data storage some:path/to/storage mypath4 296 | 297 | if data storage some:path/to/storage "some.foo.path" 298 | 299 | import nbtlib 300 | mypath5 = nbtlib.Path().something.cool[3].foo 301 | if data storage some:path/to/storage mypath5 302 | 303 | foo = nbtlib.Byte(23) 304 | data merge storage some:path/to/storage {Count: foo} 305 | 306 | from uuid import UUID 307 | 308 | def try_entity_interpolation(x): 309 | if score x some_objective matches 0 say yes 310 | 311 | try_entity_interpolation("some_fake_player") 312 | try_entity_interpolation("0-0-0-0-1") 313 | try_entity_interpolation(UUID("12345678-1234-5678-1234-567812345678")) 314 | 315 | entity_type = "creeper" 316 | at @e[type=entity_type] summon lightning_bolt 317 | 318 | language minecraft:aaaa { 319 | "menu.singleplayer": "AAAA" 320 | } 321 | 322 | some_translation = "bonjour" 323 | 324 | merge language minecraft:aaaa { 325 | "something.else": some_translation 326 | } 327 | 328 | merge language minecraft:aaaa: 329 | yaml.key1: some_translation.upper() 330 | yaml.key2: some_translation.title() 331 | 332 | x = 0 333 | y = 1 334 | z = 2 335 | 336 | data merge storage demo:foo: 337 | custom_data1: [x, y, z] 338 | custom_data2: 339 | - x 340 | - y 341 | - z 342 | 343 | def set_at_origin(block): 344 | setblock 0 0 0 block 345 | 346 | set_at_origin(minecraft:stone) 347 | 348 | some_name = ./useless/../no_quotes_lol 349 | function some_name: 350 | say that's neat 351 | 352 | if ./a/b/c == ./x/../a/b/x/../c: 353 | say same thing 354 | 355 | tellraw @a: 356 | text: 'hello world' 357 | 358 | function_tag felix:howdy: 359 | values: ['demo:foo', __name__] 360 | 361 | execute at @s run particle minecraft:dust 0 1 1 0.9 ~ ~1 ~ 0 0 0 0.01 1 force 362 | execute at @s run particle minecraft:block yellow_concrete ~ ~1.62 ~ 0 0.4 0 0 30 force 363 | execute at @s run particle minecraft:block minecraft:light_blue_concrete ~ ~1 ~ 0.05 0.1 0.05 0 3 364 | 365 | particle_file minecraft:end_rod { 366 | "textures": [ 367 | "minecraft:glitter_7", 368 | "minecraft:glitter_6", 369 | "minecraft:glitter_5", 370 | "minecraft:glitter_4", 371 | "minecraft:glitter_3", 372 | "minecraft:glitter_2", 373 | "minecraft:glitter_1", 374 | "minecraft:glitter_0" 375 | ] 376 | } 377 | 378 | for obj in ["foo", "bar"]: 379 | as @a[scores={obj=1..}] scoreboard players remove @s obj 1 380 | 381 | x = 8 382 | scoreboard players set @p tmp -x 383 | 384 | x = 12 385 | y = 23 386 | z = 34 387 | 388 | tp @s f"~{x} ~{y} ~{z}" 389 | tp @s f"~{x}" y f"~{z}" 390 | 391 | tp @s x y z 392 | tp @s ~x ~y ~z 393 | tp @s ^x ^y ^z 394 | tp @s -x -y -z 395 | tp @s ~-x ~-y ~-z 396 | tp @s ^-x ^-y ^-z 397 | 398 | for i, x in enumerate("abc"): 399 | say f"{i} {x}" 400 | 401 | major, minor, patch = "1.2.3".split(".") 402 | say major 403 | say minor 404 | say patch 405 | 406 | def check_above(n): 407 | if n > 0: 408 | if block ~ ~n ~ air: 409 | check_above(n - 1) 410 | else: 411 | say nothing above! 412 | 413 | check_above(6) 414 | 415 | math = 0 416 | say math 417 | def wat(): 418 | global math 419 | import math 420 | wat() 421 | say math.ceil(0.1) 422 | 423 | say bolt implements a __rebind__(rhs) magic method that gets called when you reassign a value to a variable 424 | say let's try it out by manually building a class 425 | 426 | def init_global_score(self, name): 427 | self.name = name 428 | 429 | def add_global_score(self, rhs): 430 | tmp = GlobalScore(generate_id("tmp.{incr}")) 431 | scoreboard players operation tmp.name global = self.name global 432 | tmp += rhs 433 | return tmp 434 | 435 | def iadd_global_score(self, rhs): 436 | if isinstance(rhs, GlobalScore): 437 | scoreboard players operation self.name global += rhs.name global 438 | else: 439 | scoreboard players add self.name global rhs 440 | return self 441 | 442 | def rebind_global_score(self, rhs): 443 | if self is not rhs: 444 | if isinstance(rhs, GlobalScore): 445 | scoreboard players operation self.name global = rhs.name global 446 | else: 447 | scoreboard players set self.name global rhs 448 | return self 449 | 450 | GlobalScore = type("GlobalScore", (), { 451 | "__init__": init_global_score, 452 | "__add__": add_global_score, 453 | "__iadd__": iadd_global_score, 454 | "__rebind__": rebind_global_score, 455 | }) 456 | 457 | a = GlobalScore("a") 458 | a = 123 459 | a = 456 460 | 461 | b = GlobalScore("b") 462 | b = 789 463 | b = a 464 | 465 | a += b + 6 + a 466 | b += 1 467 | 468 | say you can use nonlocal to create fake classes 469 | 470 | def Counter(x=0): 471 | def incr(): 472 | nonlocal x 473 | x += 1 474 | return x 475 | return {"incr": incr} 476 | 477 | counter = Counter() 478 | say counter.incr() 479 | say counter.incr() 480 | say Counter(9).incr()x 481 | 482 | def foo(msg): 483 | say msg 484 | 485 | if score #temp abc matches 1..: 486 | foo("foo") 487 | 488 | tag_name = ./yolo_funcs 489 | 490 | function_tag tag_name: 491 | values: 492 | - "demo:foo" 493 | 494 | as @p function #tag_name # this runs the tag 495 | 496 | def truth_table(): 497 | for a in [False, True]: 498 | for b in [False, True]: 499 | for c in [False, True]: 500 | for d in [False, True]: 501 | yield (a and b or c and not d) in [True] not in [False] 502 | 503 | say list(truth_table()) 504 | 505 | def not_init(self, value): 506 | self.value = value 507 | 508 | def not_not(self): 509 | say self.value 510 | return self 511 | 512 | NotPrint = type("NotPrint", (), {"__init__": not_init, "__not__": not_not}) 513 | not not not not NotPrint("hello") 514 | 515 | def thing_within(self, container): 516 | for item in container: 517 | say (self == item) 518 | return self 519 | 520 | def thing_contains(self, item): 521 | say (self == item) 522 | return self 523 | 524 | def thing_equal(self, item): 525 | return f"thing == {item}" 526 | 527 | Thing = type("Thing", (), {"__within__": thing_within, "__contains__": thing_contains, "__eq__": thing_equal}) 528 | Thing() in [1, 2, 3] in [99] 529 | "world" in ("hello" in Thing()) 530 | 531 | from contextlib import contextmanager 532 | 533 | def dsl_init(self, score, inverted=False): 534 | self.score = score 535 | self.inverted = inverted 536 | 537 | def dsl_not(self): 538 | return DSL(self.score, not self.inverted) 539 | 540 | def dsl_branch(self): 541 | if score @s self.score matches int(not self.inverted): 542 | yield True 543 | 544 | DSL = type("DSL", (), {"__init__": dsl_init, "__not__": dsl_not, "__branch__": contextmanager(dsl_branch)}) 545 | 546 | if DSL("foo"): 547 | say yes 548 | if DSL("bar"): 549 | say with bar 550 | else: 551 | say no 552 | 553 | predicate ./check_scores [ 554 | { 555 | "condition": "minecraft:entity_scores", 556 | "entity": "this", 557 | "scores": { 558 | "foo": 1 559 | } 560 | }, 561 | { 562 | "condition": "minecraft:entity_scores", 563 | "entity": "killer_player", 564 | "scores": { 565 | "bar": 1 566 | } 567 | } 568 | ] 569 | 570 | def cond_init(self, name=generate_id("tmp{incr}")): 571 | self.name = name 572 | 573 | def cond_str(self): 574 | return self.name 575 | 576 | def cond_not(self): 577 | result = Cond() 578 | result = 1 579 | unless score global self matches 0: 580 | result = 0 581 | return result 582 | 583 | def cond_dup(self): 584 | result = Cond() 585 | result = self 586 | return result 587 | 588 | def cond_rebind(self, rhs): 589 | if isinstance(rhs, Cond): 590 | scoreboard players operation global self = global rhs 591 | else: 592 | scoreboard players set global self rhs 593 | return self 594 | 595 | def cond_branch(self): 596 | unless score global self matches 0: 597 | yield True 598 | 599 | Cond = type("Cond", (), { 600 | "__init__": cond_init, 601 | "__str__": cond_str, 602 | "__not__": cond_not, 603 | "__dup__": cond_dup, 604 | "__rebind__": cond_rebind, 605 | "__branch__": contextmanager(cond_branch), 606 | }) 607 | 608 | is_awesome = Cond("is_awesome") 609 | is_awesome = Cond("is_cool") and Cond("is_nice") 610 | 611 | if is_awesome or Cond("force_awesomeness"): 612 | say hello 613 | 614 | from ./utils import something 615 | something() 616 | 617 | from ./utils import load_storage 618 | load_storage(./random_data, "random_data.json") 619 | if data storage ./random_data {value: 42} say json loaded successfully 620 | 621 | """ 622 | Multiline comments 623 | 624 | are not parsed properly 625 | 626 | apparently 627 | """ -------------------------------------------------------------------------------- /mcfunction.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Syntax Mcfunction", 3 | "scopeName": "source.mcfunction", 4 | "uuid": "8918dadd-8ebe-42d9-b1e9-0489ab228d9d", 5 | "fileTypes": [ 6 | "mcfunction", 7 | "bolt" 8 | ], 9 | "patterns": [ 10 | { 11 | "include": "#root" 12 | } 13 | ], 14 | "repository": { 15 | "root": { 16 | "patterns": [ 17 | { 18 | "include": "#literals" 19 | }, 20 | { 21 | "include": "#comments" 22 | }, 23 | { 24 | "include": "#say" 25 | }, 26 | { 27 | "include": "#names" 28 | }, 29 | { 30 | "include": "#comments_inline" 31 | }, 32 | { 33 | "include": "#subcommands" 34 | }, 35 | { 36 | "include": "#property" 37 | }, 38 | { 39 | "include": "#operators" 40 | }, 41 | { 42 | "include": "#selectors" 43 | } 44 | ] 45 | }, 46 | "comments": { 47 | "patterns": [ 48 | { 49 | "applyEndPatternLast": 1, 50 | "begin": "^\\s*(#[>!#])(.+)$", 51 | "beginCaptures": { 52 | "1": { 53 | "name": "comment.block.mcfunction" 54 | }, 55 | "2": { 56 | "name": "markup.bold.mcfunction" 57 | } 58 | }, 59 | "captures": { 60 | "0": { 61 | "name": "comment.block.mcfunction" 62 | } 63 | }, 64 | "end": "^(?!#)", 65 | "name": "meta.comments", 66 | "patterns": [ 67 | { 68 | "include": "#comments_block" 69 | } 70 | ] 71 | }, 72 | { 73 | "captures": { 74 | "0": { 75 | "name": "comment.line.mcfunction" 76 | } 77 | }, 78 | "match": "^\\s*#.*$", 79 | "name": "meta.comments" 80 | } 81 | ] 82 | }, 83 | "comments_inline": { 84 | "patterns": [ 85 | { 86 | "captures": { 87 | "0": { 88 | "name": "comment.line.mcfunction" 89 | } 90 | }, 91 | "match": "#.*$", 92 | "name": "meta.comments" 93 | } 94 | ] 95 | }, 96 | "comments_block": { 97 | "patterns": [ 98 | { 99 | "applyEndPatternLast": 1, 100 | "begin": "^\\s*#[>!]", 101 | "captures": { 102 | "0": { 103 | "name": "comment.block.mcfunction" 104 | } 105 | }, 106 | "end": "$", 107 | "name": "meta.comments_block", 108 | "patterns": [ 109 | { 110 | "include": "#comments_block_emphasized" 111 | } 112 | ] 113 | }, 114 | { 115 | "applyEndPatternLast": 1, 116 | "begin": "^\\s*#", 117 | "captures": { 118 | "0": { 119 | "name": "comment.block.mcfunction" 120 | } 121 | }, 122 | "end": "$", 123 | "name": "meta.comments_block", 124 | "patterns": [ 125 | { 126 | "include": "#comments_block_normal" 127 | } 128 | ] 129 | } 130 | ] 131 | }, 132 | "comments_block_emphasized": { 133 | "patterns": [ 134 | { 135 | "include": "#comments_block_special" 136 | }, 137 | { 138 | "captures": { 139 | "0": { 140 | "name": "markup.bold.mcfunction" 141 | } 142 | }, 143 | "match": "\\S+", 144 | "name": "meta.comments_block_emphasized" 145 | } 146 | ] 147 | }, 148 | "comments_block_normal": { 149 | "patterns": [ 150 | { 151 | "include": "#comments_block_special" 152 | }, 153 | { 154 | "captures": { 155 | "0": { 156 | "name": "comment.block.mcfunction" 157 | } 158 | }, 159 | "match": "\\S+", 160 | "name": "meta.comments_block_normal" 161 | }, 162 | { 163 | "include": "#whitespace" 164 | } 165 | ] 166 | }, 167 | "comments_block_special": { 168 | "patterns": [ 169 | { 170 | "captures": { 171 | "0": { 172 | "name": "markup.heading.mcfunction" 173 | } 174 | }, 175 | "match": "@\\S+", 176 | "name": "meta.comments_block_special" 177 | }, 178 | { 179 | "include": "#resource-name" 180 | }, 181 | { 182 | "captures": { 183 | "0": { 184 | "name": "variable.other.mcfunction" 185 | } 186 | }, 187 | "match": "[#%$][A-Za-z0-9_.#%$]+", 188 | "name": "meta.comments_block_special" 189 | } 190 | ] 191 | }, 192 | "literals": { 193 | "patterns": [ 194 | { 195 | "captures": { 196 | "0": { 197 | "name": "constant.numeric.boolean.mcfunction" 198 | } 199 | }, 200 | "match": "\\b(true|false|True|False)\\b", 201 | "name": "meta.literals" 202 | }, 203 | { 204 | "captures": { 205 | "0": { 206 | "name": "variable.uuid.mcfunction" 207 | } 208 | }, 209 | "match": "\\b[0-9a-fA-F]+(?:-[0-9a-fA-F]+){4}\\b", 210 | "name": "meta.names" 211 | }, 212 | { 213 | "captures": { 214 | "0": { 215 | "name": "constant.numeric.float.mcfunction" 216 | } 217 | }, 218 | "match": "[+-]?\\d*\\.?\\d+([eE]?[+-]?\\d+)?[df]?\\b", 219 | "name": "meta.literals" 220 | }, 221 | { 222 | "captures": { 223 | "0": { 224 | "name": "constant.numeric.integer.mcfunction" 225 | } 226 | }, 227 | "match": "[+-]?\\d+(b|B|L|l|s|S)?\\b", 228 | "name": "meta.literals" 229 | }, 230 | { 231 | "captures": { 232 | "0": { 233 | "name": "variable.other.mcfunction" 234 | } 235 | }, 236 | "match": "\\.\\.", 237 | "name": "meta.ellipse.literals" 238 | }, 239 | { 240 | "applyEndPatternLast": 1, 241 | "begin": "\"", 242 | "beginCaptures": { 243 | "0": { 244 | "name": "punctuation.definition.string.begin.mcfunction" 245 | } 246 | }, 247 | "end": "\"", 248 | "endCaptures": { 249 | "0": { 250 | "name": "punctuation.definition.string.end.mcfunction" 251 | } 252 | }, 253 | "name": "string.quoted.double.mcfunction", 254 | "patterns": [ 255 | { 256 | "include": "#literals_string-double" 257 | } 258 | ] 259 | }, 260 | { 261 | "applyEndPatternLast": 1, 262 | "begin": "'", 263 | "beginCaptures": { 264 | "0": { 265 | "name": "punctuation.definition.string.begin.mcfunction" 266 | } 267 | }, 268 | "end": "'", 269 | "endCaptures": { 270 | "0": { 271 | "name": "punctuation.definition.string.begin.mcfunction" 272 | } 273 | }, 274 | "name": "string.quoted.single.mcfunction", 275 | "patterns": [ 276 | { 277 | "include": "#literals_string-single" 278 | } 279 | ] 280 | } 281 | ] 282 | }, 283 | "subcommands": { 284 | "patterns": [ 285 | { 286 | "captures": { 287 | "0": { 288 | "name": "entity.name.class.mcfunction" 289 | } 290 | }, 291 | "match": "[a-z_]+", 292 | "name": "meta.literals" 293 | } 294 | ] 295 | }, 296 | "literals_string-double": { 297 | "patterns": [ 298 | { 299 | "captures": { 300 | "0": { 301 | "name": "constant.character.escape.mcfunction" 302 | } 303 | }, 304 | "match": "\\\\.", 305 | "name": "meta.literals_string-double" 306 | }, 307 | { 308 | "captures": { 309 | "0": { 310 | "name": "constant.character.escape.mcfunction" 311 | } 312 | }, 313 | "match": "\\\\", 314 | "name": "meta.literals_string-double" 315 | }, 316 | { 317 | "include": "#macro-name" 318 | }, 319 | { 320 | "captures": { 321 | "0": { 322 | "name": "string.quoted.double.mcfunction" 323 | } 324 | }, 325 | "match": "[^\\\\\"]", 326 | "name": "meta.literals_string-double" 327 | } 328 | ] 329 | }, 330 | "literals_string-single": { 331 | "patterns": [ 332 | { 333 | "captures": { 334 | "0": { 335 | "name": "constant.character.escape.mcfunction" 336 | } 337 | }, 338 | "match": "\\\\.", 339 | "name": "meta.literals_string-single" 340 | }, 341 | { 342 | "captures": { 343 | "0": { 344 | "name": "constant.character.escape.mcfunction" 345 | } 346 | }, 347 | "match": "\\\\", 348 | "name": "meta.literals_string-double" 349 | }, 350 | { 351 | "include": "#macro-name" 352 | }, 353 | { 354 | "captures": { 355 | "0": { 356 | "name": "string.quoted.single.mcfunction" 357 | } 358 | }, 359 | "match": "[^\\\\']", 360 | "name": "meta.literals_string-single" 361 | } 362 | ] 363 | }, 364 | "say": { 365 | "patterns": [ 366 | { 367 | "begin": "^(\\s*)(say)", 368 | "beginCaptures": { 369 | "1": { 370 | "name": "whitespace.mcfunction" 371 | }, 372 | "2": { 373 | "name": "keyword.control.flow.mcfunction" 374 | } 375 | }, 376 | "end": "\\n", 377 | "name": "meta.say.mcfunction", 378 | "patterns": [ 379 | { 380 | "captures": { 381 | "0": { 382 | "name": "constant.character.escape.mcfunction" 383 | } 384 | }, 385 | "match": "\\\\\\s*\\n", 386 | "meta": "meta.say.backslash.mcfunction" 387 | }, 388 | { 389 | "include": "#literals_string-double" 390 | }, 391 | { 392 | "include": "#literals_string-single" 393 | } 394 | ] 395 | }, 396 | { 397 | "begin": "(run)(\\s+)(say)", 398 | "beginCaptures": { 399 | "1": { 400 | "name": "entity.name.mcfunction" 401 | }, 402 | "2": { 403 | "name": "whitespace.mcfunction" 404 | }, 405 | "3": { 406 | "name": "keyword.control.flow.mcfunction" 407 | } 408 | }, 409 | "end": "\\n", 410 | "name": "meta.say.mcfunction", 411 | "patterns": [ 412 | { 413 | "captures": { 414 | "0": { 415 | "name": "constant.character.escape.mcfunction" 416 | } 417 | }, 418 | "match": "\\\\\\s*\\n", 419 | "meta": "meta.say.backslash.mcfunction" 420 | }, 421 | { 422 | "include": "#literals_string-double" 423 | }, 424 | { 425 | "include": "#literals_string-single" 426 | } 427 | ] 428 | } 429 | ] 430 | }, 431 | "names": { 432 | "patterns": [ 433 | { 434 | "captures": { 435 | "1": { 436 | "name": "whitespace.mcfunction" 437 | }, 438 | "2": { 439 | "name": "keyword.control.flow.mcfunction" 440 | } 441 | }, 442 | "match": "^(\\s*)([a-z_]+)(?=\\s)", 443 | "name": "meta.names" 444 | }, 445 | { 446 | "captures": { 447 | "1": { 448 | "name": "whitespace.mcfunction" 449 | }, 450 | "2": { 451 | "name": "markup.italic.mcfunction" 452 | }, 453 | "3": { 454 | "name": "whitespace.mcfunction" 455 | }, 456 | "4": { 457 | "name": "keyword.control.flow.mcfunction" 458 | } 459 | }, 460 | "match": "^(\\s*)(\\$)( ?)([a-z_]*)", 461 | "name": "meta.names" 462 | }, 463 | { 464 | "captures": { 465 | "1": { 466 | "name": "entity.name.mcfunction" 467 | }, 468 | "2": { 469 | "name": "whitespace.mcfunction" 470 | }, 471 | "3": { 472 | "name": "keyword.control.flow.mcfunction" 473 | } 474 | }, 475 | "match": "(run)(\\s+)([a-z_]+)", 476 | "name": "meta.names" 477 | }, 478 | { 479 | "include": "#resource-name" 480 | }, 481 | { 482 | "captures": { 483 | "0": { 484 | "name": "entity.name.mcfunction" 485 | } 486 | }, 487 | "match": "[A-Za-z]+(?=\\W)", 488 | "name": "meta.names" 489 | }, 490 | { 491 | "captures": { 492 | "0": { 493 | "name": "string.unquoted.mcfunction" 494 | } 495 | }, 496 | "match": "[A-Za-z_][A-Za-z0-9_.#%$]*", 497 | "name": "meta.names" 498 | }, 499 | { 500 | "include": "#macro-name" 501 | }, 502 | { 503 | "captures": { 504 | "0": { 505 | "name": "variable.other.mcfunction" 506 | } 507 | }, 508 | "match": "([#%$]|((?<=\\s)\\.))[A-Za-z0-9_.#%$\\-]+", 509 | "name": "meta.names" 510 | } 511 | ] 512 | }, 513 | "macro-name": { 514 | "patterns": [ 515 | { 516 | "captures": { 517 | "1": { 518 | "name": "punctuation.definition.template-expression.begin.mcfunction" 519 | }, 520 | "2": { 521 | "name": "variable.other.mcfunction" 522 | }, 523 | "3": { 524 | "name": "punctuation.definition.template-expression.end.mcfunction" 525 | } 526 | }, 527 | "match": "(\\$\\()([A-Za-z0-9_]*)(\\))", 528 | "name": "meta.macro-name" 529 | } 530 | ] 531 | }, 532 | "operators": { 533 | "patterns": [ 534 | { 535 | "captures": { 536 | "0": { 537 | "name": "constant.numeric.mcfunction" 538 | } 539 | }, 540 | "match": "[~^]", 541 | "name": "meta.operators" 542 | }, 543 | { 544 | "captures": { 545 | "0": { 546 | "name": "keyword.operator.mcfunction" 547 | } 548 | }, 549 | "match": "[\\-%?!+*<>\\\\/|&=.:,;]", 550 | "name": "meta.operators" 551 | } 552 | ] 553 | }, 554 | "property": { 555 | "patterns": [ 556 | { 557 | "applyEndPatternLast": 1, 558 | "begin": "\\{", 559 | "captures": { 560 | "0": { 561 | "name": "punctuation.mcfunction" 562 | } 563 | }, 564 | "end": "\\}", 565 | "name": "meta.property.curly", 566 | "patterns": [ 567 | { 568 | "include": "#resource-name" 569 | }, 570 | { 571 | "include": "#literals" 572 | }, 573 | { 574 | "include": "#property_key" 575 | }, 576 | { 577 | "include": "#operators" 578 | }, 579 | { 580 | "include": "#property_value" 581 | }, 582 | { 583 | "include": "$self" 584 | } 585 | ] 586 | }, 587 | { 588 | "applyEndPatternLast": 1, 589 | "begin": "\\[", 590 | "captures": { 591 | "0": { 592 | "name": "variable.other.mcfunction" 593 | } 594 | }, 595 | "end": "\\]", 596 | "name": "meta.property.square", 597 | "patterns": [ 598 | { 599 | "include": "#resource-name" 600 | }, 601 | { 602 | "include": "#literals" 603 | }, 604 | { 605 | "include": "#property_key" 606 | }, 607 | { 608 | "include": "#operators" 609 | }, 610 | { 611 | "include": "#property_value" 612 | }, 613 | { 614 | "include": "$self" 615 | } 616 | ] 617 | }, 618 | { 619 | "applyEndPatternLast": 1, 620 | "begin": "\\(", 621 | "captures": { 622 | "0": { 623 | "name": "punctuation.mcfunction" 624 | } 625 | }, 626 | "end": "\\)", 627 | "name": "meta.property.paren", 628 | "patterns": [ 629 | { 630 | "include": "#resource-name" 631 | }, 632 | { 633 | "include": "#literals" 634 | }, 635 | { 636 | "include": "#property_key" 637 | }, 638 | { 639 | "include": "#operators" 640 | }, 641 | { 642 | "include": "#property_value" 643 | }, 644 | { 645 | "include": "$self" 646 | } 647 | ] 648 | } 649 | ] 650 | }, 651 | "property_key": { 652 | "patterns": [ 653 | { 654 | "captures": { 655 | "0": { 656 | "name": "variable.other.mcfunction" 657 | } 658 | }, 659 | "match": "#?[a-z_][a-z_\\.\\-]*\\:[a-z0-9_\\.\\-/]+(?=\\s*\\=:)", 660 | "name": "meta.property_key" 661 | }, 662 | { 663 | "captures": { 664 | "0": { 665 | "name": "variable.other.mcfunction" 666 | } 667 | }, 668 | "match": "#?[a-z_][a-z0-9_\\.\\-/]+", 669 | "name": "meta.property_key" 670 | }, 671 | { 672 | "captures": { 673 | "0": { 674 | "name": "variable.other.mcfunction" 675 | } 676 | }, 677 | "match": "[A-Za-z_]+[A-Za-z_\\-\\+]*", 678 | "name": "meta.property_key" 679 | } 680 | ] 681 | }, 682 | "property_value": { 683 | "patterns": [ 684 | { 685 | "captures": { 686 | "0": { 687 | "name": "string.unquoted.mcfunction" 688 | } 689 | }, 690 | "match": "#?[a-z_][a-z_\\.\\-]*\\:[a-z0-9_\\.\\-/]+", 691 | "name": "meta.property_value" 692 | }, 693 | { 694 | "captures": { 695 | "0": { 696 | "name": "string.unquoted.mcfunction" 697 | } 698 | }, 699 | "match": "#?[a-z_][a-z0-9_\\.\\-/]+", 700 | "name": "meta.property_value" 701 | } 702 | ] 703 | }, 704 | "resource-name": { 705 | "patterns": [ 706 | { 707 | "captures": { 708 | "0": { 709 | "name": "entity.name.function.mcfunction" 710 | } 711 | }, 712 | "match": "#?[a-z_][a-z0-9_.-]*:[a-z0-9_./-]+", 713 | "name": "meta.resource-name" 714 | }, 715 | { 716 | "captures": { 717 | "0": { 718 | "name": "entity.name.function.mcfunction" 719 | } 720 | }, 721 | "match": "#?[a-z0-9_\\.\\-]+\\/[a-z0-9_\\.\\-\\/]+", 722 | "name": "meta.resource-name" 723 | } 724 | ] 725 | }, 726 | "selectors": { 727 | "patterns": [ 728 | { 729 | "captures": { 730 | "0": { 731 | "name": "support.class.mcfunction" 732 | } 733 | }, 734 | "match": "@[a-z]+", 735 | "name": "meta.selectors" 736 | } 737 | ] 738 | } 739 | } 740 | } 741 | -------------------------------------------------------------------------------- /mcfunction.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fileTypes 6 | 7 | mcfunction 8 | bolt 9 | 10 | name 11 | Syntax Mcfunction 12 | patterns 13 | 14 | 15 | include 16 | #root 17 | 18 | 19 | repository 20 | 21 | comments 22 | 23 | patterns 24 | 25 | 26 | applyEndPatternLast 27 | 1 28 | begin 29 | ^\s*(#[>!#])(.+)$ 30 | beginCaptures 31 | 32 | 1 33 | 34 | name 35 | comment.block.mcfunction 36 | 37 | 2 38 | 39 | name 40 | markup.bold.mcfunction 41 | 42 | 43 | captures 44 | 45 | 0 46 | 47 | name 48 | comment.block.mcfunction 49 | 50 | 51 | end 52 | ^(?!#) 53 | name 54 | meta.comments 55 | patterns 56 | 57 | 58 | include 59 | #comments_block 60 | 61 | 62 | 63 | 64 | captures 65 | 66 | 0 67 | 68 | name 69 | comment.line.mcfunction 70 | 71 | 72 | match 73 | ^\s*#.*$ 74 | name 75 | meta.comments 76 | 77 | 78 | 79 | comments_block 80 | 81 | patterns 82 | 83 | 84 | applyEndPatternLast 85 | 1 86 | begin 87 | ^\s*#[>!] 88 | captures 89 | 90 | 0 91 | 92 | name 93 | comment.block.mcfunction 94 | 95 | 96 | end 97 | $ 98 | name 99 | meta.comments_block 100 | patterns 101 | 102 | 103 | include 104 | #comments_block_emphasized 105 | 106 | 107 | 108 | 109 | applyEndPatternLast 110 | 1 111 | begin 112 | ^\s*# 113 | captures 114 | 115 | 0 116 | 117 | name 118 | comment.block.mcfunction 119 | 120 | 121 | end 122 | $ 123 | name 124 | meta.comments_block 125 | patterns 126 | 127 | 128 | include 129 | #comments_block_normal 130 | 131 | 132 | 133 | 134 | 135 | comments_block_emphasized 136 | 137 | patterns 138 | 139 | 140 | include 141 | #comments_block_special 142 | 143 | 144 | captures 145 | 146 | 0 147 | 148 | name 149 | markup.bold.mcfunction 150 | 151 | 152 | match 153 | \S+ 154 | name 155 | meta.comments_block_emphasized 156 | 157 | 158 | 159 | comments_block_normal 160 | 161 | patterns 162 | 163 | 164 | include 165 | #comments_block_special 166 | 167 | 168 | captures 169 | 170 | 0 171 | 172 | name 173 | comment.block.mcfunction 174 | 175 | 176 | match 177 | \S+ 178 | name 179 | meta.comments_block_normal 180 | 181 | 182 | include 183 | #whitespace 184 | 185 | 186 | 187 | comments_block_special 188 | 189 | patterns 190 | 191 | 192 | captures 193 | 194 | 0 195 | 196 | name 197 | markup.heading.mcfunction 198 | 199 | 200 | match 201 | @\S+ 202 | name 203 | meta.comments_block_special 204 | 205 | 206 | include 207 | #resource-name 208 | 209 | 210 | captures 211 | 212 | 0 213 | 214 | name 215 | variable.other.mcfunction 216 | 217 | 218 | match 219 | [#%$][A-Za-z0-9_.#%$]+ 220 | name 221 | meta.comments_block_special 222 | 223 | 224 | 225 | comments_inline 226 | 227 | patterns 228 | 229 | 230 | captures 231 | 232 | 0 233 | 234 | name 235 | comment.line.mcfunction 236 | 237 | 238 | match 239 | #.*$ 240 | name 241 | meta.comments 242 | 243 | 244 | 245 | literals 246 | 247 | patterns 248 | 249 | 250 | captures 251 | 252 | 0 253 | 254 | name 255 | constant.numeric.boolean.mcfunction 256 | 257 | 258 | match 259 | \b(true|false|True|False)\b 260 | name 261 | meta.literals 262 | 263 | 264 | captures 265 | 266 | 0 267 | 268 | name 269 | variable.uuid.mcfunction 270 | 271 | 272 | match 273 | \b[0-9a-fA-F]+(?:-[0-9a-fA-F]+){4}\b 274 | name 275 | meta.names 276 | 277 | 278 | captures 279 | 280 | 0 281 | 282 | name 283 | constant.numeric.float.mcfunction 284 | 285 | 286 | match 287 | [+-]?\d*\.?\d+([eE]?[+-]?\d+)?[df]?\b 288 | name 289 | meta.literals 290 | 291 | 292 | captures 293 | 294 | 0 295 | 296 | name 297 | constant.numeric.integer.mcfunction 298 | 299 | 300 | match 301 | [+-]?\d+(b|B|L|l|s|S)?\b 302 | name 303 | meta.literals 304 | 305 | 306 | captures 307 | 308 | 0 309 | 310 | name 311 | variable.other.mcfunction 312 | 313 | 314 | match 315 | \.\. 316 | name 317 | meta.ellipse.literals 318 | 319 | 320 | applyEndPatternLast 321 | 1 322 | begin 323 | " 324 | beginCaptures 325 | 326 | 0 327 | 328 | name 329 | punctuation.definition.string.begin.mcfunction 330 | 331 | 332 | end 333 | " 334 | endCaptures 335 | 336 | 0 337 | 338 | name 339 | punctuation.definition.string.end.mcfunction 340 | 341 | 342 | name 343 | string.quoted.double.mcfunction 344 | patterns 345 | 346 | 347 | include 348 | #literals_string-double 349 | 350 | 351 | 352 | 353 | applyEndPatternLast 354 | 1 355 | begin 356 | ' 357 | beginCaptures 358 | 359 | 0 360 | 361 | name 362 | punctuation.definition.string.begin.mcfunction 363 | 364 | 365 | end 366 | ' 367 | endCaptures 368 | 369 | 0 370 | 371 | name 372 | punctuation.definition.string.begin.mcfunction 373 | 374 | 375 | name 376 | string.quoted.single.mcfunction 377 | patterns 378 | 379 | 380 | include 381 | #literals_string-single 382 | 383 | 384 | 385 | 386 | 387 | literals_string-double 388 | 389 | patterns 390 | 391 | 392 | captures 393 | 394 | 0 395 | 396 | name 397 | constant.character.escape.mcfunction 398 | 399 | 400 | match 401 | \\. 402 | name 403 | meta.literals_string-double 404 | 405 | 406 | captures 407 | 408 | 0 409 | 410 | name 411 | constant.character.escape.mcfunction 412 | 413 | 414 | match 415 | \\ 416 | name 417 | meta.literals_string-double 418 | 419 | 420 | include 421 | #macro-name 422 | 423 | 424 | captures 425 | 426 | 0 427 | 428 | name 429 | string.quoted.double.mcfunction 430 | 431 | 432 | match 433 | [^\\"] 434 | name 435 | meta.literals_string-double 436 | 437 | 438 | 439 | literals_string-single 440 | 441 | patterns 442 | 443 | 444 | captures 445 | 446 | 0 447 | 448 | name 449 | constant.character.escape.mcfunction 450 | 451 | 452 | match 453 | \\. 454 | name 455 | meta.literals_string-single 456 | 457 | 458 | captures 459 | 460 | 0 461 | 462 | name 463 | constant.character.escape.mcfunction 464 | 465 | 466 | match 467 | \\ 468 | name 469 | meta.literals_string-double 470 | 471 | 472 | include 473 | #macro-name 474 | 475 | 476 | captures 477 | 478 | 0 479 | 480 | name 481 | string.quoted.single.mcfunction 482 | 483 | 484 | match 485 | [^\\'] 486 | name 487 | meta.literals_string-single 488 | 489 | 490 | 491 | macro-name 492 | 493 | patterns 494 | 495 | 496 | captures 497 | 498 | 1 499 | 500 | name 501 | punctuation.definition.template-expression.begin.mcfunction 502 | 503 | 2 504 | 505 | name 506 | variable.other.mcfunction 507 | 508 | 3 509 | 510 | name 511 | punctuation.definition.template-expression.end.mcfunction 512 | 513 | 514 | match 515 | (\$\()([A-Za-z0-9_]*)(\)) 516 | name 517 | meta.macro-name 518 | 519 | 520 | 521 | names 522 | 523 | patterns 524 | 525 | 526 | captures 527 | 528 | 1 529 | 530 | name 531 | whitespace.mcfunction 532 | 533 | 2 534 | 535 | name 536 | keyword.control.flow.mcfunction 537 | 538 | 539 | match 540 | ^(\s*)([a-z_]+)(?=\s) 541 | name 542 | meta.names 543 | 544 | 545 | captures 546 | 547 | 1 548 | 549 | name 550 | whitespace.mcfunction 551 | 552 | 2 553 | 554 | name 555 | markup.italic.mcfunction 556 | 557 | 3 558 | 559 | name 560 | whitespace.mcfunction 561 | 562 | 4 563 | 564 | name 565 | keyword.control.flow.mcfunction 566 | 567 | 568 | match 569 | ^(\s*)(\$)( ?)([a-z_]*) 570 | name 571 | meta.names 572 | 573 | 574 | captures 575 | 576 | 1 577 | 578 | name 579 | entity.name.mcfunction 580 | 581 | 2 582 | 583 | name 584 | whitespace.mcfunction 585 | 586 | 3 587 | 588 | name 589 | keyword.control.flow.mcfunction 590 | 591 | 592 | match 593 | (run)(\s+)([a-z_]+) 594 | name 595 | meta.names 596 | 597 | 598 | include 599 | #resource-name 600 | 601 | 602 | captures 603 | 604 | 0 605 | 606 | name 607 | entity.name.mcfunction 608 | 609 | 610 | match 611 | [A-Za-z]+(?=\W) 612 | name 613 | meta.names 614 | 615 | 616 | captures 617 | 618 | 0 619 | 620 | name 621 | string.unquoted.mcfunction 622 | 623 | 624 | match 625 | [A-Za-z_][A-Za-z0-9_.#%$]* 626 | name 627 | meta.names 628 | 629 | 630 | include 631 | #macro-name 632 | 633 | 634 | captures 635 | 636 | 0 637 | 638 | name 639 | variable.other.mcfunction 640 | 641 | 642 | match 643 | ([#%$]|((?<=\s)\.))[A-Za-z0-9_.#%$\-]+ 644 | name 645 | meta.names 646 | 647 | 648 | 649 | operators 650 | 651 | patterns 652 | 653 | 654 | captures 655 | 656 | 0 657 | 658 | name 659 | constant.numeric.mcfunction 660 | 661 | 662 | match 663 | [~^] 664 | name 665 | meta.operators 666 | 667 | 668 | captures 669 | 670 | 0 671 | 672 | name 673 | keyword.operator.mcfunction 674 | 675 | 676 | match 677 | [\-%?!+*<>\\/|&=.:,;] 678 | name 679 | meta.operators 680 | 681 | 682 | 683 | property 684 | 685 | patterns 686 | 687 | 688 | applyEndPatternLast 689 | 1 690 | begin 691 | \{ 692 | captures 693 | 694 | 0 695 | 696 | name 697 | punctuation.mcfunction 698 | 699 | 700 | end 701 | \} 702 | name 703 | meta.property.curly 704 | patterns 705 | 706 | 707 | include 708 | #resource-name 709 | 710 | 711 | include 712 | #literals 713 | 714 | 715 | include 716 | #property_key 717 | 718 | 719 | include 720 | #operators 721 | 722 | 723 | include 724 | #property_value 725 | 726 | 727 | include 728 | $self 729 | 730 | 731 | 732 | 733 | applyEndPatternLast 734 | 1 735 | begin 736 | \[ 737 | captures 738 | 739 | 0 740 | 741 | name 742 | variable.other.mcfunction 743 | 744 | 745 | end 746 | \] 747 | name 748 | meta.property.square 749 | patterns 750 | 751 | 752 | include 753 | #resource-name 754 | 755 | 756 | include 757 | #literals 758 | 759 | 760 | include 761 | #property_key 762 | 763 | 764 | include 765 | #operators 766 | 767 | 768 | include 769 | #property_value 770 | 771 | 772 | include 773 | $self 774 | 775 | 776 | 777 | 778 | applyEndPatternLast 779 | 1 780 | begin 781 | \( 782 | captures 783 | 784 | 0 785 | 786 | name 787 | punctuation.mcfunction 788 | 789 | 790 | end 791 | \) 792 | name 793 | meta.property.paren 794 | patterns 795 | 796 | 797 | include 798 | #resource-name 799 | 800 | 801 | include 802 | #literals 803 | 804 | 805 | include 806 | #property_key 807 | 808 | 809 | include 810 | #operators 811 | 812 | 813 | include 814 | #property_value 815 | 816 | 817 | include 818 | $self 819 | 820 | 821 | 822 | 823 | 824 | property_key 825 | 826 | patterns 827 | 828 | 829 | captures 830 | 831 | 0 832 | 833 | name 834 | variable.other.mcfunction 835 | 836 | 837 | match 838 | #?[a-z_][a-z_\.\-]*\:[a-z0-9_\.\-/]+(?=\s*\=:) 839 | name 840 | meta.property_key 841 | 842 | 843 | captures 844 | 845 | 0 846 | 847 | name 848 | variable.other.mcfunction 849 | 850 | 851 | match 852 | #?[a-z_][a-z0-9_\.\-/]+ 853 | name 854 | meta.property_key 855 | 856 | 857 | captures 858 | 859 | 0 860 | 861 | name 862 | variable.other.mcfunction 863 | 864 | 865 | match 866 | [A-Za-z_]+[A-Za-z_\-\+]* 867 | name 868 | meta.property_key 869 | 870 | 871 | 872 | property_value 873 | 874 | patterns 875 | 876 | 877 | captures 878 | 879 | 0 880 | 881 | name 882 | string.unquoted.mcfunction 883 | 884 | 885 | match 886 | #?[a-z_][a-z_\.\-]*\:[a-z0-9_\.\-/]+ 887 | name 888 | meta.property_value 889 | 890 | 891 | captures 892 | 893 | 0 894 | 895 | name 896 | string.unquoted.mcfunction 897 | 898 | 899 | match 900 | #?[a-z_][a-z0-9_\.\-/]+ 901 | name 902 | meta.property_value 903 | 904 | 905 | 906 | resource-name 907 | 908 | patterns 909 | 910 | 911 | captures 912 | 913 | 0 914 | 915 | name 916 | entity.name.function.mcfunction 917 | 918 | 919 | match 920 | #?[a-z_][a-z0-9_.-]*:[a-z0-9_./-]+ 921 | name 922 | meta.resource-name 923 | 924 | 925 | captures 926 | 927 | 0 928 | 929 | name 930 | entity.name.function.mcfunction 931 | 932 | 933 | match 934 | #?[a-z0-9_\.\-]+\/[a-z0-9_\.\-\/]+ 935 | name 936 | meta.resource-name 937 | 938 | 939 | 940 | root 941 | 942 | patterns 943 | 944 | 945 | include 946 | #literals 947 | 948 | 949 | include 950 | #comments 951 | 952 | 953 | include 954 | #say 955 | 956 | 957 | include 958 | #names 959 | 960 | 961 | include 962 | #comments_inline 963 | 964 | 965 | include 966 | #subcommands 967 | 968 | 969 | include 970 | #property 971 | 972 | 973 | include 974 | #operators 975 | 976 | 977 | include 978 | #selectors 979 | 980 | 981 | 982 | say 983 | 984 | patterns 985 | 986 | 987 | begin 988 | ^(\s*)(say) 989 | beginCaptures 990 | 991 | 1 992 | 993 | name 994 | whitespace.mcfunction 995 | 996 | 2 997 | 998 | name 999 | keyword.control.flow.mcfunction 1000 | 1001 | 1002 | end 1003 | \n 1004 | name 1005 | meta.say.mcfunction 1006 | patterns 1007 | 1008 | 1009 | captures 1010 | 1011 | 0 1012 | 1013 | name 1014 | constant.character.escape.mcfunction 1015 | 1016 | 1017 | match 1018 | \\\s*\n 1019 | meta 1020 | meta.say.backslash.mcfunction 1021 | 1022 | 1023 | include 1024 | #literals_string-double 1025 | 1026 | 1027 | include 1028 | #literals_string-single 1029 | 1030 | 1031 | 1032 | 1033 | begin 1034 | (run)(\s+)(say) 1035 | beginCaptures 1036 | 1037 | 1 1038 | 1039 | name 1040 | entity.name.mcfunction 1041 | 1042 | 2 1043 | 1044 | name 1045 | whitespace.mcfunction 1046 | 1047 | 3 1048 | 1049 | name 1050 | keyword.control.flow.mcfunction 1051 | 1052 | 1053 | end 1054 | \n 1055 | name 1056 | meta.say.mcfunction 1057 | patterns 1058 | 1059 | 1060 | captures 1061 | 1062 | 0 1063 | 1064 | name 1065 | constant.character.escape.mcfunction 1066 | 1067 | 1068 | match 1069 | \\\s*\n 1070 | meta 1071 | meta.say.backslash.mcfunction 1072 | 1073 | 1074 | include 1075 | #literals_string-double 1076 | 1077 | 1078 | include 1079 | #literals_string-single 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | selectors 1086 | 1087 | patterns 1088 | 1089 | 1090 | captures 1091 | 1092 | 0 1093 | 1094 | name 1095 | support.class.mcfunction 1096 | 1097 | 1098 | match 1099 | @[a-z]+ 1100 | name 1101 | meta.selectors 1102 | 1103 | 1104 | 1105 | subcommands 1106 | 1107 | patterns 1108 | 1109 | 1110 | captures 1111 | 1112 | 0 1113 | 1114 | name 1115 | entity.name.class.mcfunction 1116 | 1117 | 1118 | match 1119 | [a-z_]+ 1120 | name 1121 | meta.literals 1122 | 1123 | 1124 | 1125 | 1126 | scopeName 1127 | source.mcfunction 1128 | uuid 1129 | 8918dadd-8ebe-42d9-b1e9-0489ab228d9d 1130 | 1131 | 1132 | --------------------------------------------------------------------------------