1# go tree-sitter
  2
  3[![Build Status](https://github.com/mitjafelicijan/go-tree-sitter/workflows/Test/badge.svg?branch=master)](https://github.com/mitjafelicijan/go-tree-sitter/actions/workflows/test.yml?query=branch%3Amaster)
  4[![GoDoc](https://godoc.org/github.com/mitjafelicijan/go-tree-sitter?status.svg)](https://godoc.org/github.com/mitjafelicijan/go-tree-sitter)
  5
  6Golang bindings for [tree-sitter](https://github.com/tree-sitter/tree-sitter)
  7
  8## Usage
  9
 10Create a parser with a grammar:
 11
 12```go
 13import (
 14	"context"
 15	"fmt"
 16
 17	sitter "github.com/mitjafelicijan/go-tree-sitter"
 18	"github.com/mitjafelicijan/go-tree-sitter/javascript"
 19)
 20
 21parser := sitter.NewParser()
 22parser.SetLanguage(javascript.GetLanguage())
 23```
 24
 25Parse some code:
 26
 27```go
 28sourceCode := []byte("let a = 1")
 29tree, _ := parser.ParseCtx(context.Background(), nil, sourceCode)
 30```
 31
 32Inspect the syntax tree:
 33
 34```go
 35n := tree.RootNode()
 36
 37fmt.Println(n) // (program (lexical_declaration (variable_declarator (identifier) (number))))
 38
 39child := n.NamedChild(0)
 40fmt.Println(child.Type()) // lexical_declaration
 41fmt.Println(child.StartByte()) // 0
 42fmt.Println(child.EndByte()) // 9
 43```
 44
 45### Custom grammars
 46
 47This repository provides grammars for many common languages out of the box.
 48
 49But if you need support for any other language you can keep it inside your own project or publish it as a separate repository to share with the community. 
 50
 51See explanation on how to create a grammar for go-tree-sitter [here](https://github.com/mitjafelicijan/go-tree-sitter/issues/57).
 52
 53Known external grammars:
 54
 55- [Salesforce grammars](https://github.com/aheber/tree-sitter-sfapex) - including Apex, SOQL, and SOSL languages.
 56- [Ruby](https://github.com/shagabutdinov/go-tree-sitter-ruby) - Deprecated, grammar is provided by main repo instead
 57- [Go Template](https://github.com/mrjosh/helm-ls/tree/master/internal/tree-sitter/gotemplate) - Used for helm
 58
 59### Editing
 60
 61If your source code changes, you can update the syntax tree. This will take less time than the first parse.
 62
 63```go
 64// change 1 -> true
 65newText := []byte("let a = true")
 66tree.Edit(sitter.EditInput{
 67    StartIndex:  8,
 68    OldEndIndex: 9,
 69    NewEndIndex: 12,
 70    StartPoint: sitter.Point{
 71        Row:    0,
 72        Column: 8,
 73    },
 74    OldEndPoint: sitter.Point{
 75        Row:    0,
 76        Column: 9,
 77    },
 78    NewEndPoint: sitter.Point{
 79        Row:    0,
 80        Column: 12,
 81    },
 82})
 83
 84// check that it changed tree
 85assert.True(n.HasChanges())
 86assert.True(n.Child(0).HasChanges())
 87assert.False(n.Child(0).Child(0).HasChanges()) // left side of the tree didn't change
 88assert.True(n.Child(0).Child(1).HasChanges())
 89
 90// generate new tree
 91newTree := parser.Parse(tree, newText)
 92```
 93
 94### Predicates
 95
 96You can filter AST by using [predicate](https://tree-sitter.github.io/tree-sitter/using-parsers#predicates) S-expressions.
 97
 98Similar to [Rust](https://github.com/tree-sitter/tree-sitter/tree/master/lib/binding_rust) or [WebAssembly](https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web) bindings we support filtering on a few common predicates:
 99- `eq?`, `not-eq?`
100- `match?`, `not-match?`
101
102Usage [example](./_examples/predicates/main.go):
103
104```go
105func main() {
106	// Javascript code
107	sourceCode := []byte(`
108		const camelCaseConst = 1;
109		const SCREAMING_SNAKE_CASE_CONST = 2;
110		const lower_snake_case_const = 3;`)
111	// Query with predicates
112	screamingSnakeCasePattern := `(
113		(identifier) @constant
114		(#match? @constant "^[A-Z][A-Z_]+")
115	)`
116
117	// Parse source code
118	lang := javascript.GetLanguage()
119	n, _ := sitter.ParseCtx(context.Background(), sourceCode, lang)
120	// Execute the query
121	q, _ := sitter.NewQuery([]byte(screamingSnakeCasePattern), lang)
122	qc := sitter.NewQueryCursor()
123	qc.Exec(q, n)
124	// Iterate over query results
125	for {
126		m, ok := qc.NextMatch()
127		if !ok {
128			break
129		}
130		// Apply predicates filtering
131		m = qc.FilterPredicates(m, sourceCode)
132		for _, c := range m.Captures {
133			fmt.Println(c.Node.Content(sourceCode))
134		}
135	}
136}
137
138// Output of this program:
139// SCREAMING_SNAKE_CASE_CONST
140```
141
142## Development
143
144### Updating a grammar
145
146Check if any updates for vendored files are available:
147
148```
149go run _automation/main.go check-updates
150```
151
152Update vendor files:
153
154- open `_automation/grammars.json`
155- modify `reference` (for tagged grammars) or `revision` (for grammars from a branch)
156- run `go run _automation/main.go update <grammar-name>`
157
158It is also possible to update all grammars in one go using
159
160```
161go run _automation/main.go update-all
162```