diff --git a/.editorconfig b/.editorconfig
index d3a8b5b..65330c4 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -2,9 +2,6 @@ root = true
 
 [*]
 charset = utf-8
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
 
 [*.{json,toml,yml,gyp}]
 indent_style = space
@@ -14,14 +11,18 @@ indent_size = 2
 indent_style = space
 indent_size = 2
 
-[*.rs]
+[*.scm]
 indent_style = space
-indent_size = 4
+indent_size = 2
 
 [*.{c,cc,h}]
 indent_style = space
 indent_size = 4
 
+[*.rs]
+indent_style = space
+indent_size = 4
+
 [*.{py,pyi}]
 indent_style = space
 indent_size = 4
@@ -37,3 +38,9 @@ indent_size = 8
 [Makefile]
 indent_style = tab
 indent_size = 8
+
+[parser.c]
+indent_size = 2
+
+[{alloc,array,parser}.h]
+indent_size = 2
diff --git a/.gitattributes b/.gitattributes
index ffb52ab..7e2cae0 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,11 +1,37 @@
-* text eol=lf
+* text=auto eol=lf
 
+# Generated source files
 src/*.json linguist-generated
 src/parser.c linguist-generated
 src/tree_sitter/* linguist-generated
 
-bindings/** linguist-generated
+# C bindings
+bindings/c/* linguist-generated
+CMakeLists.txt linguist-generated
+Makefile linguist-generated
+
+# Rust bindings
+bindings/rust/* linguist-generated
+Cargo.toml linguist-generated
+Cargo.lock linguist-generated
+
+# Node.js bindings
+bindings/node/* linguist-generated
 binding.gyp linguist-generated
+package.json linguist-generated
+package-lock.json linguist-generated
+
+# Python bindings
+bindings/python/** linguist-generated
 setup.py linguist-generated
-Makefile linguist-generated
+pyproject.toml linguist-generated
+
+# Go bindings
+bindings/go/* linguist-generated
+go.mod linguist-generated
+go.sum linguist-generated
+
+# Swift bindings
+bindings/swift/** linguist-generated
 Package.swift linguist-generated
+Package.resolved linguist-generated
diff --git a/.gitignore b/.gitignore
index fe416c9..308fcab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
 # Rust artifacts
-Cargo.lock
 target/
 
 # Node artifacts
@@ -10,7 +9,11 @@ node_modules/
 # Swift artifacts
 .build/
 
+# Go artifacts
+_obj/
+
 # Python artifacts
+.venv/
 dist/
 *.egg-info
 *.whl
@@ -23,10 +26,15 @@ dist/
 *.dll
 *.pc
 
-# Examples
+# Example dirs
 /examples/*/
 
 # Grammar volatiles
 *.wasm
 *.obj
 *.o
+
+# Archives
+*.tar.gz
+*.tgz
+*.zip
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..e9b5503
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,55 @@
+cmake_minimum_required(VERSION 3.13)
+
+project(tree-sitter-vimdoc
+        VERSION "3.0.0"
+        DESCRIPTION "Vim help file grammar for tree-sitter"
+        HOMEPAGE_URL "https://github.com/neovim/tree-sitter-vimdoc"
+        LANGUAGES C)
+
+option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
+option(TREE_SITTER_REUSE_ALLOCATOR "Reuse the library allocator" OFF)
+
+set(TREE_SITTER_ABI_VERSION 14 CACHE STRING "Tree-sitter ABI version")
+if(NOT ${TREE_SITTER_ABI_VERSION} MATCHES "^[0-9]+$")
+    unset(TREE_SITTER_ABI_VERSION CACHE)
+    message(FATAL_ERROR "TREE_SITTER_ABI_VERSION must be an integer")
+endif()
+
+find_program(TREE_SITTER_CLI tree-sitter DOC "Tree-sitter CLI")
+
+add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c"
+                   DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json"
+                   COMMAND "${TREE_SITTER_CLI}" generate src/grammar.json
+                            --abi=${TREE_SITTER_ABI_VERSION}
+                   WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+                   COMMENT "Generating parser.c")
+
+add_library(tree-sitter-vimdoc src/parser.c)
+target_include_directories(tree-sitter-vimdoc PRIVATE src)
+
+target_compile_definitions(tree-sitter-vimdoc PRIVATE
+                           $<$<BOOL:${TREE_SITTER_REUSE_ALLOCATOR}>:TREE_SITTER_REUSE_ALLOCATOR>
+                           $<$<CONFIG:Debug>:TREE_SITTER_DEBUG>)
+
+set_target_properties(tree-sitter-vimdoc
+                      PROPERTIES
+                      C_STANDARD 11
+                      POSITION_INDEPENDENT_CODE ON
+                      SOVERSION "${TREE_SITTER_ABI_VERSION}.${PROJECT_VERSION_MAJOR}"
+                      DEFINE_SYMBOL "")
+
+configure_file(bindings/c/tree-sitter-vimdoc.pc.in
+               "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-vimdoc.pc" @ONLY)
+
+include(GNUInstallDirs)
+
+install(FILES bindings/c/tree-sitter-vimdoc.h
+        DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/tree_sitter")
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-vimdoc.pc"
+        DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig")
+install(TARGETS tree-sitter-vimdoc
+        LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+
+add_custom_target(test "${TREE_SITTER_CLI}" test
+                  WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+                  COMMENT "tree-sitter test")
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..5a68646
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,96 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "cc"
+version = "1.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "regex"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
+
+[[package]]
+name = "tree-sitter"
+version = "0.24.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9871f16d6cf5c4757dcf30d5d2172a2df6987c510c017bbb7abfb7f9aa24d06"
+dependencies = [
+ "cc",
+ "regex",
+ "regex-syntax",
+ "streaming-iterator",
+ "tree-sitter-language",
+]
+
+[[package]]
+name = "tree-sitter-language"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8ddffe35a0e5eeeadf13ff7350af564c6e73993a24db62caee1822b185c2600"
+
+[[package]]
+name = "tree-sitter-vimdoc"
+version = "3.0.0"
+dependencies = [
+ "cc",
+ "tree-sitter",
+ "tree-sitter-language",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 9a2069d..71ac515 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,11 +2,12 @@
 name = "tree-sitter-vimdoc"
 description = "Vim help file grammar for tree-sitter"
 version = "3.0.0"
+authors = ["Thomas Vigouroux <tomvig38@gmail.com>"]
 license = "Apache-2.0"
-keywords = ["incremental", "parsing", "neovim", "vimdoc"]
+readme = "README.md"
+keywords = ["incremental", "parsing", "tree-sitter", "vimdoc"]
 categories = ["parsing", "text-editors"]
 repository = "https://github.com/neovim/tree-sitter-vimdoc"
-authors = ["Thomas Vigouroux <tomvig38@gmail.com>"]
 edition = "2021"
 autoexamples = false
 
@@ -17,7 +18,10 @@ include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*"]
 path = "bindings/rust/lib.rs"
 
 [dependencies]
-tree-sitter = ">=0.21.0"
+tree-sitter-language = "0.1"
 
 [build-dependencies]
-cc = "^1.0.89"
+cc = "1.1.22"
+
+[dev-dependencies]
+tree-sitter = "0.24.3"
diff --git a/Makefile b/Makefile
index a1fa41a..63157e5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,26 +1,16 @@
-VERSION := 3.0.0
+ifeq ($(OS),Windows_NT)
+$(error Windows is not supported)
+endif
 
 LANGUAGE_NAME := tree-sitter-vimdoc
+HOMEPAGE_URL := https://github.com/neovim/tree-sitter-vimdoc
+VERSION := 3.0.0
 
 # repository
 SRC_DIR := src
 
-PARSER_REPO_URL := $(shell git -C $(SRC_DIR) remote get-url origin 2>/dev/null)
-
-ifeq ($(PARSER_URL),)
-	PARSER_URL := $(subst .git,,$(PARSER_REPO_URL))
-ifeq ($(shell echo $(PARSER_URL) | grep '^[a-z][-+.0-9a-z]*://'),)
-	PARSER_URL := $(subst :,/,$(PARSER_URL))
-	PARSER_URL := $(subst git@,https://,$(PARSER_URL))
-endif
-endif
-
 TS ?= tree-sitter
 
-# ABI versioning
-SONAME_MAJOR := $(word 1,$(subst ., ,$(VERSION)))
-SONAME_MINOR := $(word 2,$(subst ., ,$(VERSION)))
-
 # install directory layout
 PREFIX ?= /usr/local
 INCLUDEDIR ?= $(PREFIX)/include
@@ -28,34 +18,29 @@ LIBDIR ?= $(PREFIX)/lib
 PCLIBDIR ?= $(LIBDIR)/pkgconfig
 
 # source/object files
-SRCS := $(wildcard $(SRC_DIR)/*.c)
-OBJS := $(patsubst %.c,%.o,$(SRCS))
+PARSER := $(SRC_DIR)/parser.c
+EXTRAS := $(filter-out $(PARSER),$(wildcard $(SRC_DIR)/*.c))
+OBJS := $(patsubst %.c,%.o,$(PARSER) $(EXTRAS))
 
 # flags
-ARFLAGS := rcs
+ARFLAGS ?= rcs
 override CFLAGS += -I$(SRC_DIR) -std=c11 -fPIC
 
+# ABI versioning
+SONAME_MAJOR = $(shell sed -n 's/\#define LANGUAGE_VERSION //p' $(PARSER))
+SONAME_MINOR = $(word 1,$(subst ., ,$(VERSION)))
+
 # OS-specific bits
-ifeq ($(OS),Windows_NT)
-	$(error "Windows is not supported")
-else ifeq ($(shell uname),Darwin)
+ifeq ($(shell uname),Darwin)
 	SOEXT = dylib
-	SOEXTVER_MAJOR = $(SONAME_MAJOR).dylib
-	SOEXTVER = $(SONAME_MAJOR).$(SONAME_MINOR).dylib
-	LINKSHARED := $(LINKSHARED)-dynamiclib -Wl,
-	ifneq ($(ADDITIONAL_LIBS),)
-	LINKSHARED := $(LINKSHARED)$(ADDITIONAL_LIBS),
-	endif
-	LINKSHARED := $(LINKSHARED)-install_name,$(LIBDIR)/lib$(LANGUAGE_NAME).$(SONAME_MAJOR).dylib,-rpath,@executable_path/../Frameworks
+	SOEXTVER_MAJOR = $(SONAME_MAJOR).$(SOEXT)
+	SOEXTVER = $(SONAME_MAJOR).$(SONAME_MINOR).$(SOEXT)
+	LINKSHARED = -dynamiclib -Wl,-install_name,$(LIBDIR)/lib$(LANGUAGE_NAME).$(SOEXTVER),-rpath,@executable_path/../Frameworks
 else
 	SOEXT = so
-	SOEXTVER_MAJOR = so.$(SONAME_MAJOR)
-	SOEXTVER = so.$(SONAME_MAJOR).$(SONAME_MINOR)
-	LINKSHARED := $(LINKSHARED)-shared -Wl,
-	ifneq ($(ADDITIONAL_LIBS),)
-	LINKSHARED := $(LINKSHARED)$(ADDITIONAL_LIBS)
-	endif
-	LINKSHARED := $(LINKSHARED)-soname,lib$(LANGUAGE_NAME).so.$(SONAME_MAJOR)
+	SOEXTVER_MAJOR = $(SOEXT).$(SONAME_MAJOR)
+	SOEXTVER = $(SOEXT).$(SONAME_MAJOR).$(SONAME_MINOR)
+	LINKSHARED = -shared -Wl,-soname,lib$(LANGUAGE_NAME).$(SOEXTVER)
 endif
 ifneq ($(filter $(shell uname),FreeBSD NetBSD DragonFly),)
 	PCLIBDIR := $(PREFIX)/libdata/pkgconfig
@@ -73,22 +58,21 @@ ifneq ($(STRIP),)
 endif
 
 $(LANGUAGE_NAME).pc: bindings/c/$(LANGUAGE_NAME).pc.in
-	sed  -e 's|@URL@|$(PARSER_URL)|' \
-		-e 's|@VERSION@|$(VERSION)|' \
-		-e 's|@LIBDIR@|$(LIBDIR)|' \
-		-e 's|@INCLUDEDIR@|$(INCLUDEDIR)|' \
-		-e 's|@REQUIRES@|$(REQUIRES)|' \
-		-e 's|@ADDITIONAL_LIBS@|$(ADDITIONAL_LIBS)|' \
-		-e 's|=$(PREFIX)|=$${prefix}|' \
-		-e 's|@PREFIX@|$(PREFIX)|' $< > $@
-
-$(SRC_DIR)/parser.c: grammar.js
-	$(TS) generate --no-bindings
+	sed -e 's|@PROJECT_VERSION@|$(VERSION)|' \
+		-e 's|@CMAKE_INSTALL_LIBDIR@|$(LIBDIR:$(PREFIX)/%=%)|' \
+		-e 's|@CMAKE_INSTALL_INCLUDEDIR@|$(INCLUDEDIR:$(PREFIX)/%=%)|' \
+		-e 's|@PROJECT_DESCRIPTION@|$(DESCRIPTION)|' \
+		-e 's|@PROJECT_HOMEPAGE_URL@|$(HOMEPAGE_URL)|' \
+		-e 's|@CMAKE_INSTALL_PREFIX@|$(PREFIX)|' $< > $@
+
+$(PARSER): $(SRC_DIR)/grammar.json
+	$(TS) generate $^
 
 install: all
-	install -Dm644 bindings/c/$(LANGUAGE_NAME).h '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h
-	install -Dm644 $(LANGUAGE_NAME).pc '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc
-	install -Dm755 lib$(LANGUAGE_NAME).a '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a
+	install -d '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter '$(DESTDIR)$(PCLIBDIR)' '$(DESTDIR)$(LIBDIR)'
+	install -m644 bindings/c/$(LANGUAGE_NAME).h '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h
+	install -m644 $(LANGUAGE_NAME).pc '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc
+	install -m644 lib$(LANGUAGE_NAME).a '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a
 	install -m755 lib$(LANGUAGE_NAME).$(SOEXT) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER)
 	ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR)
 	ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT)
@@ -108,7 +92,7 @@ test:
 	$(TS) test
 
 parser/vimdoc.so: CFLAGS += -Os -shared
-parser/vimdoc.so: $(SRCS)
+parser/vimdoc.so: $(PARSER) $(EXTRAS)
 	@mkdir -p parser
 	$(CC) $(CFLAGS) $^ -o $@
 
diff --git a/Package.resolved b/Package.resolved
new file mode 100644
index 0000000..9e0a023
--- /dev/null
+++ b/Package.resolved
@@ -0,0 +1,16 @@
+{
+  "object": {
+    "pins": [
+      {
+        "package": "SwiftTreeSitter",
+        "repositoryURL": "https://github.com/ChimeHQ/SwiftTreeSitter",
+        "state": {
+          "branch": null,
+          "revision": "2599e95310b3159641469d8a21baf2d3d200e61f",
+          "version": "0.8.0"
+        }
+      }
+    ]
+  },
+  "version": 1
+}
diff --git a/Package.swift b/Package.swift
index c039847..25432c5 100644
--- a/Package.swift
+++ b/Package.swift
@@ -3,45 +3,34 @@ import PackageDescription
 
 let package = Package(
     name: "TreeSitterVimdoc",
-    platforms: [.macOS(.v10_13), .iOS(.v11)],
     products: [
         .library(name: "TreeSitterVimdoc", targets: ["TreeSitterVimdoc"]),
     ],
-    dependencies: [],
+    dependencies: [
+        .package(url: "https://github.com/ChimeHQ/SwiftTreeSitter", from: "0.8.0"),
+    ],
     targets: [
-        .target(name: "TreeSitterVimdoc",
-                path: ".",
-                exclude: [
-                    "Cargo.toml",
-                    "Makefile",
-                    "binding.gyp",
-                    "bindings/c",
-                    "bindings/go",
-                    "bindings/node",
-                    "bindings/python",
-                    "bindings/rust",
-                    "prebuilds",
-                    "node_modules",
-                    "grammar.js",
-                    "package.json",
-                    "package-lock.json",
-                    "pyproject.toml",
-                    "setup.py",
-                    "test",
-                    "examples",
-                    ".editorconfig",
-                    ".github",
-                    ".gitignore",
-                    ".gitattributes",
-                ],
-                sources: [
-                    "src/parser.c",
-                ],
-                resources: [
-                    .copy("queries")
-                ],
-                publicHeadersPath: "bindings/swift",
-                cSettings: [.headerSearchPath("src")])
+        .target(
+            name: "TreeSitterVimdoc",
+            dependencies: [],
+            path: ".",
+            sources: [
+                "src/parser.c",
+            ],
+            resources: [
+                .copy("queries")
+            ],
+            publicHeadersPath: "bindings/swift",
+            cSettings: [.headerSearchPath("src")]
+        ),
+        .testTarget(
+            name: "TreeSitterVimdocTests",
+            dependencies: [
+                "SwiftTreeSitter",
+                "TreeSitterVimdoc",
+            ],
+            path: "bindings/swift/TreeSitterVimdocTests"
+        )
     ],
     cLanguageStandard: .c11
 )
diff --git a/README.md b/README.md
index b09f09c..23827fc 100644
--- a/README.md
+++ b/README.md
@@ -65,7 +65,7 @@ Steps to perform a release:
 
 1. Bump and tag the version:
 
-   **First** bump `Cargo.toml`, `pyproject.toml`, and `Makefile` to the new version. **Then** bump the package:
+   **First** bump `Cargo.toml`, `Cargo.lock`, `pyproject.toml`, and `Makefile` to the new version. **Then** bump the package:
    ```bash
    npm version patch -m "release %s"
    ```
@@ -78,7 +78,7 @@ Steps to perform a release:
 2. Bump to prerelease, without creating a tag:
    ```bash
    npm version --no-git-tag-version prerelease --preid dev
-   git add package*.json Cargo.toml pyproject.toml Makefile
+   git add package*.json Cargo.* pyproject.toml Makefile
    git commit -m bump
    ```
 3. Push:
diff --git a/binding.gyp b/binding.gyp
index a0290f2..b3a7bbd 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -12,8 +12,17 @@
         "bindings/node/binding.cc",
         "src/parser.c",
       ],
-      "cflags_c": [
-        "-std=c11",
+      "conditions": [
+        ["OS!='win'", {
+          "cflags_c": [
+            "-std=c11",
+          ],
+        }, { # OS == "win"
+          "cflags_c": [
+            "/std:c11",
+            "/utf-8",
+          ],
+        }],
       ],
     }
   ]
diff --git a/bindings/c/tree-sitter-vimdoc.pc.in b/bindings/c/tree-sitter-vimdoc.pc.in
index e77d53a..682c7e0 100644
--- a/bindings/c/tree-sitter-vimdoc.pc.in
+++ b/bindings/c/tree-sitter-vimdoc.pc.in
@@ -1,11 +1,10 @@
-prefix=@PREFIX@
-libdir=@LIBDIR@
-includedir=@INCLUDEDIR@
+prefix=@CMAKE_INSTALL_PREFIX@
+libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
+includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
 
 Name: tree-sitter-vimdoc
-Description: Vim help file grammar for tree-sitter
-URL: @URL@
-Version: @VERSION@
-Requires: @REQUIRES@
-Libs: -L${libdir} @ADDITIONAL_LIBS@ -ltree-sitter-vimdoc
+Description: @PROJECT_DESCRIPTION@
+URL: @PROJECT_HOMEPAGE_URL@
+Version: @PROJECT_VERSION@
+Libs: -L${libdir} -ltree-sitter-vimdoc
 Cflags: -I${includedir}
diff --git a/bindings/go/binding_test.go b/bindings/go/binding_test.go
index 647e2b0..a18152b 100644
--- a/bindings/go/binding_test.go
+++ b/bindings/go/binding_test.go
@@ -3,8 +3,8 @@ package tree_sitter_vimdoc_test
 import (
 	"testing"
 
-	tree_sitter "github.com/smacker/go-tree-sitter"
-	"github.com/neovim/tree-sitter-vimdoc"
+	tree_sitter "github.com/tree-sitter/go-tree-sitter"
+	tree_sitter_vimdoc "github.com/neovim/tree-sitter-vimdoc/bindings/go"
 )
 
 func TestCanLoadGrammar(t *testing.T) {
diff --git a/bindings/go/go.mod b/bindings/go/go.mod
deleted file mode 100644
index e527451..0000000
--- a/bindings/go/go.mod
+++ /dev/null
@@ -1,5 +0,0 @@
-module github.com/neovim/tree-sitter-vimdoc
-
-go 1.22
-
-require github.com/smacker/go-tree-sitter v0.0.0-20230720070738-0d0a9f78d8f8
diff --git a/bindings/node/binding.cc b/bindings/node/binding.cc
index 54f8561..cf11048 100644
--- a/bindings/node/binding.cc
+++ b/bindings/node/binding.cc
@@ -6,7 +6,7 @@ extern "C" TSLanguage *tree_sitter_vimdoc();
 
 // "tree-sitter", "language" hashed with BLAKE2
 const napi_type_tag LANGUAGE_TYPE_TAG = {
-  0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16
+    0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16
 };
 
 Napi::Object Init(Napi::Env env, Napi::Object exports) {
diff --git a/bindings/node/binding_test.js b/bindings/node/binding_test.js
new file mode 100644
index 0000000..afede30
--- /dev/null
+++ b/bindings/node/binding_test.js
@@ -0,0 +1,9 @@
+/// <reference types="node" />
+
+const assert = require("node:assert");
+const { test } = require("node:test");
+
+test("can load grammar", () => {
+  const parser = new (require("tree-sitter"))();
+  assert.doesNotThrow(() => parser.setLanguage(require(".")));
+});
diff --git a/bindings/python/tests/test_binding.py b/bindings/python/tests/test_binding.py
new file mode 100644
index 0000000..46f6406
--- /dev/null
+++ b/bindings/python/tests/test_binding.py
@@ -0,0 +1,11 @@
+from unittest import TestCase
+
+import tree_sitter, tree_sitter_vimdoc
+
+
+class TestLanguage(TestCase):
+    def test_can_load_grammar(self):
+        try:
+            tree_sitter.Language(tree_sitter_vimdoc.language())
+        except Exception:
+            self.fail("Error loading Vimdoc grammar")
diff --git a/bindings/python/tree_sitter_vimdoc/__init__.py b/bindings/python/tree_sitter_vimdoc/__init__.py
index f25b80c..0182c70 100644
--- a/bindings/python/tree_sitter_vimdoc/__init__.py
+++ b/bindings/python/tree_sitter_vimdoc/__init__.py
@@ -1,3 +1,34 @@
-"Vim help file grammar for tree-sitter"
+"""Vim help file grammar for tree-sitter"""
+
+from importlib.resources import files as _files
 
 from ._binding import language
+
+
+def _get_query(name, file):
+    query = _files(f"{__package__}.queries.vimdoc") / file
+    globals()[name] = query.read_text()
+    return globals()[name]
+
+
+def __getattr__(name):
+    if name == "HIGHLIGHTS_QUERY":
+        return _get_query("HIGHLIGHTS_QUERY", "highlights.scm")
+    if name == "INJECTIONS_QUERY":
+        return _get_query("INJECTIONS_QUERY", "injections.scm")
+
+    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+
+
+__all__ = [
+    "language",
+    "HIGHLIGHTS_QUERY",
+    "INJECTIONS_QUERY",
+]
+
+
+def __dir__():
+    return sorted(__all__ + [
+        "__all__", "__builtins__", "__cached__", "__doc__", "__file__",
+        "__loader__", "__name__", "__package__", "__path__", "__spec__",
+    ])
diff --git a/bindings/python/tree_sitter_vimdoc/__init__.pyi b/bindings/python/tree_sitter_vimdoc/__init__.pyi
index 5416666..48688e6 100644
--- a/bindings/python/tree_sitter_vimdoc/__init__.pyi
+++ b/bindings/python/tree_sitter_vimdoc/__init__.pyi
@@ -1 +1,6 @@
-def language() -> int: ...
+from typing import Final
+
+HIGHLIGHTS_QUERY: Final[str]
+INJECTIONS_QUERY: Final[str]
+
+def language() -> object: ...
diff --git a/bindings/python/tree_sitter_vimdoc/binding.c b/bindings/python/tree_sitter_vimdoc/binding.c
index d1f9000..c5f5df6 100644
--- a/bindings/python/tree_sitter_vimdoc/binding.c
+++ b/bindings/python/tree_sitter_vimdoc/binding.c
@@ -4,8 +4,8 @@ typedef struct TSLanguage TSLanguage;
 
 TSLanguage *tree_sitter_vimdoc(void);
 
-static PyObject* _binding_language(PyObject *self, PyObject *args) {
-    return PyLong_FromVoidPtr(tree_sitter_vimdoc());
+static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) {
+    return PyCapsule_New(tree_sitter_vimdoc(), "tree_sitter.Language", NULL);
 }
 
 static PyMethodDef methods[] = {
diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs
index 51ef099..4315264 100644
--- a/bindings/rust/build.rs
+++ b/bindings/rust/build.rs
@@ -2,7 +2,7 @@ fn main() {
     let src_dir = std::path::Path::new("src");
 
     let mut c_config = cc::Build::new();
-    c_config.include(src_dir);
+    c_config.std("c11").include(src_dir);
 
     #[cfg(target_env = "msvc")]
     c_config.flag("-utf-8");
diff --git a/bindings/rust/lib.rs b/bindings/rust/lib.rs
index c0c7e88..fa7581b 100644
--- a/bindings/rust/lib.rs
+++ b/bindings/rust/lib.rs
@@ -1,6 +1,6 @@
-//! This crate provides vimdoc language support for the [tree-sitter][] parsing library.
+//! This crate provides Vimdoc language support for the [tree-sitter][] parsing library.
 //!
-//! Typically, you will use the [language][language func] function to add this language to a
+//! Typically, you will use the [LANGUAGE][] constant to add this language to a
 //! tree-sitter [Parser][], and then use the parser to parse some code:
 //!
 //! ```
@@ -12,37 +12,40 @@
 //!             • "l" |linewise|
 //!             • "listitem"
 //!   {consumes} adjacent
+//!
 //! "#;
 //! let mut parser = tree_sitter::Parser::new();
-//! parser.set_language(&tree_sitter_vimdoc::language()).expect("Error loading vimdoc grammar");
+//! let language = tree_sitter_vimdoc::LANGUAGE;
+//! parser
+//!     .set_language(&language.into())
+//!     .expect("Error loading Vimdoc parser");
 //! let tree = parser.parse(code, None).unwrap();
+//! assert!(!tree.root_node().has_error());
 //! ```
 //!
-//! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
-//! [language func]: fn.language.html
 //! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
 //! [tree-sitter]: https://tree-sitter.github.io/
 
-use tree_sitter::Language;
+use tree_sitter_language::LanguageFn;
 
 extern "C" {
-    fn tree_sitter_vimdoc() -> Language;
+    fn tree_sitter_vimdoc() -> *const ();
 }
 
-/// Get the tree-sitter [Language][] for this grammar.
+/// The tree-sitter [`LanguageFn`][LanguageFn] for this grammar.
 ///
-/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
-pub fn language() -> Language {
-    unsafe { tree_sitter_vimdoc() }
-}
+/// [LanguageFn]: https://docs.rs/tree-sitter-language/*/tree_sitter_language/struct.LanguageFn.html
+pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_vimdoc) };
 
 /// The content of the [`node-types.json`][] file for this grammar.
 ///
 /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
 pub const NODE_TYPES: &str = include_str!("../../src/node-types.json");
 
+/// The syntax highlight queries for this grammar.
 pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/vimdoc/highlights.scm");
 
+/// The language injection queries for this grammar.
 pub const INJECTIONS_QUERY: &str = include_str!("../../queries/vimdoc/injections.scm");
 
 #[cfg(test)]
@@ -51,7 +54,7 @@ mod tests {
     fn test_can_load_grammar() {
         let mut parser = tree_sitter::Parser::new();
         parser
-            .set_language(&super::language())
-            .expect("Error loading Vimdoc language");
+            .set_language(&super::LANGUAGE.into())
+            .expect("Error loading Vimdoc parser");
     }
 }
diff --git a/bindings/swift/TreeSitterVimdocTests/TreeSitterVimdocTests.swift b/bindings/swift/TreeSitterVimdocTests/TreeSitterVimdocTests.swift
new file mode 100644
index 0000000..3977605
--- /dev/null
+++ b/bindings/swift/TreeSitterVimdocTests/TreeSitterVimdocTests.swift
@@ -0,0 +1,12 @@
+import XCTest
+import SwiftTreeSitter
+import TreeSitterVimdoc
+
+final class TreeSitterVimdocTests: XCTestCase {
+    func testCanLoadGrammar() throws {
+        let parser = Parser()
+        let language = Language(language: tree_sitter_vimdoc())
+        XCTAssertNoThrow(try parser.setLanguage(language),
+                         "Error loading Vimdoc grammar")
+    }
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..f514e9c
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,9 @@
+module github.com/neovim/tree-sitter-vimdoc
+
+go 1.23
+
+toolchain go1.23.0
+
+require github.com/tree-sitter/go-tree-sitter v0.23.1
+
+require github.com/mattn/go-pointer v0.0.1 // indirect
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..d775361
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,36 @@
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
+github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/tree-sitter/go-tree-sitter v0.23.1 h1:HCfaE19sKfG7q190xfM1loUZf6wEHa4TDqDEW46s9Lg=
+github.com/tree-sitter/go-tree-sitter v0.23.1/go.mod h1:EvIVhMvvPNvhu9x+ddSPxSnUEU5AnsSwi1LMqXIVE3A=
+github.com/tree-sitter/tree-sitter-c v0.21.5-0.20240818205408-927da1f210eb h1:A8425heRM8mylnv4H58FPUiH+aYivyitre0PzxrfmWs=
+github.com/tree-sitter/tree-sitter-c v0.21.5-0.20240818205408-927da1f210eb/go.mod h1:dOF6gtQiF9UwNh995T5OphYmtIypkjsp3ap7r9AN/iA=
+github.com/tree-sitter/tree-sitter-cpp v0.22.4-0.20240818224355-b1a4e2b25148 h1:AfFPZwtwGN01BW1jDdqBVqscTwetvMpydqYZz57RSlc=
+github.com/tree-sitter/tree-sitter-cpp v0.22.4-0.20240818224355-b1a4e2b25148/go.mod h1:Bh6U3viD57rFXRYIQ+kmiYtr+1Bx0AceypDLJJSyi9s=
+github.com/tree-sitter/tree-sitter-embedded-template v0.21.1-0.20240819044651-ffbf64942c33 h1:TwqSV3qLp3tKSqirGLRHnjFk9Tc2oy57LIl+FQ4GjI4=
+github.com/tree-sitter/tree-sitter-embedded-template v0.21.1-0.20240819044651-ffbf64942c33/go.mod h1:CvCKCt3v04Ufos1zZnNCelBDeCGRpPucaN8QczoUsN4=
+github.com/tree-sitter/tree-sitter-go v0.21.3-0.20240818010209-8c0f0e7a6012 h1:Xvxck3tE5FW7F7bTS97iNM2ADMyCMJztVqn5HYKdJGo=
+github.com/tree-sitter/tree-sitter-go v0.21.3-0.20240818010209-8c0f0e7a6012/go.mod h1:T40D0O1cPvUU/+AmiXVXy1cncYQT6wem4Z0g4SfAYvY=
+github.com/tree-sitter/tree-sitter-html v0.20.5-0.20240818004741-d11201a263d0 h1:c46K6uh5Dz00zJeU9BfjXdb8I+E4RkUdfnWJpQADXFo=
+github.com/tree-sitter/tree-sitter-html v0.20.5-0.20240818004741-d11201a263d0/go.mod h1:hcNt/kOJHcIcuMvouE7LJcYdeFUFbVpBJ6d4wmOA+tU=
+github.com/tree-sitter/tree-sitter-java v0.21.1-0.20240824015150-576d8097e495 h1:jrt4qbJVEFs4H93/ITxygHc6u0TGqAkkate7TQ4wFSA=
+github.com/tree-sitter/tree-sitter-java v0.21.1-0.20240824015150-576d8097e495/go.mod h1:oyaR7fLnRV0hT9z6qwE9GkaeTom/hTDwK3H2idcOJFc=
+github.com/tree-sitter/tree-sitter-javascript v0.21.5-0.20240818005344-15887341e5b5 h1:om4X9AVg3asL8gxNJDcz4e/Wp+VpQj1PY3uJXKr6EOg=
+github.com/tree-sitter/tree-sitter-javascript v0.21.5-0.20240818005344-15887341e5b5/go.mod h1:nNqgPoV/h9uYWk6kYEFdEAhNVOacpfpRW5SFmdaP4tU=
+github.com/tree-sitter/tree-sitter-json v0.21.1-0.20240818005659-bdd69eb8c8a5 h1:pfV3G3k7NCKqKk8THBmyuh2zA33lgYHS3GVrzRR8ry4=
+github.com/tree-sitter/tree-sitter-json v0.21.1-0.20240818005659-bdd69eb8c8a5/go.mod h1:GbMKRjLfk0H+PI7nLi1Sx5lHf5wCpLz9al8tQYSxpEk=
+github.com/tree-sitter/tree-sitter-php v0.22.9-0.20240819002312-a552625b56c1 h1:ZXZMDwE+IhUtGug4Brv6NjJWUU3rfkZBKpemf6RY8/g=
+github.com/tree-sitter/tree-sitter-php v0.22.9-0.20240819002312-a552625b56c1/go.mod h1:UKCLuYnJ312Mei+3cyTmGOHzn0YAnaPRECgJmHtzrqs=
+github.com/tree-sitter/tree-sitter-python v0.21.1-0.20240818005537-55a9b8a4fbfb h1:EXEM82lFM7JjJb6qiKZXkpIDaCcbV2obNn82ghwj9lw=
+github.com/tree-sitter/tree-sitter-python v0.21.1-0.20240818005537-55a9b8a4fbfb/go.mod h1:lXCF1nGG5Dr4J3BTS0ObN4xJCCICiSu/b+Xe/VqMV7g=
+github.com/tree-sitter/tree-sitter-ruby v0.21.1-0.20240818211811-7dbc1e2d0e2d h1:fcYCvoXdcP1uRQYXqJHRy6Hec+uKScQdKVtMwK9JeCI=
+github.com/tree-sitter/tree-sitter-ruby v0.21.1-0.20240818211811-7dbc1e2d0e2d/go.mod h1:T1nShQ4v5AJtozZ8YyAS4uzUtDAJj/iv4YfwXSbUHzg=
+github.com/tree-sitter/tree-sitter-rust v0.21.3-0.20240818005432-2b43eafe6447 h1:o9alBu1J/WjrcTKEthYtXmdkDc5OVXD+PqlvnEZ0Lzc=
+github.com/tree-sitter/tree-sitter-rust v0.21.3-0.20240818005432-2b43eafe6447/go.mod h1:1Oh95COkkTn6Ezp0vcMbvfhRP5gLeqqljR0BYnBzWvc=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/package-lock.json b/package-lock.json
index 672b450..8d1e715 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,7 +1,7 @@
 {
   "name": "tree-sitter-vimdoc",
   "version": "3.0.0",
-  "lockfileVersion": 2,
+  "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
@@ -10,18 +10,18 @@
       "hasInstallScript": true,
       "license": "Apache-2.0",
       "dependencies": {
-        "node-addon-api": "^7.1.0",
-        "node-gyp-build": "^4.8.0"
+        "node-addon-api": "^8.2.1",
+        "node-gyp-build": "^4.8.2"
       },
       "devDependencies": {
-        "prebuildify": "^6.0.0",
-        "tree-sitter-cli": "^0.21.0"
+        "prebuildify": "^6.0.1",
+        "tree-sitter-cli": "^0.24.3"
       },
       "peerDependencies": {
-        "tree-sitter": "^0.21.0"
+        "tree-sitter": "^0.21.1"
       },
       "peerDependenciesMeta": {
-        "tree_sitter": {
+        "tree-sitter": {
           "optional": true
         }
       }
@@ -96,15 +96,6 @@
         "once": "^1.4.0"
       }
     },
-    "node_modules/execspawn": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/execspawn/-/execspawn-1.0.1.tgz",
-      "integrity": "sha512-s2k06Jy9i8CUkYe0+DxRlvtkZoOkwwfhB+Xxo5HGUtrISVW2m98jO2tr67DGRFxZwkjQqloA3v/tNtjhBRBieg==",
-      "dev": true,
-      "dependencies": {
-        "util-extend": "^1.0.1"
-      }
-    },
     "node_modules/fs-constants": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@@ -177,17 +168,19 @@
       }
     },
     "node_modules/node-addon-api": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz",
-      "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==",
+      "version": "8.2.1",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.2.1.tgz",
+      "integrity": "sha512-vmEOvxwiH8tlOcv4SyE8RH34rI5/nWVaigUeAUPawC6f0+HoDthwI0vkMu4tbtsZrXq6QXFfrkhjofzKEs5tpA==",
+      "license": "MIT",
       "engines": {
-        "node": "^16 || ^18 || >= 20"
+        "node": "^18 || ^20 || >= 21"
       }
     },
     "node_modules/node-gyp-build": {
-      "version": "4.8.0",
-      "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz",
-      "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==",
+      "version": "4.8.2",
+      "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz",
+      "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==",
+      "license": "MIT",
       "bin": {
         "node-gyp-build": "bin.js",
         "node-gyp-build-optional": "optional.js",
@@ -225,12 +218,12 @@
       }
     },
     "node_modules/prebuildify": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/prebuildify/-/prebuildify-6.0.0.tgz",
