chore: removed old, testing file
All checks were successful
Run Test Suite / test (push) Successful in 36s
All checks were successful
Run Test Suite / test (push) Successful in 36s
This commit is contained in:
parent
d270927ff7
commit
9926252bf8
373
main.go
373
main.go
@ -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
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user