diff --git a/.gitignore b/.gitignore
index 91d4c3a..28a376e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,9 @@
node_modules
+build
yarn-error.log
SvelteNova.novaextension/README.md
SvelteNova.novaextension/CHANGELOG.md
SvelteNova.novaextension/LICENSE
SvelteNova.novaextension/Scripts/**/*
+SvelteNova.novaextension/Syntaxes/tree-sitter-svelte.dylib
.DS_Store
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..85d152a
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "tree-sitter-svelte"]
+ path = tree-sitter-svelte
+ url = https://github.com/Himujjal/tree-sitter-svelte
diff --git a/SvelteNova.novaextension/Queries/highlights.scm b/SvelteNova.novaextension/Queries/highlights.scm
new file mode 100644
index 0000000..831f63c
--- /dev/null
+++ b/SvelteNova.novaextension/Queries/highlights.scm
@@ -0,0 +1,74 @@
+; This query file is adopted from https://github.com/Himujjal/tree-sitter-svelte/blob/master/queries/highlights.scm.
+; Nova's highlight captures are different from the original one.
+
+; ((element (start_tag (tag_name) @_tag) (text) @text.title)
+; (#match? @_tag "^(h[0-9]|title)$"))
+;
+; ((element (start_tag (tag_name) @_tag) (text) @text.strong)
+; (#match? @_tag "^(strong|b)$"))
+;
+; ((element (start_tag (tag_name) @_tag) (text) @text.emphasis)
+; (#match? @_tag "^(em|i)$"))
+;
+; ((element (start_tag (tag_name) @_tag) (text) @text.strike)
+; (#match? @_tag "^(s|del)$"))
+;
+; ((element (start_tag (tag_name) @_tag) (text) @text.underline)
+; (#eq? @_tag "u"))
+;
+; ((element (start_tag (tag_name) @_tag) (text) @text.literal)
+; (#match? @_tag "^(code|kbd)$"))
+;
+; ((element (start_tag (tag_name) @_tag) (text) @text.uri)
+; (#eq? @_tag "a"))
+
+((attribute
+ (attribute_name) @_attr
+ (quoted_attribute_value (attribute_value) @tag.attribute.value.link))
+ (#match? @_attr "^(href|src)$"))
+
+((attribute
+ (attribute_name) @tag.attribute.name
+ ["="]? @tag.attribute.operator
+ [
+ (attribute_value) @tag.attribute.value
+ (quoted_attribute_value
+ ["\"" "'"] @tag.attribute.value.delimiter.left
+ (_)? @tag.attribute.value
+ ["\"" "'"] @tag.attribute.value.delimiter.right
+ )
+ ]?
+ )
+ (#not-match? @tag.attribute.name "(?i)^(src|href)$")
+)
+
+(tag_name) @tag.name
+(erroneous_end_tag_name) @invalid
+(comment) @comment
+
+(if_start_expr (special_block_keyword) @keyword.condition)
+(else_expr (special_block_keyword) @keyword.condition)
+(else_if_expr (special_block_keyword) @keyword.condition)
+(if_end_expr (special_block_keyword) @keyword.condition)
+
+[
+ (special_block_keyword)
+ (then)
+ (as)
+] @keyword
+
+[
+ "{"
+ "}"
+ ("{" "#")
+ ("{" ":")
+ ("{" "/")
+ ("{" "@")
+] @tag.framework.bracket
+
+[
+ "<"
+ ">"
+ ""
+ "/>"
+] @tag.bracket
diff --git a/SvelteNova.novaextension/Syntaxes/Svelte.xml b/SvelteNova.novaextension/Syntaxes/Svelte.xml
index bb4ac45..c13f68e 100644
--- a/SvelteNova.novaextension/Syntaxes/Svelte.xml
+++ b/SvelteNova.novaextension/Syntaxes/Svelte.xml
@@ -9,11 +9,15 @@
svelte
html
-
+
+
+
+
+
svelte
-
+
(?x)
@@ -30,7 +34,7 @@
)
-
+
@@ -41,7 +45,7 @@
-
+
@@ -50,7 +54,7 @@
-
+
@@ -70,7 +74,7 @@
-
+
@@ -80,10 +84,10 @@
-->
-
+
-
+
&\#[0-9]+;
@@ -346,7 +350,7 @@
-
+
@@ -393,7 +397,7 @@
-
+
@@ -437,7 +441,7 @@
-
+
@@ -477,7 +481,7 @@
-
+
@@ -517,7 +521,7 @@
-
+
@@ -557,7 +561,7 @@
-
+
@@ -597,7 +601,7 @@
-
+
@@ -606,7 +610,7 @@
</([a-zA-Z_][a-zA-Z0-9_:-]*)>
-
+
@@ -639,7 +643,7 @@
-
+
@@ -669,7 +673,7 @@
-
+
@@ -767,7 +771,7 @@
-
+
@@ -794,7 +798,7 @@
-
+
@@ -873,7 +877,7 @@
-
+
@@ -903,7 +907,7 @@
-
+
@@ -928,7 +932,7 @@
-
+
@@ -986,7 +990,7 @@
-
+
@@ -1042,7 +1046,7 @@
-
+
@@ -1074,7 +1078,7 @@
-
+
@@ -1110,7 +1114,7 @@
-
+
@@ -1127,7 +1131,7 @@
-
+
@@ -1237,7 +1241,7 @@
-
+
@@ -1347,7 +1351,7 @@
-
+
@@ -1369,7 +1373,7 @@
onstorage
onundo
onunload
-
+
onblur
onchange
oncontextmenu
@@ -1380,11 +1384,11 @@
onreset
onselect
onsubmit
-
+
onkeydown
onkeypress
onkeyup
-
+
onclick
ondblclick
ondrag
@@ -1401,7 +1405,7 @@
onmouseup
onmousewheel
onscroll
-
+
onabort
oncanplay
oncanplaythrough
@@ -1440,7 +1444,7 @@
-
+
@@ -1490,7 +1494,7 @@
-
+
@@ -1507,7 +1511,7 @@
-
+
@@ -1523,7 +1527,7 @@
-
+
[a-zA-Z0-9-_]+
diff --git a/build_scripts/Makefile b/build_scripts/Makefile
new file mode 100644
index 0000000..1bd51e6
--- /dev/null
+++ b/build_scripts/Makefile
@@ -0,0 +1,65 @@
+# Repository
+SRC_DIR := src
+
+PARSER_REPO_URL ?= $(shell git -C $(SRC_DIR) remote get-url origin )
+# the # in the sed pattern has to be escaped or it will be interpreted as a comment
+PARSER_NAME ?= $(shell basename $(PARSER_REPO_URL) | cut -d '-' -f3 | sed 's\#.git\#\#')
+UPPER_PARSER_NAME := $(shell echo $(PARSER_NAME) | tr a-z A-Z )
+
+# install directory layout
+PREFIX ?= /usr/local
+INCLUDEDIR ?= $(PREFIX)/include
+LIBDIR ?= $(PREFIX)/lib
+
+# collect sources, and link if necessary
+# Some Tree Sitter grammars include .cc files directly in others,
+# so we shouldn't just wildcard select them all.
+# Only collect known file names.
+ifneq ("$(wildcard $(SRC_DIR)/parser.c)", "")
+ SRC += $(SRC_DIR)/parser.c
+endif
+ifneq ("$(wildcard $(SRC_DIR)/scanner.c)", "")
+ SRC += $(SRC_DIR)/scanner.c
+endif
+ifneq ("$(wildcard $(SRC_DIR)/parser.cc)", "")
+ CPPSRC += $(SRC_DIR)/parser.cc
+endif
+ifneq ("$(wildcard $(SRC_DIR)/scanner.cc)", "")
+ CPPSRC += $(SRC_DIR)/scanner.cc
+endif
+
+ifeq (, $(CPPSRC))
+ ADDITIONALLIBS :=
+else
+ ADDITIONALLIBS := -lc++
+endif
+
+SRC += $(CPPSRC)
+OBJ := $(addsuffix .o,$(basename $(SRC)))
+
+CFLAGS ?= -O3 -Wall -Wextra -I$(SRC_DIR)
+CXXFLAGS ?= -O3 -Wall -Wextra -I$(SRC_DIR)
+override CFLAGS += -std=gnu99 -fPIC
+override CXXFLAGS += -fPIC
+
+LINKSHARED := $(LINKSHARED)-dynamiclib -Wl,
+ifneq ($(ADDITIONALLIBS),)
+ LINKSHARED := $(LINKSHARED)$(ADDITIONALLIBS),
+endif
+LINKSHARED := $(LINKSHARED)-install_name,$(LIBDIR)/libtree-sitter-$(PARSER_NAME).dylib,-rpath,@executable_path/../Frameworks
+
+all: libtree-sitter-$(PARSER_NAME).dylib
+
+libtree-sitter-$(PARSER_NAME).dylib: $(OBJ)
+ $(CC) $(LDFLAGS) $(LINKSHARED) $^ $(LDLIBS) -o $@
+
+install: all
+ install -d '$(DESTDIR)$(LIBDIR)'
+ install -m755 libtree-sitter-$(PARSER_NAME).dylib '$(DESTDIR)$(LIBDIR)'/libtree-sitter-$(PARSER_NAME).dylib
+ install -d '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter
+
+clean:
+ rm -f $(OBJ) libtree-sitter-$(PARSER_NAME).dylib
+ rm -rf build/
+
+.PHONY: all install clean
diff --git a/build_scripts/README.md b/build_scripts/README.md
new file mode 100644
index 0000000..e6c061c
--- /dev/null
+++ b/build_scripts/README.md
@@ -0,0 +1,16 @@
+# `build_scripts`
+
+Both `compile_parser.sh` and `Makefile` are provided by Panic
+[on their doc site](https://docs.nova.app/syntax-reference/tree-sitter/#compiling-a-parser),
+and may be retrieved from
+[here](https://docs.nova.app/syntax-reference/build_script.zip).
+
+Run `build_parser.sh` (from project root) to build the
+`tree-sitter-svelte.dylib`:
+
+```sh
+./build_scripts/build_parser.sh /Applications/Nova.app
+```
+
+You may need to adjust the path to `Nova.app`. It will also copy the build
+output to correct place and cleanup temp files.
diff --git a/build_scripts/build_parser.sh b/build_scripts/build_parser.sh
new file mode 100755
index 0000000..6c3afdb
--- /dev/null
+++ b/build_scripts/build_parser.sh
@@ -0,0 +1,18 @@
+#!/bin/zsh
+
+# This script is supposed to be ran from the project root.
+# ./build_scripts/build_parser.sh path/to/Nova.app
+
+NOVAAPP=$1
+
+cp build_scripts/Makefile tree-sitter-svelte/
+
+pushd tree-sitter-svelte
+../build_scripts/compile_parser.sh . $NOVAAPP
+popd
+
+mv tree-sitter-svelte/build/lib/libtree-sitter-svelte.dylib SvelteNova.novaextension/Syntaxes/tree-sitter-svelte.dylib
+
+# Clean up the submodule folder to avoid problems
+rm -rf tree-sitter-svelte/build
+rm tree-sitter-svelte/Makefile
diff --git a/build_scripts/compile_parser.sh b/build_scripts/compile_parser.sh
new file mode 100755
index 0000000..2490fbf
--- /dev/null
+++ b/build_scripts/compile_parser.sh
@@ -0,0 +1,25 @@
+#!/bin/zsh
+set -euxo pipefail
+
+BASEDIR=$1
+APPBUNDLE=$2
+FRAMEWORKS_PATH="${APPBUNDLE}/Contents/Frameworks/"
+WORKINGDIR=$(pwd)
+
+# - Build both arm64 (Apple Silicon) and x86_64 (Intel)
+# - Require a minimum of macOS 11.0
+# - Include the /src/ directory for headers (for `tree_sitter/parser.h`)
+BUILD_FLAGS="-arch arm64 -arch x86_64 -mmacosx-version-min=11.0 -I${BASEDIR}/src/"
+
+# Build in a temporary `build/` directory.
+TMP_BUILD_DIR=$WORKINGDIR/build
+mkdir -p $TMP_BUILD_DIR
+
+pushd $BASEDIR
+
+CFLAGS="${BUILD_FLAGS} -O3" \
+CXXFLAGS="${BUILD_FLAGS} -O3" \
+LDFLAGS="${BUILD_FLAGS} -F${FRAMEWORKS_PATH} -framework SyntaxKit -rpath @loader_path/../Frameworks" \
+PREFIX="$TMP_BUILD_DIR" make install
+
+popd
diff --git a/tree-sitter-svelte b/tree-sitter-svelte
new file mode 160000
index 0000000..52e122a
--- /dev/null
+++ b/tree-sitter-svelte
@@ -0,0 +1 @@
+Subproject commit 52e122ae68b316d3aa960a0a422d3645ba717f42