highlight.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. package highlight
  2. import (
  3. "fmt"
  4. tcell "github.com/gdamore/tcell/v3"
  5. tree_sitter "github.com/tree-sitter/go-tree-sitter"
  6. tspack "github.com/xberg-io/tree-sitter-language-pack/packages/go"
  7. )
  8. type StyleMap map[string]tcell.Style
  9. type Highlighter struct {
  10. parser *tree_sitter.Parser
  11. theme StyleMap
  12. }
  13. func NewHighlighter(theme StyleMap) (*Highlighter, error) {
  14. parser := tree_sitter.NewParser()
  15. return &Highlighter{
  16. parser: parser,
  17. theme: theme,
  18. }, nil
  19. }
  20. func (h *Highlighter) Close() {
  21. if h.parser != nil {
  22. h.parser.Close()
  23. }
  24. }
  25. func (h *Highlighter) HighlightBuffer(langName string, code []byte, highlightScm []byte) ([]HighlightSpan, *tree_sitter.Tree, error) {
  26. lang, err := tspack.GetLanguage(langName)
  27. if err != nil || lang == nil {
  28. return nil, nil, fmt.Errorf("could not load language %s: %w", langName, err)
  29. }
  30. if err := h.parser.SetLanguage(lang); err != nil {
  31. return nil, nil, fmt.Errorf("failed to set language: %w", err)
  32. }
  33. tree := h.parser.Parse(code, nil)
  34. if tree == nil {
  35. return nil, nil, fmt.Errorf("failed to parse document")
  36. }
  37. query, err := tree_sitter.NewQuery(lang, string(highlightScm))
  38. if err != nil {
  39. tree.Close()
  40. return nil, nil, fmt.Errorf("invalid query file: %w", err)
  41. }
  42. defer query.Close()
  43. cursor := tree_sitter.NewQueryCursor()
  44. defer cursor.Close()
  45. captures := cursor.Captures(query, tree.RootNode(), code)
  46. var spans []HighlightSpan
  47. for match, captureIdx := captures.Next(); match != nil; match, captureIdx = captures.Next() {
  48. capture := match.Captures[captureIdx]
  49. node := capture.Node
  50. captureName := query.CaptureNames()[capture.Index]
  51. if style, ok := h.theme[captureName]; ok {
  52. spans = append(spans, HighlightSpan{
  53. StartByte: uint32(node.StartByte()),
  54. EndByte: uint32(node.EndByte()),
  55. Style: style,
  56. })
  57. }
  58. }
  59. return spans, tree, nil
  60. }
  61. type HighlightSpan struct {
  62. StartByte uint32
  63. EndByte uint32
  64. Style tcell.Style
  65. }