├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── ci.yml │ └── publish.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── Package.resolved ├── Package.swift ├── README.md ├── binding.gyp ├── bindings ├── c │ ├── tree-sitter-gitattributes.pc.in │ └── tree_sitter │ │ └── tree-sitter-gitattributes.h ├── go │ ├── binding.go │ └── binding_test.go ├── node │ ├── binding.cc │ ├── binding_test.js │ ├── index.d.ts │ └── index.js ├── python │ ├── tests │ │ └── test_binding.py │ └── tree_sitter_gitattributes │ │ ├── __init__.py │ │ ├── __init__.pyi │ │ ├── binding.c │ │ └── py.typed ├── rust │ ├── build.rs │ └── lib.rs └── swift │ ├── TreeSitterGitAttributes │ └── gitattributes.h │ └── TreeSitterGitAttributesTests │ └── TreeSitterGitAttributesTests.swift ├── go.mod ├── go.sum ├── grammar.js ├── package-lock.json ├── package.json ├── pyproject.toml ├── queries └── highlights.scm ├── setup.py ├── src ├── grammar.json ├── node-types.json ├── parser.c └── tree_sitter │ ├── alloc.h │ ├── array.h │ └── parser.h ├── test ├── corpus │ └── gitattributes.txt └── highlight │ └── test.gitattributes └── tree-sitter.json /.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 47 | -------------------------------------------------------------------------------- /.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 | build.zig linguist-generated 41 | build.zig.zon linguist-generated 42 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ObserverOfTime 2 | liberapay: ObserverOfTime 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | day: sunday 8 | commit-message: 9 | prefix: ci 10 | labels: [dependencies] 11 | open-pull-requests-limit: 1 12 | groups: 13 | actions: 14 | patterns: ["*"] 15 | 16 | - package-ecosystem: gitsubmodule 17 | directory: / 18 | schedule: 19 | interval: monthly 20 | day: friday 21 | commit-message: 22 | prefix: build 23 | labels: [dependencies] 24 | open-pull-requests-limit: 1 25 | groups: 26 | submodules: 27 | patterns: ["*"] 28 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | paths: 7 | - grammar.js 8 | - src/** 9 | - test/** 10 | - bindings/** 11 | - tree-sitter.json 12 | pull_request: 13 | paths: 14 | - grammar.js 15 | - src/** 16 | - test/** 17 | - bindings/** 18 | - tree-sitter.json 19 | 20 | concurrency: 21 | group: ${{github.workflow}}-${{github.ref}} 22 | cancel-in-progress: true 23 | 24 | jobs: 25 | test: 26 | name: Test parser 27 | runs-on: ${{matrix.os}} 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | os: [ubuntu-latest, windows-latest, macos-latest] 32 | steps: 33 | - name: Checkout repository 34 | uses: actions/checkout@v4 35 | with: 36 | submodules: true 37 | - name: Set up tree-sitter 38 | uses: tree-sitter/setup-action/cli@v2 39 | - name: Run parser and binding tests 40 | uses: tree-sitter/parser-test-action@v2 41 | with: 42 | generate: false 43 | - name: Parse sample files 44 | uses: tree-sitter/parse-action@v4 45 | id: parse-files 46 | with: 47 | files: templates/**/*.gitattributes 48 | - name: Upload failures artifact 49 | uses: actions/upload-artifact@v4 50 | if: "!cancelled() && steps.parse-files.outcome == 'failure'" 51 | with: 52 | name: failures-${{runner.os}} 53 | path: ${{steps.parse-files.outputs.failures}} 54 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish package 2 | 3 | on: 4 | push: 5 | tags: ["*"] 6 | 7 | jobs: 8 | release: 9 | uses: tree-sitter/workflows/.github/workflows/release.yml@main 10 | with: 11 | attestations: true 12 | permissions: 13 | contents: write 14 | id-token: write 15 | attestations: write 16 | npm: 17 | uses: tree-sitter/workflows/.github/workflows/package-npm.yml@main 18 | secrets: 19 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 20 | crates: 21 | uses: tree-sitter/workflows/.github/workflows/package-crates.yml@main 22 | secrets: 23 | CARGO_REGISTRY_TOKEN: ${{secrets.CARGO_TOKEN}} 24 | pypi: 25 | uses: tree-sitter/workflows/.github/workflows/package-pypi.yml@main 26 | secrets: 27 | PYPI_API_TOKEN: ${{secrets.PYPI_TOKEN}} 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Rust artifacts 2 | target/ 3 | 4 | # Node artifacts 5 | build/ 6 | prebuilds/ 7 | node_modules/ 8 | 9 | # Swift artifacts 10 | .build/ 11 | 12 | # Go artifacts 13 | _obj/ 14 | 15 | # Python artifacts 16 | .venv/ 17 | dist/ 18 | *.egg-info 19 | *.whl 20 | 21 | # C artifacts 22 | *.a 23 | *.so 24 | *.so.* 25 | *.dylib 26 | *.dll 27 | *.pc 28 | *.exp 29 | *.lib 30 | 31 | # Zig artifacts 32 | .zig-cache/ 33 | zig-cache/ 34 | zig-out/ 35 | 36 | # Example dirs 37 | /examples/*/ 38 | 39 | # Grammar volatiles 40 | *.wasm 41 | *.obj 42 | *.o 43 | 44 | # Archives 45 | *.tar.gz 46 | *.tgz 47 | *.zip 48 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "gitattributes"] 2 | url = https://github.com/gitattributes/gitattributes 3 | path = templates 4 | shallow = true 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | project(tree-sitter-gitattributes 4 | VERSION "0.2.0" 5 | DESCRIPTION "gitattributes grammar for tree-sitter" 6 | HOMEPAGE_URL "https://github.com/tree-sitter-grammars/tree-sitter-gitattributes" 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 | find_program(TREE_SITTER_CLI tree-sitter DOC "Tree-sitter CLI") 19 | 20 | add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" 21 | DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json" 22 | COMMAND "${TREE_SITTER_CLI}" generate src/grammar.json 23 | --abi=${TREE_SITTER_ABI_VERSION} 24 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 25 | COMMENT "Generating parser.c") 26 | 27 | add_library(tree-sitter-gitattributes src/parser.c) 28 | if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/scanner.c) 29 | target_sources(tree-sitter-gitattributes PRIVATE src/scanner.c) 30 | endif() 31 | target_include_directories(tree-sitter-gitattributes 32 | PRIVATE src 33 | INTERFACE $ 34 | $) 35 | 36 | target_compile_definitions(tree-sitter-gitattributes PRIVATE 37 | $<$:TREE_SITTER_REUSE_ALLOCATOR> 38 | $<$:TREE_SITTER_DEBUG>) 39 | 40 | set_target_properties(tree-sitter-gitattributes 41 | PROPERTIES 42 | C_STANDARD 11 43 | POSITION_INDEPENDENT_CODE ON 44 | SOVERSION "${TREE_SITTER_ABI_VERSION}.${PROJECT_VERSION_MAJOR}" 45 | DEFINE_SYMBOL "") 46 | 47 | configure_file(bindings/c/tree-sitter-gitattributes.pc.in 48 | "${CMAKE_CURRENT_BINARY_DIR}/tree-sitter-gitattributes.pc" @ONLY) 49 | 50 | include(GNUInstallDirs) 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-gitattributes.pc" 56 | DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig") 57 | install(TARGETS tree-sitter-gitattributes 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/gitattributes") 63 | 64 | add_custom_target(ts-test "${TREE_SITTER_CLI}" test 65 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 66 | COMMENT "tree-sitter test") 67 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "cc" 16 | version = "1.2.24" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" 19 | dependencies = [ 20 | "shlex", 21 | ] 22 | 23 | [[package]] 24 | name = "equivalent" 25 | version = "1.0.2" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 28 | 29 | [[package]] 30 | name = "hashbrown" 31 | version = "0.15.3" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" 34 | 35 | [[package]] 36 | name = "indexmap" 37 | version = "2.9.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" 40 | dependencies = [ 41 | "equivalent", 42 | "hashbrown", 43 | ] 44 | 45 | [[package]] 46 | name = "itoa" 47 | version = "1.0.15" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 50 | 51 | [[package]] 52 | name = "memchr" 53 | version = "2.7.4" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 56 | 57 | [[package]] 58 | name = "proc-macro2" 59 | version = "1.0.95" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 62 | dependencies = [ 63 | "unicode-ident", 64 | ] 65 | 66 | [[package]] 67 | name = "quote" 68 | version = "1.0.40" 69 | source = "registry+https://github.com/rust-lang/crates.io-index" 70 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 71 | dependencies = [ 72 | "proc-macro2", 73 | ] 74 | 75 | [[package]] 76 | name = "regex" 77 | version = "1.11.1" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 80 | dependencies = [ 81 | "aho-corasick", 82 | "memchr", 83 | "regex-automata", 84 | "regex-syntax", 85 | ] 86 | 87 | [[package]] 88 | name = "regex-automata" 89 | version = "0.4.9" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 92 | dependencies = [ 93 | "aho-corasick", 94 | "memchr", 95 | "regex-syntax", 96 | ] 97 | 98 | [[package]] 99 | name = "regex-syntax" 100 | version = "0.8.5" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 103 | 104 | [[package]] 105 | name = "ryu" 106 | version = "1.0.20" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 109 | 110 | [[package]] 111 | name = "serde" 112 | version = "1.0.219" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 115 | dependencies = [ 116 | "serde_derive", 117 | ] 118 | 119 | [[package]] 120 | name = "serde_derive" 121 | version = "1.0.219" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 124 | dependencies = [ 125 | "proc-macro2", 126 | "quote", 127 | "syn", 128 | ] 129 | 130 | [[package]] 131 | name = "serde_json" 132 | version = "1.0.140" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 135 | dependencies = [ 136 | "indexmap", 137 | "itoa", 138 | "memchr", 139 | "ryu", 140 | "serde", 141 | ] 142 | 143 | [[package]] 144 | name = "shlex" 145 | version = "1.3.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 148 | 149 | [[package]] 150 | name = "streaming-iterator" 151 | version = "0.1.9" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" 154 | 155 | [[package]] 156 | name = "syn" 157 | version = "2.0.101" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 160 | dependencies = [ 161 | "proc-macro2", 162 | "quote", 163 | "unicode-ident", 164 | ] 165 | 166 | [[package]] 167 | name = "tree-sitter" 168 | version = "0.25.5" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "ac5fff5c47490dfdf473b5228039bfacad9d765d9b6939d26bf7cc064c1c7822" 171 | dependencies = [ 172 | "cc", 173 | "regex", 174 | "regex-syntax", 175 | "serde_json", 176 | "streaming-iterator", 177 | "tree-sitter-language", 178 | ] 179 | 180 | [[package]] 181 | name = "tree-sitter-gitattributes" 182 | version = "0.2.0" 183 | dependencies = [ 184 | "cc", 185 | "tree-sitter", 186 | "tree-sitter-language", 187 | ] 188 | 189 | [[package]] 190 | name = "tree-sitter-language" 191 | version = "0.1.5" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "c4013970217383f67b18aef68f6fb2e8d409bc5755227092d32efb0422ba24b8" 194 | 195 | [[package]] 196 | name = "unicode-ident" 197 | version = "1.0.18" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 200 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-sitter-gitattributes" 3 | description = "gitattributes grammar for tree-sitter" 4 | version = "0.2.0" 5 | authors = ["ObserverOfTime "] 6 | license = "MIT" 7 | readme = "README.md" 8 | keywords = ["incremental", "parsing", "tree-sitter", "gitattributes"] 9 | categories = ["parser-implementations", "parsing", "text-editors"] 10 | repository = "https://github.com/tree-sitter-grammars/tree-sitter-gitattributes" 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.4" 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 ObserverOfTime 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) 2 | $(error Windows is not supported) 3 | endif 4 | 5 | LANGUAGE_NAME := tree-sitter-gitattributes 6 | HOMEPAGE_URL := https://github.com/tree-sitter-grammars/tree-sitter-gitattributes 7 | VERSION := 0.2.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/gitattributes '$(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/gitattributes 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/gitattributes 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 100 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "SwiftTreeSitter", 6 | "repositoryURL": "https://github.com/tree-sitter/swift-tree-sitter", 7 | "state": { 8 | "branch": null, 9 | "revision": "36aa61d1b531f744f35229f010efba9c6d6cbbdd", 10 | "version": "0.9.0" 11 | } 12 | }, 13 | { 14 | "package": "TreeSitter", 15 | "repositoryURL": "https://github.com/tree-sitter/tree-sitter", 16 | "state": { 17 | "branch": null, 18 | "revision": "d97db6d63507eb62c536bcb2c4ac7d70c8ec665e", 19 | "version": "0.23.2" 20 | } 21 | } 22 | ] 23 | }, 24 | "version": 1 25 | } 26 | -------------------------------------------------------------------------------- /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: "TreeSitterGitAttributes", 13 | products: [ 14 | .library(name: "TreeSitterGitAttributes", targets: ["TreeSitterGitAttributes"]), 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: "TreeSitterGitAttributes", 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: "TreeSitterGitAttributesTests", 33 | dependencies: [ 34 | "SwiftTreeSitter", 35 | "TreeSitterGitAttributes", 36 | ], 37 | path: "bindings/swift/TreeSitterGitAttributesTests" 38 | ) 39 | ], 40 | cLanguageStandard: .c11 41 | ) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tree-sitter-gitattributes 2 | 3 | [![CI][ci]](https://github.com/tree-sitter-grammars/tree-sitter-gitattributes/actions/workflows/ci.yml) 4 | [![discord][discord]](https://discord.gg/w7nTvsVJhm) 5 | [![matrix][matrix]](https://matrix.to/#/#tree-sitter-chat:matrix.org) 6 | [![npm][npm]](https://www.npmjs.com/package/tree-sitter-gitattributes) 7 | [![crates][crates]](https://crates.io/crates/tree-sitter-gitattributes) 8 | [![pypi][pypi]](https://pypi.org/project/tree-sitter-gitattributes/) 9 | 10 | A tree-sitter parser for `.gitattributes` files. 11 | 12 | ## References 13 | 14 | * [gitattributes Documentation](https://git-scm.com/docs/gitattributes) 15 | * [gitignore Documentation (Pattern Format)](https://git-scm.com/docs/gitignore#_pattern_format) 16 | * [git source code (`attr.c`)](https://github.com/git/git/blob/master/attr.c) 17 | * [git source code (`wildmatch.c`)](https://github.com/git/git/blob/master/wildmatch.c) 18 | * [ANSI-C Quoting](https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html) 19 | * [Character Classes and Bracket Expressions](https://www.gnu.org/software/grep/manual/html_node/Character-Classes-and-Bracket-Expressions.html) 20 | 21 | ## Editors 22 | 23 | - [x] Neovim 24 | - [ ] Helix _(has alternative)_ 25 | - [x] Emacs 26 | - [ ] Zed 27 | 28 | [ci]: https://img.shields.io/github/actions/workflow/status/tree-sitter-grammars/tree-sitter-gitattributes/ci.yml?logo=github&label=CI 29 | [discord]: https://img.shields.io/discord/1063097320771698699?logo=discord&label=discord 30 | [matrix]: https://img.shields.io/matrix/tree-sitter-chat%3Amatrix.org?logo=matrix&label=matrix 31 | [npm]: https://img.shields.io/npm/v/tree-sitter-gitattributes?logo=npm 32 | [crates]: https://img.shields.io/crates/v/tree-sitter-gitattributes?logo=rust 33 | [pypi]: https://img.shields.io/pypi/v/tree-sitter-gitattributes?logo=pypi&logoColor=ffd242 34 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "tree_sitter_gitattributes_binding", 5 | "dependencies": [ 6 | " 2 | 3 | typedef struct TSLanguage TSLanguage; 4 | 5 | extern "C" TSLanguage *tree_sitter_gitattributes(); 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::New(env, tree_sitter_gitattributes()); 14 | language.TypeTag(&LANGUAGE_TYPE_TAG); 15 | exports["language"] = language; 16 | return exports; 17 | } 18 | 19 | NODE_API_MODULE(tree_sitter_gitattributes_binding, Init) 20 | -------------------------------------------------------------------------------- /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 | }); 10 | -------------------------------------------------------------------------------- /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; 28 | -------------------------------------------------------------------------------- /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-gitattributes.node`) 7 | : require("node-gyp-build")(root); 8 | 9 | try { 10 | module.exports.nodeTypeInfo = require("../../src/node-types.json"); 11 | } catch (_) {} 12 | -------------------------------------------------------------------------------- /bindings/python/tests/test_binding.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import tree_sitter 4 | import tree_sitter_gitattributes 5 | 6 | 7 | class TestLanguage(TestCase): 8 | def test_can_load_grammar(self): 9 | try: 10 | tree_sitter.Language(tree_sitter_gitattributes.language()) 11 | except Exception: 12 | self.fail("Error loading gitattributes grammar") 13 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_gitattributes/__init__.py: -------------------------------------------------------------------------------- 1 | """gitattributes grammar for tree-sitter""" 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 | if name == "HIGHLIGHTS_QUERY": 16 | return _get_query("HIGHLIGHTS_QUERY", "highlights.scm") 17 | raise AttributeError(f"module {__name__!r} has no attribute {name!r}") 18 | 19 | 20 | __all__ = [ 21 | "language", 22 | "HIGHLIGHTS_QUERY", 23 | ] 24 | 25 | 26 | def __dir__(): 27 | return sorted(__all__ + [ 28 | "__all__", "__builtins__", "__cached__", "__doc__", "__file__", 29 | "__loader__", "__name__", "__package__", "__path__", "__spec__", 30 | ]) 31 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_gitattributes/__init__.pyi: -------------------------------------------------------------------------------- 1 | from typing import Final 2 | 3 | HIGHLIGHTS_QUERY: Final[str] 4 | 5 | def language() -> object: ... 6 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_gitattributes/binding.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct TSLanguage TSLanguage; 4 | 5 | TSLanguage *tree_sitter_gitattributes(void); 6 | 7 | static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) { 8 | return PyCapsule_New(tree_sitter_gitattributes(), "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 | } 36 | -------------------------------------------------------------------------------- /bindings/python/tree_sitter_gitattributes/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tree-sitter-grammars/tree-sitter-gitattributes/f23072a51e1c764d6c5bf461194a775c8a5c7a95/bindings/python/tree_sitter_gitattributes/py.typed -------------------------------------------------------------------------------- /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-gitattributes"); 21 | } 22 | -------------------------------------------------------------------------------- /bindings/rust/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides gitattributes 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 | //! * text=auto 9 | //! "#; 10 | //! let mut parser = tree_sitter::Parser::new(); 11 | //! let language = tree_sitter_gitattributes::LANGUAGE; 12 | //! parser 13 | //! .set_language(&language.into()) 14 | //! .expect("Error loading gitattributes parser"); 15 | //! let tree = parser.parse(code, None).unwrap(); 16 | //! assert!(!tree.root_node().has_error()); 17 | //! ``` 18 | //! 19 | //! [`Parser`]: https://docs.rs/tree-sitter/0.25.4/tree_sitter/struct.Parser.html 20 | //! [tree-sitter]: https://tree-sitter.github.io/ 21 | 22 | use tree_sitter_language::LanguageFn; 23 | 24 | extern "C" { 25 | fn tree_sitter_gitattributes() -> *const (); 26 | } 27 | 28 | /// The tree-sitter [`LanguageFn`] for this grammar. 29 | pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_gitattributes) }; 30 | 31 | /// The content of the [`node-types.json`] file for this grammar. 32 | /// 33 | /// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers/6-static-node-types 34 | pub const NODE_TYPES: &str = include_str!("../../src/node-types.json"); 35 | 36 | /// The syntax highlighting queries for this grammar. 37 | pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm"); 38 | 39 | #[cfg(test)] 40 | mod tests { 41 | #[test] 42 | fn test_can_load_grammar() { 43 | let mut parser = tree_sitter::Parser::new(); 44 | parser 45 | .set_language(&super::LANGUAGE.into()) 46 | .expect("Error loading gitattributes parser"); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bindings/swift/TreeSitterGitAttributes/gitattributes.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_GITATTRIBUTES_H_ 2 | #define TREE_SITTER_GITATTRIBUTES_H_ 3 | 4 | typedef struct TSLanguage TSLanguage; 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | const TSLanguage *tree_sitter_gitattributes(void); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif // TREE_SITTER_GITATTRIBUTES_H_ 17 | -------------------------------------------------------------------------------- /bindings/swift/TreeSitterGitAttributesTests/TreeSitterGitAttributesTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import SwiftTreeSitter 3 | import TreeSitterGitAttributes 4 | 5 | final class TreeSitterGitAttributesTests: XCTestCase { 6 | func testCanLoadGrammar() throws { 7 | let parser = Parser() 8 | let language = Language(language: tree_sitter_gitattributes()) 9 | XCTAssertNoThrow(try parser.setLanguage(language), 10 | "Error loading gitattributes grammar") 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tree-sitter-grammars/tree-sitter-gitattributes 2 | 3 | go 1.22 4 | 5 | require github.com/tree-sitter/go-tree-sitter v0.24.0 6 | 7 | require github.com/mattn/go-pointer v0.0.1 // indirect 8 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= 4 | github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= 5 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 6 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 7 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 8 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 9 | github.com/tree-sitter/go-tree-sitter v0.24.0 h1:kRZb6aBNfcI/u0Qh8XEt3zjNVnmxTisDBN+kXK0xRYQ= 10 | github.com/tree-sitter/go-tree-sitter v0.24.0/go.mod h1:x681iFVoLMEwOSIHA1chaLkXlroXEN7WY+VHGFaoDbk= 11 | github.com/tree-sitter/tree-sitter-c v0.21.5-0.20240818205408-927da1f210eb h1:A8425heRM8mylnv4H58FPUiH+aYivyitre0PzxrfmWs= 12 | github.com/tree-sitter/tree-sitter-c v0.21.5-0.20240818205408-927da1f210eb/go.mod h1:dOF6gtQiF9UwNh995T5OphYmtIypkjsp3ap7r9AN/iA= 13 | github.com/tree-sitter/tree-sitter-cpp v0.22.4-0.20240818224355-b1a4e2b25148 h1:AfFPZwtwGN01BW1jDdqBVqscTwetvMpydqYZz57RSlc= 14 | github.com/tree-sitter/tree-sitter-cpp v0.22.4-0.20240818224355-b1a4e2b25148/go.mod h1:Bh6U3viD57rFXRYIQ+kmiYtr+1Bx0AceypDLJJSyi9s= 15 | github.com/tree-sitter/tree-sitter-embedded-template v0.21.1-0.20240819044651-ffbf64942c33 h1:TwqSV3qLp3tKSqirGLRHnjFk9Tc2oy57LIl+FQ4GjI4= 16 | github.com/tree-sitter/tree-sitter-embedded-template v0.21.1-0.20240819044651-ffbf64942c33/go.mod h1:CvCKCt3v04Ufos1zZnNCelBDeCGRpPucaN8QczoUsN4= 17 | github.com/tree-sitter/tree-sitter-go v0.21.3-0.20240818010209-8c0f0e7a6012 h1:Xvxck3tE5FW7F7bTS97iNM2ADMyCMJztVqn5HYKdJGo= 18 | github.com/tree-sitter/tree-sitter-go v0.21.3-0.20240818010209-8c0f0e7a6012/go.mod h1:T40D0O1cPvUU/+AmiXVXy1cncYQT6wem4Z0g4SfAYvY= 19 | github.com/tree-sitter/tree-sitter-html v0.20.5-0.20240818004741-d11201a263d0 h1:c46K6uh5Dz00zJeU9BfjXdb8I+E4RkUdfnWJpQADXFo= 20 | github.com/tree-sitter/tree-sitter-html v0.20.5-0.20240818004741-d11201a263d0/go.mod h1:hcNt/kOJHcIcuMvouE7LJcYdeFUFbVpBJ6d4wmOA+tU= 21 | github.com/tree-sitter/tree-sitter-java v0.21.1-0.20240824015150-576d8097e495 h1:jrt4qbJVEFs4H93/ITxygHc6u0TGqAkkate7TQ4wFSA= 22 | github.com/tree-sitter/tree-sitter-java v0.21.1-0.20240824015150-576d8097e495/go.mod h1:oyaR7fLnRV0hT9z6qwE9GkaeTom/hTDwK3H2idcOJFc= 23 | github.com/tree-sitter/tree-sitter-javascript v0.21.5-0.20240818005344-15887341e5b5 h1:om4X9AVg3asL8gxNJDcz4e/Wp+VpQj1PY3uJXKr6EOg= 24 | github.com/tree-sitter/tree-sitter-javascript v0.21.5-0.20240818005344-15887341e5b5/go.mod h1:nNqgPoV/h9uYWk6kYEFdEAhNVOacpfpRW5SFmdaP4tU= 25 | github.com/tree-sitter/tree-sitter-json v0.21.1-0.20240818005659-bdd69eb8c8a5 h1:pfV3G3k7NCKqKk8THBmyuh2zA33lgYHS3GVrzRR8ry4= 26 | github.com/tree-sitter/tree-sitter-json v0.21.1-0.20240818005659-bdd69eb8c8a5/go.mod h1:GbMKRjLfk0H+PI7nLi1Sx5lHf5wCpLz9al8tQYSxpEk= 27 | github.com/tree-sitter/tree-sitter-php v0.22.9-0.20240819002312-a552625b56c1 h1:ZXZMDwE+IhUtGug4Brv6NjJWUU3rfkZBKpemf6RY8/g= 28 | github.com/tree-sitter/tree-sitter-php v0.22.9-0.20240819002312-a552625b56c1/go.mod h1:UKCLuYnJ312Mei+3cyTmGOHzn0YAnaPRECgJmHtzrqs= 29 | github.com/tree-sitter/tree-sitter-python v0.21.1-0.20240818005537-55a9b8a4fbfb h1:EXEM82lFM7JjJb6qiKZXkpIDaCcbV2obNn82ghwj9lw= 30 | github.com/tree-sitter/tree-sitter-python v0.21.1-0.20240818005537-55a9b8a4fbfb/go.mod h1:lXCF1nGG5Dr4J3BTS0ObN4xJCCICiSu/b+Xe/VqMV7g= 31 | github.com/tree-sitter/tree-sitter-ruby v0.21.1-0.20240818211811-7dbc1e2d0e2d h1:fcYCvoXdcP1uRQYXqJHRy6Hec+uKScQdKVtMwK9JeCI= 32 | github.com/tree-sitter/tree-sitter-ruby v0.21.1-0.20240818211811-7dbc1e2d0e2d/go.mod h1:T1nShQ4v5AJtozZ8YyAS4uzUtDAJj/iv4YfwXSbUHzg= 33 | github.com/tree-sitter/tree-sitter-rust v0.21.3-0.20240818005432-2b43eafe6447 h1:o9alBu1J/WjrcTKEthYtXmdkDc5OVXD+PqlvnEZ0Lzc= 34 | github.com/tree-sitter/tree-sitter-rust v0.21.3-0.20240818005432-2b43eafe6447/go.mod h1:1Oh95COkkTn6Ezp0vcMbvfhRP5gLeqqljR0BYnBzWvc= 35 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 36 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 37 | -------------------------------------------------------------------------------- /grammar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file gitattributes grammar for tree-sitter 3 | * @author ObserverOfTime 4 | * @license MIT 5 | */ 6 | 7 | /// 8 | // @ts-check 9 | 10 | /** Built-in attribute names */ 11 | const BUILTIN_ATTRIBUTES = [ 12 | 'text', 13 | 'eol', 14 | 'crlf', 15 | 'working-tree-encoding', 16 | 'ident', 17 | 'filter', 18 | 'diff', 19 | 'merge', 20 | 'whitespace', 21 | 'export-ignore', 22 | 'export-subst', 23 | 'delta', 24 | 'encoding', 25 | 'binary' 26 | ]; 27 | 28 | /** POSIX character classes */ 29 | const POSIX_CLASSES = [ 30 | 'alnum', 31 | 'alpha', 32 | 'blank', 33 | 'cntrl', 34 | 'digit', 35 | 'graph', 36 | 'lower', 37 | 'print', 38 | 'punct', 39 | 'space', 40 | 'upper', 41 | 'xdigit' 42 | ]; 43 | 44 | /** 45 | * Returns a quoted or unquoted pattern sequence 46 | * @param {GrammarSymbols} $ the symbols of the grammar 47 | * @param {boolean} quoted `true` if the pattern is in quotes 48 | */ 49 | const __pattern = ($, quoted) => [ 50 | optional(alias('!', $.pattern_negation)), // error 51 | optional(field('absolute', $.dir_sep)), 52 | quoted ? $._quoted_pattern : $._pattern, 53 | repeat(seq(field('relative', $.dir_sep), $._pattern)), 54 | optional(alias($.dir_sep, $.trailing_slash)) // error 55 | ] 56 | 57 | module.exports = grammar({ 58 | name: 'gitattributes', 59 | 60 | extras: _ => [], 61 | 62 | word: $ => $.attr_name, 63 | 64 | rules: { 65 | file: $ => repeat($._line), 66 | 67 | _line: $ => seq( 68 | optional($._space), 69 | optional(choice( 70 | $.comment, 71 | $._attr_list, 72 | $.macro_def 73 | )), 74 | choice($._eol, $._eof) 75 | ), 76 | 77 | _attr_list: $ => seq( 78 | prec.left(choice($.pattern, $.quoted_pattern)), 79 | repeat1(seq($._space, $.attribute)), 80 | optional($._space) 81 | ), 82 | 83 | pattern: $ => seq(...__pattern($, false)), 84 | 85 | _pattern: $ => repeat1(choice( 86 | $._pattern_char, 87 | $.wildcard, 88 | $.escaped_char, 89 | $.range_notation, 90 | alias('\\', $.redundant_escape) // error 91 | )), 92 | 93 | quoted_pattern: $ => seq( 94 | '"', ...__pattern($, true), '"' 95 | ), 96 | 97 | _quoted_pattern: $ => repeat1(choice( 98 | /[^\n/]/, 99 | choice($.ansi_c_escape, $.escaped_char), 100 | alias('\\', $.redundant_escape), 101 | )), 102 | 103 | _pattern_char: _ => /[^\s/?*]/, 104 | 105 | escaped_char: _ => /\\[\\\[\]!?*]/, 106 | 107 | ansi_c_escape: $ => prec.right( 108 | 1, choice( 109 | $._special_char, $._char_code 110 | ) 111 | ), 112 | 113 | _special_char: _ => /\\[abeEfnrtv\\'"?]/, 114 | 115 | _char_code: $ => choice( 116 | $._octal_code, 117 | $._hex_code, 118 | $._unicode_code, 119 | $._control_code 120 | ), 121 | 122 | _octal_code: _ => /\\\d{1,3}/, 123 | 124 | _hex_code: _ => /\\x[0-9A-Fa-f]{2}/, 125 | 126 | _unicode_code: _ => choice( 127 | /\\u[0-9A-Fa-f]{4}/, 128 | /\\U[0-9A-Fa-f]{8}/ 129 | ), 130 | 131 | _control_code: _ => token(choice( 132 | /\\c[\x00-\x5B\x5D-\x7F]/, /\\c\\\\/ 133 | )), 134 | 135 | range_notation: $ => prec.left(seq( 136 | '[', 137 | optional(alias( 138 | token(choice('!', '^')), 139 | $.range_negation 140 | )), 141 | repeat1(choice( 142 | $.class_range, 143 | $.character_class, 144 | $._class_char, 145 | $.ansi_c_escape, 146 | '-' 147 | )), 148 | ']' 149 | )), 150 | 151 | class_range: $ => prec.right( 152 | 2, seq( 153 | choice($._class_char, $._char_code), 154 | '-', 155 | choice($._class_char, $._char_code) 156 | ) 157 | ), 158 | 159 | _class_char: _ => token(choice( 160 | /[^-\\\]\n]/, /\\[-\\\[\]!^]/ 161 | )), 162 | 163 | character_class: _ => token(seq( 164 | '[:', choice(...POSIX_CLASSES), ':]' 165 | )), 166 | 167 | wildcard: _ => token(choice('?', '*', '**')), 168 | 169 | dir_sep: _ => '/', 170 | 171 | attribute: $ => choice( 172 | seq( 173 | choice($.attr_name, $.builtin_attr), 174 | $._attr_value 175 | ), 176 | $._prefixed_attr 177 | ), 178 | 179 | _prefixed_attr: $ => seq( 180 | optional(choice( 181 | alias('!', $.attr_reset), 182 | alias('-', $.attr_unset), 183 | )), 184 | choice($.attr_name, $.builtin_attr), 185 | prec(-1, optional( 186 | alias($._attr_value, $.ignored_value) 187 | )) 188 | ), 189 | 190 | _attr_value: $ => seq( 191 | alias('=', $.attr_set), 192 | choice( 193 | prec(2, alias( 194 | token(choice('true', 'false')), 195 | $.boolean_value 196 | )), 197 | prec(1, alias(/\S+/, $.string_value)) 198 | ) 199 | ), 200 | 201 | attr_name: _ => /[A-Za-z0-9_.][-A-Za-z0-9_.]*/, 202 | 203 | builtin_attr: _ => prec(1, choice(...BUILTIN_ATTRIBUTES)), 204 | 205 | macro_def: $ => seq( 206 | alias('[attr]', $.macro_tag), 207 | field('macro_name', $.attr_name), 208 | repeat1(seq($._space, $.attribute)), 209 | optional($._space) 210 | ), 211 | 212 | comment: _ => seq('#', repeat(/[^\n]/)), 213 | 214 | _space: _ => prec(-1, /[ \t]+/), 215 | 216 | _eol: _ => /\r?\n/, 217 | 218 | _eof: _ => '\0' 219 | } 220 | }); 221 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-gitattributes", 3 | "version": "0.2.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "tree-sitter-gitattributes", 9 | "version": "0.2.0", 10 | "hasInstallScript": true, 11 | "license": "MIT", 12 | "dependencies": { 13 | "node-addon-api": "^8.3.1", 14 | "node-gyp-build": "^4.8.4" 15 | }, 16 | "devDependencies": { 17 | "prebuildify": "^6.0.1", 18 | "tree-sitter-cli": "^0.25.4" 19 | }, 20 | "peerDependencies": { 21 | "tree-sitter": "^0.22.4" 22 | }, 23 | "peerDependenciesMeta": { 24 | "tree-sitter": { 25 | "optional": false 26 | } 27 | } 28 | }, 29 | "node_modules/base64-js": { 30 | "version": "1.5.1", 31 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 32 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 33 | "dev": true, 34 | "funding": [ 35 | { 36 | "type": "github", 37 | "url": "https://github.com/sponsors/feross" 38 | }, 39 | { 40 | "type": "patreon", 41 | "url": "https://www.patreon.com/feross" 42 | }, 43 | { 44 | "type": "consulting", 45 | "url": "https://feross.org/support" 46 | } 47 | ], 48 | "license": "MIT" 49 | }, 50 | "node_modules/bl": { 51 | "version": "4.1.0", 52 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 53 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 54 | "dev": true, 55 | "license": "MIT", 56 | "dependencies": { 57 | "buffer": "^5.5.0", 58 | "inherits": "^2.0.4", 59 | "readable-stream": "^3.4.0" 60 | } 61 | }, 62 | "node_modules/buffer": { 63 | "version": "5.7.1", 64 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 65 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 66 | "dev": true, 67 | "funding": [ 68 | { 69 | "type": "github", 70 | "url": "https://github.com/sponsors/feross" 71 | }, 72 | { 73 | "type": "patreon", 74 | "url": "https://www.patreon.com/feross" 75 | }, 76 | { 77 | "type": "consulting", 78 | "url": "https://feross.org/support" 79 | } 80 | ], 81 | "license": "MIT", 82 | "dependencies": { 83 | "base64-js": "^1.3.1", 84 | "ieee754": "^1.1.13" 85 | } 86 | }, 87 | "node_modules/chownr": { 88 | "version": "1.1.4", 89 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 90 | "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", 91 | "dev": true, 92 | "license": "ISC" 93 | }, 94 | "node_modules/end-of-stream": { 95 | "version": "1.4.4", 96 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 97 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 98 | "dev": true, 99 | "license": "MIT", 100 | "dependencies": { 101 | "once": "^1.4.0" 102 | } 103 | }, 104 | "node_modules/fs-constants": { 105 | "version": "1.0.0", 106 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 107 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", 108 | "dev": true, 109 | "license": "MIT" 110 | }, 111 | "node_modules/ieee754": { 112 | "version": "1.2.1", 113 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 114 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 115 | "dev": true, 116 | "funding": [ 117 | { 118 | "type": "github", 119 | "url": "https://github.com/sponsors/feross" 120 | }, 121 | { 122 | "type": "patreon", 123 | "url": "https://www.patreon.com/feross" 124 | }, 125 | { 126 | "type": "consulting", 127 | "url": "https://feross.org/support" 128 | } 129 | ], 130 | "license": "BSD-3-Clause" 131 | }, 132 | "node_modules/inherits": { 133 | "version": "2.0.4", 134 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 135 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 136 | "dev": true, 137 | "license": "ISC" 138 | }, 139 | "node_modules/minimist": { 140 | "version": "1.2.8", 141 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 142 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 143 | "dev": true, 144 | "license": "MIT", 145 | "funding": { 146 | "url": "https://github.com/sponsors/ljharb" 147 | } 148 | }, 149 | "node_modules/mkdirp-classic": { 150 | "version": "0.5.3", 151 | "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", 152 | "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", 153 | "dev": true, 154 | "license": "MIT" 155 | }, 156 | "node_modules/node-abi": { 157 | "version": "3.75.0", 158 | "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", 159 | "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", 160 | "dev": true, 161 | "license": "MIT", 162 | "dependencies": { 163 | "semver": "^7.3.5" 164 | }, 165 | "engines": { 166 | "node": ">=10" 167 | } 168 | }, 169 | "node_modules/node-addon-api": { 170 | "version": "8.3.1", 171 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", 172 | "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", 173 | "license": "MIT", 174 | "engines": { 175 | "node": "^18 || ^20 || >= 21" 176 | } 177 | }, 178 | "node_modules/node-gyp-build": { 179 | "version": "4.8.4", 180 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", 181 | "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", 182 | "license": "MIT", 183 | "bin": { 184 | "node-gyp-build": "bin.js", 185 | "node-gyp-build-optional": "optional.js", 186 | "node-gyp-build-test": "build-test.js" 187 | } 188 | }, 189 | "node_modules/npm-run-path": { 190 | "version": "3.1.0", 191 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", 192 | "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==", 193 | "dev": true, 194 | "license": "MIT", 195 | "dependencies": { 196 | "path-key": "^3.0.0" 197 | }, 198 | "engines": { 199 | "node": ">=8" 200 | } 201 | }, 202 | "node_modules/once": { 203 | "version": "1.4.0", 204 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 205 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 206 | "dev": true, 207 | "license": "ISC", 208 | "dependencies": { 209 | "wrappy": "1" 210 | } 211 | }, 212 | "node_modules/path-key": { 213 | "version": "3.1.1", 214 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 215 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 216 | "dev": true, 217 | "license": "MIT", 218 | "engines": { 219 | "node": ">=8" 220 | } 221 | }, 222 | "node_modules/prebuildify": { 223 | "version": "6.0.1", 224 | "resolved": "https://registry.npmjs.org/prebuildify/-/prebuildify-6.0.1.tgz", 225 | "integrity": "sha512-8Y2oOOateom/s8dNBsGIcnm6AxPmLH4/nanQzL5lQMU+sC0CMhzARZHizwr36pUPLdvBnOkCNQzxg4djuFSgIw==", 226 | "dev": true, 227 | "license": "MIT", 228 | "dependencies": { 229 | "minimist": "^1.2.5", 230 | "mkdirp-classic": "^0.5.3", 231 | "node-abi": "^3.3.0", 232 | "npm-run-path": "^3.1.0", 233 | "pump": "^3.0.0", 234 | "tar-fs": "^2.1.0" 235 | }, 236 | "bin": { 237 | "prebuildify": "bin.js" 238 | } 239 | }, 240 | "node_modules/pump": { 241 | "version": "3.0.2", 242 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", 243 | "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", 244 | "dev": true, 245 | "license": "MIT", 246 | "dependencies": { 247 | "end-of-stream": "^1.1.0", 248 | "once": "^1.3.1" 249 | } 250 | }, 251 | "node_modules/readable-stream": { 252 | "version": "3.6.2", 253 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 254 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 255 | "dev": true, 256 | "license": "MIT", 257 | "dependencies": { 258 | "inherits": "^2.0.3", 259 | "string_decoder": "^1.1.1", 260 | "util-deprecate": "^1.0.1" 261 | }, 262 | "engines": { 263 | "node": ">= 6" 264 | } 265 | }, 266 | "node_modules/safe-buffer": { 267 | "version": "5.2.1", 268 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 269 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 270 | "dev": true, 271 | "funding": [ 272 | { 273 | "type": "github", 274 | "url": "https://github.com/sponsors/feross" 275 | }, 276 | { 277 | "type": "patreon", 278 | "url": "https://www.patreon.com/feross" 279 | }, 280 | { 281 | "type": "consulting", 282 | "url": "https://feross.org/support" 283 | } 284 | ], 285 | "license": "MIT" 286 | }, 287 | "node_modules/semver": { 288 | "version": "7.7.2", 289 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", 290 | "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", 291 | "dev": true, 292 | "license": "ISC", 293 | "bin": { 294 | "semver": "bin/semver.js" 295 | }, 296 | "engines": { 297 | "node": ">=10" 298 | } 299 | }, 300 | "node_modules/string_decoder": { 301 | "version": "1.3.0", 302 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 303 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 304 | "dev": true, 305 | "license": "MIT", 306 | "dependencies": { 307 | "safe-buffer": "~5.2.0" 308 | } 309 | }, 310 | "node_modules/tar-fs": { 311 | "version": "2.1.3", 312 | "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", 313 | "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", 314 | "dev": true, 315 | "license": "MIT", 316 | "dependencies": { 317 | "chownr": "^1.1.1", 318 | "mkdirp-classic": "^0.5.2", 319 | "pump": "^3.0.0", 320 | "tar-stream": "^2.1.4" 321 | } 322 | }, 323 | "node_modules/tar-stream": { 324 | "version": "2.2.0", 325 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", 326 | "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", 327 | "dev": true, 328 | "license": "MIT", 329 | "dependencies": { 330 | "bl": "^4.0.3", 331 | "end-of-stream": "^1.4.1", 332 | "fs-constants": "^1.0.0", 333 | "inherits": "^2.0.3", 334 | "readable-stream": "^3.1.1" 335 | }, 336 | "engines": { 337 | "node": ">=6" 338 | } 339 | }, 340 | "node_modules/tree-sitter": { 341 | "version": "0.22.4", 342 | "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.22.4.tgz", 343 | "integrity": "sha512-usbHZP9/oxNsUY65MQUsduGRqDHQOou1cagUSwjhoSYAmSahjQDAVsh9s+SlZkn8X8+O1FULRGwHu7AFP3kjzg==", 344 | "hasInstallScript": true, 345 | "license": "MIT", 346 | "peer": true, 347 | "dependencies": { 348 | "node-addon-api": "^8.3.0", 349 | "node-gyp-build": "^4.8.4" 350 | } 351 | }, 352 | "node_modules/tree-sitter-cli": { 353 | "version": "0.25.5", 354 | "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.25.5.tgz", 355 | "integrity": "sha512-aLAyf+T3Y5psFZPgd7bUovhhJtplD1hk1mIwMsSZGwoEa6iGgqeGYmV9zgjatT5bMPUfcKX9x1xZfYim7aItjQ==", 356 | "dev": true, 357 | "hasInstallScript": true, 358 | "license": "MIT", 359 | "bin": { 360 | "tree-sitter": "cli.js" 361 | }, 362 | "engines": { 363 | "node": ">=12.0.0" 364 | } 365 | }, 366 | "node_modules/util-deprecate": { 367 | "version": "1.0.2", 368 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 369 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 370 | "dev": true, 371 | "license": "MIT" 372 | }, 373 | "node_modules/wrappy": { 374 | "version": "1.0.2", 375 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 376 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 377 | "dev": true, 378 | "license": "ISC" 379 | } 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-gitattributes", 3 | "version": "0.2.0", 4 | "description": "gitattributes grammar for tree-sitter", 5 | "repository": "https://github.com/tree-sitter-grammars/tree-sitter-gitattributes", 6 | "license": "MIT", 7 | "author": { 8 | "name": "ObserverOfTime", 9 | "email": "chronobserver@disroot.org" 10 | }, 11 | "main": "bindings/node", 12 | "types": "bindings/node", 13 | "keywords": [ 14 | "incremental", 15 | "parsing", 16 | "tree-sitter", 17 | "gitattributes" 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.3.1", 31 | "node-gyp-build": "^4.8.4" 32 | }, 33 | "devDependencies": { 34 | "prebuildify": "^6.0.1", 35 | "tree-sitter-cli": "^0.25.4" 36 | }, 37 | "peerDependencies": { 38 | "tree-sitter": "^0.22.4" 39 | }, 40 | "peerDependenciesMeta": { 41 | "tree-sitter": { 42 | "optional": true 43 | } 44 | }, 45 | "scripts": { 46 | "install": "node-gyp-build", 47 | "prestart": "tree-sitter build --wasm", 48 | "start": "tree-sitter playground", 49 | "test": "node --test bindings/node/*_test.js" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=42", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "tree-sitter-gitattributes" 7 | description = "gitattributes grammar for tree-sitter" 8 | version = "0.2.0" 9 | keywords = ["incremental", "parsing", "tree-sitter", "gitattributes"] 10 | classifiers = [ 11 | "Intended Audience :: Developers", 12 | "Topic :: Software Development :: Compilers", 13 | "Topic :: Text Processing :: Linguistic", 14 | "Typing :: Typed", 15 | ] 16 | authors = [{ name = "ObserverOfTime", email = "chronobserver@disroot.org" }] 17 | requires-python = ">=3.10" 18 | license.text = "MIT" 19 | readme = "README.md" 20 | 21 | [project.urls] 22 | Homepage = "https://github.com/tree-sitter-grammars/tree-sitter-gitattributes" 23 | 24 | [project.optional-dependencies] 25 | core = ["tree-sitter~=0.24"] 26 | 27 | [tool.cibuildwheel] 28 | build = "cp310-*" 29 | build-frontend = "build" 30 | -------------------------------------------------------------------------------- /queries/highlights.scm: -------------------------------------------------------------------------------- 1 | (dir_sep) @punctuation.delimiter 2 | 3 | (quoted_pattern 4 | "\"" @punctuation.special) 5 | 6 | (range_notation) @string.special 7 | 8 | (range_notation 9 | [ "[" "]" ] @punctuation.bracket) 10 | 11 | (wildcard) @string.regexp 12 | 13 | (range_negation) @operator 14 | 15 | (character_class) @constant 16 | 17 | (class_range "-" @operator) 18 | 19 | [ 20 | (ansi_c_escape) 21 | (escaped_char) 22 | ] @escape 23 | 24 | (attribute 25 | (attr_name) @variable.parameter) 26 | 27 | (attribute 28 | (builtin_attr) @variable.builtin) 29 | 30 | [ 31 | (attr_reset) 32 | (attr_unset) 33 | (attr_set) 34 | ] @operator 35 | 36 | (boolean_value) @boolean 37 | 38 | (string_value) @string 39 | 40 | (macro_tag) @keyword 41 | 42 | (macro_def 43 | macro_name: (_) @property) 44 | 45 | [ 46 | (pattern_negation) 47 | (redundant_escape) 48 | (trailing_slash) 49 | (ignored_value) 50 | ] @error 51 | 52 | (comment) @comment 53 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from os import name, 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.egg_info import egg_info 7 | from wheel.bdist_wheel import bdist_wheel 8 | 9 | sources = [ 10 | "bindings/python/tree_sitter_gitattributes/binding.c", 11 | "src/parser.c", 12 | ] 13 | if path.exists("src/scanner.c"): 14 | sources.append("src/scanner.c") 15 | 16 | macros: list[tuple[str, str | None]] = [ 17 | ("PY_SSIZE_T_CLEAN", None), 18 | ("TREE_SITTER_HIDE_SYMBOLS", None), 19 | ] 20 | if limited_api := not get_config_var("Py_GIL_DISABLED"): 21 | macros.append(("Py_LIMITED_API", "0x030A0000")) 22 | 23 | if name != "nt": 24 | cflags = ["-std=c11", "-fvisibility=hidden"] 25 | else: 26 | cflags = ["/std:c11", "/utf-8"] 27 | 28 | 29 | class Build(build): 30 | def run(self): 31 | if path.isdir("queries"): 32 | dest = path.join(self.build_lib, "tree_sitter_gitattributes", "queries") 33 | self.copy_tree("queries", dest) 34 | super().run() 35 | 36 | 37 | class BdistWheel(bdist_wheel): 38 | def get_tag(self): 39 | python, abi, platform = super().get_tag() 40 | if python.startswith("cp"): 41 | python, abi = "cp310", "abi3" 42 | return python, abi, platform 43 | 44 | 45 | class EggInfo(egg_info): 46 | def find_sources(self): 47 | super().find_sources() 48 | self.filelist.recursive_include("queries", "*.scm") 49 | self.filelist.include("src/tree_sitter/*.h") 50 | 51 | 52 | setup( 53 | packages=find_packages("bindings/python"), 54 | package_dir={"": "bindings/python"}, 55 | package_data={ 56 | "tree_sitter_gitattributes": ["*.pyi", "py.typed"], 57 | "tree_sitter_gitattributes.queries": ["*.scm"], 58 | }, 59 | ext_package="tree_sitter_gitattributes", 60 | ext_modules=[ 61 | Extension( 62 | name="_binding", 63 | sources=sources, 64 | extra_compile_args=cflags, 65 | define_macros=macros, 66 | include_dirs=["src"], 67 | py_limited_api=limited_api, 68 | ) 69 | ], 70 | cmdclass={ 71 | "build": Build, 72 | "bdist_wheel": BdistWheel, 73 | "egg_info": EggInfo, 74 | }, 75 | zip_safe=False 76 | ) 77 | -------------------------------------------------------------------------------- /src/grammar.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://tree-sitter.github.io/tree-sitter/assets/schemas/grammar.schema.json", 3 | "name": "gitattributes", 4 | "word": "attr_name", 5 | "rules": { 6 | "file": { 7 | "type": "REPEAT", 8 | "content": { 9 | "type": "SYMBOL", 10 | "name": "_line" 11 | } 12 | }, 13 | "_line": { 14 | "type": "SEQ", 15 | "members": [ 16 | { 17 | "type": "CHOICE", 18 | "members": [ 19 | { 20 | "type": "SYMBOL", 21 | "name": "_space" 22 | }, 23 | { 24 | "type": "BLANK" 25 | } 26 | ] 27 | }, 28 | { 29 | "type": "CHOICE", 30 | "members": [ 31 | { 32 | "type": "CHOICE", 33 | "members": [ 34 | { 35 | "type": "SYMBOL", 36 | "name": "comment" 37 | }, 38 | { 39 | "type": "SYMBOL", 40 | "name": "_attr_list" 41 | }, 42 | { 43 | "type": "SYMBOL", 44 | "name": "macro_def" 45 | } 46 | ] 47 | }, 48 | { 49 | "type": "BLANK" 50 | } 51 | ] 52 | }, 53 | { 54 | "type": "CHOICE", 55 | "members": [ 56 | { 57 | "type": "SYMBOL", 58 | "name": "_eol" 59 | }, 60 | { 61 | "type": "SYMBOL", 62 | "name": "_eof" 63 | } 64 | ] 65 | } 66 | ] 67 | }, 68 | "_attr_list": { 69 | "type": "SEQ", 70 | "members": [ 71 | { 72 | "type": "PREC_LEFT", 73 | "value": 0, 74 | "content": { 75 | "type": "CHOICE", 76 | "members": [ 77 | { 78 | "type": "SYMBOL", 79 | "name": "pattern" 80 | }, 81 | { 82 | "type": "SYMBOL", 83 | "name": "quoted_pattern" 84 | } 85 | ] 86 | } 87 | }, 88 | { 89 | "type": "REPEAT1", 90 | "content": { 91 | "type": "SEQ", 92 | "members": [ 93 | { 94 | "type": "SYMBOL", 95 | "name": "_space" 96 | }, 97 | { 98 | "type": "SYMBOL", 99 | "name": "attribute" 100 | } 101 | ] 102 | } 103 | }, 104 | { 105 | "type": "CHOICE", 106 | "members": [ 107 | { 108 | "type": "SYMBOL", 109 | "name": "_space" 110 | }, 111 | { 112 | "type": "BLANK" 113 | } 114 | ] 115 | } 116 | ] 117 | }, 118 | "pattern": { 119 | "type": "SEQ", 120 | "members": [ 121 | { 122 | "type": "CHOICE", 123 | "members": [ 124 | { 125 | "type": "ALIAS", 126 | "content": { 127 | "type": "STRING", 128 | "value": "!" 129 | }, 130 | "named": true, 131 | "value": "pattern_negation" 132 | }, 133 | { 134 | "type": "BLANK" 135 | } 136 | ] 137 | }, 138 | { 139 | "type": "CHOICE", 140 | "members": [ 141 | { 142 | "type": "FIELD", 143 | "name": "absolute", 144 | "content": { 145 | "type": "SYMBOL", 146 | "name": "dir_sep" 147 | } 148 | }, 149 | { 150 | "type": "BLANK" 151 | } 152 | ] 153 | }, 154 | { 155 | "type": "SYMBOL", 156 | "name": "_pattern" 157 | }, 158 | { 159 | "type": "REPEAT", 160 | "content": { 161 | "type": "SEQ", 162 | "members": [ 163 | { 164 | "type": "FIELD", 165 | "name": "relative", 166 | "content": { 167 | "type": "SYMBOL", 168 | "name": "dir_sep" 169 | } 170 | }, 171 | { 172 | "type": "SYMBOL", 173 | "name": "_pattern" 174 | } 175 | ] 176 | } 177 | }, 178 | { 179 | "type": "CHOICE", 180 | "members": [ 181 | { 182 | "type": "ALIAS", 183 | "content": { 184 | "type": "SYMBOL", 185 | "name": "dir_sep" 186 | }, 187 | "named": true, 188 | "value": "trailing_slash" 189 | }, 190 | { 191 | "type": "BLANK" 192 | } 193 | ] 194 | } 195 | ] 196 | }, 197 | "_pattern": { 198 | "type": "REPEAT1", 199 | "content": { 200 | "type": "CHOICE", 201 | "members": [ 202 | { 203 | "type": "SYMBOL", 204 | "name": "_pattern_char" 205 | }, 206 | { 207 | "type": "SYMBOL", 208 | "name": "wildcard" 209 | }, 210 | { 211 | "type": "SYMBOL", 212 | "name": "escaped_char" 213 | }, 214 | { 215 | "type": "SYMBOL", 216 | "name": "range_notation" 217 | }, 218 | { 219 | "type": "ALIAS", 220 | "content": { 221 | "type": "STRING", 222 | "value": "\\" 223 | }, 224 | "named": true, 225 | "value": "redundant_escape" 226 | } 227 | ] 228 | } 229 | }, 230 | "quoted_pattern": { 231 | "type": "SEQ", 232 | "members": [ 233 | { 234 | "type": "STRING", 235 | "value": "\"" 236 | }, 237 | { 238 | "type": "CHOICE", 239 | "members": [ 240 | { 241 | "type": "ALIAS", 242 | "content": { 243 | "type": "STRING", 244 | "value": "!" 245 | }, 246 | "named": true, 247 | "value": "pattern_negation" 248 | }, 249 | { 250 | "type": "BLANK" 251 | } 252 | ] 253 | }, 254 | { 255 | "type": "CHOICE", 256 | "members": [ 257 | { 258 | "type": "FIELD", 259 | "name": "absolute", 260 | "content": { 261 | "type": "SYMBOL", 262 | "name": "dir_sep" 263 | } 264 | }, 265 | { 266 | "type": "BLANK" 267 | } 268 | ] 269 | }, 270 | { 271 | "type": "SYMBOL", 272 | "name": "_quoted_pattern" 273 | }, 274 | { 275 | "type": "REPEAT", 276 | "content": { 277 | "type": "SEQ", 278 | "members": [ 279 | { 280 | "type": "FIELD", 281 | "name": "relative", 282 | "content": { 283 | "type": "SYMBOL", 284 | "name": "dir_sep" 285 | } 286 | }, 287 | { 288 | "type": "SYMBOL", 289 | "name": "_pattern" 290 | } 291 | ] 292 | } 293 | }, 294 | { 295 | "type": "CHOICE", 296 | "members": [ 297 | { 298 | "type": "ALIAS", 299 | "content": { 300 | "type": "SYMBOL", 301 | "name": "dir_sep" 302 | }, 303 | "named": true, 304 | "value": "trailing_slash" 305 | }, 306 | { 307 | "type": "BLANK" 308 | } 309 | ] 310 | }, 311 | { 312 | "type": "STRING", 313 | "value": "\"" 314 | } 315 | ] 316 | }, 317 | "_quoted_pattern": { 318 | "type": "REPEAT1", 319 | "content": { 320 | "type": "CHOICE", 321 | "members": [ 322 | { 323 | "type": "PATTERN", 324 | "value": "[^\\n/]" 325 | }, 326 | { 327 | "type": "CHOICE", 328 | "members": [ 329 | { 330 | "type": "SYMBOL", 331 | "name": "ansi_c_escape" 332 | }, 333 | { 334 | "type": "SYMBOL", 335 | "name": "escaped_char" 336 | } 337 | ] 338 | }, 339 | { 340 | "type": "ALIAS", 341 | "content": { 342 | "type": "STRING", 343 | "value": "\\" 344 | }, 345 | "named": true, 346 | "value": "redundant_escape" 347 | } 348 | ] 349 | } 350 | }, 351 | "_pattern_char": { 352 | "type": "PATTERN", 353 | "value": "[^\\s/?*]" 354 | }, 355 | "escaped_char": { 356 | "type": "PATTERN", 357 | "value": "\\\\[\\\\\\[\\]!?*]" 358 | }, 359 | "ansi_c_escape": { 360 | "type": "PREC_RIGHT", 361 | "value": 1, 362 | "content": { 363 | "type": "CHOICE", 364 | "members": [ 365 | { 366 | "type": "SYMBOL", 367 | "name": "_special_char" 368 | }, 369 | { 370 | "type": "SYMBOL", 371 | "name": "_char_code" 372 | } 373 | ] 374 | } 375 | }, 376 | "_special_char": { 377 | "type": "PATTERN", 378 | "value": "\\\\[abeEfnrtv\\\\'\"?]" 379 | }, 380 | "_char_code": { 381 | "type": "CHOICE", 382 | "members": [ 383 | { 384 | "type": "SYMBOL", 385 | "name": "_octal_code" 386 | }, 387 | { 388 | "type": "SYMBOL", 389 | "name": "_hex_code" 390 | }, 391 | { 392 | "type": "SYMBOL", 393 | "name": "_unicode_code" 394 | }, 395 | { 396 | "type": "SYMBOL", 397 | "name": "_control_code" 398 | } 399 | ] 400 | }, 401 | "_octal_code": { 402 | "type": "PATTERN", 403 | "value": "\\\\\\d{1,3}" 404 | }, 405 | "_hex_code": { 406 | "type": "PATTERN", 407 | "value": "\\\\x[0-9A-Fa-f]{2}" 408 | }, 409 | "_unicode_code": { 410 | "type": "CHOICE", 411 | "members": [ 412 | { 413 | "type": "PATTERN", 414 | "value": "\\\\u[0-9A-Fa-f]{4}" 415 | }, 416 | { 417 | "type": "PATTERN", 418 | "value": "\\\\U[0-9A-Fa-f]{8}" 419 | } 420 | ] 421 | }, 422 | "_control_code": { 423 | "type": "TOKEN", 424 | "content": { 425 | "type": "CHOICE", 426 | "members": [ 427 | { 428 | "type": "PATTERN", 429 | "value": "\\\\c[\\x00-\\x5B\\x5D-\\x7F]" 430 | }, 431 | { 432 | "type": "PATTERN", 433 | "value": "\\\\c\\\\\\\\" 434 | } 435 | ] 436 | } 437 | }, 438 | "range_notation": { 439 | "type": "PREC_LEFT", 440 | "value": 0, 441 | "content": { 442 | "type": "SEQ", 443 | "members": [ 444 | { 445 | "type": "STRING", 446 | "value": "[" 447 | }, 448 | { 449 | "type": "CHOICE", 450 | "members": [ 451 | { 452 | "type": "ALIAS", 453 | "content": { 454 | "type": "TOKEN", 455 | "content": { 456 | "type": "CHOICE", 457 | "members": [ 458 | { 459 | "type": "STRING", 460 | "value": "!" 461 | }, 462 | { 463 | "type": "STRING", 464 | "value": "^" 465 | } 466 | ] 467 | } 468 | }, 469 | "named": true, 470 | "value": "range_negation" 471 | }, 472 | { 473 | "type": "BLANK" 474 | } 475 | ] 476 | }, 477 | { 478 | "type": "REPEAT1", 479 | "content": { 480 | "type": "CHOICE", 481 | "members": [ 482 | { 483 | "type": "SYMBOL", 484 | "name": "class_range" 485 | }, 486 | { 487 | "type": "SYMBOL", 488 | "name": "character_class" 489 | }, 490 | { 491 | "type": "SYMBOL", 492 | "name": "_class_char" 493 | }, 494 | { 495 | "type": "SYMBOL", 496 | "name": "ansi_c_escape" 497 | }, 498 | { 499 | "type": "STRING", 500 | "value": "-" 501 | } 502 | ] 503 | } 504 | }, 505 | { 506 | "type": "STRING", 507 | "value": "]" 508 | } 509 | ] 510 | } 511 | }, 512 | "class_range": { 513 | "type": "PREC_RIGHT", 514 | "value": 2, 515 | "content": { 516 | "type": "SEQ", 517 | "members": [ 518 | { 519 | "type": "CHOICE", 520 | "members": [ 521 | { 522 | "type": "SYMBOL", 523 | "name": "_class_char" 524 | }, 525 | { 526 | "type": "SYMBOL", 527 | "name": "_char_code" 528 | } 529 | ] 530 | }, 531 | { 532 | "type": "STRING", 533 | "value": "-" 534 | }, 535 | { 536 | "type": "CHOICE", 537 | "members": [ 538 | { 539 | "type": "SYMBOL", 540 | "name": "_class_char" 541 | }, 542 | { 543 | "type": "SYMBOL", 544 | "name": "_char_code" 545 | } 546 | ] 547 | } 548 | ] 549 | } 550 | }, 551 | "_class_char": { 552 | "type": "TOKEN", 553 | "content": { 554 | "type": "CHOICE", 555 | "members": [ 556 | { 557 | "type": "PATTERN", 558 | "value": "[^-\\\\\\]\\n]" 559 | }, 560 | { 561 | "type": "PATTERN", 562 | "value": "\\\\[-\\\\\\[\\]!^]" 563 | } 564 | ] 565 | } 566 | }, 567 | "character_class": { 568 | "type": "TOKEN", 569 | "content": { 570 | "type": "SEQ", 571 | "members": [ 572 | { 573 | "type": "STRING", 574 | "value": "[:" 575 | }, 576 | { 577 | "type": "CHOICE", 578 | "members": [ 579 | { 580 | "type": "STRING", 581 | "value": "alnum" 582 | }, 583 | { 584 | "type": "STRING", 585 | "value": "alpha" 586 | }, 587 | { 588 | "type": "STRING", 589 | "value": "blank" 590 | }, 591 | { 592 | "type": "STRING", 593 | "value": "cntrl" 594 | }, 595 | { 596 | "type": "STRING", 597 | "value": "digit" 598 | }, 599 | { 600 | "type": "STRING", 601 | "value": "graph" 602 | }, 603 | { 604 | "type": "STRING", 605 | "value": "lower" 606 | }, 607 | { 608 | "type": "STRING", 609 | "value": "print" 610 | }, 611 | { 612 | "type": "STRING", 613 | "value": "punct" 614 | }, 615 | { 616 | "type": "STRING", 617 | "value": "space" 618 | }, 619 | { 620 | "type": "STRING", 621 | "value": "upper" 622 | }, 623 | { 624 | "type": "STRING", 625 | "value": "xdigit" 626 | } 627 | ] 628 | }, 629 | { 630 | "type": "STRING", 631 | "value": ":]" 632 | } 633 | ] 634 | } 635 | }, 636 | "wildcard": { 637 | "type": "TOKEN", 638 | "content": { 639 | "type": "CHOICE", 640 | "members": [ 641 | { 642 | "type": "STRING", 643 | "value": "?" 644 | }, 645 | { 646 | "type": "STRING", 647 | "value": "*" 648 | }, 649 | { 650 | "type": "STRING", 651 | "value": "**" 652 | } 653 | ] 654 | } 655 | }, 656 | "dir_sep": { 657 | "type": "STRING", 658 | "value": "/" 659 | }, 660 | "attribute": { 661 | "type": "CHOICE", 662 | "members": [ 663 | { 664 | "type": "SEQ", 665 | "members": [ 666 | { 667 | "type": "CHOICE", 668 | "members": [ 669 | { 670 | "type": "SYMBOL", 671 | "name": "attr_name" 672 | }, 673 | { 674 | "type": "SYMBOL", 675 | "name": "builtin_attr" 676 | } 677 | ] 678 | }, 679 | { 680 | "type": "SYMBOL", 681 | "name": "_attr_value" 682 | } 683 | ] 684 | }, 685 | { 686 | "type": "SYMBOL", 687 | "name": "_prefixed_attr" 688 | } 689 | ] 690 | }, 691 | "_prefixed_attr": { 692 | "type": "SEQ", 693 | "members": [ 694 | { 695 | "type": "CHOICE", 696 | "members": [ 697 | { 698 | "type": "CHOICE", 699 | "members": [ 700 | { 701 | "type": "ALIAS", 702 | "content": { 703 | "type": "STRING", 704 | "value": "!" 705 | }, 706 | "named": true, 707 | "value": "attr_reset" 708 | }, 709 | { 710 | "type": "ALIAS", 711 | "content": { 712 | "type": "STRING", 713 | "value": "-" 714 | }, 715 | "named": true, 716 | "value": "attr_unset" 717 | } 718 | ] 719 | }, 720 | { 721 | "type": "BLANK" 722 | } 723 | ] 724 | }, 725 | { 726 | "type": "CHOICE", 727 | "members": [ 728 | { 729 | "type": "SYMBOL", 730 | "name": "attr_name" 731 | }, 732 | { 733 | "type": "SYMBOL", 734 | "name": "builtin_attr" 735 | } 736 | ] 737 | }, 738 | { 739 | "type": "PREC", 740 | "value": -1, 741 | "content": { 742 | "type": "CHOICE", 743 | "members": [ 744 | { 745 | "type": "ALIAS", 746 | "content": { 747 | "type": "SYMBOL", 748 | "name": "_attr_value" 749 | }, 750 | "named": true, 751 | "value": "ignored_value" 752 | }, 753 | { 754 | "type": "BLANK" 755 | } 756 | ] 757 | } 758 | } 759 | ] 760 | }, 761 | "_attr_value": { 762 | "type": "SEQ", 763 | "members": [ 764 | { 765 | "type": "ALIAS", 766 | "content": { 767 | "type": "STRING", 768 | "value": "=" 769 | }, 770 | "named": true, 771 | "value": "attr_set" 772 | }, 773 | { 774 | "type": "CHOICE", 775 | "members": [ 776 | { 777 | "type": "PREC", 778 | "value": 2, 779 | "content": { 780 | "type": "ALIAS", 781 | "content": { 782 | "type": "TOKEN", 783 | "content": { 784 | "type": "CHOICE", 785 | "members": [ 786 | { 787 | "type": "STRING", 788 | "value": "true" 789 | }, 790 | { 791 | "type": "STRING", 792 | "value": "false" 793 | } 794 | ] 795 | } 796 | }, 797 | "named": true, 798 | "value": "boolean_value" 799 | } 800 | }, 801 | { 802 | "type": "PREC", 803 | "value": 1, 804 | "content": { 805 | "type": "ALIAS", 806 | "content": { 807 | "type": "PATTERN", 808 | "value": "\\S+" 809 | }, 810 | "named": true, 811 | "value": "string_value" 812 | } 813 | } 814 | ] 815 | } 816 | ] 817 | }, 818 | "attr_name": { 819 | "type": "PATTERN", 820 | "value": "[A-Za-z0-9_.][-A-Za-z0-9_.]*" 821 | }, 822 | "builtin_attr": { 823 | "type": "PREC", 824 | "value": 1, 825 | "content": { 826 | "type": "CHOICE", 827 | "members": [ 828 | { 829 | "type": "STRING", 830 | "value": "text" 831 | }, 832 | { 833 | "type": "STRING", 834 | "value": "eol" 835 | }, 836 | { 837 | "type": "STRING", 838 | "value": "crlf" 839 | }, 840 | { 841 | "type": "STRING", 842 | "value": "working-tree-encoding" 843 | }, 844 | { 845 | "type": "STRING", 846 | "value": "ident" 847 | }, 848 | { 849 | "type": "STRING", 850 | "value": "filter" 851 | }, 852 | { 853 | "type": "STRING", 854 | "value": "diff" 855 | }, 856 | { 857 | "type": "STRING", 858 | "value": "merge" 859 | }, 860 | { 861 | "type": "STRING", 862 | "value": "whitespace" 863 | }, 864 | { 865 | "type": "STRING", 866 | "value": "export-ignore" 867 | }, 868 | { 869 | "type": "STRING", 870 | "value": "export-subst" 871 | }, 872 | { 873 | "type": "STRING", 874 | "value": "delta" 875 | }, 876 | { 877 | "type": "STRING", 878 | "value": "encoding" 879 | }, 880 | { 881 | "type": "STRING", 882 | "value": "binary" 883 | } 884 | ] 885 | } 886 | }, 887 | "macro_def": { 888 | "type": "SEQ", 889 | "members": [ 890 | { 891 | "type": "ALIAS", 892 | "content": { 893 | "type": "STRING", 894 | "value": "[attr]" 895 | }, 896 | "named": true, 897 | "value": "macro_tag" 898 | }, 899 | { 900 | "type": "FIELD", 901 | "name": "macro_name", 902 | "content": { 903 | "type": "SYMBOL", 904 | "name": "attr_name" 905 | } 906 | }, 907 | { 908 | "type": "REPEAT1", 909 | "content": { 910 | "type": "SEQ", 911 | "members": [ 912 | { 913 | "type": "SYMBOL", 914 | "name": "_space" 915 | }, 916 | { 917 | "type": "SYMBOL", 918 | "name": "attribute" 919 | } 920 | ] 921 | } 922 | }, 923 | { 924 | "type": "CHOICE", 925 | "members": [ 926 | { 927 | "type": "SYMBOL", 928 | "name": "_space" 929 | }, 930 | { 931 | "type": "BLANK" 932 | } 933 | ] 934 | } 935 | ] 936 | }, 937 | "comment": { 938 | "type": "SEQ", 939 | "members": [ 940 | { 941 | "type": "STRING", 942 | "value": "#" 943 | }, 944 | { 945 | "type": "REPEAT", 946 | "content": { 947 | "type": "PATTERN", 948 | "value": "[^\\n]" 949 | } 950 | } 951 | ] 952 | }, 953 | "_space": { 954 | "type": "PREC", 955 | "value": -1, 956 | "content": { 957 | "type": "PATTERN", 958 | "value": "[ \\t]+" 959 | } 960 | }, 961 | "_eol": { 962 | "type": "PATTERN", 963 | "value": "\\r?\\n" 964 | }, 965 | "_eof": { 966 | "type": "STRING", 967 | "value": "\u0000" 968 | } 969 | }, 970 | "extras": [], 971 | "conflicts": [], 972 | "precedences": [], 973 | "externals": [], 974 | "inline": [], 975 | "supertypes": [], 976 | "reserved": {} 977 | } -------------------------------------------------------------------------------- /src/node-types.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "ansi_c_escape", 4 | "named": true, 5 | "fields": {} 6 | }, 7 | { 8 | "type": "attribute", 9 | "named": true, 10 | "fields": {}, 11 | "children": { 12 | "multiple": true, 13 | "required": true, 14 | "types": [ 15 | { 16 | "type": "attr_name", 17 | "named": true 18 | }, 19 | { 20 | "type": "attr_reset", 21 | "named": true 22 | }, 23 | { 24 | "type": "attr_set", 25 | "named": true 26 | }, 27 | { 28 | "type": "attr_unset", 29 | "named": true 30 | }, 31 | { 32 | "type": "boolean_value", 33 | "named": true 34 | }, 35 | { 36 | "type": "builtin_attr", 37 | "named": true 38 | }, 39 | { 40 | "type": "ignored_value", 41 | "named": true 42 | }, 43 | { 44 | "type": "string_value", 45 | "named": true 46 | } 47 | ] 48 | } 49 | }, 50 | { 51 | "type": "builtin_attr", 52 | "named": true, 53 | "fields": {} 54 | }, 55 | { 56 | "type": "class_range", 57 | "named": true, 58 | "fields": {} 59 | }, 60 | { 61 | "type": "comment", 62 | "named": true, 63 | "fields": {} 64 | }, 65 | { 66 | "type": "file", 67 | "named": true, 68 | "root": true, 69 | "fields": {}, 70 | "children": { 71 | "multiple": true, 72 | "required": false, 73 | "types": [ 74 | { 75 | "type": "attribute", 76 | "named": true 77 | }, 78 | { 79 | "type": "comment", 80 | "named": true 81 | }, 82 | { 83 | "type": "macro_def", 84 | "named": true 85 | }, 86 | { 87 | "type": "pattern", 88 | "named": true 89 | }, 90 | { 91 | "type": "quoted_pattern", 92 | "named": true 93 | } 94 | ] 95 | } 96 | }, 97 | { 98 | "type": "ignored_value", 99 | "named": true, 100 | "fields": {}, 101 | "children": { 102 | "multiple": true, 103 | "required": true, 104 | "types": [ 105 | { 106 | "type": "attr_set", 107 | "named": true 108 | }, 109 | { 110 | "type": "boolean_value", 111 | "named": true 112 | }, 113 | { 114 | "type": "string_value", 115 | "named": true 116 | } 117 | ] 118 | } 119 | }, 120 | { 121 | "type": "macro_def", 122 | "named": true, 123 | "fields": { 124 | "macro_name": { 125 | "multiple": false, 126 | "required": true, 127 | "types": [ 128 | { 129 | "type": "attr_name", 130 | "named": true 131 | } 132 | ] 133 | } 134 | }, 135 | "children": { 136 | "multiple": true, 137 | "required": true, 138 | "types": [ 139 | { 140 | "type": "attribute", 141 | "named": true 142 | }, 143 | { 144 | "type": "macro_tag", 145 | "named": true 146 | } 147 | ] 148 | } 149 | }, 150 | { 151 | "type": "pattern", 152 | "named": true, 153 | "fields": { 154 | "absolute": { 155 | "multiple": false, 156 | "required": false, 157 | "types": [ 158 | { 159 | "type": "dir_sep", 160 | "named": true 161 | } 162 | ] 163 | }, 164 | "relative": { 165 | "multiple": true, 166 | "required": false, 167 | "types": [ 168 | { 169 | "type": "dir_sep", 170 | "named": true 171 | } 172 | ] 173 | } 174 | }, 175 | "children": { 176 | "multiple": true, 177 | "required": false, 178 | "types": [ 179 | { 180 | "type": "escaped_char", 181 | "named": true 182 | }, 183 | { 184 | "type": "pattern_negation", 185 | "named": true 186 | }, 187 | { 188 | "type": "range_notation", 189 | "named": true 190 | }, 191 | { 192 | "type": "redundant_escape", 193 | "named": true 194 | }, 195 | { 196 | "type": "trailing_slash", 197 | "named": true 198 | }, 199 | { 200 | "type": "wildcard", 201 | "named": true 202 | } 203 | ] 204 | } 205 | }, 206 | { 207 | "type": "quoted_pattern", 208 | "named": true, 209 | "fields": { 210 | "absolute": { 211 | "multiple": false, 212 | "required": false, 213 | "types": [ 214 | { 215 | "type": "dir_sep", 216 | "named": true 217 | } 218 | ] 219 | }, 220 | "relative": { 221 | "multiple": true, 222 | "required": false, 223 | "types": [ 224 | { 225 | "type": "dir_sep", 226 | "named": true 227 | } 228 | ] 229 | } 230 | }, 231 | "children": { 232 | "multiple": true, 233 | "required": false, 234 | "types": [ 235 | { 236 | "type": "ansi_c_escape", 237 | "named": true 238 | }, 239 | { 240 | "type": "escaped_char", 241 | "named": true 242 | }, 243 | { 244 | "type": "pattern_negation", 245 | "named": true 246 | }, 247 | { 248 | "type": "range_notation", 249 | "named": true 250 | }, 251 | { 252 | "type": "redundant_escape", 253 | "named": true 254 | }, 255 | { 256 | "type": "trailing_slash", 257 | "named": true 258 | }, 259 | { 260 | "type": "wildcard", 261 | "named": true 262 | } 263 | ] 264 | } 265 | }, 266 | { 267 | "type": "range_notation", 268 | "named": true, 269 | "fields": {}, 270 | "children": { 271 | "multiple": true, 272 | "required": false, 273 | "types": [ 274 | { 275 | "type": "ansi_c_escape", 276 | "named": true 277 | }, 278 | { 279 | "type": "character_class", 280 | "named": true 281 | }, 282 | { 283 | "type": "class_range", 284 | "named": true 285 | }, 286 | { 287 | "type": "range_negation", 288 | "named": true 289 | } 290 | ] 291 | } 292 | }, 293 | { 294 | "type": "\u0000", 295 | "named": false 296 | }, 297 | { 298 | "type": "\"", 299 | "named": false 300 | }, 301 | { 302 | "type": "#", 303 | "named": false 304 | }, 305 | { 306 | "type": "-", 307 | "named": false 308 | }, 309 | { 310 | "type": "[", 311 | "named": false 312 | }, 313 | { 314 | "type": "]", 315 | "named": false 316 | }, 317 | { 318 | "type": "attr_name", 319 | "named": true 320 | }, 321 | { 322 | "type": "attr_reset", 323 | "named": true 324 | }, 325 | { 326 | "type": "attr_set", 327 | "named": true 328 | }, 329 | { 330 | "type": "attr_unset", 331 | "named": true 332 | }, 333 | { 334 | "type": "binary", 335 | "named": false 336 | }, 337 | { 338 | "type": "boolean_value", 339 | "named": true 340 | }, 341 | { 342 | "type": "character_class", 343 | "named": true 344 | }, 345 | { 346 | "type": "crlf", 347 | "named": false 348 | }, 349 | { 350 | "type": "delta", 351 | "named": false 352 | }, 353 | { 354 | "type": "diff", 355 | "named": false 356 | }, 357 | { 358 | "type": "dir_sep", 359 | "named": true 360 | }, 361 | { 362 | "type": "encoding", 363 | "named": false 364 | }, 365 | { 366 | "type": "eol", 367 | "named": false 368 | }, 369 | { 370 | "type": "escaped_char", 371 | "named": true 372 | }, 373 | { 374 | "type": "export-ignore", 375 | "named": false 376 | }, 377 | { 378 | "type": "export-subst", 379 | "named": false 380 | }, 381 | { 382 | "type": "filter", 383 | "named": false 384 | }, 385 | { 386 | "type": "ident", 387 | "named": false 388 | }, 389 | { 390 | "type": "macro_tag", 391 | "named": true 392 | }, 393 | { 394 | "type": "merge", 395 | "named": false 396 | }, 397 | { 398 | "type": "pattern_negation", 399 | "named": true 400 | }, 401 | { 402 | "type": "range_negation", 403 | "named": true 404 | }, 405 | { 406 | "type": "redundant_escape", 407 | "named": true 408 | }, 409 | { 410 | "type": "string_value", 411 | "named": true 412 | }, 413 | { 414 | "type": "text", 415 | "named": false 416 | }, 417 | { 418 | "type": "trailing_slash", 419 | "named": true 420 | }, 421 | { 422 | "type": "whitespace", 423 | "named": false 424 | }, 425 | { 426 | "type": "wildcard", 427 | "named": true 428 | }, 429 | { 430 | "type": "working-tree-encoding", 431 | "named": false 432 | } 433 | ] -------------------------------------------------------------------------------- /src/tree_sitter/alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ALLOC_H_ 2 | #define TREE_SITTER_ALLOC_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | // Allow clients to override allocation functions 13 | #ifdef TREE_SITTER_REUSE_ALLOCATOR 14 | 15 | extern void *(*ts_current_malloc)(size_t size); 16 | extern void *(*ts_current_calloc)(size_t count, size_t size); 17 | extern void *(*ts_current_realloc)(void *ptr, size_t size); 18 | extern void (*ts_current_free)(void *ptr); 19 | 20 | #ifndef ts_malloc 21 | #define ts_malloc ts_current_malloc 22 | #endif 23 | #ifndef ts_calloc 24 | #define ts_calloc ts_current_calloc 25 | #endif 26 | #ifndef ts_realloc 27 | #define ts_realloc ts_current_realloc 28 | #endif 29 | #ifndef ts_free 30 | #define ts_free ts_current_free 31 | #endif 32 | 33 | #else 34 | 35 | #ifndef ts_malloc 36 | #define ts_malloc malloc 37 | #endif 38 | #ifndef ts_calloc 39 | #define ts_calloc calloc 40 | #endif 41 | #ifndef ts_realloc 42 | #define ts_realloc realloc 43 | #endif 44 | #ifndef ts_free 45 | #define ts_free free 46 | #endif 47 | 48 | #endif 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | 54 | #endif // TREE_SITTER_ALLOC_H_ 55 | -------------------------------------------------------------------------------- /src/tree_sitter/array.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_ARRAY_H_ 2 | #define TREE_SITTER_ARRAY_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "./alloc.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef _MSC_VER 17 | #pragma warning(push) 18 | #pragma warning(disable : 4101) 19 | #elif defined(__GNUC__) || defined(__clang__) 20 | #pragma GCC diagnostic push 21 | #pragma GCC diagnostic ignored "-Wunused-variable" 22 | #endif 23 | 24 | #define Array(T) \ 25 | struct { \ 26 | T *contents; \ 27 | uint32_t size; \ 28 | uint32_t capacity; \ 29 | } 30 | 31 | /// Initialize an array. 32 | #define array_init(self) \ 33 | ((self)->size = 0, (self)->capacity = 0, (self)->contents = NULL) 34 | 35 | /// Create an empty array. 36 | #define array_new() \ 37 | { NULL, 0, 0 } 38 | 39 | /// Get a pointer to the element at a given `index` in the array. 40 | #define array_get(self, _index) \ 41 | (assert((uint32_t)(_index) < (self)->size), &(self)->contents[_index]) 42 | 43 | /// Get a pointer to the first element in the array. 44 | #define array_front(self) array_get(self, 0) 45 | 46 | /// Get a pointer to the last element in the array. 47 | #define array_back(self) array_get(self, (self)->size - 1) 48 | 49 | /// Clear the array, setting its size to zero. Note that this does not free any 50 | /// memory allocated for the array's contents. 51 | #define array_clear(self) ((self)->size = 0) 52 | 53 | /// Reserve `new_capacity` elements of space in the array. If `new_capacity` is 54 | /// less than the array's current capacity, this function has no effect. 55 | #define array_reserve(self, new_capacity) \ 56 | _array__reserve((Array *)(self), array_elem_size(self), new_capacity) 57 | 58 | /// Free any memory allocated for this array. Note that this does not free any 59 | /// memory allocated for the array's contents. 60 | #define array_delete(self) _array__delete((Array *)(self)) 61 | 62 | /// Push a new `element` onto the end of the array. 63 | #define array_push(self, element) \ 64 | (_array__grow((Array *)(self), 1, array_elem_size(self)), \ 65 | (self)->contents[(self)->size++] = (element)) 66 | 67 | /// Increase the array's size by `count` elements. 68 | /// New elements are zero-initialized. 69 | #define array_grow_by(self, count) \ 70 | do { \ 71 | if ((count) == 0) break; \ 72 | _array__grow((Array *)(self), count, array_elem_size(self)); \ 73 | memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)); \ 74 | (self)->size += (count); \ 75 | } while (0) 76 | 77 | /// Append all elements from one array to the end of another. 78 | #define array_push_all(self, other) \ 79 | array_extend((self), (other)->size, (other)->contents) 80 | 81 | /// Append `count` elements to the end of the array, reading their values from the 82 | /// `contents` pointer. 83 | #define array_extend(self, count, contents) \ 84 | _array__splice( \ 85 | (Array *)(self), array_elem_size(self), (self)->size, \ 86 | 0, count, contents \ 87 | ) 88 | 89 | /// Remove `old_count` elements from the array starting at the given `index`. At 90 | /// the same index, insert `new_count` new elements, reading their values from the 91 | /// `new_contents` pointer. 92 | #define array_splice(self, _index, old_count, new_count, new_contents) \ 93 | _array__splice( \ 94 | (Array *)(self), array_elem_size(self), _index, \ 95 | old_count, new_count, new_contents \ 96 | ) 97 | 98 | /// Insert one `element` into the array at the given `index`. 99 | #define array_insert(self, _index, element) \ 100 | _array__splice((Array *)(self), array_elem_size(self), _index, 0, 1, &(element)) 101 | 102 | /// Remove one element from the array at the given `index`. 103 | #define array_erase(self, _index) \ 104 | _array__erase((Array *)(self), array_elem_size(self), _index) 105 | 106 | /// Pop the last element off the array, returning the element by value. 107 | #define array_pop(self) ((self)->contents[--(self)->size]) 108 | 109 | /// Assign the contents of one array to another, reallocating if necessary. 110 | #define array_assign(self, other) \ 111 | _array__assign((Array *)(self), (const Array *)(other), array_elem_size(self)) 112 | 113 | /// Swap one array with another 114 | #define array_swap(self, other) \ 115 | _array__swap((Array *)(self), (Array *)(other)) 116 | 117 | /// Get the size of the array contents 118 | #define array_elem_size(self) (sizeof *(self)->contents) 119 | 120 | /// Search a sorted array for a given `needle` value, using the given `compare` 121 | /// callback to determine the order. 122 | /// 123 | /// If an existing element is found to be equal to `needle`, then the `index` 124 | /// out-parameter is set to the existing value's index, and the `exists` 125 | /// out-parameter is set to true. Otherwise, `index` is set to an index where 126 | /// `needle` should be inserted in order to preserve the sorting, and `exists` 127 | /// is set to false. 128 | #define array_search_sorted_with(self, compare, needle, _index, _exists) \ 129 | _array__search_sorted(self, 0, compare, , needle, _index, _exists) 130 | 131 | /// Search a sorted array for a given `needle` value, using integer comparisons 132 | /// of a given struct field (specified with a leading dot) to determine the order. 133 | /// 134 | /// See also `array_search_sorted_with`. 135 | #define array_search_sorted_by(self, field, needle, _index, _exists) \ 136 | _array__search_sorted(self, 0, _compare_int, field, needle, _index, _exists) 137 | 138 | /// Insert a given `value` into a sorted array, using the given `compare` 139 | /// callback to determine the order. 140 | #define array_insert_sorted_with(self, compare, value) \ 141 | do { \ 142 | unsigned _index, _exists; \ 143 | array_search_sorted_with(self, compare, &(value), &_index, &_exists); \ 144 | if (!_exists) array_insert(self, _index, value); \ 145 | } while (0) 146 | 147 | /// Insert a given `value` into a sorted array, using integer comparisons of 148 | /// a given struct field (specified with a leading dot) to determine the order. 149 | /// 150 | /// See also `array_search_sorted_by`. 151 | #define array_insert_sorted_by(self, field, value) \ 152 | do { \ 153 | unsigned _index, _exists; \ 154 | array_search_sorted_by(self, field, (value) field, &_index, &_exists); \ 155 | if (!_exists) array_insert(self, _index, value); \ 156 | } while (0) 157 | 158 | // Private 159 | 160 | typedef Array(void) Array; 161 | 162 | /// This is not what you're looking for, see `array_delete`. 163 | static inline void _array__delete(Array *self) { 164 | if (self->contents) { 165 | ts_free(self->contents); 166 | self->contents = NULL; 167 | self->size = 0; 168 | self->capacity = 0; 169 | } 170 | } 171 | 172 | /// This is not what you're looking for, see `array_erase`. 173 | static inline void _array__erase(Array *self, size_t element_size, 174 | uint32_t index) { 175 | assert(index < self->size); 176 | char *contents = (char *)self->contents; 177 | memmove(contents + index * element_size, contents + (index + 1) * element_size, 178 | (self->size - index - 1) * element_size); 179 | self->size--; 180 | } 181 | 182 | /// This is not what you're looking for, see `array_reserve`. 183 | static inline void _array__reserve(Array *self, size_t element_size, uint32_t new_capacity) { 184 | if (new_capacity > self->capacity) { 185 | if (self->contents) { 186 | self->contents = ts_realloc(self->contents, new_capacity * element_size); 187 | } else { 188 | self->contents = ts_malloc(new_capacity * element_size); 189 | } 190 | self->capacity = new_capacity; 191 | } 192 | } 193 | 194 | /// This is not what you're looking for, see `array_assign`. 195 | static inline void _array__assign(Array *self, const Array *other, size_t element_size) { 196 | _array__reserve(self, element_size, other->size); 197 | self->size = other->size; 198 | memcpy(self->contents, other->contents, self->size * element_size); 199 | } 200 | 201 | /// This is not what you're looking for, see `array_swap`. 202 | static inline void _array__swap(Array *self, Array *other) { 203 | Array swap = *other; 204 | *other = *self; 205 | *self = swap; 206 | } 207 | 208 | /// This is not what you're looking for, see `array_push` or `array_grow_by`. 209 | static inline void _array__grow(Array *self, uint32_t count, size_t element_size) { 210 | uint32_t new_size = self->size + count; 211 | if (new_size > self->capacity) { 212 | uint32_t new_capacity = self->capacity * 2; 213 | if (new_capacity < 8) new_capacity = 8; 214 | if (new_capacity < new_size) new_capacity = new_size; 215 | _array__reserve(self, element_size, new_capacity); 216 | } 217 | } 218 | 219 | /// This is not what you're looking for, see `array_splice`. 220 | static inline void _array__splice(Array *self, size_t element_size, 221 | uint32_t index, uint32_t old_count, 222 | uint32_t new_count, const void *elements) { 223 | uint32_t new_size = self->size + new_count - old_count; 224 | uint32_t old_end = index + old_count; 225 | uint32_t new_end = index + new_count; 226 | assert(old_end <= self->size); 227 | 228 | _array__reserve(self, element_size, new_size); 229 | 230 | char *contents = (char *)self->contents; 231 | if (self->size > old_end) { 232 | memmove( 233 | contents + new_end * element_size, 234 | contents + old_end * element_size, 235 | (self->size - old_end) * element_size 236 | ); 237 | } 238 | if (new_count > 0) { 239 | if (elements) { 240 | memcpy( 241 | (contents + index * element_size), 242 | elements, 243 | new_count * element_size 244 | ); 245 | } else { 246 | memset( 247 | (contents + index * element_size), 248 | 0, 249 | new_count * element_size 250 | ); 251 | } 252 | } 253 | self->size += new_count - old_count; 254 | } 255 | 256 | /// A binary search routine, based on Rust's `std::slice::binary_search_by`. 257 | /// This is not what you're looking for, see `array_search_sorted_with` or `array_search_sorted_by`. 258 | #define _array__search_sorted(self, start, compare, suffix, needle, _index, _exists) \ 259 | do { \ 260 | *(_index) = start; \ 261 | *(_exists) = false; \ 262 | uint32_t size = (self)->size - *(_index); \ 263 | if (size == 0) break; \ 264 | int comparison; \ 265 | while (size > 1) { \ 266 | uint32_t half_size = size / 2; \ 267 | uint32_t mid_index = *(_index) + half_size; \ 268 | comparison = compare(&((self)->contents[mid_index] suffix), (needle)); \ 269 | if (comparison <= 0) *(_index) = mid_index; \ 270 | size -= half_size; \ 271 | } \ 272 | comparison = compare(&((self)->contents[*(_index)] suffix), (needle)); \ 273 | if (comparison == 0) *(_exists) = true; \ 274 | else if (comparison < 0) *(_index) += 1; \ 275 | } while (0) 276 | 277 | /// Helper macro for the `_sorted_by` routines below. This takes the left (existing) 278 | /// parameter by reference in order to work with the generic sorting function above. 279 | #define _compare_int(a, b) ((int)*(a) - (int)(b)) 280 | 281 | #ifdef _MSC_VER 282 | #pragma warning(pop) 283 | #elif defined(__GNUC__) || defined(__clang__) 284 | #pragma GCC diagnostic pop 285 | #endif 286 | 287 | #ifdef __cplusplus 288 | } 289 | #endif 290 | 291 | #endif // TREE_SITTER_ARRAY_H_ 292 | -------------------------------------------------------------------------------- /src/tree_sitter/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef TREE_SITTER_PARSER_H_ 2 | #define TREE_SITTER_PARSER_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define ts_builtin_sym_error ((TSSymbol)-1) 13 | #define ts_builtin_sym_end 0 14 | #define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024 15 | 16 | #ifndef TREE_SITTER_API_H_ 17 | typedef uint16_t TSStateId; 18 | typedef uint16_t TSSymbol; 19 | typedef uint16_t TSFieldId; 20 | typedef struct TSLanguage TSLanguage; 21 | typedef struct TSLanguageMetadata { 22 | uint8_t major_version; 23 | uint8_t minor_version; 24 | uint8_t patch_version; 25 | } TSLanguageMetadata; 26 | #endif 27 | 28 | typedef struct { 29 | TSFieldId field_id; 30 | uint8_t child_index; 31 | bool inherited; 32 | } TSFieldMapEntry; 33 | 34 | // Used to index the field and supertype maps. 35 | typedef struct { 36 | uint16_t index; 37 | uint16_t length; 38 | } TSMapSlice; 39 | 40 | typedef struct { 41 | bool visible; 42 | bool named; 43 | bool supertype; 44 | } TSSymbolMetadata; 45 | 46 | typedef struct TSLexer TSLexer; 47 | 48 | struct TSLexer { 49 | int32_t lookahead; 50 | TSSymbol result_symbol; 51 | void (*advance)(TSLexer *, bool); 52 | void (*mark_end)(TSLexer *); 53 | uint32_t (*get_column)(TSLexer *); 54 | bool (*is_at_included_range_start)(const TSLexer *); 55 | bool (*eof)(const TSLexer *); 56 | void (*log)(const TSLexer *, const char *, ...); 57 | }; 58 | 59 | typedef enum { 60 | TSParseActionTypeShift, 61 | TSParseActionTypeReduce, 62 | TSParseActionTypeAccept, 63 | TSParseActionTypeRecover, 64 | } TSParseActionType; 65 | 66 | typedef union { 67 | struct { 68 | uint8_t type; 69 | TSStateId state; 70 | bool extra; 71 | bool repetition; 72 | } shift; 73 | struct { 74 | uint8_t type; 75 | uint8_t child_count; 76 | TSSymbol symbol; 77 | int16_t dynamic_precedence; 78 | uint16_t production_id; 79 | } reduce; 80 | uint8_t type; 81 | } TSParseAction; 82 | 83 | typedef struct { 84 | uint16_t lex_state; 85 | uint16_t external_lex_state; 86 | } TSLexMode; 87 | 88 | typedef struct { 89 | uint16_t lex_state; 90 | uint16_t external_lex_state; 91 | uint16_t reserved_word_set_id; 92 | } TSLexerMode; 93 | 94 | typedef union { 95 | TSParseAction action; 96 | struct { 97 | uint8_t count; 98 | bool reusable; 99 | } entry; 100 | } TSParseActionEntry; 101 | 102 | typedef struct { 103 | int32_t start; 104 | int32_t end; 105 | } TSCharacterRange; 106 | 107 | struct TSLanguage { 108 | uint32_t abi_version; 109 | uint32_t symbol_count; 110 | uint32_t alias_count; 111 | uint32_t token_count; 112 | uint32_t external_token_count; 113 | uint32_t state_count; 114 | uint32_t large_state_count; 115 | uint32_t production_id_count; 116 | uint32_t field_count; 117 | uint16_t max_alias_sequence_length; 118 | const uint16_t *parse_table; 119 | const uint16_t *small_parse_table; 120 | const uint32_t *small_parse_table_map; 121 | const TSParseActionEntry *parse_actions; 122 | const char * const *symbol_names; 123 | const char * const *field_names; 124 | const TSMapSlice *field_map_slices; 125 | const TSFieldMapEntry *field_map_entries; 126 | const TSSymbolMetadata *symbol_metadata; 127 | const TSSymbol *public_symbol_map; 128 | const uint16_t *alias_map; 129 | const TSSymbol *alias_sequences; 130 | const TSLexerMode *lex_modes; 131 | bool (*lex_fn)(TSLexer *, TSStateId); 132 | bool (*keyword_lex_fn)(TSLexer *, TSStateId); 133 | TSSymbol keyword_capture_token; 134 | struct { 135 | const bool *states; 136 | const TSSymbol *symbol_map; 137 | void *(*create)(void); 138 | void (*destroy)(void *); 139 | bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist); 140 | unsigned (*serialize)(void *, char *); 141 | void (*deserialize)(void *, const char *, unsigned); 142 | } external_scanner; 143 | const TSStateId *primary_state_ids; 144 | const char *name; 145 | const TSSymbol *reserved_words; 146 | uint16_t max_reserved_word_set_size; 147 | uint32_t supertype_count; 148 | const TSSymbol *supertype_symbols; 149 | const TSMapSlice *supertype_map_slices; 150 | const TSSymbol *supertype_map_entries; 151 | TSLanguageMetadata metadata; 152 | }; 153 | 154 | static inline bool set_contains(const TSCharacterRange *ranges, uint32_t len, int32_t lookahead) { 155 | uint32_t index = 0; 156 | uint32_t size = len - index; 157 | while (size > 1) { 158 | uint32_t half_size = size / 2; 159 | uint32_t mid_index = index + half_size; 160 | const TSCharacterRange *range = &ranges[mid_index]; 161 | if (lookahead >= range->start && lookahead <= range->end) { 162 | return true; 163 | } else if (lookahead > range->end) { 164 | index = mid_index; 165 | } 166 | size -= half_size; 167 | } 168 | const TSCharacterRange *range = &ranges[index]; 169 | return (lookahead >= range->start && lookahead <= range->end); 170 | } 171 | 172 | /* 173 | * Lexer Macros 174 | */ 175 | 176 | #ifdef _MSC_VER 177 | #define UNUSED __pragma(warning(suppress : 4101)) 178 | #else 179 | #define UNUSED __attribute__((unused)) 180 | #endif 181 | 182 | #define START_LEXER() \ 183 | bool result = false; \ 184 | bool skip = false; \ 185 | UNUSED \ 186 | bool eof = false; \ 187 | int32_t lookahead; \ 188 | goto start; \ 189 | next_state: \ 190 | lexer->advance(lexer, skip); \ 191 | start: \ 192 | skip = false; \ 193 | lookahead = lexer->lookahead; 194 | 195 | #define ADVANCE(state_value) \ 196 | { \ 197 | state = state_value; \ 198 | goto next_state; \ 199 | } 200 | 201 | #define ADVANCE_MAP(...) \ 202 | { \ 203 | static const uint16_t map[] = { __VA_ARGS__ }; \ 204 | for (uint32_t i = 0; i < sizeof(map) / sizeof(map[0]); i += 2) { \ 205 | if (map[i] == lookahead) { \ 206 | state = map[i + 1]; \ 207 | goto next_state; \ 208 | } \ 209 | } \ 210 | } 211 | 212 | #define SKIP(state_value) \ 213 | { \ 214 | skip = true; \ 215 | state = state_value; \ 216 | goto next_state; \ 217 | } 218 | 219 | #define ACCEPT_TOKEN(symbol_value) \ 220 | result = true; \ 221 | lexer->result_symbol = symbol_value; \ 222 | lexer->mark_end(lexer); 223 | 224 | #define END_STATE() return result; 225 | 226 | /* 227 | * Parse Table Macros 228 | */ 229 | 230 | #define SMALL_STATE(id) ((id) - LARGE_STATE_COUNT) 231 | 232 | #define STATE(id) id 233 | 234 | #define ACTIONS(id) id 235 | 236 | #define SHIFT(state_value) \ 237 | {{ \ 238 | .shift = { \ 239 | .type = TSParseActionTypeShift, \ 240 | .state = (state_value) \ 241 | } \ 242 | }} 243 | 244 | #define SHIFT_REPEAT(state_value) \ 245 | {{ \ 246 | .shift = { \ 247 | .type = TSParseActionTypeShift, \ 248 | .state = (state_value), \ 249 | .repetition = true \ 250 | } \ 251 | }} 252 | 253 | #define SHIFT_EXTRA() \ 254 | {{ \ 255 | .shift = { \ 256 | .type = TSParseActionTypeShift, \ 257 | .extra = true \ 258 | } \ 259 | }} 260 | 261 | #define REDUCE(symbol_name, children, precedence, prod_id) \ 262 | {{ \ 263 | .reduce = { \ 264 | .type = TSParseActionTypeReduce, \ 265 | .symbol = symbol_name, \ 266 | .child_count = children, \ 267 | .dynamic_precedence = precedence, \ 268 | .production_id = prod_id \ 269 | }, \ 270 | }} 271 | 272 | #define RECOVER() \ 273 | {{ \ 274 | .type = TSParseActionTypeRecover \ 275 | }} 276 | 277 | #define ACCEPT_INPUT() \ 278 | {{ \ 279 | .type = TSParseActionTypeAccept \ 280 | }} 281 | 282 | #ifdef __cplusplus 283 | } 284 | #endif 285 | 286 | #endif // TREE_SITTER_PARSER_H_ 287 | -------------------------------------------------------------------------------- /test/corpus/gitattributes.txt: -------------------------------------------------------------------------------- 1 | =========== 2 | Simple file 3 | =========== 4 | 5 | * text=auto 6 | 7 | --- 8 | 9 | (file 10 | (pattern 11 | (wildcard)) 12 | (attribute 13 | (builtin_attr) 14 | (attr_set) 15 | (string_value))) 16 | 17 | ================= 18 | Multiple patterns 19 | ================= 20 | 21 | * text=auto 22 | *.txt text 23 | *.vcproj text eol=crlf 24 | *.sh text eol=lf 25 | *.jpg -text 26 | 27 | --- 28 | 29 | (file 30 | (pattern 31 | (wildcard)) 32 | (attribute 33 | (builtin_attr) 34 | (attr_set) 35 | (string_value)) 36 | (pattern 37 | (wildcard)) 38 | (attribute 39 | (builtin_attr)) 40 | (pattern 41 | (wildcard)) 42 | (attribute 43 | (builtin_attr)) 44 | (attribute 45 | (builtin_attr) 46 | (attr_set) 47 | (string_value)) 48 | (pattern 49 | (wildcard)) 50 | (attribute 51 | (builtin_attr)) 52 | (attribute 53 | (builtin_attr) 54 | (attr_set) 55 | (string_value)) 56 | (pattern 57 | (wildcard)) 58 | (attribute 59 | (attr_unset) 60 | (builtin_attr))) 61 | 62 | 63 | ================ 64 | Macro definition 65 | ================ 66 | 67 | [attr]nodiff -diff -merge 68 | 69 | --- 70 | 71 | (file 72 | (macro_def 73 | (macro_tag) 74 | (attr_name) 75 | (attribute 76 | (attr_unset) 77 | (builtin_attr)) 78 | (attribute 79 | (attr_unset) 80 | (builtin_attr)))) 81 | 82 | ======== 83 | Comments 84 | ======== 85 | 86 | # in $GIT_DIR/info/attributes 87 | 88 | a* foo !bar -baz 89 | 90 | # in .gitattributes 91 | abc foo bar baz 92 | 93 | # in t/.gitattributes 94 | ab* merge=filfre 95 | abc -foo -bar 96 | *.c frotz 97 | 98 | --- 99 | 100 | (file 101 | (comment) 102 | (pattern 103 | (wildcard)) 104 | (attribute 105 | (attr_name)) 106 | (attribute 107 | (attr_reset) 108 | (attr_name)) 109 | (attribute 110 | (attr_unset) 111 | (attr_name)) 112 | (comment) 113 | (pattern) 114 | (attribute 115 | (attr_name)) 116 | (attribute 117 | (attr_name)) 118 | (attribute 119 | (attr_name)) 120 | (comment) 121 | (pattern 122 | (wildcard)) 123 | (attribute 124 | (builtin_attr) 125 | (attr_set) 126 | (string_value)) 127 | (pattern) 128 | (attribute 129 | (attr_unset) 130 | (attr_name)) 131 | (attribute 132 | (attr_unset) 133 | (attr_name)) 134 | (pattern 135 | (wildcard)) 136 | (attribute 137 | (attr_name))) 138 | 139 | ========== 140 | Value list 141 | ========== 142 | 143 | *.txt whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 144 | 145 | --- 146 | 147 | (file 148 | (pattern 149 | (wildcard)) 150 | (attribute 151 | (builtin_attr) 152 | (attr_set) 153 | (string_value))) 154 | 155 | ========= 156 | Recursive 157 | ========= 158 | 159 | vendor/** linguist-vendored=true 160 | 161 | --- 162 | 163 | (file 164 | (pattern 165 | (dir_sep) 166 | (wildcard)) 167 | (attribute 168 | (attr_name) 169 | (attr_set) 170 | (boolean_value))) 171 | 172 | ============= 173 | Simple ranges 174 | ============= 175 | 176 | *.[Rr]md linguist-detectable 177 | 178 | *.[bB][aA][tT] text eol=crlf 179 | 180 | --- 181 | 182 | (file 183 | (pattern 184 | (wildcard) 185 | (range_notation)) 186 | (attribute 187 | (attr_name)) 188 | (pattern 189 | (wildcard) 190 | (range_notation) 191 | (range_notation) 192 | (range_notation)) 193 | (attribute 194 | (builtin_attr)) 195 | (attribute 196 | (builtin_attr) 197 | (attr_set) 198 | (string_value))) 199 | 200 | =========== 201 | More ranges 202 | =========== 203 | 204 | [._]*.sw[a-p] -diff 205 | 206 | [^._]-[[:lower:]] !something 207 | 208 | --- 209 | 210 | (file 211 | (pattern 212 | (range_notation) 213 | (wildcard) 214 | (range_notation 215 | (class_range))) 216 | (attribute 217 | (attr_unset) 218 | (builtin_attr)) 219 | (pattern 220 | (range_notation 221 | (range_negation)) 222 | (range_notation 223 | (character_class))) 224 | (attribute 225 | (attr_reset) 226 | (attr_name))) 227 | 228 | =============== 229 | Quoted patterns 230 | =============== 231 | 232 | "file [with] spaces" text 233 | 234 | "_\u4E00\t\56txt" encoding=UTF-16 235 | 236 | --- 237 | 238 | (file 239 | (quoted_pattern) 240 | (attribute 241 | (builtin_attr)) 242 | (quoted_pattern 243 | (ansi_c_escape) 244 | (ansi_c_escape) 245 | (ansi_c_escape)) 246 | (attribute 247 | (builtin_attr) 248 | (attr_set) 249 | (string_value))) 250 | 251 | ============== 252 | Blank comments 253 | ============== 254 | 255 | # This is some text 256 | # 257 | # It is a comment 258 | 259 | --- 260 | 261 | (file 262 | (comment) 263 | (comment) 264 | (comment)) 265 | -------------------------------------------------------------------------------- /test/highlight/test.gitattributes: -------------------------------------------------------------------------------- 1 | [attr]nodiff -diff -merge 2 | # <- @keyword 3 | # ^^^^^^ @property 4 | # ^ @operator 5 | # ^^^^ @variable.builtin 6 | # ^ @operator 7 | # ^^^^^ @variable.builtin 8 | 9 | vendor/** linguist-vendored=true 10 | # ^ @punctuation.delimiter 11 | # ^^ @string.regexp 12 | # ^^^^^^^^^^^^^^^^^ @variable.parameter 13 | # ^ @operator 14 | # ^^^^ @boolean 15 | 16 | [^._]-[[:lower:]] !something 17 | # ^ @punctuation.bracket 18 | # ^ @operator 19 | # ^^ @string.special 20 | # ^ @punctuation.bracket 21 | # ^ @punctuation.bracket 22 | # ^^^^^^^^^ @constant 23 | # ^ @punctuation.bracket 24 | # ^ @operator 25 | # ^^^^^^^^^^ @variable.parameter 26 | 27 | "_\u4E00\t\56txt" encoding=UTF-16 28 | # <- @punctuation.special 29 | # ^^^^^^ @escape 30 | # ^^ @escape 31 | # ^^^ @escape 32 | # ^ @punctuation.special 33 | # ^^^^^^^^ @variable.builtin 34 | # ^ @operator 35 | # ^^^^^^ @string 36 | 37 | # vim:ft=gitattributes: 38 | -------------------------------------------------------------------------------- /tree-sitter.json: -------------------------------------------------------------------------------- 1 | { 2 | "grammars": [ 3 | { 4 | "name": "gitattributes", 5 | "path": ".", 6 | "camelcase": "GitAttributes", 7 | "title": "gitattributes", 8 | "scope": "source.gitattributes", 9 | "highlights": "queries/highlights.scm", 10 | "file-types": [ 11 | "gitattributes", 12 | ".gitattributes" 13 | ], 14 | "class-name": "TreeSitterGitAttributes" 15 | } 16 | ], 17 | "metadata": { 18 | "version": "0.2.0", 19 | "license": "MIT", 20 | "description": "gitattributes grammar for tree-sitter", 21 | "authors": [ 22 | { 23 | "name": "ObserverOfTime", 24 | "email": "chronobserver@disroot.org" 25 | } 26 | ], 27 | "links": { 28 | "repository": "https://github.com/tree-sitter-grammars/tree-sitter-gitattributes" 29 | } 30 | } 31 | } 32 | --------------------------------------------------------------------------------