-      "integrity": "sha512-DEvK4C3tcimIp7Pzqbs036n9i6CTKGp1XVEpMnr4wV3enKU5sBogPP+lP3KZw7993i42bXnsd5eIxAXQ566Cqw==",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/prebuildify/-/prebuildify-6.0.1.tgz",
+      "integrity": "sha512-8Y2oOOateom/s8dNBsGIcnm6AxPmLH4/nanQzL5lQMU+sC0CMhzARZHizwr36pUPLdvBnOkCNQzxg4djuFSgIw==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "execspawn": "^1.0.1",
         "minimist": "^1.2.5",
         "mkdirp-classic": "^0.5.3",
         "node-abi": "^3.3.0",
@@ -339,24 +332,30 @@
       }
     },
     "node_modules/tree-sitter": {
-      "version": "0.21.0",
-      "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.21.0.tgz",
-      "integrity": "sha512-WDhpLxQdW7wsmmnBsf4NGqnEKs+Kxljk/CfbJJxgzZiinfA1gAWnhi/GirQjClw+woXhYsNq930BlskFulMMBQ==",
+      "version": "0.21.1",
+      "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.21.1.tgz",
+      "integrity": "sha512-7dxoA6kYvtgWw80265MyqJlkRl4yawIjO7S5MigytjELkX43fV2WsAXzsNfO7sBpPPCF5Gp0+XzHk0DwLCq3xQ==",
       "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
       "peer": true,
       "dependencies": {
-        "node-addon-api": "^7.1.0",
+        "node-addon-api": "^8.0.0",
         "node-gyp-build": "^4.8.0"
       }
     },
     "node_modules/tree-sitter-cli": {
-      "version": "0.21.0",
-      "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.21.0.tgz",
-      "integrity": "sha512-wA7wT5724fNQW82XDH6zT6ZcYonjrAKLCHHuhLsPcAKULrhp3rNuMvlgBdB5FUBvmjHNhtTZF/qpHenMoRJPBw==",
+      "version": "0.24.3",
+      "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.24.3.tgz",
+      "integrity": "sha512-5vS0SiJf31tMTn9CYLsu5l18qXaw5MLFka3cuGxOB5f4TtgoUSK1Sog6rKmqBc7PvFJq37YcQBjj9giNy2cJPw==",
       "dev": true,
       "hasInstallScript": true,
+      "license": "MIT",
       "bin": {
         "tree-sitter": "cli.js"
+      },
+      "engines": {
+        "node": ">=12.0.0"
       }
     },
     "node_modules/util-deprecate": {
@@ -365,12 +364,6 @@
       "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
       "dev": true
     },
