chore: removed old, testing file
All checks were successful
Run Test Suite / test (push) Successful in 36s

This commit is contained in:
Hayden Hargreaves 2026-04-08 17:29:33 -07:00
parent d270927ff7
commit 9926252bf8

373
main.go
View File

@ -1,373 +0,0 @@
package main
import (
"fmt"
"os"
"sort"
"strings"
sitter "github.com/tree-sitter/go-tree-sitter"
ts_go "github.com/tree-sitter/tree-sitter-go/bindings/go"
)
// Sample Go source to highlight
const source = `
package main
func main () {
println("Hello" + 5)
}
`
type Highlight struct {
StartRow uint // 0-indexed line number
StartCol uint // 0-indexed column (bytes)
EndRow uint
EndCol uint
Capture string
}
// Theme maps capture names to ANSI escape codes.
// In your editor you'd use lipgloss styles instead.
var theme = map[string]string{
"keyword": "\033[1;35m", // bold magenta
"keyword.type": "\033[1;35m",
"keyword.function": "\033[1;35m",
"keyword.return": "\033[1;35m",
"keyword.coroutine": "\033[1;35m",
"keyword.repeat": "\033[1;35m",
"keyword.import": "\033[1;35m",
"keyword.conditional": "\033[1;35m",
"type": "\033[33m", // yellow
"type.builtin": "\033[33m",
"type.definition": "\033[1;33m", // bold yellow
"function": "\033[1;34m", // bold blue
"function.call": "\033[34m", // blue
"function.method": "\033[34m",
"function.method.call": "\033[34m",
"function.builtin": "\033[1;31m",
"variable": "\033[37m", // white
"variable.parameter": "\033[3;37m", // italic white
"variable.member": "\033[37m",
"constant": "\033[1;36m", // bold cyan
"constant.builtin": "\033[1;36m",
"string": "\033[32m", // green
"string.escape": "\033[1;32m",
"number": "\033[36m", // cyan
"number.float": "\033[36m",
"boolean": "\033[36m",
"operator": "\033[93m", // bright yellow
"comment": "\033[2;37m", // dim
"comment.documentation": "\033[2;37m",
"module": "\033[35m", // magenta
"label": "\033[33m",
"property": "\033[37m",
"constructor": "\033[1;33m",
"punctuation.delimiter": "\033[37m",
"punctuation.bracket": "\033[37m",
}
const reset = "\033[0m"
func main() {
code := []byte(source)
lines := strings.Split(source, "\n")
// --- Step 1: Parse ---
lang := sitter.NewLanguage(ts_go.Language())
parser := sitter.NewParser()
defer parser.Close()
parser.SetLanguage(lang)
tree := parser.Parse(code, nil)
defer tree.Close()
root := tree.RootNode()
// --- Step 2: Load query from highlights.scm ---
queryBytes, err := os.ReadFile("queries/go/highlights.scm")
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to read highlights.scm: %v\n", err)
return
}
query, queryErr := sitter.NewQuery(lang, string(queryBytes))
if queryErr != nil {
fmt.Fprintf(os.Stderr, "Query error: %v\n", queryErr)
return
}
defer query.Close()
// --- Step 3: Run query ---
cursor := sitter.NewQueryCursor()
defer cursor.Close()
captures := cursor.Captures(query, root, code)
var highlights []Highlight
for match, captureIdx := captures.Next(); match != nil; match, captureIdx = captures.Next() {
capture := match.Captures[captureIdx]
captureName := query.CaptureNames()[capture.Index]
// Skip @spell — it's a nvim spellcheck hint, not a highlight
if captureName == "spell" {
continue
}
node := capture.Node
start := node.StartPosition()
end := node.EndPosition()
highlights = append(highlights, Highlight{
StartRow: start.Row,
StartCol: start.Column,
EndRow: end.Row,
EndCol: end.Column,
Capture: captureName,
})
}
// --- Step 4: Show captures with positions ---
fmt.Println("=== Captures (row:col → row:col) ===")
for _, h := range highlights {
// Extract text for display using the source lines
text := extractText(lines, h)
fmt.Printf(" %d:%-2d → %d:%-2d @%-22s %q\n",
h.StartRow, h.StartCol, h.EndRow, h.EndCol, h.Capture, text)
}
fmt.Println()
// --- Step 5: Render with colors using row:col positions ---
// Build a per-line map of column ranges to capture names.
// Sort so wider (less specific) ranges come first — last writer wins.
sort.Slice(highlights, func(i, j int) bool {
if highlights[i].StartRow == highlights[j].StartRow {
if highlights[i].StartCol == highlights[j].StartCol {
// Wider range first so more specific overwrites it
if highlights[i].EndRow == highlights[j].EndRow {
return highlights[i].EndCol > highlights[j].EndCol
}
return highlights[i].EndRow > highlights[j].EndRow
}
return highlights[i].StartCol < highlights[j].StartCol
}
return highlights[i].StartRow < highlights[j].StartRow
})
// captureAt[row][col] = capture name (last writer wins)
captureAt := make(map[uint]map[uint]string)
for _, h := range highlights {
for row := h.StartRow; row <= h.EndRow; row++ {
if captureAt[row] == nil {
captureAt[row] = make(map[uint]string)
}
startCol := uint(0)
if row == h.StartRow {
startCol = h.StartCol
}
endCol := uint(len(lines[row]))
if row == h.EndRow {
endCol = h.EndCol
}
for col := startCol; col < endCol; col++ {
captureAt[row][col] = h.Capture
}
}
}
fmt.Println("=== Colored output ===")
printColored(lines, captureAt)
// =====================================================================
// INCREMENTAL PARSING DEMO
// =====================================================================
// When a user types in your editor, you don't re-parse the whole file.
// Instead you:
// 1. Tell the OLD tree what changed (tree.Edit)
// 2. Parse the new source, passing the old tree
// 3. Tree-sitter reuses unchanged nodes and only re-parses the edit
// 4. Use ChangedRanges to know which lines need re-highlighting
//
// This is O(edit size + log(file size)) instead of O(file size).
fmt.Println("\n========================================")
fmt.Println("=== INCREMENTAL PARSE DEMO ===")
fmt.Println("========================================")
fmt.Println()
// Simulate: user changes "Hello" → "Goodbye" on row 4
// Before: println("Hello" + 5)
// After: println("Goodbye" + 5)
oldSource := source
newSource := strings.Replace(oldSource, `"Hello"`, `"Goodbye"`, 1)
// Find where the edit happened (in a real editor you already know this
// from the keystroke — you don't need to search for it)
editStart := strings.Index(oldSource, `"Hello"`)
oldEnd := editStart + len(`"Hello"`)
newEnd := editStart + len(`"Goodbye"`)
// Convert byte offset to row:col for the InputEdit
editStartPoint := byteToPoint(oldSource, uint(editStart))
oldEndPoint := byteToPoint(oldSource, uint(oldEnd))
newEndPoint := byteToPoint(newSource, uint(newEnd))
fmt.Printf("Edit: replaced %q → %q\n", "Hello", "Goodbye")
fmt.Printf(" at byte %d, row %d col %d\n", editStart, editStartPoint.Row, editStartPoint.Column)
fmt.Println()
// Step 1: Tell the old tree what changed
tree.Edit(&sitter.InputEdit{
StartByte: uint(editStart),
OldEndByte: uint(oldEnd),
NewEndByte: uint(newEnd),
StartPosition: editStartPoint,
OldEndPosition: oldEndPoint,
NewEndPosition: newEndPoint,
})
// Step 2: Parse the new source, passing the old (edited) tree.
// Tree-sitter will REUSE all nodes that weren't affected by the edit
// and only re-parse the region around the change.
newCode := []byte(newSource)
newTree := parser.Parse(newCode, tree)
defer newTree.Close()
// Step 3: See exactly which ranges changed
changedRanges := newTree.ChangedRanges(tree)
fmt.Printf("Changed ranges: %d\n", len(changedRanges))
for i, r := range changedRanges {
fmt.Printf(" range %d: row %d:%d → row %d:%d\n",
i, r.StartPoint.Row, r.StartPoint.Column, r.EndPoint.Row, r.EndPoint.Column)
}
fmt.Println()
// In your editor, you'd ONLY re-run the highlight query on the changed
// ranges (using cursor.SetByteRange or cursor.SetPointRange), then
// update just those lines in your display. Everything else stays cached.
// For this demo, let's re-highlight the full new tree to show the result
newRoot := newTree.RootNode()
newLines := strings.Split(newSource, "\n")
cursor2 := sitter.NewQueryCursor()
defer cursor2.Close()
newCaptures := cursor2.Captures(query, newRoot, newCode)
var newHighlights []Highlight
for match, captureIdx := newCaptures.Next(); match != nil; match, captureIdx = newCaptures.Next() {
capture := match.Captures[captureIdx]
captureName := query.CaptureNames()[capture.Index]
if captureName == "spell" {
continue
}
node := capture.Node
start := node.StartPosition()
end := node.EndPosition()
newHighlights = append(newHighlights, Highlight{
StartRow: start.Row, StartCol: start.Column,
EndRow: end.Row, EndCol: end.Column,
Capture: captureName,
})
}
sort.Slice(newHighlights, func(i, j int) bool {
if newHighlights[i].StartRow == newHighlights[j].StartRow {
if newHighlights[i].StartCol == newHighlights[j].StartCol {
if newHighlights[i].EndRow == newHighlights[j].EndRow {
return newHighlights[i].EndCol > newHighlights[j].EndCol
}
return newHighlights[i].EndRow > newHighlights[j].EndRow
}
return newHighlights[i].StartCol < newHighlights[j].StartCol
}
return newHighlights[i].StartRow < newHighlights[j].StartRow
})
newCaptureAt := make(map[uint]map[uint]string)
for _, h := range newHighlights {
for row := h.StartRow; row <= h.EndRow; row++ {
if newCaptureAt[row] == nil {
newCaptureAt[row] = make(map[uint]string)
}
startCol := uint(0)
if row == h.StartRow {
startCol = h.StartCol
}
endCol := uint(len(newLines[row]))
if row == h.EndRow {
endCol = h.EndCol
}
for col := startCol; col < endCol; col++ {
newCaptureAt[row][col] = h.Capture
}
}
}
fmt.Println("=== After edit (colored output) ===")
printColored(newLines, newCaptureAt)
}
// printColored renders source lines with ANSI colors based on the capture map.
func printColored(lines []string, captureAt map[uint]map[uint]string) {
for row, line := range lines {
currentCapture := ""
for col := uint(0); col < uint(len(line)); col++ {
cap := ""
if rowMap, ok := captureAt[uint(row)]; ok {
cap = rowMap[col]
}
if cap != currentCapture {
if currentCapture != "" {
fmt.Print(reset)
}
if color, ok := theme[cap]; ok {
fmt.Print(color)
}
currentCapture = cap
}
fmt.Print(string(line[col]))
}
if currentCapture != "" {
fmt.Print(reset)
}
fmt.Println()
}
}
// byteToPoint converts a byte offset into a row:col Point.
func byteToPoint(src string, offset uint) sitter.Point {
row := uint(0)
col := uint(0)
for i := range offset {
if src[i] == '\n' {
row++
col = 0
} else {
col++
}
}
return sitter.NewPoint(row, col)
}
// extractText pulls the highlighted text from source lines using row:col positions.
func extractText(lines []string, h Highlight) string {
if h.StartRow == h.EndRow {
line := lines[h.StartRow]
end := min(h.EndCol, uint(len(line)))
return line[h.StartCol:end]
}
// Multi-line highlight (rare, but possible for block comments etc.)
var result string
for row := h.StartRow; row <= h.EndRow; row++ {
line := lines[row]
start := uint(0)
if row == h.StartRow {
start = h.StartCol
}
end := uint(len(line))
if row == h.EndRow {
end = h.EndCol
}
if result != "" {
result += "\n"
}
result += line[start:end]
}
return result
}