├── .gitattributes ├── .github └── workflows │ ├── ci.yml │ └── generate-parser.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── assets └── highlight.png ├── binding.gyp ├── bindings ├── node │ ├── binding.cc │ └── index.js └── rust │ ├── build.rs │ └── lib.rs ├── grammar.js ├── package-lock.json ├── package.json ├── queries └── highlights.scm ├── src ├── grammar.json ├── node-types.json ├── parser.c └── tree_sitter │ └── parser.h └── test ├── corpus ├── comments.txt └── message.txt └── highlight └── basic.COMMIT_EDITMSG /.gitattributes: -------------------------------------------------------------------------------- 1 | /src/**/* linguist-generated=true 2 | /bindings/**/* linguist-generated=true 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | bless: 7 | name: Bless 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v3 13 | 14 | - name: Install Node 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: "16.x" 18 | 19 | - name: Cache npm dependencies 20 | uses: actions/cache@v3 21 | with: 22 | path: ~/.npm 23 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 24 | restore-keys: | 25 | ${{ runner.os }}-node- 26 | 27 | - name: Install npm dependencies 28 | run: npm ci 29 | 30 | - name: Ensure generated parser files are up to date 31 | run: npx tree-sitter generate 32 | 33 | - name: Run tree-sitter tests 34 | run: npx tree-sitter test 35 | 36 | - name: Check formatting 37 | run: npm run format-check 38 | -------------------------------------------------------------------------------- /.github/workflows/generate-parser.yml: -------------------------------------------------------------------------------- 1 | # generates the parser with 'tree-sitter generate' if the parser is out of date 2 | name: Generate Parser 3 | 4 | on: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | generate: 11 | name: Generate Parser 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | 18 | - name: Install Node 19 | uses: actions/setup-node@v3 20 | with: 21 | node-version: "16.x" 22 | 23 | - name: Cache npm dependencies 24 | uses: actions/cache@v3 25 | with: 26 | path: ~/.npm 27 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 28 | restore-keys: | 29 | ${{ runner.os }}-node- 30 | 31 | - name: Install npm dependencies 32 | run: npm ci 33 | 34 | - name: Generate parser files 35 | run: | 36 | npx tree-sitter generate 37 | npx tree-sitter build-wasm 38 | 39 | - name: Commit generated parser files 40 | uses: stefanzweifel/git-auto-commit-action@v4 41 | with: 42 | commit_message: Generate parser 43 | file_pattern: src 44 | 45 | - name: Checkout gh-pages branch to ./gh-pages 46 | uses: actions/checkout@v2 47 | with: 48 | ref: gh-pages 49 | path: ./gh-pages 50 | 51 | - run: mv *.wasm ./gh-pages 52 | 53 | - name: Commit generated WASM binding 54 | uses: stefanzweifel/git-auto-commit-action@v4 55 | with: 56 | commit_message: Generate WASM binding 57 | file_pattern: "*.wasm" 58 | repository: ./gh-pages 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /build/ 3 | /tmp/ 4 | /log.html 5 | /*.wasm 6 | /gh-pages/ 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tree-sitter-gitcommit" 3 | description = "gitcommit grammar for the tree-sitter parsing library" 4 | version = "0.0.1" 5 | keywords = ["incremental", "parsing", "gitcommit"] 6 | categories = ["parsing", "text-editors"] 7 | repository = "https://github.com/tree-sitter/tree-sitter-gitcommit" 8 | edition = "2018" 9 | license = "MIT" 10 | 11 | build = "bindings/rust/build.rs" 12 | include = [ 13 | "bindings/rust/*", 14 | "grammar.js", 15 | "queries/*", 16 | "src/*", 17 | ] 18 | 19 | [lib] 20 | path = "bindings/rust/lib.rs" 21 | 22 | [dependencies] 23 | tree-sitter = "~0.20" 24 | 25 | [build-dependencies] 26 | cc = "1.0" 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Michael Davis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `tree-sitter-git-commit` 2 | 3 | [![CI](https://github.com/the-mikedavis/tree-sitter-git-commit/actions/workflows/ci.yml/badge.svg)](https://github.com/the-mikedavis/tree-sitter-git-commit/actions/workflows/ci.yml) 4 | 5 | A [tree-sitter](https://tree-sitter.github.io/tree-sitter/) grammar for git commit messages. 6 | 7 | ### Status 8 | 9 | Somewhat complete but needs testing and highlight queries. 10 | 11 | ### Example 12 | 13 | In the [Helix](https://github.com/helix-editor/helix) editor: 14 | 15 | 16 | -------------------------------------------------------------------------------- /assets/highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-mikedavis/tree-sitter-git-commit/6f193a66e9aa872760823dff020960c6cedc37b3/assets/highlight.png -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "tree_sitter_gitcommit_binding", 5 | "include_dirs": [ 6 | " 3 | #include "nan.h" 4 | 5 | using namespace v8; 6 | 7 | extern "C" TSLanguage * tree_sitter_git_commit(); 8 | 9 | namespace { 10 | 11 | NAN_METHOD(New) {} 12 | 13 | void Init(Local exports, Local module) { 14 | Local tpl = Nan::New(New); 15 | tpl->SetClassName(Nan::New("Language").ToLocalChecked()); 16 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 17 | 18 | Local constructor = Nan::GetFunction(tpl).ToLocalChecked(); 19 | Local instance = constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked(); 20 | Nan::SetInternalFieldPointer(instance, 0, tree_sitter_git_commit()); 21 | 22 | Nan::Set(instance, Nan::New("name").ToLocalChecked(), Nan::New("git_commit").ToLocalChecked()); 23 | Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance); 24 | } 25 | 26 | NODE_MODULE(tree_sitter_git_commit_binding, Init) 27 | 28 | } // namespace 29 | -------------------------------------------------------------------------------- /bindings/node/index.js: -------------------------------------------------------------------------------- 1 | try { 2 | module.exports = require("../../build/Release/tree_sitter_git_commit_binding"); 3 | } catch (error1) { 4 | if (error1.code !== 'MODULE_NOT_FOUND') { 5 | throw error1; 6 | } 7 | try { 8 | module.exports = require("../../build/Debug/tree_sitter_git_commit_binding"); 9 | } catch (error2) { 10 | if (error2.code !== 'MODULE_NOT_FOUND') { 11 | throw error2; 12 | } 13 | throw error1 14 | } 15 | } 16 | 17 | try { 18 | module.exports.nodeTypeInfo = require("../../src/node-types.json"); 19 | } catch (_) {} 20 | -------------------------------------------------------------------------------- /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.include(&src_dir); 6 | c_config 7 | .flag_if_supported("-Wno-unused-parameter") 8 | .flag_if_supported("-Wno-unused-but-set-variable") 9 | .flag_if_supported("-Wno-trigraphs"); 10 | let parser_path = src_dir.join("parser.c"); 11 | c_config.file(&parser_path); 12 | 13 | // If your language uses an external scanner written in C, 14 | // then include this block of code: 15 | 16 | /* 17 | let scanner_path = src_dir.join("scanner.c"); 18 | c_config.file(&scanner_path); 19 | println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 20 | */ 21 | 22 | c_config.compile("parser"); 23 | println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); 24 | 25 | // If your language uses an external scanner written in C++, 26 | // then include this block of code: 27 | 28 | /* 29 | let mut cpp_config = cc::Build::new(); 30 | cpp_config.cpp(true); 31 | cpp_config.include(&src_dir); 32 | cpp_config 33 | .flag_if_supported("-Wno-unused-parameter") 34 | .flag_if_supported("-Wno-unused-but-set-variable"); 35 | let scanner_path = src_dir.join("scanner.cc"); 36 | cpp_config.file(&scanner_path); 37 | cpp_config.compile("scanner"); 38 | println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); 39 | */ 40 | } 41 | -------------------------------------------------------------------------------- /bindings/rust/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides git_commit language support for the [tree-sitter][] parsing library. 2 | //! 3 | //! Typically, you will use the [language][language func] function to add this language to a 4 | //! tree-sitter [Parser][], and then use the parser to parse some code: 5 | //! 6 | //! ``` 7 | //! let code = ""; 8 | //! let mut parser = tree_sitter::Parser::new(); 9 | //! parser.set_language(tree_sitter_git_commit::language()).expect("Error loading git_commit grammar"); 10 | //! let tree = parser.parse(code, None).unwrap(); 11 | //! ``` 12 | //! 13 | //! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 14 | //! [language func]: fn.language.html 15 | //! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html 16 | //! [tree-sitter]: https://tree-sitter.github.io/ 17 | 18 | use tree_sitter::Language; 19 | 20 | extern "C" { 21 | fn tree_sitter_git_commit() -> Language; 22 | } 23 | 24 | /// Get the tree-sitter [Language][] for this grammar. 25 | /// 26 | /// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html 27 | pub fn language() -> Language { 28 | unsafe { tree_sitter_git_commit() } 29 | } 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#static-node-types 34 | pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json"); 35 | 36 | // Uncomment these to include any queries that this grammar contains 37 | 38 | // pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm"); 39 | // pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm"); 40 | // pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm"); 41 | // pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm"); 42 | 43 | #[cfg(test)] 44 | mod tests { 45 | #[test] 46 | fn test_can_load_grammar() { 47 | let mut parser = tree_sitter::Parser::new(); 48 | parser 49 | .set_language(super::language()) 50 | .expect("Error loading git_commit language"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /grammar.js: -------------------------------------------------------------------------------- 1 | /// 2 | /* glossary: 3 | - subject: the first line of a commit 4 | - message: the body of a commit 5 | - usually starts on the third line of the file (1-indexed) 6 | - may be interspersed with comments 7 | - item: an issue or PR 8 | - change: how a file will change with this commit 9 | - either 'new file', 'modified', 'renamed' or 'deleted' 10 | */ 11 | 12 | /** 13 | * Whitespace that is ignored by the parser: tab, form feed, vertical tab, and 14 | * space characters. Note that whitespace doesn't include newlines! 15 | */ 16 | const WHITE_SPACE = /[\t\f\v ]+/; 17 | const ANYTHING = /[^\n\r]+/; 18 | 19 | // see https://tree-sitter.github.io/tree-sitter/creating-parsers#lexical-precedence-vs-parse-precedence 20 | const PARSE_PRECEDENCE = { 21 | NONSENSE: -1, 22 | BODY_LINE: 1, 23 | COMMENT: 2, 24 | TRAILERS: 3, 25 | }; 26 | const LEXICAL_PRECEDENCE = { 27 | ANY_WORD: -3, 28 | NONSENSE: -2, 29 | NON_PUNCTUATED_WORD: -1, 30 | // string literals and RegExes not decorated with `prec(LEXICAL_PRECEDENCE._)` 31 | // have precedence 0. 32 | PATH: 1, 33 | PATH_SEPARATOR_ARROW: 2, 34 | ITEM: 3, 35 | USER: 4, 36 | SUBJECT_FIRST_CHAR: 5, 37 | }; 38 | 39 | const SCISSORS = 40 | /# -+ >8 -+\r?\n# Do not modify or remove the line above.\r?\n# Everything below it will be ignored.\r?\n?/; 41 | 42 | module.exports = grammar({ 43 | name: "git_commit", 44 | 45 | extras: ($) => [WHITE_SPACE], 46 | 47 | rules: { 48 | source: ($) => 49 | seq( 50 | optional(choice($.comment, $.subject)), 51 | repeat($._body_line), 52 | repeat($._trailer), 53 | repeat(seq($._newline, optional($.comment))), 54 | optional( 55 | seq(alias(SCISSORS, $.scissors), optional(alias($._rest, $.message))) 56 | ) 57 | ), 58 | 59 | /** 60 | * The subject of the commit message: the first line. 61 | */ 62 | subject: ($) => seq(/[^#\r\n]/, repeat(ANYTHING)), 63 | 64 | _body_line: ($) => 65 | prec.right( 66 | PARSE_PRECEDENCE.BODY_LINE, 67 | seq($._newline, optional(choice($.message, $.comment))) 68 | ), 69 | 70 | _trailer: ($) => 71 | prec(PARSE_PRECEDENCE.TRAILERS, seq($._newline, $.trailer)), 72 | 73 | /** 74 | * Non-comment body 75 | */ 76 | message: ($) => 77 | choice( 78 | // Lines starting with spaces are certainly messages and may start with any characters. 79 | seq(WHITE_SPACE, repeat($._text)), 80 | // Otherwise message lines must not start with '#'. 81 | seq( 82 | choice($.user, $.commit, $._non_punctuated_word, $._non_comment), 83 | repeat($._text) 84 | ) 85 | ), 86 | 87 | _text: ($) => choice($.user, $.item, $.commit, $._word), 88 | 89 | comment: ($) => seq("#", optional($._comment_body)), 90 | 91 | _comment_body: ($) => 92 | choice( 93 | alias($._rebase_summary, $.summary), 94 | $.summary, 95 | $._branch_declaration, 96 | // fallback to regular comment words if the words are nonsense 97 | repeat1($._word) 98 | ), 99 | 100 | /** 101 | * Trailers are "lines that look similar to RFC 822 e-mail headers at the 102 | * end of the otherwise free-form part of a commit message." For more details 103 | * on the format, see https://git-scm.com/docs/git-interpret-trailers 104 | */ 105 | trailer: ($) => 106 | seq( 107 | field("key", $.trailer_key), 108 | field("separator", choice(":", "=")), 109 | field("value", $.trailer_value) 110 | ), 111 | 112 | trailer_key: ($) => $._word, 113 | trailer_value: ($) => repeat1(choice($.user, $.item, $.commit, $._word)), 114 | 115 | _rebase_summary: ($) => 116 | seq( 117 | seq( 118 | "interactive", 119 | "rebase", 120 | "in", 121 | "progress", 122 | ";", 123 | "onto", 124 | $.commit, 125 | $._newline 126 | ), 127 | seq("#", alias($._rebase_header, $.header), $._newline), 128 | repeat(seq("#", $.rebase_command, $._newline)), 129 | seq("#", alias($._rebase_header, $.header), $._newline), 130 | repeat(seq("#", $.rebase_command, $._newline)), 131 | seq( 132 | "#", 133 | "You", 134 | "are", 135 | "currently", 136 | repeat(/\S+/), 137 | "rebasing", 138 | "branch", 139 | "'", 140 | $.branch, 141 | "'", 142 | "on", 143 | "'", 144 | $.commit, 145 | "'", 146 | "." 147 | ), 148 | $._newline, 149 | optional("#") 150 | ), 151 | 152 | _rebase_header: ($) => 153 | choice( 154 | seq( 155 | "Last", 156 | /commands?/, 157 | "done", 158 | "(", 159 | /\d+/, 160 | /commands?/, 161 | "done", 162 | ")", 163 | ":" 164 | ), 165 | seq( 166 | "Next", 167 | /commands?/, 168 | "to", 169 | "do", 170 | "(", 171 | /\d+/, 172 | "remaining", 173 | /commands?/, 174 | ")", 175 | ":" 176 | ), 177 | seq("No", "commands", "remaining", ".") 178 | ), 179 | 180 | summary: ($) => 181 | choice( 182 | seq( 183 | alias($._change_header, $.header), 184 | $._newline, 185 | repeat1(seq("#", $.change, $._newline)), 186 | optional("#") 187 | ), 188 | seq( 189 | $.header, 190 | $._newline, 191 | repeat1(seq("#", $.path, $._newline)), 192 | optional("#") 193 | ) 194 | ), 195 | 196 | _change_header: ($) => 197 | choice( 198 | seq("Changes", "to", "be", "committed", ":"), 199 | seq("Changes", "not", "staged", "for", "commit", ":") 200 | ), 201 | 202 | _branch_declaration: ($) => 203 | choice( 204 | seq("On", "branch", $.branch), 205 | seq( 206 | "Your", 207 | "branch", 208 | "is", 209 | "up", 210 | "to", 211 | "date", 212 | "with", 213 | "'", 214 | $.branch, 215 | "'." 216 | ), 217 | seq( 218 | "Your", 219 | "branch", 220 | "is", 221 | choice(seq("ahead", "of"), "behind"), 222 | "'", 223 | $.branch, 224 | "'", 225 | "by", 226 | /\d+/, 227 | /commits?/, 228 | "." 229 | ), 230 | seq( 231 | "Your", 232 | "branch", 233 | "and", 234 | "'", 235 | $.branch, 236 | "'", 237 | "have", 238 | "diverged", 239 | "," 240 | ), 241 | // # HEAD detached at upstream/gh-pages 242 | seq("HEAD", "detached", "at", choice($.commit, $.branch)) 243 | ), 244 | 245 | header: ($) => seq(choice("Conflicts", seq("Untracked", "files")), ":"), 246 | 247 | change: ($) => 248 | seq( 249 | field("kind", choice("new file", "modified", "renamed", "deleted")), 250 | ":", 251 | $.path, 252 | optional( 253 | seq( 254 | token(prec(LEXICAL_PRECEDENCE.PATH_SEPARATOR_ARROW, "->")), 255 | $.path 256 | ) 257 | ) 258 | ), 259 | 260 | commit: ($) => /[a-f0-9]{7,40}/, 261 | 262 | _non_punctuated_word: ($) => 263 | token(prec(LEXICAL_PRECEDENCE.NON_PUNCTUATED_WORD, /[-\w]+/)), 264 | _non_comment: ($) => token(prec(LEXICAL_PRECEDENCE.NONSENSE, /[^#\s]+/)), 265 | _any_word: ($) => token(prec(LEXICAL_PRECEDENCE.ANY_WORD, /\S+/)), 266 | _word: ($) => 267 | prec( 268 | PARSE_PRECEDENCE.NONSENSE, 269 | choice($._non_punctuated_word, $._non_comment, $._any_word) 270 | ), 271 | /** 272 | * For most of the details on branch name constraints, see https://git-scm.com/docs/git-check-ref-format 273 | */ 274 | branch: ($) => /[^\.\s']+/, 275 | 276 | rebase_command: ($) => 277 | seq( 278 | choice( 279 | "pick", 280 | "edit", 281 | "squash", 282 | "merge", 283 | "fixup", 284 | "drop", 285 | "reword", 286 | "exec", 287 | "label", 288 | "reset" 289 | ), 290 | repeat1(/\S+/) 291 | ), 292 | 293 | path: ($) => repeat1(token(prec(LEXICAL_PRECEDENCE.PATH, /\S+/))), 294 | /** a github-style issue or PR reference */ 295 | item: ($) => token(prec(LEXICAL_PRECEDENCE.ITEM, /#\d+/)), 296 | 297 | user: ($) => token(prec(LEXICAL_PRECEDENCE.USER, /@[a-zA-Z0-9_-]+/)), 298 | 299 | _rest: ($) => repeat1(choice(/.*/, $._newline)), 300 | _newline: ($) => /\r?\n/, 301 | }, 302 | }); 303 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-gitcommit", 3 | "version": "0.0.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "tree-sitter-gitcommit", 9 | "version": "0.0.1", 10 | "license": "MIT", 11 | "dependencies": { 12 | "nan": "^2.15.0" 13 | }, 14 | "devDependencies": { 15 | "prettier": "^2.5.1", 16 | "tree-sitter-cli": "^0.20.7" 17 | } 18 | }, 19 | "node_modules/nan": { 20 | "version": "2.15.0", 21 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", 22 | "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" 23 | }, 24 | "node_modules/prettier": { 25 | "version": "2.8.4", 26 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", 27 | "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", 28 | "dev": true, 29 | "bin": { 30 | "prettier": "bin-prettier.js" 31 | }, 32 | "engines": { 33 | "node": ">=10.13.0" 34 | }, 35 | "funding": { 36 | "url": "https://github.com/prettier/prettier?sponsor=1" 37 | } 38 | }, 39 | "node_modules/tree-sitter-cli": { 40 | "version": "0.20.7", 41 | "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.20.7.tgz", 42 | "integrity": "sha512-MHABT8oCPr4D0fatsPo6ATQ9H4h9vHpPRjlxkxJs80tpfAEKGn6A1zU3eqfCKBcgmfZDe9CiL3rKOGMzYHwA3w==", 43 | "dev": true, 44 | "hasInstallScript": true, 45 | "bin": { 46 | "tree-sitter": "cli.js" 47 | } 48 | } 49 | }, 50 | "dependencies": { 51 | "nan": { 52 | "version": "2.15.0", 53 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", 54 | "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" 55 | }, 56 | "prettier": { 57 | "version": "2.8.4", 58 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", 59 | "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", 60 | "dev": true 61 | }, 62 | "tree-sitter-cli": { 63 | "version": "0.20.7", 64 | "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.20.7.tgz", 65 | "integrity": "sha512-MHABT8oCPr4D0fatsPo6ATQ9H4h9vHpPRjlxkxJs80tpfAEKGn6A1zU3eqfCKBcgmfZDe9CiL3rKOGMzYHwA3w==", 66 | "dev": true 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tree-sitter-gitcommit", 3 | "version": "0.0.1", 4 | "description": "A tree-sitter grammar for git commit messages", 5 | "main": "bindings/node", 6 | "scripts": { 7 | "test": "tree-sitter test", 8 | "format": "prettier --write grammar.js package.json", 9 | "format-check": "prettier --check grammar.js package.json" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/the-mikedavis/tree-sitter-gitcommit.git" 14 | }, 15 | "keywords": [ 16 | "tree-sitter", 17 | "parser", 18 | "lexer", 19 | "git", 20 | "commit" 21 | ], 22 | "author": "the-mikedavis", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/the-mikedavis/tree-sitter-gitcommit/issues" 26 | }, 27 | "homepage": "https://github.com/the-mikedavis/tree-sitter-gitcommit#readme", 28 | "dependencies": { 29 | "nan": "^2.15.0" 30 | }, 31 | "devDependencies": { 32 | "prettier": "^2.5.1", 33 | "tree-sitter-cli": "^0.20.7" 34 | }, 35 | "tree-sitter": [ 36 | { 37 | "file-types": [ 38 | "COMMIT_EDITMSG" 39 | ] 40 | } 41 | ], 42 | "prettier": { 43 | "trailingComma": "es5" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /queries/highlights.scm: -------------------------------------------------------------------------------- 1 | ; # Reference highlights 2 | ; 3 | ; These highlights use custom scopes that won't exist in the editor or app 4 | ; you want to use tree-sitter-git-commit in. These are just for testing 5 | ; purposes. When modifying/re-using these queries, be sure to change the 6 | ; scopes to the scopes your editor uses. 7 | ; 8 | ; Note: these highlights also won't look good if you use 9 | ; 'tree-sitter highlight' from tree-sitter-cli. 10 | 11 | (subject) @subject 12 | (path) @path 13 | (branch) @branch 14 | (commit) @commit 15 | (item) @item 16 | (header) @header 17 | (message) @message 18 | 19 | (change kind: "new file" @plus) 20 | (change kind: "deleted" @minus) 21 | (change kind: "modified" @delta) 22 | (change kind: "renamed" @delta.renamed) 23 | 24 | (trailer 25 | key: (trailer_key) @trailer.key 26 | value: (trailer_value) @trailer.value) 27 | 28 | [":" "=" "->" (scissors)] @punctuation.delimiter 29 | (comment) @comment 30 | 31 | -------------------------------------------------------------------------------- /src/grammar.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "git_commit", 3 | "rules": { 4 | "source": { 5 | "type": "SEQ", 6 | "members": [ 7 | { 8 | "type": "CHOICE", 9 | "members": [ 10 | { 11 | "type": "CHOICE", 12 | "members": [ 13 | { 14 | "type": "SYMBOL", 15 | "name": "comment" 16 | }, 17 | { 18 | "type": "SYMBOL", 19 | "name": "subject" 20 | } 21 | ] 22 | }, 23 | { 24 | "type": "BLANK" 25 | } 26 | ] 27 | }, 28 | { 29 | "type": "REPEAT", 30 | "content": { 31 | "type": "SYMBOL", 32 | "name": "_body_line" 33 | } 34 | }, 35 | { 36 | "type": "REPEAT", 37 | "content": { 38 | "type": "SYMBOL", 39 | "name": "_trailer" 40 | } 41 | }, 42 | { 43 | "type": "REPEAT", 44 | "content": { 45 | "type": "SEQ", 46 | "members": [ 47 | { 48 | "type": "SYMBOL", 49 | "name": "_newline" 50 | }, 51 | { 52 | "type": "CHOICE", 53 | "members": [ 54 | { 55 | "type": "SYMBOL", 56 | "name": "comment" 57 | }, 58 | { 59 | "type": "BLANK" 60 | } 61 | ] 62 | } 63 | ] 64 | } 65 | }, 66 | { 67 | "type": "CHOICE", 68 | "members": [ 69 | { 70 | "type": "SEQ", 71 | "members": [ 72 | { 73 | "type": "ALIAS", 74 | "content": { 75 | "type": "PATTERN", 76 | "value": "# -+ >8 -+\\r?\\n# Do not modify or remove the line above.\\r?\\n# Everything below it will be ignored.\\r?\\n?" 77 | }, 78 | "named": true, 79 | "value": "scissors" 80 | }, 81 | { 82 | "type": "CHOICE", 83 | "members": [ 84 | { 85 | "type": "ALIAS", 86 | "content": { 87 | "type": "SYMBOL", 88 | "name": "_rest" 89 | }, 90 | "named": true, 91 | "value": "message" 92 | }, 93 | { 94 | "type": "BLANK" 95 | } 96 | ] 97 | } 98 | ] 99 | }, 100 | { 101 | "type": "BLANK" 102 | } 103 | ] 104 | } 105 | ] 106 | }, 107 | "subject": { 108 | "type": "SEQ", 109 | "members": [ 110 | { 111 | "type": "PATTERN", 112 | "value": "[^#\\r\\n]" 113 | }, 114 | { 115 | "type": "REPEAT", 116 | "content": { 117 | "type": "PATTERN", 118 | "value": "[^\\n\\r]+" 119 | } 120 | } 121 | ] 122 | }, 123 | "_body_line": { 124 | "type": "PREC_RIGHT", 125 | "value": 1, 126 | "content": { 127 | "type": "SEQ", 128 | "members": [ 129 | { 130 | "type": "SYMBOL", 131 | "name": "_newline" 132 | }, 133 | { 134 | "type": "CHOICE", 135 | "members": [ 136 | { 137 | "type": "CHOICE", 138 | "members": [ 139 | { 140 | "type": "SYMBOL", 141 | "name": "message" 142 | }, 143 | { 144 | "type": "SYMBOL", 145 | "name": "comment" 146 | } 147 | ] 148 | }, 149 | { 150 | "type": "BLANK" 151 | } 152 | ] 153 | } 154 | ] 155 | } 156 | }, 157 | "_trailer": { 158 | "type": "PREC", 159 | "value": 3, 160 | "content": { 161 | "type": "SEQ", 162 | "members": [ 163 | { 164 | "type": "SYMBOL", 165 | "name": "_newline" 166 | }, 167 | { 168 | "type": "SYMBOL", 169 | "name": "trailer" 170 | } 171 | ] 172 | } 173 | }, 174 | "message": { 175 | "type": "CHOICE", 176 | "members": [ 177 | { 178 | "type": "SEQ", 179 | "members": [ 180 | { 181 | "type": "PATTERN", 182 | "value": "[\\t\\f\\v ]+" 183 | }, 184 | { 185 | "type": "REPEAT", 186 | "content": { 187 | "type": "SYMBOL", 188 | "name": "_text" 189 | } 190 | } 191 | ] 192 | }, 193 | { 194 | "type": "SEQ", 195 | "members": [ 196 | { 197 | "type": "CHOICE", 198 | "members": [ 199 | { 200 | "type": "SYMBOL", 201 | "name": "user" 202 | }, 203 | { 204 | "type": "SYMBOL", 205 | "name": "commit" 206 | }, 207 | { 208 | "type": "SYMBOL", 209 | "name": "_non_punctuated_word" 210 | }, 211 | { 212 | "type": "SYMBOL", 213 | "name": "_non_comment" 214 | } 215 | ] 216 | }, 217 | { 218 | "type": "REPEAT", 219 | "content": { 220 | "type": "SYMBOL", 221 | "name": "_text" 222 | } 223 | } 224 | ] 225 | } 226 | ] 227 | }, 228 | "_text": { 229 | "type": "CHOICE", 230 | "members": [ 231 | { 232 | "type": "SYMBOL", 233 | "name": "user" 234 | }, 235 | { 236 | "type": "SYMBOL", 237 | "name": "item" 238 | }, 239 | { 240 | "type": "SYMBOL", 241 | "name": "commit" 242 | }, 243 | { 244 | "type": "SYMBOL", 245 | "name": "_word" 246 | } 247 | ] 248 | }, 249 | "comment": { 250 | "type": "SEQ", 251 | "members": [ 252 | { 253 | "type": "STRING", 254 | "value": "#" 255 | }, 256 | { 257 | "type": "CHOICE", 258 | "members": [ 259 | { 260 | "type": "SYMBOL", 261 | "name": "_comment_body" 262 | }, 263 | { 264 | "type": "BLANK" 265 | } 266 | ] 267 | } 268 | ] 269 | }, 270 | "_comment_body": { 271 | "type": "CHOICE", 272 | "members": [ 273 | { 274 | "type": "ALIAS", 275 | "content": { 276 | "type": "SYMBOL", 277 | "name": "_rebase_summary" 278 | }, 279 | "named": true, 280 | "value": "summary" 281 | }, 282 | { 283 | "type": "SYMBOL", 284 | "name": "summary" 285 | }, 286 | { 287 | "type": "SYMBOL", 288 | "name": "_branch_declaration" 289 | }, 290 | { 291 | "type": "REPEAT1", 292 | "content": { 293 | "type": "SYMBOL", 294 | "name": "_word" 295 | } 296 | } 297 | ] 298 | }, 299 | "trailer": { 300 | "type": "SEQ", 301 | "members": [ 302 | { 303 | "type": "FIELD", 304 | "name": "key", 305 | "content": { 306 | "type": "SYMBOL", 307 | "name": "trailer_key" 308 | } 309 | }, 310 | { 311 | "type": "FIELD", 312 | "name": "separator", 313 | "content": { 314 | "type": "CHOICE", 315 | "members": [ 316 | { 317 | "type": "STRING", 318 | "value": ":" 319 | }, 320 | { 321 | "type": "STRING", 322 | "value": "=" 323 | } 324 | ] 325 | } 326 | }, 327 | { 328 | "type": "FIELD", 329 | "name": "value", 330 | "content": { 331 | "type": "SYMBOL", 332 | "name": "trailer_value" 333 | } 334 | } 335 | ] 336 | }, 337 | "trailer_key": { 338 | "type": "SYMBOL", 339 | "name": "_word" 340 | }, 341 | "trailer_value": { 342 | "type": "REPEAT1", 343 | "content": { 344 | "type": "CHOICE", 345 | "members": [ 346 | { 347 | "type": "SYMBOL", 348 | "name": "user" 349 | }, 350 | { 351 | "type": "SYMBOL", 352 | "name": "item" 353 | }, 354 | { 355 | "type": "SYMBOL", 356 | "name": "commit" 357 | }, 358 | { 359 | "type": "SYMBOL", 360 | "name": "_word" 361 | } 362 | ] 363 | } 364 | }, 365 | "_rebase_summary": { 366 | "type": "SEQ", 367 | "members": [ 368 | { 369 | "type": "SEQ", 370 | "members": [ 371 | { 372 | "type": "STRING", 373 | "value": "interactive" 374 | }, 375 | { 376 | "type": "STRING", 377 | "value": "rebase" 378 | }, 379 | { 380 | "type": "STRING", 381 | "value": "in" 382 | }, 383 | { 384 | "type": "STRING", 385 | "value": "progress" 386 | }, 387 | { 388 | "type": "STRING", 389 | "value": ";" 390 | }, 391 | { 392 | "type": "STRING", 393 | "value": "onto" 394 | }, 395 | { 396 | "type": "SYMBOL", 397 | "name": "commit" 398 | }, 399 | { 400 | "type": "SYMBOL", 401 | "name": "_newline" 402 | } 403 | ] 404 | }, 405 | { 406 | "type": "SEQ", 407 | "members": [ 408 | { 409 | "type": "STRING", 410 | "value": "#" 411 | }, 412 | { 413 | "type": "ALIAS", 414 | "content": { 415 | "type": "SYMBOL", 416 | "name": "_rebase_header" 417 | }, 418 | "named": true, 419 | "value": "header" 420 | }, 421 | { 422 | "type": "SYMBOL", 423 | "name": "_newline" 424 | } 425 | ] 426 | }, 427 | { 428 | "type": "REPEAT", 429 | "content": { 430 | "type": "SEQ", 431 | "members": [ 432 | { 433 | "type": "STRING", 434 | "value": "#" 435 | }, 436 | { 437 | "type": "SYMBOL", 438 | "name": "rebase_command" 439 | }, 440 | { 441 | "type": "SYMBOL", 442 | "name": "_newline" 443 | } 444 | ] 445 | } 446 | }, 447 | { 448 | "type": "SEQ", 449 | "members": [ 450 | { 451 | "type": "STRING", 452 | "value": "#" 453 | }, 454 | { 455 | "type": "ALIAS", 456 | "content": { 457 | "type": "SYMBOL", 458 | "name": "_rebase_header" 459 | }, 460 | "named": true, 461 | "value": "header" 462 | }, 463 | { 464 | "type": "SYMBOL", 465 | "name": "_newline" 466 | } 467 | ] 468 | }, 469 | { 470 | "type": "REPEAT", 471 | "content": { 472 | "type": "SEQ", 473 | "members": [ 474 | { 475 | "type": "STRING", 476 | "value": "#" 477 | }, 478 | { 479 | "type": "SYMBOL", 480 | "name": "rebase_command" 481 | }, 482 | { 483 | "type": "SYMBOL", 484 | "name": "_newline" 485 | } 486 | ] 487 | } 488 | }, 489 | { 490 | "type": "SEQ", 491 | "members": [ 492 | { 493 | "type": "STRING", 494 | "value": "#" 495 | }, 496 | { 497 | "type": "STRING", 498 | "value": "You" 499 | }, 500 | { 501 | "type": "STRING", 502 | "value": "are" 503 | }, 504 | { 505 | "type": "STRING", 506 | "value": "currently" 507 | }, 508 | { 509 | "type": "REPEAT", 510 | "content": { 511 | "type": "PATTERN", 512 | "value": "\\S+" 513 | } 514 | }, 515 | { 516 | "type": "STRING", 517 | "value": "rebasing" 518 | }, 519 | { 520 | "type": "STRING", 521 | "value": "branch" 522 | }, 523 | { 524 | "type": "STRING", 525 | "value": "'" 526 | }, 527 | { 528 | "type": "SYMBOL", 529 | "name": "branch" 530 | }, 531 | { 532 | "type": "STRING", 533 | "value": "'" 534 | }, 535 | { 536 | "type": "STRING", 537 | "value": "on" 538 | }, 539 | { 540 | "type": "STRING", 541 | "value": "'" 542 | }, 543 | { 544 | "type": "SYMBOL", 545 | "name": "commit" 546 | }, 547 | { 548 | "type": "STRING", 549 | "value": "'" 550 | }, 551 | { 552 | "type": "STRING", 553 | "value": "." 554 | } 555 | ] 556 | }, 557 | { 558 | "type": "SYMBOL", 559 | "name": "_newline" 560 | }, 561 | { 562 | "type": "CHOICE", 563 | "members": [ 564 | { 565 | "type": "STRING", 566 | "value": "#" 567 | }, 568 | { 569 | "type": "BLANK" 570 | } 571 | ] 572 | } 573 | ] 574 | }, 575 | "_rebase_header": { 576 | "type": "CHOICE", 577 | "members": [ 578 | { 579 | "type": "SEQ", 580 | "members": [ 581 | { 582 | "type": "STRING", 583 | "value": "Last" 584 | }, 585 | { 586 | "type": "PATTERN", 587 | "value": "commands?" 588 | }, 589 | { 590 | "type": "STRING", 591 | "value": "done" 592 | }, 593 | { 594 | "type": "STRING", 595 | "value": "(" 596 | }, 597 | { 598 | "type": "PATTERN", 599 | "value": "\\d+" 600 | }, 601 | { 602 | "type": "PATTERN", 603 | "value": "commands?" 604 | }, 605 | { 606 | "type": "STRING", 607 | "value": "done" 608 | }, 609 | { 610 | "type": "STRING", 611 | "value": ")" 612 | }, 613 | { 614 | "type": "STRING", 615 | "value": ":" 616 | } 617 | ] 618 | }, 619 | { 620 | "type": "SEQ", 621 | "members": [ 622 | { 623 | "type": "STRING", 624 | "value": "Next" 625 | }, 626 | { 627 | "type": "PATTERN", 628 | "value": "commands?" 629 | }, 630 | { 631 | "type": "STRING", 632 | "value": "to" 633 | }, 634 | { 635 | "type": "STRING", 636 | "value": "do" 637 | }, 638 | { 639 | "type": "STRING", 640 | "value": "(" 641 | }, 642 | { 643 | "type": "PATTERN", 644 | "value": "\\d+" 645 | }, 646 | { 647 | "type": "STRING", 648 | "value": "remaining" 649 | }, 650 | { 651 | "type": "PATTERN", 652 | "value": "commands?" 653 | }, 654 | { 655 | "type": "STRING", 656 | "value": ")" 657 | }, 658 | { 659 | "type": "STRING", 660 | "value": ":" 661 | } 662 | ] 663 | }, 664 | { 665 | "type": "SEQ", 666 | "members": [ 667 | { 668 | "type": "STRING", 669 | "value": "No" 670 | }, 671 | { 672 | "type": "STRING", 673 | "value": "commands" 674 | }, 675 | { 676 | "type": "STRING", 677 | "value": "remaining" 678 | }, 679 | { 680 | "type": "STRING", 681 | "value": "." 682 | } 683 | ] 684 | } 685 | ] 686 | }, 687 | "summary": { 688 | "type": "CHOICE", 689 | "members": [ 690 | { 691 | "type": "SEQ", 692 | "members": [ 693 | { 694 | "type": "ALIAS", 695 | "content": { 696 | "type": "SYMBOL", 697 | "name": "_change_header" 698 | }, 699 | "named": true, 700 | "value": "header" 701 | }, 702 | { 703 | "type": "SYMBOL", 704 | "name": "_newline" 705 | }, 706 | { 707 | "type": "REPEAT1", 708 | "content": { 709 | "type": "SEQ", 710 | "members": [ 711 | { 712 | "type": "STRING", 713 | "value": "#" 714 | }, 715 | { 716 | "type": "SYMBOL", 717 | "name": "change" 718 | }, 719 | { 720 | "type": "SYMBOL", 721 | "name": "_newline" 722 | } 723 | ] 724 | } 725 | }, 726 | { 727 | "type": "CHOICE", 728 | "members": [ 729 | { 730 | "type": "STRING", 731 | "value": "#" 732 | }, 733 | { 734 | "type": "BLANK" 735 | } 736 | ] 737 | } 738 | ] 739 | }, 740 | { 741 | "type": "SEQ", 742 | "members": [ 743 | { 744 | "type": "SYMBOL", 745 | "name": "header" 746 | }, 747 | { 748 | "type": "SYMBOL", 749 | "name": "_newline" 750 | }, 751 | { 752 | "type": "REPEAT1", 753 | "content": { 754 | "type": "SEQ", 755 | "members": [ 756 | { 757 | "type": "STRING", 758 | "value": "#" 759 | }, 760 | { 761 | "type": "SYMBOL", 762 | "name": "path" 763 | }, 764 | { 765 | "type": "SYMBOL", 766 | "name": "_newline" 767 | } 768 | ] 769 | } 770 | }, 771 | { 772 | "type": "CHOICE", 773 | "members": [ 774 | { 775 | "type": "STRING", 776 | "value": "#" 777 | }, 778 | { 779 | "type": "BLANK" 780 | } 781 | ] 782 | } 783 | ] 784 | } 785 | ] 786 | }, 787 | "_change_header": { 788 | "type": "CHOICE", 789 | "members": [ 790 | { 791 | "type": "SEQ", 792 | "members": [ 793 | { 794 | "type": "STRING", 795 | "value": "Changes" 796 | }, 797 | { 798 | "type": "STRING", 799 | "value": "to" 800 | }, 801 | { 802 | "type": "STRING", 803 | "value": "be" 804 | }, 805 | { 806 | "type": "STRING", 807 | "value": "committed" 808 | }, 809 | { 810 | "type": "STRING", 811 | "value": ":" 812 | } 813 | ] 814 | }, 815 | { 816 | "type": "SEQ", 817 | "members": [ 818 | { 819 | "type": "STRING", 820 | "value": "Changes" 821 | }, 822 | { 823 | "type": "STRING", 824 | "value": "not" 825 | }, 826 | { 827 | "type": "STRING", 828 | "value": "staged" 829 | }, 830 | { 831 | "type": "STRING", 832 | "value": "for" 833 | }, 834 | { 835 | "type": "STRING", 836 | "value": "commit" 837 | }, 838 | { 839 | "type": "STRING", 840 | "value": ":" 841 | } 842 | ] 843 | } 844 | ] 845 | }, 846 | "_branch_declaration": { 847 | "type": "CHOICE", 848 | "members": [ 849 | { 850 | "type": "SEQ", 851 | "members": [ 852 | { 853 | "type": "STRING", 854 | "value": "On" 855 | }, 856 | { 857 | "type": "STRING", 858 | "value": "branch" 859 | }, 860 | { 861 | "type": "SYMBOL", 862 | "name": "branch" 863 | } 864 | ] 865 | }, 866 | { 867 | "type": "SEQ", 868 | "members": [ 869 | { 870 | "type": "STRING", 871 | "value": "Your" 872 | }, 873 | { 874 | "type": "STRING", 875 | "value": "branch" 876 | }, 877 | { 878 | "type": "STRING", 879 | "value": "is" 880 | }, 881 | { 882 | "type": "STRING", 883 | "value": "up" 884 | }, 885 | { 886 | "type": "STRING", 887 | "value": "to" 888 | }, 889 | { 890 | "type": "STRING", 891 | "value": "date" 892 | }, 893 | { 894 | "type": "STRING", 895 | "value": "with" 896 | }, 897 | { 898 | "type": "STRING", 899 | "value": "'" 900 | }, 901 | { 902 | "type": "SYMBOL", 903 | "name": "branch" 904 | }, 905 | { 906 | "type": "STRING", 907 | "value": "'." 908 | } 909 | ] 910 | }, 911 | { 912 | "type": "SEQ", 913 | "members": [ 914 | { 915 | "type": "STRING", 916 | "value": "Your" 917 | }, 918 | { 919 | "type": "STRING", 920 | "value": "branch" 921 | }, 922 | { 923 | "type": "STRING", 924 | "value": "is" 925 | }, 926 | { 927 | "type": "CHOICE", 928 | "members": [ 929 | { 930 | "type": "SEQ", 931 | "members": [ 932 | { 933 | "type": "STRING", 934 | "value": "ahead" 935 | }, 936 | { 937 | "type": "STRING", 938 | "value": "of" 939 | } 940 | ] 941 | }, 942 | { 943 | "type": "STRING", 944 | "value": "behind" 945 | } 946 | ] 947 | }, 948 | { 949 | "type": "STRING", 950 | "value": "'" 951 | }, 952 | { 953 | "type": "SYMBOL", 954 | "name": "branch" 955 | }, 956 | { 957 | "type": "STRING", 958 | "value": "'" 959 | }, 960 | { 961 | "type": "STRING", 962 | "value": "by" 963 | }, 964 | { 965 | "type": "PATTERN", 966 | "value": "\\d+" 967 | }, 968 | { 969 | "type": "PATTERN", 970 | "value": "commits?" 971 | }, 972 | { 973 | "type": "STRING", 974 | "value": "." 975 | } 976 | ] 977 | }, 978 | { 979 | "type": "SEQ", 980 | "members": [ 981 | { 982 | "type": "STRING", 983 | "value": "Your" 984 | }, 985 | { 986 | "type": "STRING", 987 | "value": "branch" 988 | }, 989 | { 990 | "type": "STRING", 991 | "value": "and" 992 | }, 993 | { 994 | "type": "STRING", 995 | "value": "'" 996 | }, 997 | { 998 | "type": "SYMBOL", 999 | "name": "branch" 1000 | }, 1001 | { 1002 | "type": "STRING", 1003 | "value": "'" 1004 | }, 1005 | { 1006 | "type": "STRING", 1007 | "value": "have" 1008 | }, 1009 | { 1010 | "type": "STRING", 1011 | "value": "diverged" 1012 | }, 1013 | { 1014 | "type": "STRING", 1015 | "value": "," 1016 | } 1017 | ] 1018 | }, 1019 | { 1020 | "type": "SEQ", 1021 | "members": [ 1022 | { 1023 | "type": "STRING", 1024 | "value": "HEAD" 1025 | }, 1026 | { 1027 | "type": "STRING", 1028 | "value": "detached" 1029 | }, 1030 | { 1031 | "type": "STRING", 1032 | "value": "at" 1033 | }, 1034 | { 1035 | "type": "CHOICE", 1036 | "members": [ 1037 | { 1038 | "type": "SYMBOL", 1039 | "name": "commit" 1040 | }, 1041 | { 1042 | "type": "SYMBOL", 1043 | "name": "branch" 1044 | } 1045 | ] 1046 | } 1047 | ] 1048 | } 1049 | ] 1050 | }, 1051 | "header": { 1052 | "type": "SEQ", 1053 | "members": [ 1054 | { 1055 | "type": "CHOICE", 1056 | "members": [ 1057 | { 1058 | "type": "STRING", 1059 | "value": "Conflicts" 1060 | }, 1061 | { 1062 | "type": "SEQ", 1063 | "members": [ 1064 | { 1065 | "type": "STRING", 1066 | "value": "Untracked" 1067 | }, 1068 | { 1069 | "type": "STRING", 1070 | "value": "files" 1071 | } 1072 | ] 1073 | } 1074 | ] 1075 | }, 1076 | { 1077 | "type": "STRING", 1078 | "value": ":" 1079 | } 1080 | ] 1081 | }, 1082 | "change": { 1083 | "type": "SEQ", 1084 | "members": [ 1085 | { 1086 | "type": "FIELD", 1087 | "name": "kind", 1088 | "content": { 1089 | "type": "CHOICE", 1090 | "members": [ 1091 | { 1092 | "type": "STRING", 1093 | "value": "new file" 1094 | }, 1095 | { 1096 | "type": "STRING", 1097 | "value": "modified" 1098 | }, 1099 | { 1100 | "type": "STRING", 1101 | "value": "renamed" 1102 | }, 1103 | { 1104 | "type": "STRING", 1105 | "value": "deleted" 1106 | } 1107 | ] 1108 | } 1109 | }, 1110 | { 1111 | "type": "STRING", 1112 | "value": ":" 1113 | }, 1114 | { 1115 | "type": "SYMBOL", 1116 | "name": "path" 1117 | }, 1118 | { 1119 | "type": "CHOICE", 1120 | "members": [ 1121 | { 1122 | "type": "SEQ", 1123 | "members": [ 1124 | { 1125 | "type": "TOKEN", 1126 | "content": { 1127 | "type": "PREC", 1128 | "value": 2, 1129 | "content": { 1130 | "type": "STRING", 1131 | "value": "->" 1132 | } 1133 | } 1134 | }, 1135 | { 1136 | "type": "SYMBOL", 1137 | "name": "path" 1138 | } 1139 | ] 1140 | }, 1141 | { 1142 | "type": "BLANK" 1143 | } 1144 | ] 1145 | } 1146 | ] 1147 | }, 1148 | "commit": { 1149 | "type": "PATTERN", 1150 | "value": "[a-f0-9]{7,40}" 1151 | }, 1152 | "_non_punctuated_word": { 1153 | "type": "TOKEN", 1154 | "content": { 1155 | "type": "PREC", 1156 | "value": -1, 1157 | "content": { 1158 | "type": "PATTERN", 1159 | "value": "[-\\w]+" 1160 | } 1161 | } 1162 | }, 1163 | "_non_comment": { 1164 | "type": "TOKEN", 1165 | "content": { 1166 | "type": "PREC", 1167 | "value": -2, 1168 | "content": { 1169 | "type": "PATTERN", 1170 | "value": "[^#\\s]+" 1171 | } 1172 | } 1173 | }, 1174 | "_any_word": { 1175 | "type": "TOKEN", 1176 | "content": { 1177 | "type": "PREC", 1178 | "value": -3, 1179 | "content": { 1180 | "type": "PATTERN", 1181 | "value": "\\S+" 1182 | } 1183 | } 1184 | }, 1185 | "_word": { 1186 | "type": "PREC", 1187 | "value": -1, 1188 | "content": { 1189 | "type": "CHOICE", 1190 | "members": [ 1191 | { 1192 | "type": "SYMBOL", 1193 | "name": "_non_punctuated_word" 1194 | }, 1195 | { 1196 | "type": "SYMBOL", 1197 | "name": "_non_comment" 1198 | }, 1199 | { 1200 | "type": "SYMBOL", 1201 | "name": "_any_word" 1202 | } 1203 | ] 1204 | } 1205 | }, 1206 | "branch": { 1207 | "type": "PATTERN", 1208 | "value": "[^\\.\\s']+" 1209 | }, 1210 | "rebase_command": { 1211 | "type": "SEQ", 1212 | "members": [ 1213 | { 1214 | "type": "CHOICE", 1215 | "members": [ 1216 | { 1217 | "type": "STRING", 1218 | "value": "pick" 1219 | }, 1220 | { 1221 | "type": "STRING", 1222 | "value": "edit" 1223 | }, 1224 | { 1225 | "type": "STRING", 1226 | "value": "squash" 1227 | }, 1228 | { 1229 | "type": "STRING", 1230 | "value": "merge" 1231 | }, 1232 | { 1233 | "type": "STRING", 1234 | "value": "fixup" 1235 | }, 1236 | { 1237 | "type": "STRING", 1238 | "value": "drop" 1239 | }, 1240 | { 1241 | "type": "STRING", 1242 | "value": "reword" 1243 | }, 1244 | { 1245 | "type": "STRING", 1246 | "value": "exec" 1247 | }, 1248 | { 1249 | "type": "STRING", 1250 | "value": "label" 1251 | }, 1252 | { 1253 | "type": "STRING", 1254 | "value": "reset" 1255 | } 1256 | ] 1257 | }, 1258 | { 1259 | "type": "REPEAT1", 1260 | "content": { 1261 | "type": "PATTERN", 1262 | "value": "\\S+" 1263 | } 1264 | } 1265 | ] 1266 | }, 1267 | "path": { 1268 | "type": "REPEAT1", 1269 | "content": { 1270 | "type": "TOKEN", 1271 | "content": { 1272 | "type": "PREC", 1273 | "value": 1, 1274 | "content": { 1275 | "type": "PATTERN", 1276 | "value": "\\S+" 1277 | } 1278 | } 1279 | } 1280 | }, 1281 | "item": { 1282 | "type": "TOKEN", 1283 | "content": { 1284 | "type": "PREC", 1285 | "value": 3, 1286 | "content": { 1287 | "type": "PATTERN", 1288 | "value": "#\\d+" 1289 | } 1290 | } 1291 | }, 1292 | "user": { 1293 | "type": "TOKEN", 1294 | "content": { 1295 | "type": "PREC", 1296 | "value": 4, 1297 | "content": { 1298 | "type": "PATTERN", 1299 | "value": "@[a-zA-Z0-9_-]+" 1300 | } 1301 | } 1302 | }, 1303 | "_rest": { 1304 | "type": "REPEAT1", 1305 | "content": { 1306 | "type": "CHOICE", 1307 | "members": [ 1308 | { 1309 | "type": "PATTERN", 1310 | "value": ".*" 1311 | }, 1312 | { 1313 | "type": "SYMBOL", 1314 | "name": "_newline" 1315 | } 1316 | ] 1317 | } 1318 | }, 1319 | "_newline": { 1320 | "type": "PATTERN", 1321 | "value": "\\r?\\n" 1322 | } 1323 | }, 1324 | "extras": [ 1325 | { 1326 | "type": "PATTERN", 1327 | "value": "[\\t\\f\\v ]+" 1328 | } 1329 | ], 1330 | "conflicts": [], 1331 | "precedences": [], 1332 | "externals": [], 1333 | "inline": [], 1334 | "supertypes": [] 1335 | } 1336 | 1337 | -------------------------------------------------------------------------------- /src/node-types.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "change", 4 | "named": true, 5 | "fields": { 6 | "kind": { 7 | "multiple": false, 8 | "required": true, 9 | "types": [ 10 | { 11 | "type": "deleted", 12 | "named": false 13 | }, 14 | { 15 | "type": "modified", 16 | "named": false 17 | }, 18 | { 19 | "type": "new file", 20 | "named": false 21 | }, 22 | { 23 | "type": "renamed", 24 | "named": false 25 | } 26 | ] 27 | } 28 | }, 29 | "children": { 30 | "multiple": true, 31 | "required": true, 32 | "types": [ 33 | { 34 | "type": "path", 35 | "named": true 36 | } 37 | ] 38 | } 39 | }, 40 | { 41 | "type": "comment", 42 | "named": true, 43 | "fields": {}, 44 | "children": { 45 | "multiple": false, 46 | "required": false, 47 | "types": [ 48 | { 49 | "type": "branch", 50 | "named": true 51 | }, 52 | { 53 | "type": "commit", 54 | "named": true 55 | }, 56 | { 57 | "type": "summary", 58 | "named": true 59 | } 60 | ] 61 | } 62 | }, 63 | { 64 | "type": "header", 65 | "named": true, 66 | "fields": {} 67 | }, 68 | { 69 | "type": "message", 70 | "named": true, 71 | "fields": {}, 72 | "children": { 73 | "multiple": true, 74 | "required": false, 75 | "types": [ 76 | { 77 | "type": "commit", 78 | "named": true 79 | }, 80 | { 81 | "type": "item", 82 | "named": true 83 | }, 84 | { 85 | "type": "user", 86 | "named": true 87 | } 88 | ] 89 | } 90 | }, 91 | { 92 | "type": "path", 93 | "named": true, 94 | "fields": {} 95 | }, 96 | { 97 | "type": "rebase_command", 98 | "named": true, 99 | "fields": {} 100 | }, 101 | { 102 | "type": "source", 103 | "named": true, 104 | "fields": {}, 105 | "children": { 106 | "multiple": true, 107 | "required": false, 108 | "types": [ 109 | { 110 | "type": "comment", 111 | "named": true 112 | }, 113 | { 114 | "type": "message", 115 | "named": true 116 | }, 117 | { 118 | "type": "scissors", 119 | "named": true 120 | }, 121 | { 122 | "type": "subject", 123 | "named": true 124 | }, 125 | { 126 | "type": "trailer", 127 | "named": true 128 | } 129 | ] 130 | } 131 | }, 132 | { 133 | "type": "subject", 134 | "named": true, 135 | "fields": {} 136 | }, 137 | { 138 | "type": "summary", 139 | "named": true, 140 | "fields": {}, 141 | "children": { 142 | "multiple": true, 143 | "required": true, 144 | "types": [ 145 | { 146 | "type": "branch", 147 | "named": true 148 | }, 149 | { 150 | "type": "change", 151 | "named": true 152 | }, 153 | { 154 | "type": "commit", 155 | "named": true 156 | }, 157 | { 158 | "type": "header", 159 | "named": true 160 | }, 161 | { 162 | "type": "path", 163 | "named": true 164 | }, 165 | { 166 | "type": "rebase_command", 167 | "named": true 168 | } 169 | ] 170 | } 171 | }, 172 | { 173 | "type": "trailer", 174 | "named": true, 175 | "fields": { 176 | "key": { 177 | "multiple": false, 178 | "required": true, 179 | "types": [ 180 | { 181 | "type": "trailer_key", 182 | "named": true 183 | } 184 | ] 185 | }, 186 | "separator": { 187 | "multiple": false, 188 | "required": true, 189 | "types": [ 190 | { 191 | "type": ":", 192 | "named": false 193 | }, 194 | { 195 | "type": "=", 196 | "named": false 197 | } 198 | ] 199 | }, 200 | "value": { 201 | "multiple": false, 202 | "required": true, 203 | "types": [ 204 | { 205 | "type": "trailer_value", 206 | "named": true 207 | } 208 | ] 209 | } 210 | } 211 | }, 212 | { 213 | "type": "trailer_key", 214 | "named": true, 215 | "fields": {} 216 | }, 217 | { 218 | "type": "trailer_value", 219 | "named": true, 220 | "fields": {}, 221 | "children": { 222 | "multiple": true, 223 | "required": false, 224 | "types": [ 225 | { 226 | "type": "commit", 227 | "named": true 228 | }, 229 | { 230 | "type": "item", 231 | "named": true 232 | }, 233 | { 234 | "type": "user", 235 | "named": true 236 | } 237 | ] 238 | } 239 | }, 240 | { 241 | "type": "#", 242 | "named": false 243 | }, 244 | { 245 | "type": "'", 246 | "named": false 247 | }, 248 | { 249 | "type": "'.", 250 | "named": false 251 | }, 252 | { 253 | "type": "(", 254 | "named": false 255 | }, 256 | { 257 | "type": ")", 258 | "named": false 259 | }, 260 | { 261 | "type": ",", 262 | "named": false 263 | }, 264 | { 265 | "type": "->", 266 | "named": false 267 | }, 268 | { 269 | "type": ".", 270 | "named": false 271 | }, 272 | { 273 | "type": ":", 274 | "named": false 275 | }, 276 | { 277 | "type": ";", 278 | "named": false 279 | }, 280 | { 281 | "type": "=", 282 | "named": false 283 | }, 284 | { 285 | "type": "Changes", 286 | "named": false 287 | }, 288 | { 289 | "type": "Conflicts", 290 | "named": false 291 | }, 292 | { 293 | "type": "HEAD", 294 | "named": false 295 | }, 296 | { 297 | "type": "Last", 298 | "named": false 299 | }, 300 | { 301 | "type": "Next", 302 | "named": false 303 | }, 304 | { 305 | "type": "No", 306 | "named": false 307 | }, 308 | { 309 | "type": "On", 310 | "named": false 311 | }, 312 | { 313 | "type": "Untracked", 314 | "named": false 315 | }, 316 | { 317 | "type": "You", 318 | "named": false 319 | }, 320 | { 321 | "type": "Your", 322 | "named": false 323 | }, 324 | { 325 | "type": "ahead", 326 | "named": false 327 | }, 328 | { 329 | "type": "and", 330 | "named": false 331 | }, 332 | { 333 | "type": "are", 334 | "named": false 335 | }, 336 | { 337 | "type": "at", 338 | "named": false 339 | }, 340 | { 341 | "type": "be", 342 | "named": false 343 | }, 344 | { 345 | "type": "behind", 346 | "named": false 347 | }, 348 | { 349 | "type": "branch", 350 | "named": false 351 | }, 352 | { 353 | "type": "branch", 354 | "named": true 355 | }, 356 | { 357 | "type": "by", 358 | "named": false 359 | }, 360 | { 361 | "type": "commands", 362 | "named": false 363 | }, 364 | { 365 | "type": "commit", 366 | "named": true 367 | }, 368 | { 369 | "type": "commit", 370 | "named": false 371 | }, 372 | { 373 | "type": "committed", 374 | "named": false 375 | }, 376 | { 377 | "type": "currently", 378 | "named": false 379 | }, 380 | { 381 | "type": "date", 382 | "named": false 383 | }, 384 | { 385 | "type": "deleted", 386 | "named": false 387 | }, 388 | { 389 | "type": "detached", 390 | "named": false 391 | }, 392 | { 393 | "type": "diverged", 394 | "named": false 395 | }, 396 | { 397 | "type": "do", 398 | "named": false 399 | }, 400 | { 401 | "type": "done", 402 | "named": false 403 | }, 404 | { 405 | "type": "drop", 406 | "named": false 407 | }, 408 | { 409 | "type": "edit", 410 | "named": false 411 | }, 412 | { 413 | "type": "exec", 414 | "named": false 415 | }, 416 | { 417 | "type": "files", 418 | "named": false 419 | }, 420 | { 421 | "type": "fixup", 422 | "named": false 423 | }, 424 | { 425 | "type": "for", 426 | "named": false 427 | }, 428 | { 429 | "type": "have", 430 | "named": false 431 | }, 432 | { 433 | "type": "in", 434 | "named": false 435 | }, 436 | { 437 | "type": "interactive", 438 | "named": false 439 | }, 440 | { 441 | "type": "is", 442 | "named": false 443 | }, 444 | { 445 | "type": "item", 446 | "named": true 447 | }, 448 | { 449 | "type": "label", 450 | "named": false 451 | }, 452 | { 453 | "type": "merge", 454 | "named": false 455 | }, 456 | { 457 | "type": "modified", 458 | "named": false 459 | }, 460 | { 461 | "type": "new file", 462 | "named": false 463 | }, 464 | { 465 | "type": "not", 466 | "named": false 467 | }, 468 | { 469 | "type": "of", 470 | "named": false 471 | }, 472 | { 473 | "type": "on", 474 | "named": false 475 | }, 476 | { 477 | "type": "onto", 478 | "named": false 479 | }, 480 | { 481 | "type": "pick", 482 | "named": false 483 | }, 484 | { 485 | "type": "progress", 486 | "named": false 487 | }, 488 | { 489 | "type": "rebase", 490 | "named": false 491 | }, 492 | { 493 | "type": "rebasing", 494 | "named": false 495 | }, 496 | { 497 | "type": "remaining", 498 | "named": false 499 | }, 500 | { 501 | "type": "renamed", 502 | "named": false 503 | }, 504 | { 505 | "type": "reset", 506 | "named": false 507 | }, 508 | { 509 | "type": "reword", 510 | "named": false 511 | }, 512 | { 513 | "type": "scissors", 514 | "named": true 515 | }, 516 | { 517 | "type": "squash", 518 | "named": false 519 | }, 520 | { 521 | "type": "staged", 522 | "named": false 523 | }, 524 | { 525 | "type": "to", 526 | "named": false 527 | }, 528 | { 529 | "type": "up", 530 | "named": false 531 | }, 532 | { 533 | "type": "user", 534 | "named": true 535 | }, 536 | { 537 | "type": "with", 538 | "named": false 539 | } 540 | ] -------------------------------------------------------------------------------- /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 | typedef uint16_t TSStateId; 17 | 18 | #ifndef TREE_SITTER_API_H_ 19 | typedef uint16_t TSSymbol; 20 | typedef uint16_t TSFieldId; 21 | typedef struct TSLanguage TSLanguage; 22 | #endif 23 | 24 | typedef struct { 25 | TSFieldId field_id; 26 | uint8_t child_index; 27 | bool inherited; 28 | } TSFieldMapEntry; 29 | 30 | typedef struct { 31 | uint16_t index; 32 | uint16_t length; 33 | } TSFieldMapSlice; 34 | 35 | typedef struct { 36 | bool visible; 37 | bool named; 38 | bool supertype; 39 | } TSSymbolMetadata; 40 | 41 | typedef struct TSLexer TSLexer; 42 | 43 | struct TSLexer { 44 | int32_t lookahead; 45 | TSSymbol result_symbol; 46 | void (*advance)(TSLexer *, bool); 47 | void (*mark_end)(TSLexer *); 48 | uint32_t (*get_column)(TSLexer *); 49 | bool (*is_at_included_range_start)(const TSLexer *); 50 | bool (*eof)(const TSLexer *); 51 | }; 52 | 53 | typedef enum { 54 | TSParseActionTypeShift, 55 | TSParseActionTypeReduce, 56 | TSParseActionTypeAccept, 57 | TSParseActionTypeRecover, 58 | } TSParseActionType; 59 | 60 | typedef union { 61 | struct { 62 | uint8_t type; 63 | TSStateId state; 64 | bool extra; 65 | bool repetition; 66 | } shift; 67 | struct { 68 | uint8_t type; 69 | uint8_t child_count; 70 | TSSymbol symbol; 71 | int16_t dynamic_precedence; 72 | uint16_t production_id; 73 | } reduce; 74 | uint8_t type; 75 | } TSParseAction; 76 | 77 | typedef struct { 78 | uint16_t lex_state; 79 | uint16_t external_lex_state; 80 | } TSLexMode; 81 | 82 | typedef union { 83 | TSParseAction action; 84 | struct { 85 | uint8_t count; 86 | bool reusable; 87 | } entry; 88 | } TSParseActionEntry; 89 | 90 | struct TSLanguage { 91 | uint32_t version; 92 | uint32_t symbol_count; 93 | uint32_t alias_count; 94 | uint32_t token_count; 95 | uint32_t external_token_count; 96 | uint32_t state_count; 97 | uint32_t large_state_count; 98 | uint32_t production_id_count; 99 | uint32_t field_count; 100 | uint16_t max_alias_sequence_length; 101 | const uint16_t *parse_table; 102 | const uint16_t *small_parse_table; 103 | const uint32_t *small_parse_table_map; 104 | const TSParseActionEntry *parse_actions; 105 | const char * const *symbol_names; 106 | const char * const *field_names; 107 | const TSFieldMapSlice *field_map_slices; 108 | const TSFieldMapEntry *field_map_entries; 109 | const TSSymbolMetadata *symbol_metadata; 110 | const TSSymbol *public_symbol_map; 111 | const uint16_t *alias_map; 112 | const TSSymbol *alias_sequences; 113 | const TSLexMode *lex_modes; 114 | bool (*lex_fn)(TSLexer *, TSStateId); 115 | bool (*keyword_lex_fn)(TSLexer *, TSStateId); 116 | TSSymbol keyword_capture_token; 117 | struct { 118 | const bool *states; 119 | const TSSymbol *symbol_map; 120 | void *(*create)(void); 121 | void (*destroy)(void *); 122 | bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist); 123 | unsigned (*serialize)(void *, char *); 124 | void (*deserialize)(void *, const char *, unsigned); 125 | } external_scanner; 126 | const TSStateId *primary_state_ids; 127 | }; 128 | 129 | /* 130 | * Lexer Macros 131 | */ 132 | 133 | #define START_LEXER() \ 134 | bool result = false; \ 135 | bool skip = false; \ 136 | bool eof = false; \ 137 | int32_t lookahead; \ 138 | goto start; \ 139 | next_state: \ 140 | lexer->advance(lexer, skip); \ 141 | start: \ 142 | skip = false; \ 143 | lookahead = lexer->lookahead; 144 | 145 | #define ADVANCE(state_value) \ 146 | { \ 147 | state = state_value; \ 148 | goto next_state; \ 149 | } 150 | 151 | #define SKIP(state_value) \ 152 | { \ 153 | skip = true; \ 154 | state = state_value; \ 155 | goto next_state; \ 156 | } 157 | 158 | #define ACCEPT_TOKEN(symbol_value) \ 159 | result = true; \ 160 | lexer->result_symbol = symbol_value; \ 161 | lexer->mark_end(lexer); 162 | 163 | #define END_STATE() return result; 164 | 165 | /* 166 | * Parse Table Macros 167 | */ 168 | 169 | #define SMALL_STATE(id) id - LARGE_STATE_COUNT 170 | 171 | #define STATE(id) id 172 | 173 | #define ACTIONS(id) id 174 | 175 | #define SHIFT(state_value) \ 176 | {{ \ 177 | .shift = { \ 178 | .type = TSParseActionTypeShift, \ 179 | .state = state_value \ 180 | } \ 181 | }} 182 | 183 | #define SHIFT_REPEAT(state_value) \ 184 | {{ \ 185 | .shift = { \ 186 | .type = TSParseActionTypeShift, \ 187 | .state = state_value, \ 188 | .repetition = true \ 189 | } \ 190 | }} 191 | 192 | #define SHIFT_EXTRA() \ 193 | {{ \ 194 | .shift = { \ 195 | .type = TSParseActionTypeShift, \ 196 | .extra = true \ 197 | } \ 198 | }} 199 | 200 | #define REDUCE(symbol_val, child_count_val, ...) \ 201 | {{ \ 202 | .reduce = { \ 203 | .type = TSParseActionTypeReduce, \ 204 | .symbol = symbol_val, \ 205 | .child_count = child_count_val, \ 206 | __VA_ARGS__ \ 207 | }, \ 208 | }} 209 | 210 | #define RECOVER() \ 211 | {{ \ 212 | .type = TSParseActionTypeRecover \ 213 | }} 214 | 215 | #define ACCEPT_INPUT() \ 216 | {{ \ 217 | .type = TSParseActionTypeAccept \ 218 | }} 219 | 220 | #ifdef __cplusplus 221 | } 222 | #endif 223 | 224 | #endif // TREE_SITTER_PARSER_H_ 225 | -------------------------------------------------------------------------------- /test/corpus/comments.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | Current branch 3 | ================================================================================ 4 | # On branch main 5 | 6 | -------------------------------------------------------------------------------- 7 | 8 | (source 9 | (comment 10 | (branch))) 11 | 12 | ================================================================================ 13 | Branch is up to date with remote 14 | ================================================================================ 15 | # Your branch is up to date with 'origin/main'. 16 | 17 | -------------------------------------------------------------------------------- 18 | 19 | (source 20 | (comment 21 | (branch))) 22 | 23 | ================================================================================ 24 | Branch diverged from remote 25 | ================================================================================ 26 | # Your branch and 'origin/md-top-level-expressions' have diverged, 27 | # and have 10 and 1 different commits each, respectively. 28 | # (use "git pull" to merge the remote branch into yours) 29 | 30 | -------------------------------------------------------------------------------- 31 | 32 | (source 33 | (comment 34 | (branch)) 35 | (comment) 36 | (comment)) 37 | 38 | ================================================================================ 39 | Head detached at commit 40 | ================================================================================ 41 | # HEAD detached at f5d1c0b8 42 | 43 | -------------------------------------------------------------------------------- 44 | 45 | (source 46 | (comment 47 | (commit))) 48 | 49 | ================================================================================ 50 | Changing files 51 | ================================================================================ 52 | # Changes to be committed: 53 | # deleted: Cargo.lock 54 | # modified: Cargo.toml 55 | # renamed: LICENSE -> LICENSE.md 56 | # modified: cli/Cargo.toml 57 | # new file: tmp.txt 58 | 59 | -------------------------------------------------------------------------------- 60 | 61 | (source 62 | (comment 63 | (summary 64 | (header) 65 | (change 66 | (path)) 67 | (change 68 | (path)) 69 | (change 70 | (path) 71 | (path)) 72 | (change 73 | (path)) 74 | (change 75 | (path))))) 76 | 77 | ================================================================================ 78 | New file with space in name 79 | ================================================================================ 80 | # Changes to be committed: 81 | # new file: file with space.txt 82 | 83 | -------------------------------------------------------------------------------- 84 | 85 | (source 86 | (comment 87 | (summary 88 | (header) 89 | (change 90 | (path))))) 91 | 92 | ================================================================================ 93 | Renamed file with arrow in name 94 | ================================================================================ 95 | # Changes to be committed: 96 | # renamed: a->b.tx -> a->b.txt 97 | 98 | -------------------------------------------------------------------------------- 99 | 100 | (source 101 | (comment 102 | (summary 103 | (header) 104 | (change 105 | (path) 106 | (path))))) 107 | 108 | ================================================================================ 109 | Merge conflict paths 110 | ================================================================================ 111 | # Conflicts: 112 | # languages.toml 113 | 114 | -------------------------------------------------------------------------------- 115 | 116 | (source 117 | (comment 118 | (summary 119 | (header) 120 | (path)))) 121 | 122 | ================================================================================ 123 | Untracked paths 124 | ================================================================================ 125 | # Untracked files: 126 | # languages.toml 127 | 128 | -------------------------------------------------------------------------------- 129 | 130 | (source 131 | (comment 132 | (summary 133 | (header) 134 | (path)))) 135 | 136 | ================================================================================ 137 | Scissors 138 | ================================================================================ 139 | # ------------------------ >8 ------------------------ 140 | # Do not modify or remove the line above. 141 | # Everything below it will be ignored. 142 | 143 | -------------------------------------------------------------------------------- 144 | 145 | (source 146 | (scissors)) 147 | 148 | ================================================================================ 149 | Verbose amended commit with diff 150 | ================================================================================ 151 | # Please enter the commit message for your changes. Lines starting 152 | # with '#' will be ignored, and an empty message aborts the commit. 153 | # 154 | # Date: Thu Dec 23 08:06:48 2021 -0600 155 | # 156 | # ..snip.. 157 | # 158 | # ------------------------ >8 ------------------------ 159 | # Do not modify or remove the line above. 160 | # Everything below it will be ignored. 161 | diff --git a/tmp.txt b/tmp.txt 162 | new file mode 100644 163 | index 00000000..ee9808dc 164 | --- /dev/null 165 | +++ b/tmp.txt 166 | @@ -0,0 +1 @@ 167 | +aaaaaa 168 | \ No newline at end of file 169 | 170 | -------------------------------------------------------------------------------- 171 | 172 | (source 173 | (comment) 174 | (comment) 175 | (comment) 176 | (comment) 177 | (comment) 178 | (comment) 179 | (comment) 180 | (scissors) 181 | (message)) 182 | 183 | ================================================================================ 184 | Commit with scissors cleanup 185 | ================================================================================ 186 | tmp 187 | 188 | # ------------------------ >8 ------------------------ 189 | # Do not modify or remove the line above. 190 | # Everything below it will be ignored. 191 | # 192 | # Date: Thu Dec 23 08:06:48 2021 -0600 193 | # ..snip.. 194 | 195 | -------------------------------------------------------------------------------- 196 | 197 | (source 198 | (subject) 199 | (scissors) 200 | (message)) 201 | 202 | ================================================================================ 203 | Changes not staged for commit 204 | ================================================================================ 205 | # Changes not staged for commit: 206 | # modified: flake.nix 207 | 208 | -------------------------------------------------------------------------------- 209 | 210 | (source 211 | (comment 212 | (summary 213 | (header) 214 | (change 215 | (path))))) 216 | 217 | ================================================================================ 218 | Comment starting after column one is not a column 219 | ================================================================================ 220 | add LICENSE 221 | 222 | # done 223 | 224 | -------------------------------------------------------------------------------- 225 | 226 | (source 227 | (subject) 228 | (message)) 229 | 230 | ================================================================================ 231 | Rebase with multiple commands done, one command to go 232 | ================================================================================ 233 | Update onedark theme to use new scopes (#1297) 234 | 235 | # interactive rebase in progress; onto 176fbe7 236 | # Last commands done (10 commands done): 237 | # reword 02f24e1 Fix match brackets comment (#1346) 238 | # reword b2f8f2b Update onedark theme to use new scopes (#1297) 239 | # Next command to do (1 remaining command): 240 | # reword 13d8044 Enable Rust proc macro support (#1350) 241 | # You are currently editing a commit while rebasing branch 'master' on '176fbe7'. 242 | # 243 | # Changes to be committed: 244 | # modified: runtime/themes/onedark.toml 245 | # 246 | 247 | -------------------------------------------------------------------------------- 248 | 249 | (source 250 | (subject) 251 | (comment 252 | (summary 253 | (commit) 254 | (header) 255 | (rebase_command) 256 | (rebase_command) 257 | (header) 258 | (rebase_command) 259 | (branch) 260 | (commit))) 261 | (comment 262 | (summary 263 | (header) 264 | (change 265 | (path))))) 266 | 267 | ================================================================================ 268 | Rebase with multiple commands done, zero commands to go 269 | ================================================================================ 270 | # interactive rebase in progress; onto 176fbe7 271 | # Last commands done (11 commands done): 272 | # reword b2f8f2b Update onedark theme to use new scopes (#1297) 273 | # reword 13d8044 Enable Rust proc macro support (#1350) 274 | # No commands remaining. 275 | # You are currently editing a commit while rebasing branch 'master' on '176fbe7'. 276 | # 277 | # Changes to be committed: 278 | # modified: languages.toml 279 | # 280 | 281 | -------------------------------------------------------------------------------- 282 | 283 | (source 284 | (comment 285 | (summary 286 | (commit) 287 | (header) 288 | (rebase_command) 289 | (rebase_command) 290 | (header) 291 | (branch) 292 | (commit))) 293 | (comment 294 | (summary 295 | (header) 296 | (change 297 | (path))))) 298 | 299 | ================================================================================ 300 | Comment without separating space 301 | ================================================================================ 302 | Title 303 | 304 | #This is a comment without the space separating the '#' and comment. 305 | 306 | -------------------------------------------------------------------------------- 307 | 308 | (source 309 | (subject) 310 | (comment)) 311 | 312 | ================================================================================ 313 | Comment without separating space with extra newline 314 | ================================================================================ 315 | Title 316 | 317 | 318 | #This case failed in the past because of bad handling of multiple newlines 319 | 320 | -------------------------------------------------------------------------------- 321 | 322 | (source 323 | (subject) 324 | (comment)) 325 | -------------------------------------------------------------------------------- /test/corpus/message.txt: -------------------------------------------------------------------------------- 1 | ================================================================================ 2 | Empty message 3 | ================================================================================ 4 | 5 | # Please enter the commit message for your changes. Lines starting 6 | # with '#' will be ignored, and an empty message aborts the commit. 7 | # 8 | # On branch main 9 | # Your branch is up to date with 'origin/main'. 10 | # 11 | 12 | -------------------------------------------------------------------------------- 13 | 14 | (source 15 | (comment) 16 | (comment) 17 | (comment) 18 | (comment 19 | (branch)) 20 | (comment 21 | (branch)) 22 | (comment)) 23 | 24 | ================================================================================ 25 | Freeform subject, closes item 26 | ================================================================================ 27 | add LICENSE 28 | 29 | closes #123 30 | 31 | # Please enter the commit message for your changes. Lines starting 32 | # with '#' will be ignored, and an empty message aborts the commit. 33 | 34 | -------------------------------------------------------------------------------- 35 | 36 | (source 37 | (subject) 38 | (message 39 | (item)) 40 | (comment) 41 | (comment)) 42 | 43 | ================================================================================ 44 | Subject and body collide 45 | ================================================================================ 46 | add LICENSE 47 | closes #123 48 | 49 | # Please enter the commit message for your changes. Lines starting 50 | # with '#' will be ignored, and an empty message aborts the commit. 51 | 52 | -------------------------------------------------------------------------------- 53 | 54 | (source 55 | (subject) 56 | (message 57 | (item)) 58 | (comment) 59 | (comment)) 60 | 61 | ================================================================================ 62 | Subject and comment collide 63 | ================================================================================ 64 | add LICENSE 65 | # highlight comment goes here 66 | 67 | -------------------------------------------------------------------------------- 68 | 69 | (source 70 | (subject) 71 | (comment)) 72 | 73 | ================================================================================ 74 | Single-line subject with no terminator 75 | ================================================================================ 76 | add LICENSE 77 | -------------------------------------------------------------------------------- 78 | 79 | (source 80 | (subject)) 81 | 82 | ================================================================================ 83 | Subject and body with no terminator 84 | ================================================================================ 85 | add LICENSE 86 | 87 | closes #123 88 | -------------------------------------------------------------------------------- 89 | 90 | (source 91 | (subject) 92 | (message 93 | (item))) 94 | 95 | ================================================================================ 96 | Body messages interspersed with comments 97 | ================================================================================ 98 | add LICENSE 99 | 100 | # this won't show up 101 | closes #123 102 | # but the issue number there will because it isn't a comment 103 | 104 | -------------------------------------------------------------------------------- 105 | 106 | (source 107 | (subject) 108 | (comment) 109 | (message 110 | (item)) 111 | (comment)) 112 | 113 | ================================================================================ 114 | Real rebase commit body 115 | ================================================================================ 116 | add submodule on tree-sitter-rebase, add to languages 117 | 118 | # Conflicts: 119 | # languages.toml 120 | 121 | # Please enter the commit message for your changes. Lines starting 122 | # with '#' will be ignored, and an empty message aborts the commit. 123 | # 124 | # interactive rebase in progress; onto 34766e2 125 | # Last command done (1 command done): 126 | # pick 1bc0b2c add submodule on tree-sitter-rebase, add to languages 127 | # Next commands to do (5 remaining commands): 128 | # pick e339083 add basic highlights query 129 | # pick 00c5a26 inject bash in execute statements 130 | # You are currently rebasing branch 'md-tree-sitter-rebase' on '34766e2'. 131 | # 132 | # Changes to be committed: 133 | # modified: .gitmodules 134 | # new file: helix-syntax/languages/tree-sitter-rebase 135 | # modified: languages.toml 136 | # 137 | # Untracked files: 138 | # COMMIT_EDITMSG 139 | # 140 | 141 | -------------------------------------------------------------------------------- 142 | 143 | (source 144 | (subject) 145 | (comment 146 | (summary 147 | (header) 148 | (path))) 149 | (comment) 150 | (comment) 151 | (comment) 152 | (comment 153 | (summary 154 | (commit) 155 | (header) 156 | (rebase_command) 157 | (header) 158 | (rebase_command) 159 | (rebase_command) 160 | (branch) 161 | (commit))) 162 | (comment 163 | (summary 164 | (header) 165 | (change 166 | (path)) 167 | (change 168 | (path)) 169 | (change 170 | (path)))) 171 | (comment 172 | (summary 173 | (header) 174 | (path)))) 175 | 176 | ================================================================================ 177 | User mention 178 | ================================================================================ 179 | add LICENSE 180 | 181 | recommended by @my-lawyer 182 | 183 | # Please enter the commit message for your changes. Lines starting 184 | # with '#' will be ignored, and an empty message aborts the commit. 185 | 186 | -------------------------------------------------------------------------------- 187 | 188 | (source 189 | (subject) 190 | (message 191 | (user)) 192 | (comment) 193 | (comment)) 194 | 195 | ================================================================================ 196 | Basic trailer 197 | ================================================================================ 198 | subject 199 | 200 | message 201 | 202 | Signed-off-by: Alice 203 | Signed-off-by: Bob 204 | 205 | -------------------------------------------------------------------------------- 206 | 207 | (source 208 | (subject) 209 | (message) 210 | (trailer 211 | (trailer_key) 212 | (trailer_value)) 213 | (trailer 214 | (trailer_key) 215 | (trailer_value))) 216 | 217 | ================================================================================ 218 | Another basic trailer 219 | ================================================================================ 220 | subject 221 | 222 | message 223 | 224 | Signed-off-by: Bob 225 | Acked-by: Alice 226 | -------------------------------------------------------------------------------- 227 | 228 | (source 229 | (subject) 230 | (message) 231 | (trailer 232 | (trailer_key) 233 | (trailer_value)) 234 | (trailer 235 | (trailer_key) 236 | (trailer_value))) 237 | 238 | ================================================================================ 239 | Trailer with commit 240 | ================================================================================ 241 | subject 242 | 243 | message 244 | 245 | Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07) 246 | -------------------------------------------------------------------------------- 247 | 248 | (source 249 | (subject) 250 | (message) 251 | (trailer 252 | (trailer_key) 253 | (trailer_value 254 | (commit)))) 255 | 256 | ================================================================================ 257 | Trailer with user-mention 258 | ================================================================================ 259 | subject 260 | 261 | message 262 | 263 | Thanks-to: @user 264 | -------------------------------------------------------------------------------- 265 | 266 | (source 267 | (subject) 268 | (message) 269 | (trailer 270 | (trailer_key) 271 | (trailer_value 272 | (user)))) 273 | 274 | ================================================================================ 275 | Trailer with issue-mention 276 | ================================================================================ 277 | subject 278 | 279 | message 280 | 281 | Fixes: #123 282 | -------------------------------------------------------------------------------- 283 | 284 | (source 285 | (subject) 286 | (message) 287 | (trailer 288 | (trailer_key) 289 | (trailer_value 290 | (item)))) 291 | 292 | ================================================================================ 293 | Trailer followed by a comment 294 | ================================================================================ 295 | subject 296 | 297 | Fixes: #123 298 | # comment 299 | -------------------------------------------------------------------------------- 300 | 301 | (source 302 | (subject) 303 | (trailer 304 | (trailer_key) 305 | (trailer_value 306 | (item))) 307 | (comment)) 308 | -------------------------------------------------------------------------------- /test/highlight/basic.COMMIT_EDITMSG: -------------------------------------------------------------------------------- 1 | This is the subject line 2 | # <- subject 3 | # ^ subject 4 | # ^ subject 5 | 6 | This is a message in the body 7 | # <- message 8 | # ^ message 9 | # ^ message 10 | 11 | Here's a commit: deadbeef 12 | # <- message 13 | # ^ commit 14 | 15 | Signed-off-by: me! 16 | # <- trailer.key 17 | # ^ trailer.key 18 | # ^ punctuation.delimiter 19 | # ^ trailer.value 20 | --------------------------------------------------------------------------------