-    "node_modules/util-extend": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz",
-      "integrity": "sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==",
-      "dev": true
-    },
     "node_modules/wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -383,265 +376,5 @@
       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
       "dev": true
     }
-  },
-  "dependencies": {
-    "base64-js": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
-      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
-      "dev": true
-    },
-    "bl": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
-      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
-      "dev": true,
-      "requires": {
-        "buffer": "^5.5.0",
-        "inherits": "^2.0.4",
-        "readable-stream": "^3.4.0"
-      }
-    },
-    "buffer": {
-      "version": "5.7.1",
-      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
-      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
-      "dev": true,
-      "requires": {
-        "base64-js": "^1.3.1",
-        "ieee754": "^1.1.13"
-      }
-    },
-    "chownr": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
-      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
-      "dev": true
-    },
-    "end-of-stream": {
-      "version": "1.4.4",
-      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
-      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
-      "dev": true,
-      "requires": {
-        "once": "^1.4.0"
-      }
-    },
-    "execspawn": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/execspawn/-/execspawn-1.0.1.tgz",
-      "integrity": "sha512-s2k06Jy9i8CUkYe0+DxRlvtkZoOkwwfhB+Xxo5HGUtrISVW2m98jO2tr67DGRFxZwkjQqloA3v/tNtjhBRBieg==",
-      "dev": true,
-      "requires": {
-        "util-extend": "^1.0.1"
-      }
-    },
-    "fs-constants": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
-      "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
-      "dev": true
-    },
-    "ieee754": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
-      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
-      "dev": true
-    },
-    "inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
-    },
-    "lru-cache": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
-      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
-      "dev": true,
-      "requires": {
-        "yallist": "^4.0.0"
-      }
-    },
-    "minimist": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
-      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
-      "dev": true
-    },
-    "mkdirp-classic": {
-      "version": "0.5.3",
-      "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
-      "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
-      "dev": true
-    },
-    "node-abi": {
-      "version": "3.56.0",
-      "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.56.0.tgz",
-      "integrity": "sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==",
-      "dev": true,
-      "requires": {
-        "semver": "^7.3.5"
-      }
-    },
-    "node-addon-api": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz",
-      "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g=="
-    },
-    "node-gyp-build": {
-      "version": "4.8.0",
-      "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz",
-      "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og=="
-    },
-    "npm-run-path": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz",
-      "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==",
-      "dev": true,
-      "requires": {
-        "path-key": "^3.0.0"
-      }
-    },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
-      "dev": true,
-      "requires": {
-        "wrappy": "1"
-      }
-    },
-    "path-key": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
-      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
-      "dev": true
-    },
-    "prebuildify": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/prebuildify/-/prebuildify-6.0.0.tgz",
-      "integrity": "sha512-DEvK4C3tcimIp7Pzqbs036n9i6CTKGp1XVEpMnr4wV3enKU5sBogPP+lP3KZw7993i42bXnsd5eIxAXQ566Cqw==",
-      "dev": true,
-      "requires": {
-        "execspawn": "^1.0.1",
-        "minimist": "^1.2.5",
-        "mkdirp-classic": "^0.5.3",
-        "node-abi": "^3.3.0",
-        "npm-run-path": "^3.1.0",
-        "pump": "^3.0.0",
-        "tar-fs": "^2.1.0"
-      }
-    },
-    "pump": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
-      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
-      "dev": true,
-      "requires": {
-        "end-of-stream": "^1.1.0",
-        "once": "^1.3.1"
-      }
-    },
-    "readable-stream": {
-      "version": "3.6.2",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
-      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.3",
-        "string_decoder": "^1.1.1",
-        "util-deprecate": "^1.0.1"
-      }
-    },
-    "safe-buffer": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
-      "dev": true
-    },
-    "semver": {
-      "version": "7.6.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
-      "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
-      "dev": true,
-      "requires": {
-        "lru-cache": "^6.0.0"
-      }
-    },
-    "string_decoder": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
-      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "~5.2.0"
-      }
-    },
-    "tar-fs": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
-      "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
-      "dev": true,
-      "requires": {
-        "chownr": "^1.1.1",
-        "mkdirp-classic": "^0.5.2",
-        "pump": "^3.0.0",
-        "tar-stream": "^2.1.4"
-      }
-    },
-    "tar-stream": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
-      "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
-      "dev": true,
-      "requires": {
-        "bl": "^4.0.3",
-        "end-of-stream": "^1.4.1",
-        "fs-constants": "^1.0.0",
-        "inherits": "^2.0.3",
-        "readable-stream": "^3.1.1"
-      }
-    },
-    "tree-sitter": {
-      "version": "0.21.0",
-      "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.21.0.tgz",
-      "integrity": "sha512-WDhpLxQdW7wsmmnBsf4NGqnEKs+Kxljk/CfbJJxgzZiinfA1gAWnhi/GirQjClw+woXhYsNq930BlskFulMMBQ==",
-      "peer": true,
-      "requires": {
-        "node-addon-api": "^7.1.0",
-        "node-gyp-build": "^4.8.0"
-      }
-    },
-    "tree-sitter-cli": {
-      "version": "0.21.0",
-      "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.21.0.tgz",
-      "integrity": "sha512-wA7wT5724fNQW82XDH6zT6ZcYonjrAKLCHHuhLsPcAKULrhp3rNuMvlgBdB5FUBvmjHNhtTZF/qpHenMoRJPBw==",
-      "dev": true
-    },
-    "util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
-      "dev": true
-    },
-    "util-extend": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz",
-      "integrity": "sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==",
-      "dev": true
-    },
-    "wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
-      "dev": true
-    },
-    "yallist": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
-      "dev": true
-    }
   }
 }
