Skip to content

Instantly share code, notes, and snippets.

@Melkor333
Last active October 24, 2025 13:29
Show Gist options
  • Select an option

  • Save Melkor333/3fa4738238fde02187b303f41eb33b36 to your computer and use it in GitHub Desktop.

Select an option

Save Melkor333/3fa4738238fde02187b303f41eb33b36 to your computer and use it in GitHub Desktop.
Example Golang Syntax Highlighter with Tree-Sitter using Terminal escapes for coloring
package main
import (
"context"
_ "embed"
"fmt"
tree_sitter "github.com/tree-sitter/go-tree-sitter"
tree_sitter_javascript "github.com/tree-sitter/tree-sitter-javascript/bindings/go"
highlight "go.gopad.dev/go-tree-sitter-highlight"
"log"
"strings"
)
// We need at least 2 additional `.scm` files:
// https://github.com/nvim-treesitter/nvim-treesitter/blob/42fc28ba918343ebfd5565147a42a26580579482/queries/ecma/highlights.scm
//
//go:embed highlights.scm
var highlights []byte
// https://github.com/nvim-treesitter/nvim-treesitter/blob/42fc28ba918343ebfd5565147a42a26580579482/queries/ecma/locals.scm
//
//go:embed locals.scm
var locals []byte
// No injections for now!
// Would be: https://github.com/nvim-treesitter/nvim-treesitter/blob/42fc28ba918343ebfd5565147a42a26580579482/queries/ecma/injections.scm
// But this means we'd maintain a language stack, additional languages, etc.
var injections = []byte{}
func main() {
// The code we want to highlight
code := []byte(`
const foo = 1 + 2;
const bar = 2 + "hello"
$( document ).ready(function() {
console.log( "ready!" );
});`)
// The types we want to highlight
// Order is relevant
captureNames := []string{
"variable",
"function",
"string",
"keyword",
"comment",
"constant",
"number",
}
// the colors for each type
// better approach would be reading a theme:
// https://tree-sitter.github.io/tree-sitter/cli/init-config.html#theme
colormap := map[string]string{
"black": "\033[0m",
"constant": "\033[0;31m",
"type": "\033[0;33m",
"constructor": "\033[0;36m",
"type.builtin": "\033[0;32m",
"label": "\033[0;35m",
"variable.member": "\033[0;37m",
"variable": "\033[0;38m",
}
// set up the language and parser
language := tree_sitter.NewLanguage(tree_sitter_javascript.Language())
parser := tree_sitter.NewParser()
defer parser.Close()
parser.SetLanguage(language)
// Parse and create the query
tree := parser.Parse(code, nil)
defer tree.Close()
q, _ := tree_sitter.NewQuery(language, string(highlights))
qc := tree_sitter.NewQueryCursor()
defer q.Close()
defer qc.Close()
// Get a list of all captures
// More efficient would be to make the colormap based on int -> color instead of string -> color
existingCaptures := q.CaptureNames()
// Set up the highlighter and its config
cfg, err := highlight.NewConfiguration(language, "go", highlights, injections, locals)
if err != nil {
log.Fatal(err)
}
// What objects we care about
cfg.Configure(captureNames)
// Create the highlighter and
highlighter := highlight.New()
events := highlighter.Highlight(context.Background(), *cfg, code, func(name string) *highlight.Configuration {
return nil
})
// The final string containing all highlights
var s strings.Builder
// Current highlighting type
var t string
for event, err := range events {
if err != nil {
log.Fatal(err)
}
switch e := event.(type) {
//case highlight.EventLayerStart:
// Only required if language injections exists
// In which case we need to add the new language to the stack
// and make sure to use the same language down below
//case highlight.EventLayerEnd:
// Only required if language injections exists
// In which case we need to remove the language from the stack
case highlight.EventCaptureStart:
log.Printf("New highlight capture %v, %v", e.Highlight, existingCaptures[e.Highlight])
t = existingCaptures[e.Highlight]
case highlight.EventCaptureEnd:
//log.Printf("Capture end")
case highlight.EventSource:
log.Printf("Highlight range %d-%d", e.StartByte, e.EndByte)
if t != "" {
s.WriteString(colormap[t])
}
s.Write(code[e.StartByte:e.EndByte])
if t != "" {
s.WriteString(colormap["black"])
}
t = ""
}
}
fmt.Println(s.String())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment