Approval-based snapshot testing library for Go (mirror)
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: CLAUDE.md and tree sitter init

+1029
+76
CLAUDE.md
··· 1 + # CLAUDE.md 2 + 3 + This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 + 5 + ## Project Overview 6 + 7 + Shutter is a snapshot testing library for Go, inspired by Rust's [insta](https://github.com/mitsuhiko/insta) and Gleam's [birdie](https://github.com/giacomocavalieri/birdie). It provides functions for capturing test output as snapshots and tools for reviewing snapshot changes. 8 + 9 + ## Build Commands 10 + 11 + ```bash 12 + # Build the TUI binary 13 + just build 14 + 15 + # Run tests with coverage 16 + just test 17 + 18 + # Clean snapshots and run tests 19 + just clean-test 20 + 21 + # Run the TUI review tool (after building) 22 + just review 23 + 24 + # Run CLI review tool 25 + just cli 26 + ``` 27 + 28 + Direct Go commands: 29 + ```bash 30 + go test ./... # Run all tests 31 + go test ./... -run TestName # Run specific test 32 + go test -v ./internal/transform/... # Run tests in specific package 33 + cd ./cmd/shutter && go build -o shutter ./main.go # Build TUI 34 + ``` 35 + 36 + ## Architecture 37 + 38 + ``` 39 + ┌─────────────────────────────────────────────────────────────────┐ 40 + │ Public API (shutter.go) │ 41 + │ Snap() | SnapMany() | SnapString() | SnapJSON() │ 42 + ├─────────────────────────────────────────────────────────────────┤ 43 + │ Options (scrubbers.go, ignore.go) │ 44 + │ Scrubbers: text transformation before snapshot │ 45 + │ IgnorePatterns: field removal (SnapJSON only) │ 46 + ├─────────────────────────────────────────────────────────────────┤ 47 + │ Internal Modules │ 48 + │ ├─ internal/snapshots/ - Core comparison logic │ 49 + │ ├─ internal/files/ - Snapshot file I/O (YAML headers) │ 50 + │ ├─ internal/transform/ - JSON ignore pattern application │ 51 + │ ├─ internal/diff/ - Histogram diff algorithm │ 52 + │ ├─ internal/pretty/ - Formatting and display boxes │ 53 + │ └─ internal/review/ - Review workflow logic │ 54 + ├─────────────────────────────────────────────────────────────────┤ 55 + │ Review Tools │ 56 + │ ├─ cmd/shutter/ - TUI (Bubbletea) - separate go.mod │ 57 + │ └─ cmd/cli/ - CLI review tool │ 58 + └─────────────────────────────────────────────────────────────────┘ 59 + ``` 60 + 61 + **Data flow:** Test Value → Pretty format (utter) → Ignore Patterns → Scrubbers → Snapshot file 62 + 63 + **Snapshot storage:** `__snapshots__/` directories contain YAML-header files with metadata (title, test_name, file_name, version) followed by `---` delimiter and content. 64 + 65 + ## Key Design Decisions 66 + 67 + - **Option interface pattern**: `Scrubber` and `IgnorePattern` both implement `Option` for type-safe compile-time separation 68 + - **IgnorePatterns only work with SnapJSON()** - using them with Snap/SnapMany/SnapString returns an error 69 + - **TUI is a separate Go module** (cmd/shutter/) to keep Bubbletea dependencies optional 70 + - **Execution order**: Ignore patterns run first, then scrubbers 71 + 72 + ## Module Structure 73 + 74 + - Root module (`go.mod`): Main library - Go 1.23.12+ 75 + - TUI module (`cmd/shutter/go.mod`): Separate module with Bubbletea dependencies - Go 1.25.2 76 + - `/editors/`: Tree-sitter grammar for snapshot format (Node.js/Rust/Python/Swift bindings)
+46
editors/.editorconfig
··· 1 + root = true 2 + 3 + [*] 4 + charset = utf-8 5 + 6 + [*.{json,toml,yml,gyp}] 7 + indent_style = space 8 + indent_size = 2 9 + 10 + [*.js] 11 + indent_style = space 12 + indent_size = 2 13 + 14 + [*.scm] 15 + indent_style = space 16 + indent_size = 2 17 + 18 + [*.{c,cc,h}] 19 + indent_style = space 20 + indent_size = 4 21 + 22 + [*.rs] 23 + indent_style = space 24 + indent_size = 4 25 + 26 + [*.{py,pyi}] 27 + indent_style = space 28 + indent_size = 4 29 + 30 + [*.swift] 31 + indent_style = space 32 + indent_size = 4 33 + 34 + [*.go] 35 + indent_style = tab 36 + indent_size = 8 37 + 38 + [Makefile] 39 + indent_style = tab 40 + indent_size = 8 41 + 42 + [parser.c] 43 + indent_size = 2 44 + 45 + [{alloc,array,parser}.h] 46 + indent_size = 2
+42
editors/.gitattributes
··· 1 + * text=auto eol=lf 2 + 3 + # Generated source files 4 + src/*.json linguist-generated 5 + src/parser.c linguist-generated 6 + src/tree_sitter/* linguist-generated 7 + 8 + # C bindings 9 + bindings/c/** linguist-generated 10 + CMakeLists.txt linguist-generated 11 + Makefile linguist-generated 12 + 13 + # Rust bindings 14 + bindings/rust/* linguist-generated 15 + Cargo.toml linguist-generated 16 + Cargo.lock linguist-generated 17 + 18 + # Node.js bindings 19 + bindings/node/* linguist-generated 20 + binding.gyp linguist-generated 21 + package.json linguist-generated 22 + package-lock.json linguist-generated 23 + 24 + # Python bindings 25 + bindings/python/** linguist-generated 26 + setup.py linguist-generated 27 + pyproject.toml linguist-generated 28 + 29 + # Go bindings 30 + bindings/go/* linguist-generated 31 + go.mod linguist-generated 32 + go.sum linguist-generated 33 + 34 + # Swift bindings 35 + bindings/swift/** linguist-generated 36 + Package.swift linguist-generated 37 + Package.resolved linguist-generated 38 + 39 + # Zig bindings 40 + bindings/zig/* linguist-generated 41 + build.zig linguist-generated 42 + build.zig.zon linguist-generated
+50
editors/.gitignore
··· 1 + # Rust artifacts 2 + target/ 3 + Cargo.lock 4 + 5 + # Node artifacts 6 + build/ 7 + prebuilds/ 8 + node_modules/ 9 + package-lock.json 10 + 11 + # Swift artifacts 12 + .build/ 13 + Package.resolved 14 + 15 + # Go artifacts 16 + _obj/ 17 + 18 + # Python artifacts 19 + .venv/ 20 + dist/ 21 + *.egg-info 22 + *.whl 23 + 24 + # C artifacts 25 + *.a 26 + *.so 27 + *.so.* 28 + *.dylib 29 + *.dll 30 + *.pc 31 + *.exp 32 + *.lib 33 + 34 + # Zig artifacts 35 + .zig-cache/ 36 + zig-cache/ 37 + zig-out/ 38 + 39 + # Example dirs 40 + /examples/*/ 41 + 42 + # Grammar volatiles 43 + *.wasm 44 + *.obj 45 + *.o 46 + 47 + # Archives 48 + *.tar.gz 49 + *.tgz 50 + *.zip
+66
editors/CMakeLists.txt
··· 1 + cmake_minimum_required(VERSION 3.13) 2 + 3 + project(tree-sitter-snapshot 4 + VERSION "0.1.0" 5 + DESCRIPTION "Snapshot test files generated by Shutter" 6 + HOMEPAGE_URL "https://github.com/ptdewey/shutter" 7 + LANGUAGES C) 8 + 9 + option(BUILD_SHARED_LIBS "Build using shared libraries" ON) 10 + option(TREE_SITTER_REUSE_ALLOCATOR "Reuse the library allocator" OFF) 11 + 12 + set(TREE_SITTER_ABI_VERSION 15 CACHE STRING "Tree-sitter ABI version") 13 + if(NOT ${TREE_SITTER_ABI_VERSION} MATCHES "^[0-9]+$") 14 + unset(TREE_SITTER_ABI_VERSION CACHE) 15 + message(FATAL_ERROR "TREE_SITTER_ABI_VERSION must be an integer") 16 + endif() 17 + 18 + include(GNUInstallDirs) 19 + 20 + find_program(TREE_SITTER_CLI tree-sitter DOC "Tree-sitter CLI") 21 + 22 + add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" 23 + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json" 24 + COMMAND "${TREE_SITTER_CLI}" generate src/grammar.json 25 + --abi=${TREE_SITTER_ABI_VERSION} 26 + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 27 + COMMENT "Generating parser.c") 28 + 29 + add_library(tree-sitter-snapshot src/parser.c) 30 + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/scanner.c) 31 + target_sources(tree-sitter-snapshot PRIVATE src/scanner.c) 32 + endif() 33 + target_include_directories(tree-sitter-snapshot 34 + PRIVATE src 35 + INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/bindings/c> 36 + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>) 37 + 38 + target_compile_definitions(tree-sitter-snapshot PRIVATE 39 + $<$<BOOL:${TREE_SITTER_REUSE_ALLOCATOR}>:TREE_SITTER_REUSE_ALLOCATOR> 40 + $<$<CONFIG:Debug>:TREE_SITTER_DEBUG>) 41 + 42 + set_target_properties(tree-sitter-snapshot 43 + PROPERTIES 44 + C_STANDARD 11 45 + POSITION_INDEPENDENT_CODE ON 46 + SOVERSION "${TREE_SITTER_ABI_VERSION}.${PROJECT_VERSION_MAJOR}" 47 + DEFINE_SYMBOL "") 48 + 49 + configure_file(bindings/c/tree-sitter-snapshot.pc.in 50 + "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-snapshot.pc" @ONLY) 51 + 52 + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bindings/c/tree_sitter" 53 + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 54 + FILES_MATCHING PATTERN "*.h") 55 + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-snapshot.pc" 56 + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 57 + install(TARGETS tree-sitter-snapshot 58 + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") 59 + 60 + file(GLOB QUERIES queries/*.scm) 61 + install(FILES ${QUERIES} 62 + DESTINATION "${CMAKE_INSTALL_DATADIR}/tree-sitter/queries/snapshot") 63 + 64 + add_custom_target(ts-test "${TREE_SITTER_CLI}" test 65 + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 66 + COMMENT "tree-sitter test")
+34
editors/Cargo.toml
··· 1 + [package] 2 + name = "tree-sitter-snapshot" 3 + description = "Snapshot test files generated by Shutter" 4 + version = "0.1.0" 5 + authors = ["ptdewey"] 6 + license = "MIT" 7 + readme = "README.md" 8 + keywords = ["incremental", "parsing", "tree-sitter", "snapshot"] 9 + categories = ["parser-implementations", "parsing", "text-editors"] 10 + repository = "https://github.com/ptdewey/shutter" 11 + edition = "2021" 12 + autoexamples = false 13 + 14 + build = "bindings/rust/build.rs" 15 + include = [ 16 + "bindings/rust/*", 17 + "grammar.js", 18 + "queries/*", 19 + "src/*", 20 + "tree-sitter.json", 21 + "/LICENSE", 22 + ] 23 + 24 + [lib] 25 + path = "bindings/rust/lib.rs" 26 + 27 + [dependencies] 28 + tree-sitter-language = "0.1" 29 + 30 + [build-dependencies] 31 + cc = "1.2" 32 + 33 + [dev-dependencies] 34 + tree-sitter = "0.25.10"
+99
editors/Makefile
··· 1 + ifeq ($(OS),Windows_NT) 2 + $(error Windows is not supported) 3 + endif 4 + 5 + LANGUAGE_NAME := tree-sitter-snapshot 6 + HOMEPAGE_URL := https://github.com/ptdewey/shutter 7 + VERSION := 0.1.0 8 + 9 + # repository 10 + SRC_DIR := src 11 + 12 + TS ?= tree-sitter 13 + 14 + # install directory layout 15 + PREFIX ?= /usr/local 16 + DATADIR ?= $(PREFIX)/share 17 + INCLUDEDIR ?= $(PREFIX)/include 18 + LIBDIR ?= $(PREFIX)/lib 19 + PCLIBDIR ?= $(LIBDIR)/pkgconfig 20 + 21 + # source/object files 22 + PARSER := $(SRC_DIR)/parser.c 23 + EXTRAS := $(filter-out $(PARSER),$(wildcard $(SRC_DIR)/*.c)) 24 + OBJS := $(patsubst %.c,%.o,$(PARSER) $(EXTRAS)) 25 + 26 + # flags 27 + ARFLAGS ?= rcs 28 + override CFLAGS += -I$(SRC_DIR) -std=c11 -fPIC 29 + 30 + # ABI versioning 31 + SONAME_MAJOR = $(shell sed -n 's/\#define LANGUAGE_VERSION //p' $(PARSER)) 32 + SONAME_MINOR = $(word 1,$(subst ., ,$(VERSION))) 33 + 34 + # OS-specific bits 35 + ifeq ($(shell uname),Darwin) 36 + SOEXT = dylib 37 + SOEXTVER_MAJOR = $(SONAME_MAJOR).$(SOEXT) 38 + SOEXTVER = $(SONAME_MAJOR).$(SONAME_MINOR).$(SOEXT) 39 + LINKSHARED = -dynamiclib -Wl,-install_name,$(LIBDIR)/lib$(LANGUAGE_NAME).$(SOEXTVER),-rpath,@executable_path/../Frameworks 40 + else 41 + SOEXT = so 42 + SOEXTVER_MAJOR = $(SOEXT).$(SONAME_MAJOR) 43 + SOEXTVER = $(SOEXT).$(SONAME_MAJOR).$(SONAME_MINOR) 44 + LINKSHARED = -shared -Wl,-soname,lib$(LANGUAGE_NAME).$(SOEXTVER) 45 + endif 46 + ifneq ($(filter $(shell uname),FreeBSD NetBSD DragonFly),) 47 + PCLIBDIR := $(PREFIX)/libdata/pkgconfig 48 + endif 49 + 50 + all: lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) $(LANGUAGE_NAME).pc 51 + 52 + lib$(LANGUAGE_NAME).a: $(OBJS) 53 + $(AR) $(ARFLAGS) $@ $^ 54 + 55 + lib$(LANGUAGE_NAME).$(SOEXT): $(OBJS) 56 + $(CC) $(LDFLAGS) $(LINKSHARED) $^ $(LDLIBS) -o $@ 57 + ifneq ($(STRIP),) 58 + $(STRIP) $@ 59 + endif 60 + 61 + $(LANGUAGE_NAME).pc: bindings/c/$(LANGUAGE_NAME).pc.in 62 + sed -e 's|@PROJECT_VERSION@|$(VERSION)|' \ 63 + -e 's|@CMAKE_INSTALL_LIBDIR@|$(LIBDIR:$(PREFIX)/%=%)|' \ 64 + -e 's|@CMAKE_INSTALL_INCLUDEDIR@|$(INCLUDEDIR:$(PREFIX)/%=%)|' \ 65 + -e 's|@PROJECT_DESCRIPTION@|$(DESCRIPTION)|' \ 66 + -e 's|@PROJECT_HOMEPAGE_URL@|$(HOMEPAGE_URL)|' \ 67 + -e 's|@CMAKE_INSTALL_PREFIX@|$(PREFIX)|' $< > $@ 68 + 69 + $(PARSER): $(SRC_DIR)/grammar.json 70 + $(TS) generate $^ 71 + 72 + install: all 73 + install -d '$(DESTDIR)$(DATADIR)'/tree-sitter/queries/snapshot '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter '$(DESTDIR)$(PCLIBDIR)' '$(DESTDIR)$(LIBDIR)' 74 + install -m644 bindings/c/tree_sitter/$(LANGUAGE_NAME).h '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h 75 + install -m644 $(LANGUAGE_NAME).pc '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc 76 + install -m644 lib$(LANGUAGE_NAME).a '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a 77 + install -m755 lib$(LANGUAGE_NAME).$(SOEXT) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) 78 + ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) 79 + ln -sf lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) 80 + ifneq ($(wildcard queries/*.scm),) 81 + install -m644 queries/*.scm '$(DESTDIR)$(DATADIR)'/tree-sitter/queries/snapshot 82 + endif 83 + 84 + uninstall: 85 + $(RM) '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).a \ 86 + '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER) \ 87 + '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXTVER_MAJOR) \ 88 + '$(DESTDIR)$(LIBDIR)'/lib$(LANGUAGE_NAME).$(SOEXT) \ 89 + '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter/$(LANGUAGE_NAME).h \ 90 + '$(DESTDIR)$(PCLIBDIR)'/$(LANGUAGE_NAME).pc 91 + $(RM) -r '$(DESTDIR)$(DATADIR)'/tree-sitter/queries/snapshot 92 + 93 + clean: 94 + $(RM) $(OBJS) $(LANGUAGE_NAME).pc lib$(LANGUAGE_NAME).a lib$(LANGUAGE_NAME).$(SOEXT) 95 + 96 + test: 97 + $(TS) test 98 + 99 + .PHONY: all install uninstall clean test
+41
editors/Package.swift
··· 1 + // swift-tools-version:5.3 2 + 3 + import Foundation 4 + import PackageDescription 5 + 6 + var sources = ["src/parser.c"] 7 + if FileManager.default.fileExists(atPath: "src/scanner.c") { 8 + sources.append("src/scanner.c") 9 + } 10 + 11 + let package = Package( 12 + name: "TreeSitterSnapshot", 13 + products: [ 14 + .library(name: "TreeSitterSnapshot", targets: ["TreeSitterSnapshot"]), 15 + ], 16 + dependencies: [ 17 + .package(name: "SwiftTreeSitter", url: "https://github.com/tree-sitter/swift-tree-sitter", from: "0.9.0"), 18 + ], 19 + targets: [ 20 + .target( 21 + name: "TreeSitterSnapshot", 22 + dependencies: [], 23 + path: ".", 24 + sources: sources, 25 + resources: [ 26 + .copy("queries") 27 + ], 28 + publicHeadersPath: "bindings/swift", 29 + cSettings: [.headerSearchPath("src")] 30 + ), 31 + .testTarget( 32 + name: "TreeSitterSnapshotTests", 33 + dependencies: [ 34 + "SwiftTreeSitter", 35 + "TreeSitterSnapshot", 36 + ], 37 + path: "bindings/swift/TreeSitterSnapshotTests" 38 + ) 39 + ], 40 + cLanguageStandard: .c11 41 + )
+35
editors/binding.gyp
··· 1 + { 2 + "targets": [ 3 + { 4 + "target_name": "tree_sitter_snapshot_binding", 5 + "dependencies": [ 6 + "<!(node -p \"require('node-addon-api').targets\"):node_addon_api_except", 7 + ], 8 + "include_dirs": [ 9 + "src", 10 + ], 11 + "sources": [ 12 + "bindings/node/binding.cc", 13 + "src/parser.c", 14 + ], 15 + "variables": { 16 + "has_scanner": "<!(node -p \"fs.existsSync('src/scanner.c')\")" 17 + }, 18 + "conditions": [ 19 + ["has_scanner=='true'", { 20 + "sources+": ["src/scanner.c"], 21 + }], 22 + ["OS!='win'", { 23 + "cflags_c": [ 24 + "-std=c11", 25 + ], 26 + }, { # OS == "win" 27 + "cflags_c": [ 28 + "/std:c11", 29 + "/utf-8", 30 + ], 31 + }], 32 + ], 33 + } 34 + ] 35 + }
+10
editors/bindings/c/tree-sitter-snapshot.pc.in
··· 1 + prefix=@CMAKE_INSTALL_PREFIX@ 2 + libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ 3 + includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ 4 + 5 + Name: tree-sitter-snapshot 6 + Description: @PROJECT_DESCRIPTION@ 7 + URL: @PROJECT_HOMEPAGE_URL@ 8 + Version: @PROJECT_VERSION@ 9 + Libs: -L${libdir} -ltree-sitter-snapshot 10 + Cflags: -I${includedir}
+16
editors/bindings/c/tree_sitter/tree-sitter-snapshot.h
··· 1 + #ifndef TREE_SITTER_SNAPSHOT_H_ 2 + #define TREE_SITTER_SNAPSHOT_H_ 3 + 4 + typedef struct TSLanguage TSLanguage; 5 + 6 + #ifdef __cplusplus 7 + extern "C" { 8 + #endif 9 + 10 + const TSLanguage *tree_sitter_snapshot(void); 11 + 12 + #ifdef __cplusplus 13 + } 14 + #endif 15 + 16 + #endif // TREE_SITTER_SNAPSHOT_H_
+15
editors/bindings/go/binding.go
··· 1 + package tree_sitter_snapshot 2 + 3 + // #cgo CFLAGS: -std=c11 -fPIC 4 + // #include "../../src/parser.c" 5 + // #if __has_include("../../src/scanner.c") 6 + // #include "../../src/scanner.c" 7 + // #endif 8 + import "C" 9 + 10 + import "unsafe" 11 + 12 + // Get the tree-sitter Language for this grammar. 13 + func Language() unsafe.Pointer { 14 + return unsafe.Pointer(C.tree_sitter_snapshot()) 15 + }
+15
editors/bindings/go/binding_test.go
··· 1 + package tree_sitter_snapshot_test 2 + 3 + import ( 4 + "testing" 5 + 6 + tree_sitter "github.com/tree-sitter/go-tree-sitter" 7 + tree_sitter_snapshot "github.com/ptdewey/shutter/bindings/go" 8 + ) 9 + 10 + func TestCanLoadGrammar(t *testing.T) { 11 + language := tree_sitter.NewLanguage(tree_sitter_snapshot.Language()) 12 + if language == nil { 13 + t.Errorf("Error loading Snapshot grammar") 14 + } 15 + }
+19
editors/bindings/node/binding.cc
··· 1 + #include <napi.h> 2 + 3 + typedef struct TSLanguage TSLanguage; 4 + 5 + extern "C" TSLanguage *tree_sitter_snapshot(); 6 + 7 + // "tree-sitter", "language" hashed with BLAKE2 8 + const napi_type_tag LANGUAGE_TYPE_TAG = { 9 + 0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16 10 + }; 11 + 12 + Napi::Object Init(Napi::Env env, Napi::Object exports) { 13 + auto language = Napi::External<TSLanguage>::New(env, tree_sitter_snapshot()); 14 + language.TypeTag(&LANGUAGE_TYPE_TAG); 15 + exports["language"] = language; 16 + return exports; 17 + } 18 + 19 + NODE_API_MODULE(tree_sitter_snapshot_binding, Init)
+9
editors/bindings/node/binding_test.js
··· 1 + const assert = require("node:assert"); 2 + const { test } = require("node:test"); 3 + 4 + const Parser = require("tree-sitter"); 5 + 6 + test("can load grammar", () => { 7 + const parser = new Parser(); 8 + assert.doesNotThrow(() => parser.setLanguage(require("."))); 9 + });
+27
editors/bindings/node/index.d.ts
··· 1 + type BaseNode = { 2 + type: string; 3 + named: boolean; 4 + }; 5 + 6 + type ChildNode = { 7 + multiple: boolean; 8 + required: boolean; 9 + types: BaseNode[]; 10 + }; 11 + 12 + type NodeInfo = 13 + | (BaseNode & { 14 + subtypes: BaseNode[]; 15 + }) 16 + | (BaseNode & { 17 + fields: { [name: string]: ChildNode }; 18 + children: ChildNode[]; 19 + }); 20 + 21 + type Language = { 22 + language: unknown; 23 + nodeTypeInfo: NodeInfo[]; 24 + }; 25 + 26 + declare const language: Language; 27 + export = language;
+11
editors/bindings/node/index.js
··· 1 + const root = require("path").join(__dirname, "..", ".."); 2 + 3 + module.exports = 4 + typeof process.versions.bun === "string" 5 + // Support `bun build --compile` by being statically analyzable enough to find the .node file at build-time 6 + ? require(`../../prebuilds/${process.platform}-${process.arch}/tree-sitter-snapshot.node`) 7 + : require("node-gyp-build")(root); 8 + 9 + try { 10 + module.exports.nodeTypeInfo = require("../../src/node-types.json"); 11 + } catch (_) {}
+12
editors/bindings/python/tests/test_binding.py
··· 1 + from unittest import TestCase 2 + 3 + from tree_sitter import Language, Parser 4 + import tree_sitter_snapshot 5 + 6 + 7 + class TestLanguage(TestCase): 8 + def test_can_load_grammar(self): 9 + try: 10 + Parser(Language(tree_sitter_snapshot.language())) 11 + except Exception: 12 + self.fail("Error loading Snapshot grammar")
+42
editors/bindings/python/tree_sitter_snapshot/__init__.py
··· 1 + """Snapshot test files generated by Shutter""" 2 + 3 + from importlib.resources import files as _files 4 + 5 + from ._binding import language 6 + 7 + 8 + def _get_query(name, file): 9 + query = _files(f"{__package__}.queries") / file 10 + globals()[name] = query.read_text() 11 + return globals()[name] 12 + 13 + 14 + def __getattr__(name): 15 + # NOTE: uncomment these to include any queries that this grammar contains: 16 + 17 + # if name == "HIGHLIGHTS_QUERY": 18 + # return _get_query("HIGHLIGHTS_QUERY", "highlights.scm") 19 + # if name == "INJECTIONS_QUERY": 20 + # return _get_query("INJECTIONS_QUERY", "injections.scm") 21 + # if name == "LOCALS_QUERY": 22 + # return _get_query("LOCALS_QUERY", "locals.scm") 23 + # if name == "TAGS_QUERY": 24 + # return _get_query("TAGS_QUERY", "tags.scm") 25 + 26 + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 27 + 28 + 29 + __all__ = [ 30 + "language", 31 + # "HIGHLIGHTS_QUERY", 32 + # "INJECTIONS_QUERY", 33 + # "LOCALS_QUERY", 34 + # "TAGS_QUERY", 35 + ] 36 + 37 + 38 + def __dir__(): 39 + return sorted(__all__ + [ 40 + "__all__", "__builtins__", "__cached__", "__doc__", "__file__", 41 + "__loader__", "__name__", "__package__", "__path__", "__spec__", 42 + ])
+10
editors/bindings/python/tree_sitter_snapshot/__init__.pyi
··· 1 + from typing import Final 2 + 3 + # NOTE: uncomment these to include any queries that this grammar contains: 4 + 5 + # HIGHLIGHTS_QUERY: Final[str] 6 + # INJECTIONS_QUERY: Final[str] 7 + # LOCALS_QUERY: Final[str] 8 + # TAGS_QUERY: Final[str] 9 + 10 + def language() -> object: ...
+35
editors/bindings/python/tree_sitter_snapshot/binding.c
··· 1 + #include <Python.h> 2 + 3 + typedef struct TSLanguage TSLanguage; 4 + 5 + TSLanguage *tree_sitter_snapshot(void); 6 + 7 + static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { 8 + return PyCapsule_New(tree_sitter_snapshot(), "tree_sitter.Language", NULL); 9 + } 10 + 11 + static struct PyModuleDef_Slot slots[] = { 12 + #ifdef Py_GIL_DISABLED 13 + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, 14 + #endif 15 + {0, NULL} 16 + }; 17 + 18 + static PyMethodDef methods[] = { 19 + {"language", _binding_language, METH_NOARGS, 20 + "Get the tree-sitter language for this grammar."}, 21 + {NULL, NULL, 0, NULL} 22 + }; 23 + 24 + static struct PyModuleDef module = { 25 + .m_base = PyModuleDef_HEAD_INIT, 26 + .m_name = "_binding", 27 + .m_doc = NULL, 28 + .m_size = 0, 29 + .m_methods = methods, 30 + .m_slots = slots, 31 + }; 32 + 33 + PyMODINIT_FUNC PyInit__binding(void) { 34 + return PyModuleDef_Init(&module); 35 + }
editors/bindings/python/tree_sitter_snapshot/py.typed

This is a binary file and will not be displayed.

+21
editors/bindings/rust/build.rs
··· 1 + fn main() { 2 + let src_dir = std::path::Path::new("src"); 3 + 4 + let mut c_config = cc::Build::new(); 5 + c_config.std("c11").include(src_dir); 6 + 7 + #[cfg(target_env = "msvc")] 8 + c_config.flag("-utf-8"); 9 + 10 + let parser_path = src_dir.join("parser.c"); 11 + c_config.file(&parser_path); 12 + println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); 13 + 14 + let scanner_path = src_dir.join("scanner.c"); 15 + if scanner_path.exists() { 16 + c_config.file(&scanner_path); 17 + println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 18 + } 19 + 20 + c_config.compile("tree-sitter-snapshot"); 21 + }
+51
editors/bindings/rust/lib.rs
··· 1 + //! This crate provides Snapshot language support for the [tree-sitter] parsing library. 2 + //! 3 + //! Typically, you will use the [`LANGUAGE`] constant to add this language to a 4 + //! tree-sitter [`Parser`], and then use the parser to parse some code: 5 + //! 6 + //! ``` 7 + //! let code = r#" 8 + //! "#; 9 + //! let mut parser = tree_sitter::Parser::new(); 10 + //! let language = tree_sitter_snapshot::LANGUAGE; 11 + //! parser 12 + //! .set_language(&language.into()) 13 + //! .expect("Error loading Snapshot parser"); 14 + //! let tree = parser.parse(code, None).unwrap(); 15 + //! assert!(!tree.root_node().has_error()); 16 + //! ``` 17 + //! 18 + //! [`Parser`]: https://docs.rs/tree-sitter/0.25.10/tree_sitter/struct.Parser.html 19 + //! [tree-sitter]: https://tree-sitter.github.io/ 20 + 21 + use tree_sitter_language::LanguageFn; 22 + 23 + extern "C" { 24 + fn tree_sitter_snapshot() -> *const (); 25 + } 26 + 27 + /// The tree-sitter [`LanguageFn`] for this grammar. 28 + pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_snapshot) }; 29 + 30 + /// The content of the [`node-types.json`] file for this grammar. 31 + /// 32 + /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers/6-static-node-types 33 + pub const NODE_TYPES: &str = include_str!("../../src/node-types.json"); 34 + 35 + // NOTE: uncomment these to include any queries that this grammar contains: 36 + 37 + // pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm"); 38 + // pub const INJECTIONS_QUERY: &str = include_str!("../../queries/injections.scm"); 39 + // pub const LOCALS_QUERY: &str = include_str!("../../queries/locals.scm"); 40 + // pub const TAGS_QUERY: &str = include_str!("../../queries/tags.scm"); 41 + 42 + #[cfg(test)] 43 + mod tests { 44 + #[test] 45 + fn test_can_load_grammar() { 46 + let mut parser = tree_sitter::Parser::new(); 47 + parser 48 + .set_language(&super::LANGUAGE.into()) 49 + .expect("Error loading Snapshot parser"); 50 + } 51 + }
+16
editors/bindings/swift/TreeSitterSnapshot/snapshot.h
··· 1 + #ifndef TREE_SITTER_SNAPSHOT_H_ 2 + #define TREE_SITTER_SNAPSHOT_H_ 3 + 4 + typedef struct TSLanguage TSLanguage; 5 + 6 + #ifdef __cplusplus 7 + extern "C" { 8 + #endif 9 + 10 + const TSLanguage *tree_sitter_snapshot(void); 11 + 12 + #ifdef __cplusplus 13 + } 14 + #endif 15 + 16 + #endif // TREE_SITTER_SNAPSHOT_H_
+12
editors/bindings/swift/TreeSitterSnapshotTests/TreeSitterSnapshotTests.swift
··· 1 + import XCTest 2 + import SwiftTreeSitter 3 + import TreeSitterSnapshot 4 + 5 + final class TreeSitterSnapshotTests: XCTestCase { 6 + func testCanLoadGrammar() throws { 7 + let parser = Parser() 8 + let language = Language(language: tree_sitter_snapshot()) 9 + XCTAssertNoThrow(try parser.setLanguage(language), 10 + "Error loading Snapshot grammar") 11 + } 12 + }
+5
editors/go.mod
··· 1 + module github.com/ptdewey/shutter 2 + 3 + go 1.22 4 + 5 + require github.com/tree-sitter/go-tree-sitter v0.24.0
+17
editors/grammar.js
··· 1 + /** 2 + * @file Snapshot test files generated by Shutter 3 + * @author ptdewey 4 + * @license MIT 5 + */ 6 + 7 + /// <reference types="tree-sitter-cli/dsl" /> 8 + // @ts-check 9 + 10 + module.exports = grammar({ 11 + name: "snapshot", 12 + 13 + rules: { 14 + // TODO: add the actual grammar rules 15 + source_file: $ => "hello" 16 + } 17 + });
+52
editors/package.json
··· 1 + { 2 + "name": "tree-sitter-snapshot", 3 + "version": "0.1.0", 4 + "description": "Snapshot test files generated by Shutter", 5 + "repository": "https://github.com/ptdewey/shutter", 6 + "license": "MIT", 7 + "author": { 8 + "name": "ptdewey", 9 + "url": "https://github.com/ptdewey" 10 + }, 11 + "main": "bindings/node", 12 + "types": "bindings/node", 13 + "keywords": [ 14 + "incremental", 15 + "parsing", 16 + "tree-sitter", 17 + "snapshot" 18 + ], 19 + "files": [ 20 + "grammar.js", 21 + "tree-sitter.json", 22 + "binding.gyp", 23 + "prebuilds/**", 24 + "bindings/node/*", 25 + "queries/*", 26 + "src/**", 27 + "*.wasm" 28 + ], 29 + "dependencies": { 30 + "node-addon-api": "^8.5.0", 31 + "node-gyp-build": "^4.8.4" 32 + }, 33 + "devDependencies": { 34 + "prebuildify": "^6.0.1", 35 + "tree-sitter": "^0.22.4", 36 + "tree-sitter-cli": "^0.25.10" 37 + }, 38 + "peerDependencies": { 39 + "tree-sitter": "^0.22.4" 40 + }, 41 + "peerDependenciesMeta": { 42 + "tree-sitter": { 43 + "optional": true 44 + } 45 + }, 46 + "scripts": { 47 + "install": "node-gyp-build", 48 + "prestart": "tree-sitter build --wasm", 49 + "start": "tree-sitter playground", 50 + "test": "node --test bindings/node/*_test.js" 51 + } 52 + }
+29
editors/pyproject.toml
··· 1 + [build-system] 2 + requires = ["setuptools>=62.4.0", "wheel"] 3 + build-backend = "setuptools.build_meta" 4 + 5 + [project] 6 + name = "tree-sitter-snapshot" 7 + description = "Snapshot test files generated by Shutter" 8 + version = "0.1.0" 9 + keywords = ["incremental", "parsing", "tree-sitter", "snapshot"] 10 + classifiers = [ 11 + "Intended Audience :: Developers", 12 + "Topic :: Software Development :: Compilers", 13 + "Topic :: Text Processing :: Linguistic", 14 + "Typing :: Typed", 15 + ] 16 + authors = [{ name = "ptdewey" }] 17 + requires-python = ">=3.10" 18 + license.text = "MIT" 19 + readme = "README.md" 20 + 21 + [project.urls] 22 + Homepage = "https://github.com/ptdewey/shutter" 23 + 24 + [project.optional-dependencies] 25 + core = ["tree-sitter~=0.24"] 26 + 27 + [tool.cibuildwheel] 28 + build = "cp310-*" 29 + build-frontend = "build"
+77
editors/setup.py
··· 1 + from os import path 2 + from sysconfig import get_config_var 3 + 4 + from setuptools import Extension, find_packages, setup 5 + from setuptools.command.build import build 6 + from setuptools.command.build_ext import build_ext 7 + from setuptools.command.egg_info import egg_info 8 + from wheel.bdist_wheel import bdist_wheel 9 + 10 + 11 + class Build(build): 12 + def run(self): 13 + if path.isdir("queries"): 14 + dest = path.join(self.build_lib, "tree_sitter_snapshot", "queries") 15 + self.copy_tree("queries", dest) 16 + super().run() 17 + 18 + 19 + class BuildExt(build_ext): 20 + def build_extension(self, ext: Extension): 21 + if self.compiler.compiler_type != "msvc": 22 + ext.extra_compile_args = ["-std=c11", "-fvisibility=hidden"] 23 + else: 24 + ext.extra_compile_args = ["/std:c11", "/utf-8"] 25 + if path.exists("src/scanner.c"): 26 + ext.sources.append("src/scanner.c") 27 + if ext.py_limited_api: 28 + ext.define_macros.append(("Py_LIMITED_API", "0x030A0000")) 29 + super().build_extension(ext) 30 + 31 + 32 + class BdistWheel(bdist_wheel): 33 + def get_tag(self): 34 + python, abi, platform = super().get_tag() 35 + if python.startswith("cp"): 36 + python, abi = "cp310", "abi3" 37 + return python, abi, platform 38 + 39 + 40 + class EggInfo(egg_info): 41 + def find_sources(self): 42 + super().find_sources() 43 + self.filelist.recursive_include("queries", "*.scm") 44 + self.filelist.include("src/tree_sitter/*.h") 45 + 46 + 47 + setup( 48 + packages=find_packages("bindings/python"), 49 + package_dir={"": "bindings/python"}, 50 + package_data={ 51 + "tree_sitter_snapshot": ["*.pyi", "py.typed"], 52 + "tree_sitter_snapshot.queries": ["*.scm"], 53 + }, 54 + ext_package="tree_sitter_snapshot", 55 + ext_modules=[ 56 + Extension( 57 + name="_binding", 58 + sources=[ 59 + "bindings/python/tree_sitter_snapshot/binding.c", 60 + "src/parser.c", 61 + ], 62 + define_macros=[ 63 + ("PY_SSIZE_T_CLEAN", None), 64 + ("TREE_SITTER_HIDE_SYMBOLS", None), 65 + ], 66 + include_dirs=["src"], 67 + py_limited_api=not get_config_var("Py_GIL_DISABLED"), 68 + ) 69 + ], 70 + cmdclass={ 71 + "build": Build, 72 + "build_ext": BuildExt, 73 + "bdist_wheel": BdistWheel, 74 + "egg_info": EggInfo, 75 + }, 76 + zip_safe=False 77 + )
+39
editors/tree-sitter.json
··· 1 + { 2 + "$schema": "https://tree-sitter.github.io/tree-sitter/assets/schemas/config.schema.json", 3 + "grammars": [ 4 + { 5 + "name": "snapshot", 6 + "camelcase": "Snapshot", 7 + "title": "Snapshot", 8 + "scope": "source.snapshot", 9 + "file-types": [ 10 + "snapshot" 11 + ], 12 + "injection-regex": "^snapshot$", 13 + "class-name": "TreeSitterSnapshot" 14 + } 15 + ], 16 + "metadata": { 17 + "version": "0.1.0", 18 + "license": "MIT", 19 + "description": "Snapshot test files generated by Shutter", 20 + "authors": [ 21 + { 22 + "name": "ptdewey", 23 + "url": "https://github.com/ptdewey" 24 + } 25 + ], 26 + "links": { 27 + "repository": "https://github.com/ptdewey/shutter" 28 + } 29 + }, 30 + "bindings": { 31 + "c": true, 32 + "go": true, 33 + "node": true, 34 + "python": true, 35 + "rust": true, 36 + "swift": true, 37 + "zig": false 38 + } 39 + }