diff --git a/package.json b/package.json
index d5c9b24..b99f694 100644
--- a/package.json
+++ b/package.json
@@ -3,18 +3,18 @@
   "version": "3.0.0",
   "description": "Vim help file grammar for tree-sitter",
   "repository": "github:neovim/tree-sitter-vimdoc",
-  "author": "Thomas Vigouroux <tomvig38@gmail.com>",
   "license": "Apache-2.0",
+  "author": {
+    "name": "Thomas Vigouroux",
+    "email": "tomvig38@gmail.com"
+  },
   "main": "bindings/node",
   "types": "bindings/node",
   "keywords": [
     "incremental",
     "parsing",
     "tree-sitter",
-    "vimdoc",
-    "vim",
-    "nvim",
-    "neovim"
+    "vimdoc"
   ],
   "files": [
     "grammar.js",
@@ -22,36 +22,29 @@
     "prebuilds/**",
     "bindings/node/*",
     "queries/*",
-    "src/**"
+    "src/**",
+    "*.wasm"
   ],
   "dependencies": {
-    "node-addon-api": "^7.1.0",
-    "node-gyp-build": "^4.8.0"
+    "node-addon-api": "^8.2.1",
+    "node-gyp-build": "^4.8.2"
   },
   "devDependencies": {
-    "tree-sitter-cli": "^0.21.0",
-    "prebuildify": "^6.0.0"
+    "prebuildify": "^6.0.1",
+    "tree-sitter-cli": "^0.24.3"
   },
   "peerDependencies": {
-    "tree-sitter": "^0.21.0"
+    "tree-sitter": "^0.21.1"
   },
   "peerDependenciesMeta": {
-    "tree_sitter": {
+    "tree-sitter": {
       "optional": true
     }
   },
   "scripts": {
-    "build": "tree-sitter generate --no-bindings",
-    "test": "tree-sitter test",
     "install": "node-gyp-build",
-    "prebuildify": "prebuildify --napi --strip"
-  },
-  "tree-sitter": [
-    {
-      "scope": "text.vimdoc",
-      "first-line-regex": "^\\*[\\w-]+\\.txt\\*",
-      "highlights": "queries/vimdoc/highlights.scm",
-      "injections": "queries/vimdoc/injections.scm"
-    }
-  ]
+    "prestart": "tree-sitter build --wasm",
+    "start": "tree-sitter playground",
+    "test": "node --test bindings/node/*_test.js"
+  }
 }
diff --git a/pyproject.toml b/pyproject.toml
index e310745..cacf74a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,24 +9,22 @@ version = "3.0.0"
 keywords = ["incremental", "parsing", "tree-sitter", "vimdoc"]
 classifiers = [
   "Intended Audience :: Developers",
-  "License :: OSI Approved :: MIT License",
+  "License :: OSI Approved :: Apache Software License",
   "Topic :: Software Development :: Compilers",
   "Topic :: Text Processing :: Linguistic",
-  "Typing :: Typed"
+  "Typing :: Typed",
 ]
-authors = [
-  {name = "Thomas Vigouroux", email = "tomvig38@gmail.com"}
-]
-requires-python = ">=3.8"
-license.text = "MIT"
+authors = [{ name = "Thomas Vigouroux", email = "tomvig38@gmail.com" }]
+requires-python = ">=3.9"
+license.text = "Apache-2.0"
 readme = "README.md"
 
 [project.urls]
 Homepage = "https://github.com/neovim/tree-sitter-vimdoc"
 
 [project.optional-dependencies]
-core = ["tree-sitter~=0.21"]
+core = ["tree-sitter~=0.22"]
 
 [tool.cibuildwheel]
-build = "cp38-*"
+build = "cp39-*"
 build-frontend = "build"
diff --git a/setup.py b/setup.py
index 9592282..2faf72f 100644
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@ class BdistWheel(bdist_wheel):
     def get_tag(self):
         python, abi, platform = super().get_tag()
         if python.startswith("cp"):
-            python, abi = "cp38", "abi3"
+            python, abi = "cp39", "abi3"
         return python, abi, platform
 
 
@@ -37,12 +37,17 @@ def get_tag(self):
                 "bindings/python/tree_sitter_vimdoc/binding.c",
                 "src/parser.c",
             ],
-            extra_compile_args=(
-                ["-std=c11"] if system() != 'Windows' else []
-            ),
+            extra_compile_args=[
+                "-std=c11",
+                "-fvisibility=hidden",
+            ] if system() != "Windows" else [
+                "/std:c11",
+                "/utf-8",
+            ],
             define_macros=[
-                ("Py_LIMITED_API", "0x03080000"),
-                ("PY_SSIZE_T_CLEAN", None)
+                ("Py_LIMITED_API", "0x03090000"),
+                ("PY_SSIZE_T_CLEAN", None),
+                ("TREE_SITTER_HIDE_SYMBOLS", None),
             ],
             include_dirs=["src"],
             py_limited_api=True,
diff --git a/tree-sitter.json b/tree-sitter.json
new file mode 100644
index 0000000..f59f159
--- /dev/null
+++ b/tree-sitter.json
@@ -0,0 +1,26 @@
+{
+  "grammars": [
+    {
+      "name": "vimdoc",
+      "path": ".",
+      "scope": "text.vimdoc",
+      "first-line-regex": "^\\*[\\w-]+\\.txt\\*",
+      "highlights": "queries/vimdoc/highlights.scm",
+      "injections": "queries/vimdoc/injections.scm"
+    }
+  ],
+  "metadata": {
+    "version": "3.0.0",
+    "license": "Apache-2.0",
+    "description": "Vim help file grammar for tree-sitter",
+    "links": {
+      "repository": "https://github.com/neovim/tree-sitter-vimdoc"
+    },
+    "authors": [
+      {
+        "name": "Thomas Vigouroux",
+        "email": "tomvig38@gmail.com"
+      }
+    ]
+  }
+}