Compare commits
7 Commits
32fe3f1edd
...
6034e44364
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6034e44364 | ||
|
|
760b7dd15a | ||
|
|
624439a0cf | ||
|
|
7c15f41ab1 | ||
|
|
16d1318c22 | ||
|
|
f96c1c1302 | ||
|
|
bc08213f07 |
@ -295,7 +295,7 @@ Buffers are in-memory representations of files. A buffer exists for each open fi
|
||||
|
||||
### Buffer State
|
||||
- [ ] Track cursor position per buffer
|
||||
- [ ] Track undo history per buffer
|
||||
- [x] Track undo history per buffer
|
||||
- [ ] Track marks per buffer
|
||||
- [ ] Remember scroll position when switching
|
||||
- [ ] Alternate buffer (`#`) tracking
|
||||
|
||||
2
V0.1.md
2
V0.1.md
@ -20,7 +20,7 @@
|
||||
- [ ] Configuration file support (~/.gimrc or similar)
|
||||
- [ ] Persistent undo history
|
||||
- [ ] Line wrapping display option
|
||||
- [ ] Handle large files gracefully (>10k lines)
|
||||
- [ ] Handle large files gracefully (>10k lines) [Pretty sure this is fine]
|
||||
|
||||
## Nice to Have (Polish)
|
||||
- [ ] Incremental search (search as you type)
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
export GOOS=linux
|
||||
export GOARCH=amd64
|
||||
export CGO_CFLAGS=-Wno-error=cpp;
|
||||
export CGO_ENABLED=0
|
||||
export CGO_ENABLED=1
|
||||
|
||||
# Exec zsh to replace the current shell process with zsh.
|
||||
# This ensures your prompt and zsh configurations load correctly.
|
||||
|
||||
18
go.mod
18
go.mod
@ -3,10 +3,24 @@ module git.gophernest.net/azpect/TextEditor
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/alecthomas/chroma/v2 v2.23.1
|
||||
github.com/charmbracelet/bubbletea v1.3.10
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/charmbracelet/x/exp/teatest v0.0.0-20260209132835-6b065b8ba62c
|
||||
github.com/tree-sitter/go-tree-sitter v0.25.0
|
||||
github.com/tree-sitter/tree-sitter-bash v0.25.1
|
||||
github.com/tree-sitter/tree-sitter-c v0.24.1
|
||||
github.com/tree-sitter/tree-sitter-c-sharp v0.23.1
|
||||
github.com/tree-sitter/tree-sitter-cpp v0.23.4
|
||||
github.com/tree-sitter/tree-sitter-css v0.25.0
|
||||
github.com/tree-sitter/tree-sitter-go v0.25.0
|
||||
github.com/tree-sitter/tree-sitter-html v0.23.2
|
||||
github.com/tree-sitter/tree-sitter-java v0.23.5
|
||||
github.com/tree-sitter/tree-sitter-javascript v0.25.0
|
||||
github.com/tree-sitter/tree-sitter-json v0.24.8
|
||||
github.com/tree-sitter/tree-sitter-python v0.25.0
|
||||
github.com/tree-sitter/tree-sitter-ruby v0.23.1
|
||||
github.com/tree-sitter/tree-sitter-rust v0.24.2
|
||||
github.com/tree-sitter/tree-sitter-typescript v0.23.2
|
||||
)
|
||||
|
||||
require (
|
||||
@ -20,11 +34,11 @@ require (
|
||||
github.com/clipperhouse/displaywidth v0.9.0 // indirect
|
||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-pointer v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
|
||||
54
go.sum
54
go.sum
@ -1,9 +1,3 @@
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY=
|
||||
github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=
|
||||
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
||||
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY=
|
||||
@ -30,18 +24,18 @@ github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfa
|
||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||
github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
|
||||
github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
|
||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
|
||||
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
@ -50,8 +44,46 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tree-sitter/go-tree-sitter v0.25.0 h1:sx6kcg8raRFCvc9BnXglke6axya12krCJF5xJ2sftRU=
|
||||
github.com/tree-sitter/go-tree-sitter v0.25.0/go.mod h1:r77ig7BikoZhHrrsjAnv8RqGti5rtSyvDHPzgTPsUuU=
|
||||
github.com/tree-sitter/tree-sitter-bash v0.25.1 h1:ZD3MK4oDB5lAsFztqbdcyYEd24pxDtx3g9UOWA062rE=
|
||||
github.com/tree-sitter/tree-sitter-bash v0.25.1/go.mod h1:AksQ6zE+sP9hnp7mKTMT7Q+CwpthV7VGQLXvweVXz9U=
|
||||
github.com/tree-sitter/tree-sitter-c v0.24.1 h1:GV9DjvIV6uYe3W/JBKMFwE4hJcRxzRDq63llxNFHOkY=
|
||||
github.com/tree-sitter/tree-sitter-c v0.24.1/go.mod h1:/SpJlv2BuiCgFA5xvtgukFGi51WxctByPUGDxPl60fc=
|
||||
github.com/tree-sitter/tree-sitter-c-sharp v0.23.1 h1:ddG6osP34sMieVNN6lu5ZG/3N8Wn+67+43BmipqidyM=
|
||||
github.com/tree-sitter/tree-sitter-c-sharp v0.23.1/go.mod h1:H7/aFm5vR1A8Yn5VIOfLWPdlKuJsMgZ5eDmaJdv8bY0=
|
||||
github.com/tree-sitter/tree-sitter-cpp v0.23.4 h1:LaWZsiqQKvR65yHgKmnaqA+uz6tlDJTJFCyFIeZU/8w=
|
||||
github.com/tree-sitter/tree-sitter-cpp v0.23.4/go.mod h1:doqNW64BriC7WBCQ1klf0KmJpdEvfxyXtoEybnBo6v8=
|
||||
github.com/tree-sitter/tree-sitter-css v0.25.0 h1:S5NbzhdZ5LE5V474wmdg+7NthmLjIg5v4wbyewMpziw=
|
||||
github.com/tree-sitter/tree-sitter-css v0.25.0/go.mod h1:0Z46XCb3L16nVOVw0Lhb43pzloUG/4T6E/pAOE62fEw=
|
||||
github.com/tree-sitter/tree-sitter-embedded-template v0.23.2 h1:nFkkH6Sbe56EXLmZBqHHcamTpmz3TId97I16EnGy4rg=
|
||||
github.com/tree-sitter/tree-sitter-embedded-template v0.23.2/go.mod h1:HNPOhN0qF3hWluYLdxWs5WbzP/iE4aaRVPMsdxuzIaQ=
|
||||
github.com/tree-sitter/tree-sitter-go v0.25.0 h1:cEB0Q3LHgZtS+ECHx9wcP7AwzoOddJFQCVmytX42cVU=
|
||||
github.com/tree-sitter/tree-sitter-go v0.25.0/go.mod h1:Jrx8QqYN0v7npv1fJRH1AznddllYiCMUChtVjxPK040=
|
||||
github.com/tree-sitter/tree-sitter-html v0.23.2 h1:1UYDV+Yd05GGRhVnTcbP58GkKLSHHZwVaN+lBZV11Lc=
|
||||
github.com/tree-sitter/tree-sitter-html v0.23.2/go.mod h1:gpUv/dG3Xl/eebqgeYeFMt+JLOY9cgFinb/Nw08a9og=
|
||||
github.com/tree-sitter/tree-sitter-java v0.23.5 h1:J9YeMGMwXYlKSP3K4Us8CitC6hjtMjqpeOf2GGo6tig=
|
||||
github.com/tree-sitter/tree-sitter-java v0.23.5/go.mod h1:NRKlI8+EznxA7t1Yt3xtraPk1Wzqh3GAIC46wxvc320=
|
||||
github.com/tree-sitter/tree-sitter-javascript v0.25.0 h1:ZkWETb66/w8cc13yhfnNuHOLDQWl3BnKlH6f9AdR88c=
|
||||
github.com/tree-sitter/tree-sitter-javascript v0.25.0/go.mod h1:lmGD1EJdCA+v0S1u2fFgepMg/opzSg/4pgFym2FPGAs=
|
||||
github.com/tree-sitter/tree-sitter-json v0.24.8 h1:tV5rMkihgtiOe14a9LHfDY5kzTl5GNUYe6carZBn0fQ=
|
||||
github.com/tree-sitter/tree-sitter-json v0.24.8/go.mod h1:F351KK0KGvCaYbZ5zxwx/gWWvZhIDl0eMtn+1r+gQbo=
|
||||
github.com/tree-sitter/tree-sitter-php v0.23.11 h1:iHewsLNDmznh8kgGyfWfujsZxIz1YGbSd2ZTEM0ZiP8=
|
||||
github.com/tree-sitter/tree-sitter-php v0.23.11/go.mod h1:T/kbfi+UcCywQfUNAJnGTN/fMSUjnwPXA8k4yoIks74=
|
||||
github.com/tree-sitter/tree-sitter-python v0.25.0 h1:O6XD9v8U1LOcRc3cNj9nM7XufrtEBezE6VrpRrHZDf0=
|
||||
github.com/tree-sitter/tree-sitter-python v0.25.0/go.mod h1:cpdthSy/Yoa28aJFBscFHlGiU+cnSiSh1kuDVtI8YeM=
|
||||
github.com/tree-sitter/tree-sitter-ruby v0.23.1 h1:T/NKHUA+iVbHM440hFx+lzVOzS4dV6z8Qw8ai+72bYo=
|
||||
github.com/tree-sitter/tree-sitter-ruby v0.23.1/go.mod h1:kUS4kCCQloFcdX6sdpr8p6r2rogbM6ZjTox5ZOQy8cA=
|
||||
github.com/tree-sitter/tree-sitter-rust v0.24.2 h1:NL4nF67ib21RMzzfvkmXlVwe45vvhW10DVyO+D0z/W0=
|
||||
github.com/tree-sitter/tree-sitter-rust v0.24.2/go.mod h1:hfeGWic9BAfgTrc7Xf6FaOAguCFJRo3RBbs7QJ6D7MI=
|
||||
github.com/tree-sitter/tree-sitter-typescript v0.23.2 h1:/Odvphn18PniVixb9e97X0DbNVsU6Qocv9mfkyzdXwU=
|
||||
github.com/tree-sitter/tree-sitter-typescript v0.23.2/go.mod h1:zjzMXT/Ulffel2xfOcAkQQkiAkmgnbtPGlFQw/5X4xA=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
|
||||
@ -62,3 +94,5 @@ golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@ -13,7 +13,6 @@ import (
|
||||
"git.gophernest.net/azpect/TextEditor/internal/action"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/style"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
@ -883,29 +882,31 @@ func parseSetOption(m action.Model, opt string) error {
|
||||
// Colorscheme Commands
|
||||
// --------------------------------------------------
|
||||
|
||||
// TODO: Implement this using the new colorschemes
|
||||
func cmdColorscheme(m action.Model, args []string, force bool) tea.Cmd {
|
||||
_ = force
|
||||
|
||||
// No args, just print the current scheme
|
||||
if len(args) == 0 {
|
||||
s := m.Styles().ChromaStyle
|
||||
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
m.SetCommandOutput(&core.CommandOutput{
|
||||
Lines: []string{s.Name},
|
||||
Lines: []string{"default"},
|
||||
Inline: true,
|
||||
IsError: false,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Args given, set the scheme
|
||||
name := strings.Join(args, " ")
|
||||
|
||||
chromaStyle := styles.Registry[name]
|
||||
if chromaStyle == nil {
|
||||
// Theme switching is disabled while migrating away from Chroma.
|
||||
name := strings.TrimSpace(strings.Join(args, " "))
|
||||
if name == "" {
|
||||
m.SetCommandOutput(&core.CommandOutput{
|
||||
Lines: []string{"colorscheme not found: "},
|
||||
Inline: true,
|
||||
IsError: true,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
if name != "" && strings.ToLower(name) != "default" {
|
||||
m.SetCommandOutput(&core.CommandOutput{
|
||||
Lines: []string{fmt.Sprintf("colorscheme not found: %s", name)},
|
||||
Inline: true,
|
||||
@ -914,14 +915,14 @@ func cmdColorscheme(m action.Model, args []string, force bool) tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
|
||||
m.SetStyles(style.ChromaStyles(chromaStyle))
|
||||
m.SetStyles(style.DefaultStyles())
|
||||
return nil
|
||||
}
|
||||
|
||||
func cmdListColorschemes(m action.Model, args []string, force bool) tea.Cmd {
|
||||
_, _ = args, force
|
||||
|
||||
colors := styles.Names()
|
||||
colors := []string{"default"}
|
||||
|
||||
m.SetMode(core.CommandOutputMode)
|
||||
m.SetCommandOutput(&core.CommandOutput{
|
||||
|
||||
@ -10,7 +10,6 @@ import (
|
||||
"git.gophernest.net/azpect/TextEditor/internal/action"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/style"
|
||||
cStyles "github.com/alecthomas/chroma/v2/styles"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
@ -5515,22 +5514,24 @@ func TestCmdColorscheme(t *testing.T) {
|
||||
|
||||
t.Run("valid name updates styles on model", func(t *testing.T) {
|
||||
m := action.NewMockModel()
|
||||
m.SetStyles(style.DefaultStyles())
|
||||
before := m.StylesVal.BackgroundStyle.Render(" ")
|
||||
|
||||
cmdColorscheme(m, []string{"onedark"}, false)
|
||||
cmdColorscheme(m, []string{"default"}, false)
|
||||
|
||||
name := m.Styles().ChromaStyle.Name
|
||||
if name != "onedark" {
|
||||
t.Error("expected styles to change after setting a valid colorscheme")
|
||||
after := m.StylesVal.BackgroundStyle.Render(" ")
|
||||
if after != before {
|
||||
t.Error("expected default styles to remain stable after applying default")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("same valid name applied twice produces same styles", func(t *testing.T) {
|
||||
m := action.NewMockModel()
|
||||
|
||||
cmdColorscheme(m, []string{"monokai"}, false)
|
||||
cmdColorscheme(m, []string{"default"}, false)
|
||||
first := m.StylesVal.BackgroundStyle.Render(" ")
|
||||
|
||||
cmdColorscheme(m, []string{"monokai"}, false)
|
||||
cmdColorscheme(m, []string{"default"}, false)
|
||||
second := m.StylesVal.BackgroundStyle.Render(" ")
|
||||
|
||||
if first != second {
|
||||
@ -5541,7 +5542,7 @@ func TestCmdColorscheme(t *testing.T) {
|
||||
t.Run("valid name sets no error output", func(t *testing.T) {
|
||||
m := action.NewMockModel()
|
||||
|
||||
cmdColorscheme(m, []string{"monokai"}, false)
|
||||
cmdColorscheme(m, []string{"default"}, false)
|
||||
|
||||
if m.CommandOutputVal != nil && m.CommandOutputVal.IsError {
|
||||
t.Error("expected no error output for a valid colorscheme name")
|
||||
@ -5551,7 +5552,7 @@ func TestCmdColorscheme(t *testing.T) {
|
||||
t.Run("valid name returns nil tea.Cmd", func(t *testing.T) {
|
||||
m := action.NewMockModel()
|
||||
|
||||
cmd := cmdColorscheme(m, []string{"monokai"}, false)
|
||||
cmd := cmdColorscheme(m, []string{"default"}, false)
|
||||
|
||||
if cmd != nil {
|
||||
t.Error("expected nil tea.Cmd for colorscheme command")
|
||||
@ -5651,7 +5652,7 @@ func TestCmdColorscheme(t *testing.T) {
|
||||
t.Run("extra args beyond name do not panic", func(t *testing.T) {
|
||||
m := action.NewMockModel()
|
||||
|
||||
cmdColorscheme(m, []string{"monokai", "extra", "args"}, false)
|
||||
cmdColorscheme(m, []string{"default", "extra", "args"}, false)
|
||||
})
|
||||
|
||||
// --------------------------------------------------
|
||||
@ -5661,10 +5662,9 @@ func TestCmdColorscheme(t *testing.T) {
|
||||
t.Run("force flag with valid name still sets styles", func(t *testing.T) {
|
||||
m := action.NewMockModel()
|
||||
|
||||
cmdColorscheme(m, []string{"monokai"}, true)
|
||||
cmdColorscheme(m, []string{"default"}, true)
|
||||
|
||||
name := m.Styles().ChromaStyle.Name
|
||||
if name != "monokai" {
|
||||
if m.CommandOutputVal != nil && m.CommandOutputVal.IsError {
|
||||
t.Error("expected styles to change with force=true and valid name")
|
||||
}
|
||||
})
|
||||
@ -5742,7 +5742,7 @@ func TestCmdListColorschemes(t *testing.T) {
|
||||
cmdListColorschemes(m, []string{}, false)
|
||||
|
||||
lines := m.CommandOutputVal.Lines
|
||||
known := []string{"monokai", "github-dark", "dracula"}
|
||||
known := []string{"default"}
|
||||
for _, name := range known {
|
||||
found := false
|
||||
for _, l := range lines {
|
||||
@ -5762,7 +5762,7 @@ func TestCmdListColorschemes(t *testing.T) {
|
||||
|
||||
cmdListColorschemes(m, []string{}, false)
|
||||
|
||||
expected := cStyles.Names()
|
||||
expected := []string{"default"}
|
||||
if len(m.CommandOutputVal.Lines) != len(expected) {
|
||||
t.Errorf("expected %d colorschemes, got %d", len(expected), len(m.CommandOutputVal.Lines))
|
||||
}
|
||||
@ -5785,7 +5785,7 @@ func TestCmdListColorschemes(t *testing.T) {
|
||||
m2 := action.NewMockModel()
|
||||
|
||||
cmdListColorschemes(m1, []string{}, false)
|
||||
cmdListColorschemes(m2, []string{"monokai", "extra"}, true)
|
||||
cmdListColorschemes(m2, []string{"default", "extra"}, true)
|
||||
|
||||
if len(m1.CommandOutputVal.Lines) != len(m2.CommandOutputVal.Lines) {
|
||||
t.Error("expected args and force to have no effect on list output")
|
||||
|
||||
@ -1,9 +1,48 @@
|
||||
package core
|
||||
|
||||
import "strings"
|
||||
|
||||
type BufferOptions struct {
|
||||
// tabstop expandtab
|
||||
}
|
||||
|
||||
type BufferChangeKind int
|
||||
|
||||
const (
|
||||
BufferChangeSetLine BufferChangeKind = iota
|
||||
BufferChangeInsertLine
|
||||
BufferChangeDeleteLine
|
||||
BufferChangeSetLines
|
||||
)
|
||||
|
||||
type BufferChange struct {
|
||||
Kind BufferChangeKind
|
||||
StartLine int
|
||||
EndLine int
|
||||
Edit *BufferEdit
|
||||
}
|
||||
|
||||
// TextPoint is a byte-oriented row/column point.
|
||||
//
|
||||
// Column is counted in bytes (Tree-sitter compatible), not runes.
|
||||
type TextPoint struct {
|
||||
Row uint
|
||||
Column uint
|
||||
}
|
||||
|
||||
// BufferEdit represents a text edit in byte offsets and points.
|
||||
//
|
||||
// These fields map directly to Tree-sitter incremental edit inputs.
|
||||
type BufferEdit struct {
|
||||
StartByte uint
|
||||
OldEndByte uint
|
||||
NewEndByte uint
|
||||
|
||||
StartPoint TextPoint
|
||||
OldEndPoint TextPoint
|
||||
NewEndPoint TextPoint
|
||||
}
|
||||
|
||||
type BufferType int
|
||||
|
||||
const (
|
||||
@ -30,6 +69,9 @@ type Buffer struct {
|
||||
|
||||
// Options BufferOptions
|
||||
UndoStack *UndoStack
|
||||
|
||||
// Optional change callback used by higher layers (editor/syntax) to react to edits.
|
||||
OnChange func(change BufferChange)
|
||||
}
|
||||
|
||||
// ==================================================
|
||||
@ -48,20 +90,33 @@ func (b *Buffer) Line(idx int) string {
|
||||
// Buffer.SetLine: Set the content of the line at an index. Does nothing if the
|
||||
// index is out of bounds. This function sets the modified flag.
|
||||
func (b *Buffer) SetLine(idx int, content string) {
|
||||
oldSource := b.sourceString()
|
||||
changed := false
|
||||
if idx >= 0 && idx < len(b.Lines) {
|
||||
// Record set line in undo stack
|
||||
if b.UndoStack != nil {
|
||||
b.UndoStack.RecordSetLine(idx, b.Lines[idx].String(), content)
|
||||
}
|
||||
b.Lines[idx].Set(content)
|
||||
changed = true
|
||||
}
|
||||
b.Modified = true
|
||||
if changed {
|
||||
newSource := b.sourceString()
|
||||
edit, ok := computeBufferEdit(oldSource, newSource)
|
||||
change := BufferChange{Kind: BufferChangeSetLine, StartLine: idx, EndLine: idx}
|
||||
if ok {
|
||||
change.Edit = &edit
|
||||
}
|
||||
b.notifyChange(change)
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer.InsertLine: Insert a line with content at an index. The index is clamped
|
||||
// to valid bounds (0 to len(Lines)). The new line is inserted before the line at
|
||||
// the given index. This function sets the modified flag.
|
||||
func (b *Buffer) InsertLine(idx int, content string) {
|
||||
oldSource := b.sourceString()
|
||||
if idx < 0 {
|
||||
idx = 0
|
||||
}
|
||||
@ -77,19 +132,39 @@ func (b *Buffer) InsertLine(idx int, content string) {
|
||||
newLine := NewGapBuffer(content)
|
||||
b.Lines = append(b.Lines[:idx], append([]*GapBuffer{newLine}, b.Lines[idx:]...)...)
|
||||
b.Modified = true
|
||||
|
||||
newSource := b.sourceString()
|
||||
edit, ok := computeBufferEdit(oldSource, newSource)
|
||||
change := BufferChange{Kind: BufferChangeInsertLine, StartLine: idx, EndLine: len(b.Lines) - 1}
|
||||
if ok {
|
||||
change.Edit = &edit
|
||||
}
|
||||
b.notifyChange(change)
|
||||
}
|
||||
|
||||
// Buffer.DeleteLine: Delete a line at an index. Does nothing if the index is out
|
||||
// of bounds. This function sets the modified flag.
|
||||
func (b *Buffer) DeleteLine(idx int) {
|
||||
oldSource := b.sourceString()
|
||||
changed := false
|
||||
if idx >= 0 && idx < len(b.Lines) {
|
||||
// Record delete line in undo stack
|
||||
if b.UndoStack != nil {
|
||||
b.UndoStack.RecordDeleteLine(idx, b.Lines[idx].String())
|
||||
}
|
||||
b.Lines = append(b.Lines[:idx], b.Lines[idx+1:]...)
|
||||
changed = true
|
||||
}
|
||||
b.Modified = true
|
||||
if changed {
|
||||
newSource := b.sourceString()
|
||||
edit, ok := computeBufferEdit(oldSource, newSource)
|
||||
change := BufferChange{Kind: BufferChangeDeleteLine, StartLine: idx, EndLine: len(b.Lines) - 1}
|
||||
if ok {
|
||||
change.Edit = &edit
|
||||
}
|
||||
b.notifyChange(change)
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer.LineCount: Get the number of lines in the buffer.
|
||||
@ -105,6 +180,8 @@ func (b *Buffer) Undo(w *Window) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
oldSource := b.sourceString()
|
||||
|
||||
block := b.UndoStack.Undo()
|
||||
if block == nil {
|
||||
return false
|
||||
@ -144,6 +221,16 @@ func (b *Buffer) Undo(w *Window) bool {
|
||||
w.SetCursorLine(block.OldCursor.Line)
|
||||
w.SetCursorCol(block.OldCursor.Col)
|
||||
|
||||
newSource := b.sourceString()
|
||||
if edit, ok := computeBufferEdit(oldSource, newSource); ok {
|
||||
b.notifyChange(BufferChange{
|
||||
Kind: BufferChangeSetLines,
|
||||
StartLine: 0,
|
||||
EndLine: max(0, len(b.Lines)-1),
|
||||
Edit: &edit,
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -152,6 +239,8 @@ func (b *Buffer) Redo(w *Window) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
oldSource := b.sourceString()
|
||||
|
||||
block := b.UndoStack.Redo()
|
||||
if block == nil {
|
||||
return false
|
||||
@ -189,6 +278,16 @@ func (b *Buffer) Redo(w *Window) bool {
|
||||
w.SetCursorLine(block.NewCursor.Line)
|
||||
w.SetCursorCol(block.NewCursor.Col)
|
||||
|
||||
newSource := b.sourceString()
|
||||
if edit, ok := computeBufferEdit(oldSource, newSource); ok {
|
||||
b.notifyChange(BufferChange{
|
||||
Kind: BufferChangeSetLines,
|
||||
StartLine: 0,
|
||||
EndLine: max(0, len(b.Lines)-1),
|
||||
Edit: &edit,
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -211,10 +310,93 @@ func (b *Buffer) SetFiletype(filetype string) {
|
||||
// Buffer.SetLines: Replace all lines in the buffer with the provided lines.
|
||||
// This is useful when loading a file or resetting buffer content.
|
||||
func (b *Buffer) SetLines(lines []string) {
|
||||
oldSource := b.sourceString()
|
||||
b.Lines = make([]*GapBuffer, len(lines))
|
||||
for i, line := range lines {
|
||||
b.Lines[i] = NewGapBuffer(line)
|
||||
}
|
||||
|
||||
newSource := b.sourceString()
|
||||
edit, ok := computeBufferEdit(oldSource, newSource)
|
||||
change := BufferChange{Kind: BufferChangeSetLines, StartLine: 0, EndLine: len(lines) - 1}
|
||||
if ok {
|
||||
change.Edit = &edit
|
||||
}
|
||||
b.notifyChange(change)
|
||||
}
|
||||
|
||||
func (b *Buffer) notifyChange(change BufferChange) {
|
||||
if b.OnChange != nil {
|
||||
b.OnChange(change)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) sourceString() string {
|
||||
if len(b.Lines) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
parts := make([]string, len(b.Lines))
|
||||
for i := range b.Lines {
|
||||
parts[i] = b.Lines[i].String()
|
||||
}
|
||||
|
||||
return strings.Join(parts, "\n")
|
||||
}
|
||||
|
||||
func computeBufferEdit(oldSource, newSource string) (BufferEdit, bool) {
|
||||
if oldSource == newSource {
|
||||
return BufferEdit{}, false
|
||||
}
|
||||
|
||||
oldBytes := []byte(oldSource)
|
||||
newBytes := []byte(newSource)
|
||||
|
||||
prefix := 0
|
||||
maxPrefix := min(len(oldBytes), len(newBytes))
|
||||
for prefix < maxPrefix && oldBytes[prefix] == newBytes[prefix] {
|
||||
prefix++
|
||||
}
|
||||
|
||||
oldEnd := len(oldBytes)
|
||||
newEnd := len(newBytes)
|
||||
for oldEnd > prefix && newEnd > prefix && oldBytes[oldEnd-1] == newBytes[newEnd-1] {
|
||||
oldEnd--
|
||||
newEnd--
|
||||
}
|
||||
|
||||
edit := BufferEdit{
|
||||
StartByte: uint(prefix),
|
||||
OldEndByte: uint(oldEnd),
|
||||
NewEndByte: uint(newEnd),
|
||||
StartPoint: byteOffsetToPoint(oldBytes, prefix),
|
||||
OldEndPoint: byteOffsetToPoint(oldBytes, oldEnd),
|
||||
NewEndPoint: byteOffsetToPoint(newBytes, newEnd),
|
||||
}
|
||||
|
||||
return edit, true
|
||||
}
|
||||
|
||||
func byteOffsetToPoint(src []byte, offset int) TextPoint {
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
if offset > len(src) {
|
||||
offset = len(src)
|
||||
}
|
||||
|
||||
var row uint
|
||||
var col uint
|
||||
for i := 0; i < offset; i++ {
|
||||
if src[i] == '\n' {
|
||||
row++
|
||||
col = 0
|
||||
} else {
|
||||
col++
|
||||
}
|
||||
}
|
||||
|
||||
return TextPoint{Row: row, Column: col}
|
||||
}
|
||||
|
||||
// Buffer.SetModified: Set the modified flag for this buffer. A modified buffer
|
||||
|
||||
62
internal/core/buffer_edit_fuzz_test.go
Normal file
62
internal/core/buffer_edit_fuzz_test.go
Normal file
@ -0,0 +1,62 @@
|
||||
package core
|
||||
|
||||
import "testing"
|
||||
|
||||
func FuzzComputeBufferEditInvariants(f *testing.F) {
|
||||
f.Add("abc\ndef", "abc\nxyz")
|
||||
f.Add("", "x")
|
||||
f.Add("same", "same")
|
||||
f.Add("hello", "")
|
||||
|
||||
f.Fuzz(func(t *testing.T, oldSource, newSource string) {
|
||||
edit, ok := computeBufferEdit(oldSource, newSource)
|
||||
|
||||
if oldSource == newSource {
|
||||
if ok {
|
||||
t.Fatalf("expected no edit when strings are equal")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
t.Fatalf("expected edit for differing strings")
|
||||
}
|
||||
|
||||
oldBytes := []byte(oldSource)
|
||||
newBytes := []byte(newSource)
|
||||
start := int(edit.StartByte)
|
||||
oldEnd := int(edit.OldEndByte)
|
||||
newEnd := int(edit.NewEndByte)
|
||||
|
||||
if start < 0 || start > len(oldBytes) || start > len(newBytes) {
|
||||
t.Fatalf("invalid start byte: %d", start)
|
||||
}
|
||||
if oldEnd < start || oldEnd > len(oldBytes) {
|
||||
t.Fatalf("invalid old end byte: %d", oldEnd)
|
||||
}
|
||||
if newEnd < start || newEnd > len(newBytes) {
|
||||
t.Fatalf("invalid new end byte: %d", newEnd)
|
||||
}
|
||||
|
||||
if string(oldBytes[:start]) != string(newBytes[:start]) {
|
||||
t.Fatalf("prefix before edit start must match")
|
||||
}
|
||||
if string(oldBytes[oldEnd:]) != string(newBytes[newEnd:]) {
|
||||
t.Fatalf("suffix after edit end must match")
|
||||
}
|
||||
|
||||
sp := byteOffsetToPoint(oldBytes, start)
|
||||
op := byteOffsetToPoint(oldBytes, oldEnd)
|
||||
np := byteOffsetToPoint(newBytes, newEnd)
|
||||
|
||||
if sp != edit.StartPoint {
|
||||
t.Fatalf("start point mismatch: got %+v want %+v", edit.StartPoint, sp)
|
||||
}
|
||||
if op != edit.OldEndPoint {
|
||||
t.Fatalf("old end point mismatch: got %+v want %+v", edit.OldEndPoint, op)
|
||||
}
|
||||
if np != edit.NewEndPoint {
|
||||
t.Fatalf("new end point mismatch: got %+v want %+v", edit.NewEndPoint, np)
|
||||
}
|
||||
})
|
||||
}
|
||||
103
internal/core/buffer_edit_test.go
Normal file
103
internal/core/buffer_edit_test.go
Normal file
@ -0,0 +1,103 @@
|
||||
package core
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestComputeBufferEditReplaceLine(t *testing.T) {
|
||||
oldSource := "abc\ndef"
|
||||
newSource := "abc\nxyz"
|
||||
|
||||
edit, ok := computeBufferEdit(oldSource, newSource)
|
||||
if !ok {
|
||||
t.Fatalf("expected edit to be detected")
|
||||
}
|
||||
|
||||
if edit.StartPoint.Row != 1 || edit.StartPoint.Column != 0 {
|
||||
t.Fatalf("unexpected start point: %+v", edit.StartPoint)
|
||||
}
|
||||
if edit.OldEndPoint.Row != 1 || edit.OldEndPoint.Column != 3 {
|
||||
t.Fatalf("unexpected old end point: %+v", edit.OldEndPoint)
|
||||
}
|
||||
if edit.NewEndPoint.Row != 1 || edit.NewEndPoint.Column != 3 {
|
||||
t.Fatalf("unexpected new end point: %+v", edit.NewEndPoint)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeBufferEditInsertAtEnd(t *testing.T) {
|
||||
oldSource := "a\nb"
|
||||
newSource := "a\nbb"
|
||||
|
||||
edit, ok := computeBufferEdit(oldSource, newSource)
|
||||
if !ok {
|
||||
t.Fatalf("expected edit to be detected")
|
||||
}
|
||||
|
||||
if edit.StartByte != 3 || edit.OldEndByte != 3 || edit.NewEndByte != 4 {
|
||||
t.Fatalf("unexpected byte offsets: %+v", edit)
|
||||
}
|
||||
if edit.StartPoint.Row != 1 || edit.StartPoint.Column != 1 {
|
||||
t.Fatalf("unexpected start point: %+v", edit.StartPoint)
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteOffsetToPoint(t *testing.T) {
|
||||
src := []byte("ab\ncd\nef")
|
||||
|
||||
p := byteOffsetToPoint(src, 0)
|
||||
if p.Row != 0 || p.Column != 0 {
|
||||
t.Fatalf("offset 0 mismatch: %+v", p)
|
||||
}
|
||||
|
||||
p = byteOffsetToPoint(src, 4) // right after 'c'
|
||||
if p.Row != 1 || p.Column != 1 {
|
||||
t.Fatalf("offset 4 mismatch: %+v", p)
|
||||
}
|
||||
|
||||
p = byteOffsetToPoint(src, len(src))
|
||||
if p.Row != 2 || p.Column != 2 {
|
||||
t.Fatalf("end offset mismatch: %+v", p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUndoRedoEmitBufferChange(t *testing.T) {
|
||||
b := NewBufferBuilder().WithFiletype("go").WithLines([]string{"one", "two"}).Build()
|
||||
buf := &b
|
||||
|
||||
win := NewWindowBuilder().WithBuffer(buf).Build()
|
||||
w := &win
|
||||
|
||||
buf.UndoStack.BeginBlock(Position{Line: 0, Col: 0})
|
||||
buf.SetLine(0, "ONE")
|
||||
buf.UndoStack.EndBlock(Position{Line: 0, Col: 3})
|
||||
|
||||
changes := []BufferChange{}
|
||||
buf.OnChange = func(change BufferChange) {
|
||||
changes = append(changes, change)
|
||||
}
|
||||
|
||||
if !buf.Undo(w) {
|
||||
t.Fatalf("expected undo to succeed")
|
||||
}
|
||||
if len(changes) != 1 {
|
||||
t.Fatalf("expected one change notification on undo, got %d", len(changes))
|
||||
}
|
||||
if changes[0].Edit == nil {
|
||||
t.Fatalf("expected undo change to include edit metadata")
|
||||
}
|
||||
if got := buf.Line(0); got != "one" {
|
||||
t.Fatalf("undo did not restore content, got %q", got)
|
||||
}
|
||||
|
||||
changes = nil
|
||||
if !buf.Redo(w) {
|
||||
t.Fatalf("expected redo to succeed")
|
||||
}
|
||||
if len(changes) != 1 {
|
||||
t.Fatalf("expected one change notification on redo, got %d", len(changes))
|
||||
}
|
||||
if changes[0].Edit == nil {
|
||||
t.Fatalf("expected redo change to include edit metadata")
|
||||
}
|
||||
if got := buf.Line(0); got != "ONE" {
|
||||
t.Fatalf("redo did not reapply content, got %q", got)
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/input"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/style"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/syntax"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
@ -51,6 +52,7 @@ type Model struct {
|
||||
|
||||
// Visual styles
|
||||
styles style.Styles
|
||||
syntax syntax.Engine
|
||||
|
||||
// Dot operator state
|
||||
lastChangeKeys []string
|
||||
@ -87,6 +89,7 @@ func (m *Model) Buffers() []*core.Buffer {
|
||||
|
||||
func (m *Model) SetBuffers(bufs []*core.Buffer) {
|
||||
m.buffers = bufs
|
||||
m.bindBufferSyntaxHooks(bufs)
|
||||
}
|
||||
|
||||
func (m *Model) ActiveBuffer() *core.Buffer {
|
||||
@ -348,6 +351,42 @@ func (m *Model) SetStyles(s style.Styles) {
|
||||
m.styles = s
|
||||
}
|
||||
|
||||
func (m *Model) Syntax() syntax.Engine {
|
||||
return m.syntax
|
||||
}
|
||||
|
||||
func (m *Model) SetSyntax(s syntax.Engine) {
|
||||
m.syntax = s
|
||||
m.bindBufferSyntaxHooks(m.buffers)
|
||||
}
|
||||
|
||||
func (m *Model) bindBufferSyntaxHooks(bufs []*core.Buffer) {
|
||||
if m.syntax == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, buf := range bufs {
|
||||
if buf == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
b := buf
|
||||
b.OnChange = func(change core.BufferChange) {
|
||||
if change.Edit != nil {
|
||||
m.syntax.ApplyEdit(b, change.Edit)
|
||||
return
|
||||
}
|
||||
|
||||
switch change.Kind {
|
||||
case core.BufferChangeSetLine:
|
||||
m.syntax.InvalidateLines(b, change.StartLine, change.EndLine)
|
||||
default:
|
||||
m.syntax.InvalidateBuffer(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================================================
|
||||
// Registers
|
||||
// ==================================================
|
||||
|
||||
@ -4,7 +4,7 @@ import (
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/input"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/style"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/syntax"
|
||||
)
|
||||
|
||||
type ModelBuilder struct {
|
||||
@ -13,7 +13,7 @@ type ModelBuilder struct {
|
||||
|
||||
// NewModelBuilder: Builds and returns a new model, using the default color scheme (kanagawa-wave).
|
||||
func NewModelBuilder() *ModelBuilder {
|
||||
chromaStyle := styles.Get("kanagawa-wave")
|
||||
editorStyles := style.DefaultStyles()
|
||||
|
||||
return &ModelBuilder{
|
||||
model: Model{
|
||||
@ -32,7 +32,8 @@ func NewModelBuilder() *ModelBuilder {
|
||||
commandOutput: nil,
|
||||
settings: core.NewDefaultSettings(),
|
||||
registers: core.DefaultRegisters(),
|
||||
styles: style.ChromaStyles(chromaStyle),
|
||||
styles: editorStyles,
|
||||
syntax: syntax.NewTreeSitterEngine(editorStyles),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -133,5 +134,7 @@ func (mb *ModelBuilder) WithStyles(styles style.Styles) *ModelBuilder {
|
||||
|
||||
// ModelBuilder.Build: Build and return the configured Model instance.
|
||||
func (mb *ModelBuilder) Build() *Model {
|
||||
return &mb.model
|
||||
m := &mb.model
|
||||
m.bindBufferSyntaxHooks(m.buffers)
|
||||
return m
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/style"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/syntax"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
@ -28,7 +29,7 @@ func (m Model) View() string {
|
||||
options.GutterSize = max(options.GutterSize, maxLineLen+2)
|
||||
|
||||
// Draw window
|
||||
view := viewWindow(win, styles, options, m.Mode())
|
||||
view := viewWindow(win, styles, options, m.Mode(), m.Syntax())
|
||||
|
||||
// Command bar is seperate
|
||||
cmdBar := drawCommandBar(m)
|
||||
@ -46,21 +47,24 @@ func (m Model) View() string {
|
||||
|
||||
// viewWindow: Renders a single window's content including line numbers and buffer text.
|
||||
// Each window has its own line numbers, gutter, and viewport dimensions.
|
||||
func viewWindow(w *core.Window, styles style.Styles, options core.WinOptions, mode core.Mode) string {
|
||||
func viewWindow(w *core.Window, styles style.Styles, options core.WinOptions, mode core.Mode, sx syntax.Engine) string {
|
||||
buf := w.Buffer
|
||||
var view strings.Builder
|
||||
if sx != nil {
|
||||
sx.PrepareBuffer(buf)
|
||||
}
|
||||
|
||||
// Compute window size (y)
|
||||
start := w.ScrollY
|
||||
end := w.ScrollY + w.ViewportHeight()
|
||||
|
||||
// Chroma stuff
|
||||
lexer := style.GetLexer(buf)
|
||||
|
||||
// Draw buffer lines
|
||||
for lineNum := start; lineNum < end; lineNum++ {
|
||||
if lineNum < buf.LineCount() {
|
||||
styleMap := styles.MakeStyleMap(lexer, buf.Line(lineNum))
|
||||
styleMap := make([]lipgloss.Style, len([]rune(buf.Line(lineNum))))
|
||||
if sx != nil {
|
||||
styleMap = sx.LineStyleMap(buf, lineNum)
|
||||
}
|
||||
line := drawLine(w, styles, options, mode, buf.Line(lineNum), lineNum, styleMap)
|
||||
view.WriteString(line)
|
||||
} else {
|
||||
|
||||
52
internal/style/capture_theme.go
Normal file
52
internal/style/capture_theme.go
Normal file
@ -0,0 +1,52 @@
|
||||
package style
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
func CaptureStyle(base lipgloss.Style, capture string) lipgloss.Style {
|
||||
full := strings.ToLower(strings.TrimSpace(capture))
|
||||
baseName := strings.Split(full, ".")[0]
|
||||
|
||||
switch full {
|
||||
case "keyword", "keyword.type", "keyword.function", "keyword.coroutine", "keyword.repeat", "keyword.import", "keyword.conditional":
|
||||
return base.Foreground(lipgloss.Color("#c678dd"))
|
||||
case "function", "function.call", "function.method", "function.method.call":
|
||||
return base.Foreground(lipgloss.Color("#61afef"))
|
||||
case "function.builtin", "constructor", "keyword.return":
|
||||
return base.Foreground(lipgloss.Color("#ff5f5f"))
|
||||
case "type", "type.builtin", "type.definition":
|
||||
return base.Foreground(lipgloss.Color("#e5c07b"))
|
||||
case "string", "string.escape":
|
||||
return base.Foreground(lipgloss.Color("#98c379"))
|
||||
case "number", "number.float", "boolean", "constant", "constant.builtin":
|
||||
return base.Foreground(lipgloss.Color("#56b6c2"))
|
||||
case "operator", "punctuation.delimiter", "punctuation.bracket":
|
||||
return base.Foreground(lipgloss.Color("#d19a66"))
|
||||
case "comment", "comment.documentation":
|
||||
return base.Foreground(lipgloss.Color("#7f848e"))
|
||||
case "variable.parameter":
|
||||
return base.Foreground(lipgloss.Color("#dcdfe4")).Italic(true)
|
||||
case "module", "label", "property", "variable.member", "variable":
|
||||
return base.Foreground(lipgloss.Color("#dcdfe4"))
|
||||
}
|
||||
|
||||
switch baseName {
|
||||
case "keyword":
|
||||
return base.Foreground(lipgloss.Color("#c678dd"))
|
||||
case "function":
|
||||
return base.Foreground(lipgloss.Color("#61afef"))
|
||||
case "type":
|
||||
return base.Foreground(lipgloss.Color("#e5c07b"))
|
||||
case "string":
|
||||
return base.Foreground(lipgloss.Color("#98c379"))
|
||||
case "number", "boolean", "constant":
|
||||
return base.Foreground(lipgloss.Color("#56b6c2"))
|
||||
case "comment":
|
||||
return base.Foreground(lipgloss.Color("#7f848e"))
|
||||
default:
|
||||
return base
|
||||
}
|
||||
}
|
||||
223
internal/style/style.go
Executable file → Normal file
223
internal/style/style.go
Executable file → Normal file
@ -1,11 +1,7 @@
|
||||
package style
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/lexers"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
@ -23,7 +19,7 @@ type Styles struct {
|
||||
|
||||
// Visual mode
|
||||
VisualHighlight lipgloss.Style
|
||||
VisualAnchor lipgloss.Style // debugging
|
||||
VisualAnchor lipgloss.Style
|
||||
|
||||
// Status bar
|
||||
StatusBar lipgloss.Style
|
||||
@ -34,132 +30,72 @@ type Styles struct {
|
||||
CommandOutputBorder lipgloss.Style
|
||||
CommandContinueMessage lipgloss.Style
|
||||
|
||||
// General Styles
|
||||
LineStyle lipgloss.Style // This is a simple background with no text coloring
|
||||
BackgroundStyle lipgloss.Style // This is just the background
|
||||
|
||||
// Chroma data
|
||||
ChromaStyle *chroma.Style
|
||||
// General styles
|
||||
LineStyle lipgloss.Style
|
||||
BackgroundStyle lipgloss.Style
|
||||
}
|
||||
|
||||
// DefaultStyles: Returns the default editor color scheme.
|
||||
func DefaultStyles() Styles {
|
||||
return Styles{
|
||||
CursorNormal: lipgloss.NewStyle().Reverse(true),
|
||||
CursorInsert: lipgloss.NewStyle().Underline(true),
|
||||
CursorCommand: lipgloss.NewStyle().Reverse(true),
|
||||
CursorReplace: lipgloss.NewStyle().Underline(true),
|
||||
|
||||
Gutter: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color("236")).
|
||||
Foreground(lipgloss.Color("243")),
|
||||
|
||||
GutterCurrentLine: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color("236")).
|
||||
Foreground(lipgloss.Color("#d69d00")),
|
||||
|
||||
VisualHighlight: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color("#7a6a00")),
|
||||
|
||||
VisualAnchor: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color("#a89020")),
|
||||
|
||||
StatusBar: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color("236")).
|
||||
Foreground(lipgloss.Color("243")),
|
||||
|
||||
StatusBarActive: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color("62")).
|
||||
Foreground(lipgloss.Color("230")),
|
||||
|
||||
CommandError: lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("#e3203a")),
|
||||
|
||||
CommandOutputBorder: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color("#000000")),
|
||||
|
||||
CommandContinueMessage: lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("#546fba")),
|
||||
|
||||
ChromaStyle: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func ChromaStyles(chromaStyle *chroma.Style) Styles {
|
||||
bgString := chromaStyle.Get(chroma.Background).Background.String()
|
||||
lineNumbers := chromaStyle.Get(chroma.LineTableTD)
|
||||
lineHighlight := chromaStyle.Get(chroma.LineHighlight)
|
||||
bg := lipgloss.Color("#1f2335")
|
||||
fg := lipgloss.Color("#dcd7ba")
|
||||
|
||||
return Styles{
|
||||
CursorNormal: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(bgString)).
|
||||
Reverse(true),
|
||||
Background(fg).
|
||||
Foreground(bg),
|
||||
|
||||
CursorInsert: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(bgString)).
|
||||
Bold(true).
|
||||
Background(bg).
|
||||
Foreground(fg).
|
||||
Underline(true),
|
||||
|
||||
CursorCommand: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(bgString)).
|
||||
Reverse(true),
|
||||
Background(fg).
|
||||
Foreground(bg),
|
||||
|
||||
CursorReplace: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(bgString)).
|
||||
Background(bg).
|
||||
Foreground(fg).
|
||||
Underline(true),
|
||||
|
||||
Gutter: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(
|
||||
darkenColor(lineNumbers.Background, 0.9).String()),
|
||||
).
|
||||
Foreground(lipgloss.Color(lineNumbers.Colour.String())),
|
||||
Background(lipgloss.Color("#181b2a")).
|
||||
Foreground(lipgloss.Color("#7e8399")),
|
||||
|
||||
GutterCurrentLine: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(
|
||||
darkenColor(lineNumbers.Background, 0.9).String()),
|
||||
).
|
||||
Foreground(lipgloss.Color(lineNumbers.Colour.String())),
|
||||
Background(lipgloss.Color("#181b2a")).
|
||||
Foreground(lipgloss.Color("#e6c384")),
|
||||
|
||||
VisualHighlight: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(lineHighlight.Background.String())).
|
||||
Foreground(lipgloss.Color(lineHighlight.Colour.String())),
|
||||
Background(lipgloss.Color("#2f334d")),
|
||||
|
||||
VisualAnchor: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(lineHighlight.Background.String())).
|
||||
Foreground(lipgloss.Color(lineHighlight.Colour.String())),
|
||||
Background(lipgloss.Color("#3a3f5f")),
|
||||
|
||||
StatusBar: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(bgString)).
|
||||
Foreground(lipgloss.Color("243")),
|
||||
Background(lipgloss.Color("#181b2a")).
|
||||
Foreground(lipgloss.Color("#8ea4a2")),
|
||||
|
||||
StatusBarActive: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(bgString)).
|
||||
Foreground(lipgloss.Color("230")),
|
||||
Background(lipgloss.Color("#223249")).
|
||||
Foreground(lipgloss.Color("#9ec1cf")),
|
||||
|
||||
CommandError: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(bgString)).
|
||||
Foreground(lipgloss.Color("#e3203a")),
|
||||
Foreground(lipgloss.Color("#e82424")),
|
||||
|
||||
CommandOutputBorder: lipgloss.NewStyle().
|
||||
Background(
|
||||
lipgloss.Color(
|
||||
darkenColor(
|
||||
chromaStyle.Get(chroma.Background).Background, 0.5).
|
||||
String(),
|
||||
),
|
||||
),
|
||||
Background(lipgloss.Color("#11131d")),
|
||||
|
||||
CommandContinueMessage: lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(bgString)).
|
||||
Foreground(lipgloss.Color("#546fba")),
|
||||
Foreground(lipgloss.Color("#7aa2f7")),
|
||||
|
||||
LineStyle: lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color(chromaStyle.Get(chroma.Line).Colour.String())).
|
||||
Background(lipgloss.Color(bgString)),
|
||||
Foreground(fg).
|
||||
Background(bg),
|
||||
|
||||
BackgroundStyle: lipgloss.NewStyle().Background(lipgloss.Color(bgString)),
|
||||
|
||||
ChromaStyle: chromaStyle,
|
||||
BackgroundStyle: lipgloss.NewStyle().
|
||||
Background(bg),
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,106 +113,29 @@ func (s Styles) DefaultCursorStyle(mode core.Mode) lipgloss.Style {
|
||||
}
|
||||
}
|
||||
|
||||
// Styles.CursorStyle: Returns a cursor style derived from a chroma style. This function should preferred
|
||||
// over the DefaultCursorStyle, but in cases where there is no style to apply, the DefaultCursorStyle
|
||||
// will always work.
|
||||
func (s Styles) CursorStyle(mode core.Mode, style lipgloss.Style) lipgloss.Style {
|
||||
// Styles.CursorStyle: Returns a cursor style derived from the text style.
|
||||
func (s Styles) CursorStyle(mode core.Mode, textStyle lipgloss.Style) lipgloss.Style {
|
||||
switch mode {
|
||||
case core.NormalMode, core.VisualLineMode, core.VisualBlockMode, core.VisualMode:
|
||||
return lipgloss.NewStyle().
|
||||
Background(style.GetForeground()).
|
||||
Foreground(style.GetBackground())
|
||||
Background(textStyle.GetForeground()).
|
||||
Foreground(textStyle.GetBackground())
|
||||
case core.ReplaceMode, core.WaitingMode:
|
||||
return lipgloss.NewStyle().
|
||||
Background(style.GetBackground()).
|
||||
Foreground(style.GetForeground()).
|
||||
Background(textStyle.GetBackground()).
|
||||
Foreground(textStyle.GetForeground()).
|
||||
Underline(true)
|
||||
default:
|
||||
return lipgloss.NewStyle().
|
||||
Background(s.BackgroundStyle.GetBackground()).
|
||||
Foreground(style.GetForeground()).
|
||||
Foreground(textStyle.GetForeground()).
|
||||
Underline(true)
|
||||
}
|
||||
}
|
||||
|
||||
// Styles.VisualHighlightWithTextColor: Works analogously to CursorStyle vs DefaultCursorStyle. When a
|
||||
// style is available, this function should be used, so the text color will be rendered in front
|
||||
// of the background. Otherwise, the VisualHighlight property will always work.
|
||||
func (s Styles) VisualHighlightWithTextColor(style lipgloss.Style) lipgloss.Style {
|
||||
// Styles.VisualHighlightWithTextColor: Applies visual background while preserving text color.
|
||||
func (s Styles) VisualHighlightWithTextColor(textStyle lipgloss.Style) lipgloss.Style {
|
||||
return lipgloss.NewStyle().
|
||||
Background(s.VisualHighlight.GetBackground()).
|
||||
Foreground(style.GetForeground())
|
||||
}
|
||||
|
||||
// Styles.MakeStyleMap: Generates a style map for a single line. A style map is a mapping from
|
||||
// column a lipgloss style. Cursor styles are not handled by this map, but they can be derived
|
||||
// by inverting the background and foreground (and rolling back to the default).
|
||||
func (s Styles) MakeStyleMap(lexer chroma.Lexer, line string) []lipgloss.Style {
|
||||
m := make([]lipgloss.Style, len(line))
|
||||
|
||||
if s.ChromaStyle == nil {
|
||||
return m
|
||||
}
|
||||
|
||||
iter, err := lexer.Tokenise(nil, line)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
col := 0
|
||||
for _, token := range iter.Tokens() {
|
||||
entry := s.ChromaStyle.Get(token.Type)
|
||||
s := lipgloss.NewStyle().
|
||||
Background(lipgloss.Color(entry.Background.String())).
|
||||
Foreground(lipgloss.Color(entry.Colour.String()))
|
||||
for _, char := range token.Value {
|
||||
if char == '\n' {
|
||||
continue
|
||||
}
|
||||
if col < len(m) {
|
||||
m[col] = s
|
||||
}
|
||||
col++
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// darkenColor: Uses a factor (0.0 to 1.0) to darken a color using its opacity.
|
||||
func darkenColor(c chroma.Colour, factor float64) chroma.Colour {
|
||||
r := uint8(float64(c.Red()) * factor)
|
||||
g := uint8(float64(c.Green()) * factor)
|
||||
b := uint8(float64(c.Blue()) * factor)
|
||||
return chroma.NewColour(r, g, b)
|
||||
}
|
||||
|
||||
// GetLexer: Uses buffer meta data or content to pick a lexer for use in applying
|
||||
// highlights.
|
||||
func GetLexer(buf *core.Buffer) chroma.Lexer {
|
||||
var lexer chroma.Lexer
|
||||
|
||||
if buf.Filetype != "" {
|
||||
lexer = lexers.Get(strings.TrimPrefix(buf.Filetype, "."))
|
||||
}
|
||||
|
||||
if lexer == nil && buf.Filename != "" {
|
||||
lexer = lexers.Match(buf.Filename)
|
||||
}
|
||||
|
||||
if lexer == nil && len(buf.Lines) > 0 {
|
||||
// Get first few lines for content analysis
|
||||
var content strings.Builder
|
||||
for i := 0; i < min(len(buf.Lines), 10); i++ {
|
||||
content.WriteString(buf.Lines[i].String() + "\n")
|
||||
}
|
||||
lexer = lexers.Analyse(content.String())
|
||||
}
|
||||
|
||||
if lexer == nil {
|
||||
lexer = lexers.Fallback
|
||||
}
|
||||
|
||||
lexer = chroma.Coalesce(lexer) // Merge tokens together
|
||||
return lexer
|
||||
Foreground(textStyle.GetForeground())
|
||||
}
|
||||
|
||||
10
internal/syntax/README.md
Normal file
10
internal/syntax/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# How to add more languages now (quick workflow)
|
||||
1. Add binding dependency in go.mod
|
||||
2. Add query file under internal/syntax/queries/<lang>/highlights.scm
|
||||
3. Embed it in internal/syntax/query_assets.go
|
||||
4. Add one register(...) block in internal/syntax/registry.go
|
||||
5. Update internal/syntax/query_assets_test.go with another test
|
||||
|
||||
## Where to get .scm files
|
||||
|
||||
[nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter/tree/master/queries)
|
||||
27
internal/syntax/engine.go
Normal file
27
internal/syntax/engine.go
Normal file
@ -0,0 +1,27 @@
|
||||
package syntax
|
||||
|
||||
import (
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// Engine provides syntax highlight data for buffers.
|
||||
//
|
||||
// The renderer should consume this interface rather than doing parse/token work
|
||||
// directly.
|
||||
type Engine interface {
|
||||
// Engine.PrepareBuffer: Ensure syntax state for a buffer is ready.
|
||||
PrepareBuffer(buf *core.Buffer)
|
||||
|
||||
// Engine.ApplyEdit: Apply an incremental text edit to syntax state.
|
||||
ApplyEdit(buf *core.Buffer, edit *core.BufferEdit)
|
||||
|
||||
// Engine.LineStyleMap: Returns per-rune styles for a line.
|
||||
LineStyleMap(buf *core.Buffer, line int) []lipgloss.Style
|
||||
|
||||
// Engine.InvalidateBuffer: Marks all syntax state for a buffer as stale.
|
||||
InvalidateBuffer(buf *core.Buffer)
|
||||
|
||||
// Engine.InvalidateLines: Marks a line range as stale.
|
||||
InvalidateLines(buf *core.Buffer, startLine, endLine int)
|
||||
}
|
||||
261
internal/syntax/queries/bash/highlights.scm
Normal file
261
internal/syntax/queries/bash/highlights.scm
Normal file
@ -0,0 +1,261 @@
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"{"
|
||||
"}"
|
||||
"["
|
||||
"]"
|
||||
"[["
|
||||
"]]"
|
||||
"(("
|
||||
"))"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
";"
|
||||
";;"
|
||||
";&"
|
||||
";;&"
|
||||
"&"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
">"
|
||||
">>"
|
||||
"<"
|
||||
"<<"
|
||||
"&&"
|
||||
"|"
|
||||
"|&"
|
||||
"||"
|
||||
"="
|
||||
"+="
|
||||
"=~"
|
||||
"=="
|
||||
"!="
|
||||
"&>"
|
||||
"&>>"
|
||||
"<&"
|
||||
">&"
|
||||
">|"
|
||||
"<&-"
|
||||
">&-"
|
||||
"<<-"
|
||||
"<<<"
|
||||
".."
|
||||
"!"
|
||||
] @operator
|
||||
|
||||
; Do *not* spell check strings since they typically have some sort of
|
||||
; interpolation in them, or, are typically used for things like filenames, URLs,
|
||||
; flags and file content.
|
||||
[
|
||||
(string)
|
||||
(raw_string)
|
||||
(ansi_c_string)
|
||||
(heredoc_body)
|
||||
] @string
|
||||
|
||||
[
|
||||
(heredoc_start)
|
||||
(heredoc_end)
|
||||
] @label
|
||||
|
||||
(variable_assignment
|
||||
(word) @string)
|
||||
|
||||
(command
|
||||
argument: "$" @string) ; bare dollar
|
||||
|
||||
(concatenation
|
||||
(word) @string)
|
||||
|
||||
[
|
||||
"if"
|
||||
"then"
|
||||
"else"
|
||||
"elif"
|
||||
"fi"
|
||||
"case"
|
||||
"in"
|
||||
"esac"
|
||||
] @keyword.conditional
|
||||
|
||||
[
|
||||
"for"
|
||||
"do"
|
||||
"done"
|
||||
"select"
|
||||
"until"
|
||||
"while"
|
||||
] @keyword.repeat
|
||||
|
||||
[
|
||||
"declare"
|
||||
"typeset"
|
||||
"readonly"
|
||||
"local"
|
||||
"unset"
|
||||
"unsetenv"
|
||||
] @keyword
|
||||
|
||||
"export" @keyword.import
|
||||
|
||||
"function" @keyword.function
|
||||
|
||||
(special_variable_name) @constant
|
||||
|
||||
; trap -l
|
||||
((word) @constant.builtin
|
||||
(#any-of? @constant.builtin
|
||||
"SIGHUP" "SIGINT" "SIGQUIT" "SIGILL" "SIGTRAP" "SIGABRT" "SIGBUS" "SIGFPE" "SIGKILL" "SIGUSR1"
|
||||
"SIGSEGV" "SIGUSR2" "SIGPIPE" "SIGALRM" "SIGTERM" "SIGSTKFLT" "SIGCHLD" "SIGCONT" "SIGSTOP"
|
||||
"SIGTSTP" "SIGTTIN" "SIGTTOU" "SIGURG" "SIGXCPU" "SIGXFSZ" "SIGVTALRM" "SIGPROF" "SIGWINCH"
|
||||
"SIGIO" "SIGPWR" "SIGSYS" "SIGRTMIN" "SIGRTMIN+1" "SIGRTMIN+2" "SIGRTMIN+3" "SIGRTMIN+4"
|
||||
"SIGRTMIN+5" "SIGRTMIN+6" "SIGRTMIN+7" "SIGRTMIN+8" "SIGRTMIN+9" "SIGRTMIN+10" "SIGRTMIN+11"
|
||||
"SIGRTMIN+12" "SIGRTMIN+13" "SIGRTMIN+14" "SIGRTMIN+15" "SIGRTMAX-14" "SIGRTMAX-13"
|
||||
"SIGRTMAX-12" "SIGRTMAX-11" "SIGRTMAX-10" "SIGRTMAX-9" "SIGRTMAX-8" "SIGRTMAX-7" "SIGRTMAX-6"
|
||||
"SIGRTMAX-5" "SIGRTMAX-4" "SIGRTMAX-3" "SIGRTMAX-2" "SIGRTMAX-1" "SIGRTMAX"))
|
||||
|
||||
((word) @boolean
|
||||
(#any-of? @boolean "true" "false"))
|
||||
|
||||
(comment) @comment @spell
|
||||
|
||||
(test_operator) @operator
|
||||
|
||||
(command_substitution
|
||||
"$(" @punctuation.special
|
||||
")" @punctuation.special)
|
||||
|
||||
(process_substitution
|
||||
[
|
||||
"<("
|
||||
">("
|
||||
] @punctuation.special
|
||||
")" @punctuation.special)
|
||||
|
||||
(arithmetic_expansion
|
||||
[
|
||||
"$(("
|
||||
"(("
|
||||
] @punctuation.special
|
||||
"))" @punctuation.special)
|
||||
|
||||
(arithmetic_expansion
|
||||
"," @punctuation.delimiter)
|
||||
|
||||
(ternary_expression
|
||||
[
|
||||
"?"
|
||||
":"
|
||||
] @keyword.conditional.ternary)
|
||||
|
||||
(binary_expression
|
||||
operator: _ @operator)
|
||||
|
||||
(unary_expression
|
||||
operator: _ @operator)
|
||||
|
||||
(postfix_expression
|
||||
operator: _ @operator)
|
||||
|
||||
(function_definition
|
||||
name: (word) @function)
|
||||
|
||||
(command_name
|
||||
(word) @function.call)
|
||||
|
||||
(command_name
|
||||
(word) @function.builtin
|
||||
(#any-of? @function.builtin
|
||||
"." ":" "alias" "bg" "bind" "break" "builtin" "caller" "cd" "command" "compgen" "complete"
|
||||
"compopt" "continue" "coproc" "dirs" "disown" "echo" "enable" "eval" "exec" "exit" "false" "fc"
|
||||
"fg" "getopts" "hash" "help" "history" "jobs" "kill" "let" "logout" "mapfile" "popd" "printf"
|
||||
"pushd" "pwd" "read" "readarray" "return" "set" "shift" "shopt" "source" "suspend" "test" "time"
|
||||
"times" "trap" "true" "type" "typeset" "ulimit" "umask" "unalias" "wait"))
|
||||
|
||||
(command
|
||||
argument: [
|
||||
(word) @variable.parameter
|
||||
(concatenation
|
||||
(word) @variable.parameter)
|
||||
])
|
||||
|
||||
(declaration_command
|
||||
(word) @variable.parameter)
|
||||
|
||||
(unset_command
|
||||
(word) @variable.parameter)
|
||||
|
||||
(number) @number
|
||||
|
||||
((word) @number
|
||||
(#lua-match? @number "^[0-9]+$"))
|
||||
|
||||
(file_redirect
|
||||
(word) @string.special.path)
|
||||
|
||||
(herestring_redirect
|
||||
(word) @string)
|
||||
|
||||
(file_descriptor) @operator
|
||||
|
||||
(simple_expansion
|
||||
"$" @punctuation.special) @none
|
||||
|
||||
(expansion
|
||||
"${" @punctuation.special
|
||||
"}" @punctuation.special) @none
|
||||
|
||||
(expansion
|
||||
operator: _ @punctuation.special)
|
||||
|
||||
(expansion
|
||||
"@"
|
||||
.
|
||||
operator: _ @character.special)
|
||||
|
||||
((expansion
|
||||
(subscript
|
||||
index: (word) @character.special))
|
||||
(#any-of? @character.special "@" "*"))
|
||||
|
||||
"``" @punctuation.special
|
||||
|
||||
(variable_name) @variable
|
||||
|
||||
((variable_name) @constant
|
||||
(#lua-match? @constant "^[A-Z][A-Z_0-9]*$"))
|
||||
|
||||
((variable_name) @variable.builtin
|
||||
(#any-of? @variable.builtin
|
||||
; https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Variables.html
|
||||
"CDPATH" "HOME" "IFS" "MAIL" "MAILPATH" "OPTARG" "OPTIND" "PATH" "PS1" "PS2"
|
||||
; https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
|
||||
"_" "BASH" "BASHOPTS" "BASHPID" "BASH_ALIASES" "BASH_ARGC" "BASH_ARGV" "BASH_ARGV0" "BASH_CMDS"
|
||||
"BASH_COMMAND" "BASH_COMPAT" "BASH_ENV" "BASH_EXECUTION_STRING" "BASH_LINENO"
|
||||
"BASH_LOADABLES_PATH" "BASH_REMATCH" "BASH_SOURCE" "BASH_SUBSHELL" "BASH_VERSINFO"
|
||||
"BASH_VERSION" "BASH_XTRACEFD" "CHILD_MAX" "COLUMNS" "COMP_CWORD" "COMP_LINE" "COMP_POINT"
|
||||
"COMP_TYPE" "COMP_KEY" "COMP_WORDBREAKS" "COMP_WORDS" "COMPREPLY" "COPROC" "DIRSTACK" "EMACS"
|
||||
"ENV" "EPOCHREALTIME" "EPOCHSECONDS" "EUID" "EXECIGNORE" "FCEDIT" "FIGNORE" "FUNCNAME"
|
||||
"FUNCNEST" "GLOBIGNORE" "GROUPS" "histchars" "HISTCMD" "HISTCONTROL" "HISTFILE" "HISTFILESIZE"
|
||||
"HISTIGNORE" "HISTSIZE" "HISTTIMEFORMAT" "HOSTFILE" "HOSTNAME" "HOSTTYPE" "IGNOREEOF" "INPUTRC"
|
||||
"INSIDE_EMACS" "LANG" "LC_ALL" "LC_COLLATE" "LC_CTYPE" "LC_MESSAGES" "LC_NUMERIC" "LC_TIME"
|
||||
"LINENO" "LINES" "MACHTYPE" "MAILCHECK" "MAPFILE" "OLDPWD" "OPTERR" "OSTYPE" "PIPESTATUS"
|
||||
"POSIXLY_CORRECT" "PPID" "PROMPT_COMMAND" "PROMPT_DIRTRIM" "PS0" "PS3" "PS4" "PWD" "RANDOM"
|
||||
"READLINE_ARGUMENT" "READLINE_LINE" "READLINE_MARK" "READLINE_POINT" "REPLY" "SECONDS" "SHELL"
|
||||
"SHELLOPTS" "SHLVL" "SRANDOM" "TIMEFORMAT" "TMOUT" "TMPDIR" "UID"))
|
||||
|
||||
(case_item
|
||||
value: (word) @variable.parameter)
|
||||
|
||||
[
|
||||
(regex)
|
||||
(extglob_pattern)
|
||||
] @string.regexp
|
||||
|
||||
((program
|
||||
.
|
||||
(comment) @keyword.directive @nospell)
|
||||
(#lua-match? @keyword.directive "^#!/"))
|
||||
341
internal/syntax/queries/c/highlights.scm
Normal file
341
internal/syntax/queries/c/highlights.scm
Normal file
@ -0,0 +1,341 @@
|
||||
; Lower priority to prefer @variable.parameter when identifier appears in parameter_declaration.
|
||||
((identifier) @variable
|
||||
(#set! priority 95))
|
||||
|
||||
(preproc_def
|
||||
(preproc_arg) @variable)
|
||||
|
||||
[
|
||||
"default"
|
||||
"goto"
|
||||
"asm"
|
||||
"__asm__"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"enum"
|
||||
"struct"
|
||||
"union"
|
||||
"typedef"
|
||||
] @keyword.type
|
||||
|
||||
[
|
||||
"sizeof"
|
||||
"offsetof"
|
||||
] @keyword.operator
|
||||
|
||||
(alignof_expression
|
||||
.
|
||||
_ @keyword.operator)
|
||||
|
||||
"return" @keyword.return
|
||||
|
||||
[
|
||||
"while"
|
||||
"for"
|
||||
"do"
|
||||
"continue"
|
||||
"break"
|
||||
] @keyword.repeat
|
||||
|
||||
[
|
||||
"if"
|
||||
"else"
|
||||
"case"
|
||||
"switch"
|
||||
] @keyword.conditional
|
||||
|
||||
[
|
||||
"#if"
|
||||
"#ifdef"
|
||||
"#ifndef"
|
||||
"#else"
|
||||
"#elif"
|
||||
"#endif"
|
||||
"#elifdef"
|
||||
"#elifndef"
|
||||
(preproc_directive)
|
||||
] @keyword.directive
|
||||
|
||||
"#define" @keyword.directive.define
|
||||
|
||||
"#include" @keyword.import
|
||||
|
||||
[
|
||||
";"
|
||||
":"
|
||||
","
|
||||
"."
|
||||
"::"
|
||||
] @punctuation.delimiter
|
||||
|
||||
"..." @punctuation.special
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
"="
|
||||
"-"
|
||||
"*"
|
||||
"/"
|
||||
"+"
|
||||
"%"
|
||||
"~"
|
||||
"|"
|
||||
"&"
|
||||
"^"
|
||||
"<<"
|
||||
">>"
|
||||
"->"
|
||||
"<"
|
||||
"<="
|
||||
">="
|
||||
">"
|
||||
"=="
|
||||
"!="
|
||||
"!"
|
||||
"&&"
|
||||
"||"
|
||||
"-="
|
||||
"+="
|
||||
"*="
|
||||
"/="
|
||||
"%="
|
||||
"|="
|
||||
"&="
|
||||
"^="
|
||||
">>="
|
||||
"<<="
|
||||
"--"
|
||||
"++"
|
||||
] @operator
|
||||
|
||||
; Make sure the comma operator is given a highlight group after the comma
|
||||
; punctuator so the operator is highlighted properly.
|
||||
(comma_expression
|
||||
"," @operator)
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
] @boolean
|
||||
|
||||
(conditional_expression
|
||||
[
|
||||
"?"
|
||||
":"
|
||||
] @keyword.conditional.ternary)
|
||||
|
||||
(string_literal) @string
|
||||
|
||||
(system_lib_string) @string
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
(null) @constant.builtin
|
||||
|
||||
(number_literal) @number
|
||||
|
||||
(char_literal) @character
|
||||
|
||||
(preproc_defined) @function.macro
|
||||
|
||||
((field_expression
|
||||
(field_identifier) @property) @_parent
|
||||
(#not-has-parent? @_parent template_method function_declarator call_expression))
|
||||
|
||||
(field_designator) @property
|
||||
|
||||
((field_identifier) @property
|
||||
(#has-ancestor? @property field_declaration)
|
||||
(#not-has-ancestor? @property function_declarator))
|
||||
|
||||
(statement_identifier) @label
|
||||
|
||||
(declaration
|
||||
type: (type_identifier) @_type
|
||||
declarator: (identifier) @label
|
||||
(#eq? @_type "__label__"))
|
||||
|
||||
[
|
||||
(type_identifier)
|
||||
(type_descriptor)
|
||||
] @type
|
||||
|
||||
(storage_class_specifier) @keyword.modifier
|
||||
|
||||
[
|
||||
(type_qualifier)
|
||||
(gnu_asm_qualifier)
|
||||
"__extension__"
|
||||
] @keyword.modifier
|
||||
|
||||
(linkage_specification
|
||||
"extern" @keyword.modifier)
|
||||
|
||||
(type_definition
|
||||
declarator: (type_identifier) @type.definition)
|
||||
|
||||
(primitive_type) @type.builtin
|
||||
|
||||
(sized_type_specifier
|
||||
_ @type.builtin
|
||||
type: _?)
|
||||
|
||||
((identifier) @constant
|
||||
(#lua-match? @constant "^[A-Z][A-Z0-9_]+$"))
|
||||
|
||||
(preproc_def
|
||||
(preproc_arg) @constant
|
||||
(#lua-match? @constant "^[A-Z][A-Z0-9_]+$"))
|
||||
|
||||
(enumerator
|
||||
name: (identifier) @constant)
|
||||
|
||||
(case_statement
|
||||
value: (identifier) @constant)
|
||||
|
||||
((identifier) @constant.builtin
|
||||
; format-ignore
|
||||
(#any-of? @constant.builtin
|
||||
"stderr" "stdin" "stdout"
|
||||
"__FILE__" "__LINE__" "__DATE__" "__TIME__"
|
||||
"__STDC__" "__STDC_VERSION__" "__STDC_HOSTED__"
|
||||
"__cplusplus" "__OBJC__" "__ASSEMBLER__"
|
||||
"__BASE_FILE__" "__FILE_NAME__" "__INCLUDE_LEVEL__"
|
||||
"__TIMESTAMP__" "__clang__" "__clang_major__"
|
||||
"__clang_minor__" "__clang_patchlevel__"
|
||||
"__clang_version__" "__clang_literal_encoding__"
|
||||
"__clang_wide_literal_encoding__"
|
||||
"__FUNCTION__" "__func__" "__PRETTY_FUNCTION__"
|
||||
"__VA_ARGS__" "__VA_OPT__"))
|
||||
|
||||
(preproc_def
|
||||
(preproc_arg) @constant.builtin
|
||||
; format-ignore
|
||||
(#any-of? @constant.builtin
|
||||
"stderr" "stdin" "stdout"
|
||||
"__FILE__" "__LINE__" "__DATE__" "__TIME__"
|
||||
"__STDC__" "__STDC_VERSION__" "__STDC_HOSTED__"
|
||||
"__cplusplus" "__OBJC__" "__ASSEMBLER__"
|
||||
"__BASE_FILE__" "__FILE_NAME__" "__INCLUDE_LEVEL__"
|
||||
"__TIMESTAMP__" "__clang__" "__clang_major__"
|
||||
"__clang_minor__" "__clang_patchlevel__"
|
||||
"__clang_version__" "__clang_literal_encoding__"
|
||||
"__clang_wide_literal_encoding__"
|
||||
"__FUNCTION__" "__func__" "__PRETTY_FUNCTION__"
|
||||
"__VA_ARGS__" "__VA_OPT__"))
|
||||
|
||||
(attribute_specifier
|
||||
(argument_list
|
||||
(identifier) @variable.builtin))
|
||||
|
||||
(attribute_specifier
|
||||
(argument_list
|
||||
(call_expression
|
||||
function: (identifier) @variable.builtin)))
|
||||
|
||||
((call_expression
|
||||
function: (identifier) @function.builtin)
|
||||
(#lua-match? @function.builtin "^__builtin_"))
|
||||
|
||||
((call_expression
|
||||
function: (identifier) @function.builtin)
|
||||
(#has-ancestor? @function.builtin attribute_specifier))
|
||||
|
||||
; Preproc def / undef
|
||||
(preproc_def
|
||||
name: (_) @constant.macro)
|
||||
|
||||
(preproc_call
|
||||
directive: (preproc_directive) @_u
|
||||
argument: (_) @constant.macro
|
||||
(#eq? @_u "#undef"))
|
||||
|
||||
(preproc_ifdef
|
||||
name: (identifier) @constant.macro)
|
||||
|
||||
(preproc_elifdef
|
||||
name: (identifier) @constant.macro)
|
||||
|
||||
(preproc_defined
|
||||
(identifier) @constant.macro)
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @function.call)
|
||||
|
||||
(call_expression
|
||||
function: (field_expression
|
||||
field: (field_identifier) @function.call))
|
||||
|
||||
(function_declarator
|
||||
declarator: (identifier) @function)
|
||||
|
||||
(function_declarator
|
||||
declarator: (parenthesized_declarator
|
||||
(pointer_declarator
|
||||
declarator: (field_identifier) @function)))
|
||||
|
||||
(preproc_function_def
|
||||
name: (identifier) @function.macro)
|
||||
|
||||
(comment) @comment @spell
|
||||
|
||||
((comment) @comment.documentation
|
||||
(#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$"))
|
||||
|
||||
; Parameters
|
||||
(parameter_declaration
|
||||
declarator: (identifier) @variable.parameter)
|
||||
|
||||
(parameter_declaration
|
||||
declarator: (array_declarator) @variable.parameter)
|
||||
|
||||
(parameter_declaration
|
||||
declarator: (pointer_declarator) @variable.parameter)
|
||||
|
||||
; K&R functions
|
||||
; To enable support for K&R functions,
|
||||
; add the following lines to your own query config and uncomment them.
|
||||
; They are commented out as they'll conflict with C++
|
||||
; Note that you'll need to have `; extends` at the top of your query file.
|
||||
;
|
||||
; (parameter_list (identifier) @variable.parameter)
|
||||
;
|
||||
; (function_definition
|
||||
; declarator: _
|
||||
; (declaration
|
||||
; declarator: (identifier) @variable.parameter))
|
||||
;
|
||||
; (function_definition
|
||||
; declarator: _
|
||||
; (declaration
|
||||
; declarator: (array_declarator) @variable.parameter))
|
||||
;
|
||||
; (function_definition
|
||||
; declarator: _
|
||||
; (declaration
|
||||
; declarator: (pointer_declarator) @variable.parameter))
|
||||
(preproc_params
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
[
|
||||
"__attribute__"
|
||||
"__declspec"
|
||||
"__based"
|
||||
"__cdecl"
|
||||
"__clrcall"
|
||||
"__stdcall"
|
||||
"__fastcall"
|
||||
"__thiscall"
|
||||
"__vectorcall"
|
||||
(ms_pointer_modifier)
|
||||
(attribute_declaration)
|
||||
] @attribute
|
||||
608
internal/syntax/queries/cpp/highlights.scm
Normal file
608
internal/syntax/queries/cpp/highlights.scm
Normal file
@ -0,0 +1,608 @@
|
||||
; Lower priority to prefer @variable.parameter when identifier appears in parameter_declaration.
|
||||
((identifier) @variable
|
||||
(#set! priority 95))
|
||||
|
||||
(preproc_def
|
||||
(preproc_arg) @variable)
|
||||
|
||||
[
|
||||
"default"
|
||||
"goto"
|
||||
"asm"
|
||||
"__asm__"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"enum"
|
||||
"struct"
|
||||
"union"
|
||||
"typedef"
|
||||
] @keyword.type
|
||||
|
||||
[
|
||||
"sizeof"
|
||||
"offsetof"
|
||||
] @keyword.operator
|
||||
|
||||
(alignof_expression
|
||||
.
|
||||
_ @keyword.operator)
|
||||
|
||||
"return" @keyword.return
|
||||
|
||||
[
|
||||
"while"
|
||||
"for"
|
||||
"do"
|
||||
"continue"
|
||||
"break"
|
||||
] @keyword.repeat
|
||||
|
||||
[
|
||||
"if"
|
||||
"else"
|
||||
"case"
|
||||
"switch"
|
||||
] @keyword.conditional
|
||||
|
||||
[
|
||||
"#if"
|
||||
"#ifdef"
|
||||
"#ifndef"
|
||||
"#else"
|
||||
"#elif"
|
||||
"#endif"
|
||||
"#elifdef"
|
||||
"#elifndef"
|
||||
(preproc_directive)
|
||||
] @keyword.directive
|
||||
|
||||
"#define" @keyword.directive.define
|
||||
|
||||
"#include" @keyword.import
|
||||
|
||||
[
|
||||
";"
|
||||
":"
|
||||
","
|
||||
"."
|
||||
"::"
|
||||
] @punctuation.delimiter
|
||||
|
||||
"..." @punctuation.special
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
"="
|
||||
"-"
|
||||
"*"
|
||||
"/"
|
||||
"+"
|
||||
"%"
|
||||
"~"
|
||||
"|"
|
||||
"&"
|
||||
"^"
|
||||
"<<"
|
||||
">>"
|
||||
"->"
|
||||
"<"
|
||||
"<="
|
||||
">="
|
||||
">"
|
||||
"=="
|
||||
"!="
|
||||
"!"
|
||||
"&&"
|
||||
"||"
|
||||
"-="
|
||||
"+="
|
||||
"*="
|
||||
"/="
|
||||
"%="
|
||||
"|="
|
||||
"&="
|
||||
"^="
|
||||
">>="
|
||||
"<<="
|
||||
"--"
|
||||
"++"
|
||||
] @operator
|
||||
|
||||
; Make sure the comma operator is given a highlight group after the comma
|
||||
; punctuator so the operator is highlighted properly.
|
||||
(comma_expression
|
||||
"," @operator)
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
] @boolean
|
||||
|
||||
(conditional_expression
|
||||
[
|
||||
"?"
|
||||
":"
|
||||
] @keyword.conditional.ternary)
|
||||
|
||||
(string_literal) @string
|
||||
|
||||
(system_lib_string) @string
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
(null) @constant.builtin
|
||||
|
||||
(number_literal) @number
|
||||
|
||||
(char_literal) @character
|
||||
|
||||
(preproc_defined) @function.macro
|
||||
|
||||
((field_expression
|
||||
(field_identifier) @property) @_parent
|
||||
(#not-has-parent? @_parent template_method function_declarator call_expression))
|
||||
|
||||
(field_designator) @property
|
||||
|
||||
((field_identifier) @property
|
||||
(#has-ancestor? @property field_declaration)
|
||||
(#not-has-ancestor? @property function_declarator))
|
||||
|
||||
(statement_identifier) @label
|
||||
|
||||
(declaration
|
||||
type: (type_identifier) @_type
|
||||
declarator: (identifier) @label
|
||||
(#eq? @_type "__label__"))
|
||||
|
||||
[
|
||||
(type_identifier)
|
||||
(type_descriptor)
|
||||
] @type
|
||||
|
||||
(storage_class_specifier) @keyword.modifier
|
||||
|
||||
[
|
||||
(type_qualifier)
|
||||
(gnu_asm_qualifier)
|
||||
"__extension__"
|
||||
] @keyword.modifier
|
||||
|
||||
(linkage_specification
|
||||
"extern" @keyword.modifier)
|
||||
|
||||
(type_definition
|
||||
declarator: (type_identifier) @type.definition)
|
||||
|
||||
(primitive_type) @type.builtin
|
||||
|
||||
(sized_type_specifier
|
||||
_ @type.builtin
|
||||
type: _?)
|
||||
|
||||
((identifier) @constant
|
||||
(#lua-match? @constant "^[A-Z][A-Z0-9_]+$"))
|
||||
|
||||
(preproc_def
|
||||
(preproc_arg) @constant
|
||||
(#lua-match? @constant "^[A-Z][A-Z0-9_]+$"))
|
||||
|
||||
(enumerator
|
||||
name: (identifier) @constant)
|
||||
|
||||
(case_statement
|
||||
value: (identifier) @constant)
|
||||
|
||||
((identifier) @constant.builtin
|
||||
; format-ignore
|
||||
(#any-of? @constant.builtin
|
||||
"stderr" "stdin" "stdout"
|
||||
"__FILE__" "__LINE__" "__DATE__" "__TIME__"
|
||||
"__STDC__" "__STDC_VERSION__" "__STDC_HOSTED__"
|
||||
"__cplusplus" "__OBJC__" "__ASSEMBLER__"
|
||||
"__BASE_FILE__" "__FILE_NAME__" "__INCLUDE_LEVEL__"
|
||||
"__TIMESTAMP__" "__clang__" "__clang_major__"
|
||||
"__clang_minor__" "__clang_patchlevel__"
|
||||
"__clang_version__" "__clang_literal_encoding__"
|
||||
"__clang_wide_literal_encoding__"
|
||||
"__FUNCTION__" "__func__" "__PRETTY_FUNCTION__"
|
||||
"__VA_ARGS__" "__VA_OPT__"))
|
||||
|
||||
(preproc_def
|
||||
(preproc_arg) @constant.builtin
|
||||
; format-ignore
|
||||
(#any-of? @constant.builtin
|
||||
"stderr" "stdin" "stdout"
|
||||
"__FILE__" "__LINE__" "__DATE__" "__TIME__"
|
||||
"__STDC__" "__STDC_VERSION__" "__STDC_HOSTED__"
|
||||
"__cplusplus" "__OBJC__" "__ASSEMBLER__"
|
||||
"__BASE_FILE__" "__FILE_NAME__" "__INCLUDE_LEVEL__"
|
||||
"__TIMESTAMP__" "__clang__" "__clang_major__"
|
||||
"__clang_minor__" "__clang_patchlevel__"
|
||||
"__clang_version__" "__clang_literal_encoding__"
|
||||
"__clang_wide_literal_encoding__"
|
||||
"__FUNCTION__" "__func__" "__PRETTY_FUNCTION__"
|
||||
"__VA_ARGS__" "__VA_OPT__"))
|
||||
|
||||
(attribute_specifier
|
||||
(argument_list
|
||||
(identifier) @variable.builtin))
|
||||
|
||||
(attribute_specifier
|
||||
(argument_list
|
||||
(call_expression
|
||||
function: (identifier) @variable.builtin)))
|
||||
|
||||
((call_expression
|
||||
function: (identifier) @function.builtin)
|
||||
(#lua-match? @function.builtin "^__builtin_"))
|
||||
|
||||
((call_expression
|
||||
function: (identifier) @function.builtin)
|
||||
(#has-ancestor? @function.builtin attribute_specifier))
|
||||
|
||||
; Preproc def / undef
|
||||
(preproc_def
|
||||
name: (_) @constant.macro)
|
||||
|
||||
(preproc_call
|
||||
directive: (preproc_directive) @_u
|
||||
argument: (_) @constant.macro
|
||||
(#eq? @_u "#undef"))
|
||||
|
||||
(preproc_ifdef
|
||||
name: (identifier) @constant.macro)
|
||||
|
||||
(preproc_elifdef
|
||||
name: (identifier) @constant.macro)
|
||||
|
||||
(preproc_defined
|
||||
(identifier) @constant.macro)
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @function.call)
|
||||
|
||||
(call_expression
|
||||
function: (field_expression
|
||||
field: (field_identifier) @function.call))
|
||||
|
||||
(function_declarator
|
||||
declarator: (identifier) @function)
|
||||
|
||||
(function_declarator
|
||||
declarator: (parenthesized_declarator
|
||||
(pointer_declarator
|
||||
declarator: (field_identifier) @function)))
|
||||
|
||||
(preproc_function_def
|
||||
name: (identifier) @function.macro)
|
||||
|
||||
(comment) @comment @spell
|
||||
|
||||
((comment) @comment.documentation
|
||||
(#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$"))
|
||||
|
||||
; Parameters
|
||||
(parameter_declaration
|
||||
declarator: (identifier) @variable.parameter)
|
||||
|
||||
(parameter_declaration
|
||||
declarator: (array_declarator) @variable.parameter)
|
||||
|
||||
(parameter_declaration
|
||||
declarator: (pointer_declarator) @variable.parameter)
|
||||
|
||||
; K&R functions
|
||||
; To enable support for K&R functions,
|
||||
; add the following lines to your own query config and uncomment them.
|
||||
; They are commented out as they'll conflict with C++
|
||||
; Note that you'll need to have `; extends` at the top of your query file.
|
||||
;
|
||||
; (parameter_list (identifier) @variable.parameter)
|
||||
;
|
||||
; (function_definition
|
||||
; declarator: _
|
||||
; (declaration
|
||||
; declarator: (identifier) @variable.parameter))
|
||||
;
|
||||
; (function_definition
|
||||
; declarator: _
|
||||
; (declaration
|
||||
; declarator: (array_declarator) @variable.parameter))
|
||||
;
|
||||
; (function_definition
|
||||
; declarator: _
|
||||
; (declaration
|
||||
; declarator: (pointer_declarator) @variable.parameter))
|
||||
(preproc_params
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
[
|
||||
"__attribute__"
|
||||
"__declspec"
|
||||
"__based"
|
||||
"__cdecl"
|
||||
"__clrcall"
|
||||
"__stdcall"
|
||||
"__fastcall"
|
||||
"__thiscall"
|
||||
"__vectorcall"
|
||||
(ms_pointer_modifier)
|
||||
(attribute_declaration)
|
||||
] @attribute
|
||||
|
||||
((identifier) @variable.member
|
||||
(#lua-match? @variable.member "^m_.*$"))
|
||||
|
||||
(parameter_declaration
|
||||
declarator: (reference_declarator) @variable.parameter)
|
||||
|
||||
; function(Foo ...foo)
|
||||
(variadic_parameter_declaration
|
||||
declarator: (variadic_declarator
|
||||
(_) @variable.parameter))
|
||||
|
||||
; int foo = 0
|
||||
(optional_parameter_declaration
|
||||
declarator: (_) @variable.parameter)
|
||||
|
||||
;(field_expression) @variable.parameter ;; How to highlight this?
|
||||
((field_expression
|
||||
(field_identifier) @function.method) @_parent
|
||||
(#has-parent? @_parent template_method function_declarator))
|
||||
|
||||
(field_declaration
|
||||
(field_identifier) @variable.member)
|
||||
|
||||
(field_initializer
|
||||
(field_identifier) @property)
|
||||
|
||||
(function_declarator
|
||||
declarator: (field_identifier) @function.method)
|
||||
|
||||
(concept_definition
|
||||
name: (identifier) @type.definition)
|
||||
|
||||
(alias_declaration
|
||||
name: (type_identifier) @type.definition)
|
||||
|
||||
(auto) @type.builtin
|
||||
|
||||
(namespace_identifier) @module
|
||||
|
||||
((namespace_identifier) @type
|
||||
(#lua-match? @type "^[%u]"))
|
||||
|
||||
(case_statement
|
||||
value: (qualified_identifier
|
||||
(identifier) @constant))
|
||||
|
||||
(using_declaration
|
||||
.
|
||||
"using"
|
||||
.
|
||||
"namespace"
|
||||
.
|
||||
[
|
||||
(qualified_identifier)
|
||||
(identifier)
|
||||
] @module)
|
||||
|
||||
(destructor_name
|
||||
(identifier) @function.method)
|
||||
|
||||
; functions
|
||||
(function_declarator
|
||||
(qualified_identifier
|
||||
(identifier) @function))
|
||||
|
||||
(function_declarator
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(identifier) @function)))
|
||||
|
||||
(function_declarator
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(identifier) @function))))
|
||||
|
||||
((qualified_identifier
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(identifier) @function)))) @_parent
|
||||
(#has-ancestor? @_parent function_declarator))
|
||||
|
||||
(function_declarator
|
||||
(template_function
|
||||
(identifier) @function))
|
||||
|
||||
(operator_name) @function
|
||||
|
||||
"operator" @function
|
||||
|
||||
"static_assert" @function.builtin
|
||||
|
||||
(call_expression
|
||||
(qualified_identifier
|
||||
(identifier) @function.call))
|
||||
|
||||
(call_expression
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(identifier) @function.call)))
|
||||
|
||||
(call_expression
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(identifier) @function.call))))
|
||||
|
||||
((qualified_identifier
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(identifier) @function.call)))) @_parent
|
||||
(#has-ancestor? @_parent call_expression))
|
||||
|
||||
(call_expression
|
||||
(template_function
|
||||
(identifier) @function.call))
|
||||
|
||||
(call_expression
|
||||
(qualified_identifier
|
||||
(template_function
|
||||
(identifier) @function.call)))
|
||||
|
||||
(call_expression
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(template_function
|
||||
(identifier) @function.call))))
|
||||
|
||||
(call_expression
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(template_function
|
||||
(identifier) @function.call)))))
|
||||
|
||||
((qualified_identifier
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(qualified_identifier
|
||||
(template_function
|
||||
(identifier) @function.call))))) @_parent
|
||||
(#has-ancestor? @_parent call_expression))
|
||||
|
||||
; methods
|
||||
(function_declarator
|
||||
(template_method
|
||||
(field_identifier) @function.method))
|
||||
|
||||
(call_expression
|
||||
(field_expression
|
||||
(field_identifier) @function.method.call))
|
||||
|
||||
; constructors
|
||||
((function_declarator
|
||||
(qualified_identifier
|
||||
(identifier) @constructor))
|
||||
(#lua-match? @constructor "^%u"))
|
||||
|
||||
((call_expression
|
||||
function: (identifier) @constructor)
|
||||
(#lua-match? @constructor "^%u"))
|
||||
|
||||
((call_expression
|
||||
function: (qualified_identifier
|
||||
name: (identifier) @constructor))
|
||||
(#lua-match? @constructor "^%u"))
|
||||
|
||||
((call_expression
|
||||
function: (field_expression
|
||||
field: (field_identifier) @constructor))
|
||||
(#lua-match? @constructor "^%u"))
|
||||
|
||||
; constructing a type in an initializer list: Constructor (): **SuperType (1)**
|
||||
((field_initializer
|
||||
(field_identifier) @constructor
|
||||
(argument_list))
|
||||
(#lua-match? @constructor "^%u"))
|
||||
|
||||
; Constants
|
||||
(this) @variable.builtin
|
||||
|
||||
(null
|
||||
"nullptr" @constant.builtin)
|
||||
|
||||
(true) @boolean
|
||||
|
||||
(false) @boolean
|
||||
|
||||
; Literals
|
||||
(raw_string_literal) @string
|
||||
|
||||
; Keywords
|
||||
[
|
||||
"try"
|
||||
"catch"
|
||||
"noexcept"
|
||||
"throw"
|
||||
] @keyword.exception
|
||||
|
||||
[
|
||||
"decltype"
|
||||
"explicit"
|
||||
"friend"
|
||||
"override"
|
||||
"using"
|
||||
"requires"
|
||||
"constexpr"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"class"
|
||||
"namespace"
|
||||
"template"
|
||||
"typename"
|
||||
"concept"
|
||||
] @keyword.type
|
||||
|
||||
[
|
||||
"co_await"
|
||||
"co_yield"
|
||||
"co_return"
|
||||
] @keyword.coroutine
|
||||
|
||||
[
|
||||
"public"
|
||||
"private"
|
||||
"protected"
|
||||
"final"
|
||||
"virtual"
|
||||
] @keyword.modifier
|
||||
|
||||
[
|
||||
"new"
|
||||
"delete"
|
||||
"xor"
|
||||
"bitand"
|
||||
"bitor"
|
||||
"compl"
|
||||
"not"
|
||||
"xor_eq"
|
||||
"and_eq"
|
||||
"or_eq"
|
||||
"not_eq"
|
||||
"and"
|
||||
"or"
|
||||
] @keyword.operator
|
||||
|
||||
"<=>" @operator
|
||||
|
||||
"::" @punctuation.delimiter
|
||||
|
||||
(template_argument_list
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(template_parameter_list
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(literal_suffix) @operator
|
||||
577
internal/syntax/queries/csharp/highlights.scm
Normal file
577
internal/syntax/queries/csharp/highlights.scm
Normal file
@ -0,0 +1,577 @@
|
||||
[
|
||||
(identifier)
|
||||
(preproc_arg)
|
||||
] @variable
|
||||
|
||||
((preproc_arg) @constant.macro
|
||||
(#lua-match? @constant.macro "^[_A-Z][_A-Z0-9]*$"))
|
||||
|
||||
((identifier) @keyword
|
||||
(#eq? @keyword "value")
|
||||
(#has-ancestor? @keyword accessor_declaration))
|
||||
|
||||
(method_declaration
|
||||
name: (identifier) @function.method)
|
||||
|
||||
(local_function_statement
|
||||
name: (identifier) @function.method)
|
||||
|
||||
(method_declaration
|
||||
returns: [
|
||||
(identifier) @type
|
||||
(generic_name
|
||||
(identifier) @type)
|
||||
])
|
||||
|
||||
(event_declaration
|
||||
type: (identifier) @type)
|
||||
|
||||
(event_declaration
|
||||
name: (identifier) @variable.member)
|
||||
|
||||
(event_field_declaration
|
||||
(variable_declaration
|
||||
(variable_declarator
|
||||
name: (identifier) @variable.member)))
|
||||
|
||||
(declaration_pattern
|
||||
type: (identifier) @type)
|
||||
|
||||
(local_function_statement
|
||||
type: (identifier) @type)
|
||||
|
||||
(interpolation) @none
|
||||
|
||||
(member_access_expression
|
||||
name: (identifier) @variable.member)
|
||||
|
||||
(invocation_expression
|
||||
(member_access_expression
|
||||
name: (identifier) @function.method.call))
|
||||
|
||||
(invocation_expression
|
||||
function: (conditional_access_expression
|
||||
(member_binding_expression
|
||||
name: (identifier) @function.method.call)))
|
||||
|
||||
(namespace_declaration
|
||||
name: [
|
||||
(qualified_name)
|
||||
(identifier)
|
||||
] @module)
|
||||
|
||||
(qualified_name
|
||||
(identifier) @type)
|
||||
|
||||
(namespace_declaration
|
||||
name: (identifier) @module)
|
||||
|
||||
(file_scoped_namespace_declaration
|
||||
name: (identifier) @module)
|
||||
|
||||
(qualified_name
|
||||
(identifier) @module
|
||||
(#not-has-ancestor? @module method_declaration)
|
||||
(#not-has-ancestor? @module record_declaration)
|
||||
(#has-ancestor? @module namespace_declaration file_scoped_namespace_declaration))
|
||||
|
||||
(invocation_expression
|
||||
(identifier) @function.method.call)
|
||||
|
||||
(field_declaration
|
||||
(variable_declaration
|
||||
(variable_declarator
|
||||
(identifier) @variable.member)))
|
||||
|
||||
(initializer_expression
|
||||
(assignment_expression
|
||||
left: (identifier) @variable.member))
|
||||
|
||||
(parameter
|
||||
name: (identifier) @variable.parameter)
|
||||
|
||||
(parameter_list
|
||||
name: (identifier) @variable.parameter)
|
||||
|
||||
(bracketed_parameter_list
|
||||
name: (identifier) @variable.parameter)
|
||||
|
||||
(implicit_parameter) @variable.parameter
|
||||
|
||||
(parameter_list
|
||||
(parameter
|
||||
type: (identifier) @type))
|
||||
|
||||
(integer_literal) @number
|
||||
|
||||
(real_literal) @number.float
|
||||
|
||||
(null_literal) @constant.builtin
|
||||
|
||||
(calling_convention
|
||||
[
|
||||
(identifier)
|
||||
"Cdecl"
|
||||
"Stdcall"
|
||||
"Thiscall"
|
||||
"Fastcall"
|
||||
] @attribute.builtin)
|
||||
|
||||
(character_literal) @character
|
||||
|
||||
[
|
||||
(string_literal)
|
||||
(raw_string_literal)
|
||||
(verbatim_string_literal)
|
||||
(interpolated_string_expression)
|
||||
] @string
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
[
|
||||
"true"
|
||||
"false"
|
||||
] @boolean
|
||||
|
||||
(predefined_type) @type.builtin
|
||||
|
||||
(implicit_type) @keyword
|
||||
|
||||
(comment) @comment @spell
|
||||
|
||||
((comment) @comment.documentation
|
||||
(#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$"))
|
||||
|
||||
((comment) @comment.documentation
|
||||
(#lua-match? @comment.documentation "^///[^/]"))
|
||||
|
||||
((comment) @comment.documentation
|
||||
(#lua-match? @comment.documentation "^///$"))
|
||||
|
||||
(using_directive
|
||||
(identifier) @type)
|
||||
|
||||
(using_directive
|
||||
(type) @type.definition)
|
||||
|
||||
(property_declaration
|
||||
name: (identifier) @property)
|
||||
|
||||
(property_declaration
|
||||
type: (identifier) @type)
|
||||
|
||||
(nullable_type
|
||||
type: (identifier) @type)
|
||||
|
||||
(array_type
|
||||
type: (identifier) @type)
|
||||
|
||||
(ref_type
|
||||
type: (identifier) @type)
|
||||
|
||||
(pointer_type
|
||||
type: (identifier) @type)
|
||||
|
||||
(catch_declaration
|
||||
type: (identifier) @type)
|
||||
|
||||
(interface_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(class_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(record_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(struct_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(enum_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(enum_member_declaration
|
||||
name: (identifier) @variable.member)
|
||||
|
||||
(operator_declaration
|
||||
type: (identifier) @type)
|
||||
|
||||
(conversion_operator_declaration
|
||||
type: (identifier) @type)
|
||||
|
||||
(explicit_interface_specifier
|
||||
[
|
||||
(identifier) @type
|
||||
(generic_name
|
||||
(identifier) @type)
|
||||
])
|
||||
|
||||
(explicit_interface_specifier
|
||||
(identifier) @type)
|
||||
|
||||
(primary_constructor_base_type
|
||||
type: (identifier) @type)
|
||||
|
||||
[
|
||||
"assembly"
|
||||
"module"
|
||||
"this"
|
||||
"base"
|
||||
] @variable.builtin
|
||||
|
||||
(constructor_declaration
|
||||
name: (identifier) @constructor)
|
||||
|
||||
(destructor_declaration
|
||||
name: (identifier) @constructor)
|
||||
|
||||
(constructor_initializer
|
||||
"base" @constructor)
|
||||
|
||||
(variable_declaration
|
||||
(identifier) @type)
|
||||
|
||||
(object_creation_expression
|
||||
(identifier) @type)
|
||||
|
||||
; Generic Types.
|
||||
(typeof_expression
|
||||
(generic_name
|
||||
(identifier) @type))
|
||||
|
||||
(type_argument_list
|
||||
(generic_name
|
||||
(identifier) @type))
|
||||
|
||||
(base_list
|
||||
(generic_name
|
||||
(identifier) @type))
|
||||
|
||||
(type_parameter_constraint
|
||||
[
|
||||
(identifier) @type
|
||||
(type
|
||||
(generic_name
|
||||
(identifier) @type))
|
||||
])
|
||||
|
||||
(object_creation_expression
|
||||
(generic_name
|
||||
(identifier) @type))
|
||||
|
||||
(property_declaration
|
||||
(generic_name
|
||||
(identifier) @type))
|
||||
|
||||
(_
|
||||
type: (generic_name
|
||||
(identifier) @type))
|
||||
|
||||
; Generic Method invocation with generic type
|
||||
(invocation_expression
|
||||
function: (generic_name
|
||||
.
|
||||
(identifier) @function.method.call))
|
||||
|
||||
(invocation_expression
|
||||
(member_access_expression
|
||||
(generic_name
|
||||
(identifier) @function.method)))
|
||||
|
||||
(base_list
|
||||
(identifier) @type)
|
||||
|
||||
(type_argument_list
|
||||
(identifier) @type)
|
||||
|
||||
(type_parameter_list
|
||||
(type_parameter) @type)
|
||||
|
||||
(type_parameter
|
||||
name: (identifier) @type)
|
||||
|
||||
(type_parameter_constraints_clause
|
||||
"where"
|
||||
.
|
||||
(identifier) @type)
|
||||
|
||||
(attribute
|
||||
name: (identifier) @attribute)
|
||||
|
||||
(foreach_statement
|
||||
type: (identifier) @type)
|
||||
|
||||
(goto_statement
|
||||
(identifier) @label)
|
||||
|
||||
(labeled_statement
|
||||
(identifier) @label)
|
||||
|
||||
(tuple_element
|
||||
type: (identifier) @type)
|
||||
|
||||
(tuple_expression
|
||||
(argument
|
||||
(declaration_expression
|
||||
type: (identifier) @type)))
|
||||
|
||||
(cast_expression
|
||||
type: (identifier) @type)
|
||||
|
||||
(lambda_expression
|
||||
type: (identifier) @type)
|
||||
|
||||
(as_expression
|
||||
right: (identifier) @type)
|
||||
|
||||
(typeof_expression
|
||||
(identifier) @type)
|
||||
|
||||
(preproc_error) @keyword.exception
|
||||
|
||||
[
|
||||
"#define"
|
||||
"#undef"
|
||||
] @keyword.directive.define
|
||||
|
||||
[
|
||||
"#if"
|
||||
"#elif"
|
||||
"#else"
|
||||
"#endif"
|
||||
"#region"
|
||||
"#endregion"
|
||||
"#line"
|
||||
"#pragma"
|
||||
"#nullable"
|
||||
"#error"
|
||||
(shebang_directive)
|
||||
] @keyword.directive
|
||||
|
||||
[
|
||||
(preproc_line)
|
||||
(preproc_pragma)
|
||||
(preproc_nullable)
|
||||
] @constant.macro
|
||||
|
||||
(preproc_pragma
|
||||
(identifier) @constant)
|
||||
|
||||
(preproc_if
|
||||
(identifier) @constant)
|
||||
|
||||
[
|
||||
"if"
|
||||
"else"
|
||||
"switch"
|
||||
"break"
|
||||
"case"
|
||||
"when"
|
||||
] @keyword.conditional
|
||||
|
||||
[
|
||||
"while"
|
||||
"for"
|
||||
"do"
|
||||
"continue"
|
||||
"goto"
|
||||
"foreach"
|
||||
] @keyword.repeat
|
||||
|
||||
[
|
||||
"try"
|
||||
"catch"
|
||||
"throw"
|
||||
"finally"
|
||||
] @keyword.exception
|
||||
|
||||
[
|
||||
"+"
|
||||
"?"
|
||||
":"
|
||||
"++"
|
||||
"-"
|
||||
"--"
|
||||
"&"
|
||||
"&&"
|
||||
"|"
|
||||
"||"
|
||||
"!"
|
||||
"!="
|
||||
"=="
|
||||
"*"
|
||||
"/"
|
||||
"%"
|
||||
"<"
|
||||
"<="
|
||||
">"
|
||||
">="
|
||||
"="
|
||||
"-="
|
||||
"+="
|
||||
"*="
|
||||
"/="
|
||||
"%="
|
||||
"^"
|
||||
"^="
|
||||
"&="
|
||||
"|="
|
||||
"~"
|
||||
">>"
|
||||
">>>"
|
||||
"<<"
|
||||
"<<="
|
||||
">>="
|
||||
">>>="
|
||||
"=>"
|
||||
"??"
|
||||
"??="
|
||||
".."
|
||||
] @operator
|
||||
|
||||
(list_pattern
|
||||
".." @character.special)
|
||||
|
||||
(discard) @character.special
|
||||
|
||||
[
|
||||
";"
|
||||
"."
|
||||
","
|
||||
":"
|
||||
] @punctuation.delimiter
|
||||
|
||||
(conditional_expression
|
||||
[
|
||||
"?"
|
||||
":"
|
||||
] @keyword.conditional.ternary)
|
||||
|
||||
[
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
"("
|
||||
")"
|
||||
] @punctuation.bracket
|
||||
|
||||
(interpolation_brace) @punctuation.special
|
||||
|
||||
(type_argument_list
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
[
|
||||
"using"
|
||||
"as"
|
||||
] @keyword.import
|
||||
|
||||
(alias_qualified_name
|
||||
(identifier
|
||||
"global") @keyword.import)
|
||||
|
||||
[
|
||||
"with"
|
||||
"new"
|
||||
"typeof"
|
||||
"sizeof"
|
||||
"is"
|
||||
"and"
|
||||
"or"
|
||||
"not"
|
||||
"stackalloc"
|
||||
"__makeref"
|
||||
"__reftype"
|
||||
"__refvalue"
|
||||
"in"
|
||||
"out"
|
||||
"ref"
|
||||
] @keyword.operator
|
||||
|
||||
[
|
||||
"lock"
|
||||
"params"
|
||||
"operator"
|
||||
"default"
|
||||
"implicit"
|
||||
"explicit"
|
||||
"override"
|
||||
"get"
|
||||
"set"
|
||||
"init"
|
||||
"where"
|
||||
"add"
|
||||
"remove"
|
||||
"checked"
|
||||
"unchecked"
|
||||
"fixed"
|
||||
"alias"
|
||||
"file"
|
||||
"unsafe"
|
||||
] @keyword
|
||||
|
||||
(attribute_target_specifier
|
||||
.
|
||||
_ @keyword)
|
||||
|
||||
[
|
||||
"enum"
|
||||
"record"
|
||||
"class"
|
||||
"struct"
|
||||
"interface"
|
||||
"namespace"
|
||||
"event"
|
||||
"delegate"
|
||||
] @keyword.type
|
||||
|
||||
[
|
||||
"async"
|
||||
"await"
|
||||
] @keyword.coroutine
|
||||
|
||||
[
|
||||
"const"
|
||||
"extern"
|
||||
"readonly"
|
||||
"static"
|
||||
"volatile"
|
||||
"required"
|
||||
"managed"
|
||||
"unmanaged"
|
||||
"notnull"
|
||||
"abstract"
|
||||
"private"
|
||||
"protected"
|
||||
"internal"
|
||||
"public"
|
||||
"partial"
|
||||
"sealed"
|
||||
"virtual"
|
||||
"global"
|
||||
] @keyword.modifier
|
||||
|
||||
(scoped_type
|
||||
"scoped" @keyword.modifier)
|
||||
|
||||
(query_expression
|
||||
(_
|
||||
[
|
||||
"from"
|
||||
"orderby"
|
||||
"select"
|
||||
"group"
|
||||
"by"
|
||||
"ascending"
|
||||
"descending"
|
||||
"equals"
|
||||
"let"
|
||||
] @keyword))
|
||||
|
||||
[
|
||||
"return"
|
||||
"yield"
|
||||
] @keyword.return
|
||||
109
internal/syntax/queries/css/highlights.scm
Normal file
109
internal/syntax/queries/css/highlights.scm
Normal file
@ -0,0 +1,109 @@
|
||||
[
|
||||
"@media"
|
||||
"@charset"
|
||||
"@namespace"
|
||||
"@supports"
|
||||
"@keyframes"
|
||||
(at_keyword)
|
||||
] @keyword.directive
|
||||
|
||||
"@import" @keyword.import
|
||||
|
||||
[
|
||||
(to)
|
||||
(from)
|
||||
] @keyword
|
||||
|
||||
(comment) @comment @spell
|
||||
|
||||
(tag_name) @tag
|
||||
|
||||
(class_name) @type
|
||||
|
||||
(id_name) @constant
|
||||
|
||||
[
|
||||
(property_name)
|
||||
(feature_name)
|
||||
] @property
|
||||
|
||||
[
|
||||
(nesting_selector)
|
||||
(universal_selector)
|
||||
] @character.special
|
||||
|
||||
(function_name) @function
|
||||
|
||||
[
|
||||
"~"
|
||||
">"
|
||||
"+"
|
||||
"-"
|
||||
"*"
|
||||
"/"
|
||||
"="
|
||||
"^="
|
||||
"|="
|
||||
"~="
|
||||
"$="
|
||||
"*="
|
||||
] @operator
|
||||
|
||||
[
|
||||
"and"
|
||||
"or"
|
||||
"not"
|
||||
"only"
|
||||
] @keyword.operator
|
||||
|
||||
(important) @keyword.modifier
|
||||
|
||||
(attribute_selector
|
||||
(plain_value) @string)
|
||||
|
||||
(pseudo_element_selector
|
||||
"::"
|
||||
(tag_name) @attribute)
|
||||
|
||||
(pseudo_class_selector
|
||||
(class_name) @attribute)
|
||||
|
||||
(attribute_name) @tag.attribute
|
||||
|
||||
(namespace_name) @module
|
||||
|
||||
(keyframes_name) @variable
|
||||
|
||||
((property_name) @variable
|
||||
(#lua-match? @variable "^[-][-]"))
|
||||
|
||||
((plain_value) @variable
|
||||
(#lua-match? @variable "^[-][-]"))
|
||||
|
||||
[
|
||||
(string_value)
|
||||
(color_value)
|
||||
(unit)
|
||||
] @string
|
||||
|
||||
(integer_value) @number
|
||||
|
||||
(float_value) @number.float
|
||||
|
||||
[
|
||||
"#"
|
||||
","
|
||||
"."
|
||||
":"
|
||||
"::"
|
||||
";"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"{"
|
||||
")"
|
||||
"("
|
||||
"}"
|
||||
"["
|
||||
"]"
|
||||
] @punctuation.bracket
|
||||
254
internal/syntax/queries/go/highlights.scm
Normal file
254
internal/syntax/queries/go/highlights.scm
Normal file
@ -0,0 +1,254 @@
|
||||
; Forked from tree-sitter-go
|
||||
; Copyright (c) 2014 Max Brunsfeld (The MIT License)
|
||||
;
|
||||
; Identifiers
|
||||
(type_identifier) @type
|
||||
|
||||
(type_spec
|
||||
name: (type_identifier) @type.definition)
|
||||
|
||||
(field_identifier) @property
|
||||
|
||||
(identifier) @variable
|
||||
|
||||
(package_identifier) @module
|
||||
|
||||
(parameter_declaration
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(variadic_parameter_declaration
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(label_name) @label
|
||||
|
||||
(const_spec
|
||||
name: (identifier) @constant)
|
||||
|
||||
; Function calls
|
||||
(call_expression
|
||||
function: (identifier) @function.call)
|
||||
|
||||
(call_expression
|
||||
function: (selector_expression
|
||||
field: (field_identifier) @function.method.call))
|
||||
|
||||
; Function definitions
|
||||
(function_declaration
|
||||
name: (identifier) @function)
|
||||
|
||||
(method_declaration
|
||||
name: (field_identifier) @function.method)
|
||||
|
||||
(method_elem
|
||||
name: (field_identifier) @function.method)
|
||||
|
||||
; Constructors
|
||||
((call_expression
|
||||
(identifier) @constructor)
|
||||
(#lua-match? @constructor "^[nN]ew.+$"))
|
||||
|
||||
((call_expression
|
||||
(identifier) @constructor)
|
||||
(#lua-match? @constructor "^[mM]ake.+$"))
|
||||
|
||||
; Operators
|
||||
[
|
||||
"--"
|
||||
"-"
|
||||
"-="
|
||||
":="
|
||||
"!"
|
||||
"!="
|
||||
"..."
|
||||
"*"
|
||||
"*"
|
||||
"*="
|
||||
"/"
|
||||
"/="
|
||||
"&"
|
||||
"&&"
|
||||
"&="
|
||||
"&^"
|
||||
"&^="
|
||||
"%"
|
||||
"%="
|
||||
"^"
|
||||
"^="
|
||||
"+"
|
||||
"++"
|
||||
"+="
|
||||
"<-"
|
||||
"<"
|
||||
"<<"
|
||||
"<<="
|
||||
"<="
|
||||
"="
|
||||
"=="
|
||||
">"
|
||||
">="
|
||||
">>"
|
||||
">>="
|
||||
"|"
|
||||
"|="
|
||||
"||"
|
||||
"~"
|
||||
] @operator
|
||||
|
||||
; Keywords
|
||||
[
|
||||
"break"
|
||||
"const"
|
||||
"continue"
|
||||
"default"
|
||||
"defer"
|
||||
"goto"
|
||||
"range"
|
||||
"select"
|
||||
"var"
|
||||
"fallthrough"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"type"
|
||||
"struct"
|
||||
"interface"
|
||||
] @keyword.type
|
||||
|
||||
"func" @keyword.function
|
||||
|
||||
"return" @keyword.return
|
||||
|
||||
"go" @keyword.coroutine
|
||||
|
||||
"for" @keyword.repeat
|
||||
|
||||
[
|
||||
"import"
|
||||
"package"
|
||||
] @keyword.import
|
||||
|
||||
[
|
||||
"else"
|
||||
"case"
|
||||
"switch"
|
||||
"if"
|
||||
] @keyword.conditional
|
||||
|
||||
; Builtin types
|
||||
[
|
||||
"chan"
|
||||
"map"
|
||||
] @type.builtin
|
||||
|
||||
((type_identifier) @type.builtin
|
||||
(#any-of? @type.builtin
|
||||
"any" "bool" "byte" "comparable" "complex128" "complex64" "error" "float32" "float64" "int"
|
||||
"int16" "int32" "int64" "int8" "rune" "string" "uint" "uint16" "uint32" "uint64" "uint8"
|
||||
"uintptr"))
|
||||
|
||||
; Builtin functions
|
||||
((identifier) @function.builtin
|
||||
(#any-of? @function.builtin
|
||||
"append" "cap" "clear" "close" "complex" "copy" "delete" "imag" "len" "make" "max" "min" "new"
|
||||
"panic" "print" "println" "real" "recover"))
|
||||
|
||||
; Delimiters
|
||||
"." @punctuation.delimiter
|
||||
|
||||
"," @punctuation.delimiter
|
||||
|
||||
":" @punctuation.delimiter
|
||||
|
||||
";" @punctuation.delimiter
|
||||
|
||||
"(" @punctuation.bracket
|
||||
|
||||
")" @punctuation.bracket
|
||||
|
||||
"{" @punctuation.bracket
|
||||
|
||||
"}" @punctuation.bracket
|
||||
|
||||
"[" @punctuation.bracket
|
||||
|
||||
"]" @punctuation.bracket
|
||||
|
||||
; Literals
|
||||
(interpreted_string_literal) @string
|
||||
|
||||
(raw_string_literal) @string
|
||||
|
||||
(rune_literal) @string
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
(int_literal) @number
|
||||
|
||||
(float_literal) @number.float
|
||||
|
||||
(imaginary_literal) @number
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
] @boolean
|
||||
|
||||
[
|
||||
(nil)
|
||||
(iota)
|
||||
] @constant.builtin
|
||||
|
||||
(keyed_element
|
||||
.
|
||||
(literal_element
|
||||
(identifier) @variable.member))
|
||||
|
||||
(field_declaration
|
||||
name: (field_identifier) @variable.member)
|
||||
|
||||
; Comments
|
||||
(comment) @comment @spell
|
||||
|
||||
; Doc Comments
|
||||
(source_file
|
||||
.
|
||||
(comment)+ @comment.documentation)
|
||||
|
||||
(source_file
|
||||
(comment)+ @comment.documentation
|
||||
.
|
||||
(const_declaration))
|
||||
|
||||
(source_file
|
||||
(comment)+ @comment.documentation
|
||||
.
|
||||
(function_declaration))
|
||||
|
||||
(source_file
|
||||
(comment)+ @comment.documentation
|
||||
.
|
||||
(type_declaration))
|
||||
|
||||
(source_file
|
||||
(comment)+ @comment.documentation
|
||||
.
|
||||
(var_declaration))
|
||||
|
||||
; Spell
|
||||
((interpreted_string_literal) @spell
|
||||
(#not-has-parent? @spell import_spec))
|
||||
|
||||
; Regex
|
||||
(call_expression
|
||||
(selector_expression) @_function
|
||||
(#any-of? @_function
|
||||
"regexp.Match" "regexp.MatchReader" "regexp.MatchString" "regexp.Compile" "regexp.CompilePOSIX"
|
||||
"regexp.MustCompile" "regexp.MustCompilePOSIX")
|
||||
(argument_list
|
||||
.
|
||||
[
|
||||
(raw_string_literal
|
||||
(raw_string_literal_content) @string.regexp)
|
||||
(interpreted_string_literal
|
||||
(interpreted_string_literal_content) @string.regexp)
|
||||
]))
|
||||
105
internal/syntax/queries/html/highlights.scm
Normal file
105
internal/syntax/queries/html/highlights.scm
Normal file
@ -0,0 +1,105 @@
|
||||
(tag_name) @tag
|
||||
|
||||
; (erroneous_end_tag_name) @error ; we do not lint syntax errors
|
||||
(comment) @comment @spell
|
||||
|
||||
(attribute_name) @tag.attribute
|
||||
|
||||
((attribute
|
||||
(quoted_attribute_value) @string)
|
||||
(#set! priority 99))
|
||||
|
||||
(text) @none @spell
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.heading)
|
||||
(#eq? @_tag "title"))
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.heading.1)
|
||||
(#eq? @_tag "h1"))
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.heading.2)
|
||||
(#eq? @_tag "h2"))
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.heading.3)
|
||||
(#eq? @_tag "h3"))
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.heading.4)
|
||||
(#eq? @_tag "h4"))
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.heading.5)
|
||||
(#eq? @_tag "h5"))
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.heading.6)
|
||||
(#eq? @_tag "h6"))
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.strong)
|
||||
(#any-of? @_tag "strong" "b"))
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.italic)
|
||||
(#any-of? @_tag "em" "i"))
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.strikethrough)
|
||||
(#any-of? @_tag "s" "del"))
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.underline)
|
||||
(#eq? @_tag "u"))
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.raw)
|
||||
(#any-of? @_tag "code" "kbd"))
|
||||
|
||||
((element
|
||||
(start_tag
|
||||
(tag_name) @_tag)
|
||||
(text) @markup.link.label)
|
||||
(#eq? @_tag "a"))
|
||||
|
||||
((attribute
|
||||
(attribute_name) @_attr
|
||||
(quoted_attribute_value
|
||||
(attribute_value) @string.special.url))
|
||||
(#any-of? @_attr "href" "src"))
|
||||
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
"</"
|
||||
"/>"
|
||||
] @tag.delimiter
|
||||
|
||||
"=" @operator
|
||||
330
internal/syntax/queries/java/highlights.scm
Normal file
330
internal/syntax/queries/java/highlights.scm
Normal file
@ -0,0 +1,330 @@
|
||||
; CREDITS @maxbrunsfeld (maxbrunsfeld@gmail.com)
|
||||
; Variables
|
||||
(identifier) @variable
|
||||
|
||||
(underscore_pattern) @character.special
|
||||
|
||||
; Methods
|
||||
(method_declaration
|
||||
name: (identifier) @function.method)
|
||||
|
||||
(method_invocation
|
||||
name: (identifier) @function.method.call)
|
||||
|
||||
(super) @function.builtin
|
||||
|
||||
; Parameters
|
||||
(formal_parameter
|
||||
name: (identifier) @variable.parameter)
|
||||
|
||||
(spread_parameter
|
||||
(variable_declarator
|
||||
name: (identifier) @variable.parameter)) ; int... foo
|
||||
|
||||
; Lambda parameter
|
||||
(inferred_parameters
|
||||
(identifier) @variable.parameter) ; (x,y) -> ...
|
||||
|
||||
(lambda_expression
|
||||
parameters: (identifier) @variable.parameter) ; x -> ...
|
||||
|
||||
; Operators
|
||||
[
|
||||
"+"
|
||||
":"
|
||||
"++"
|
||||
"-"
|
||||
"--"
|
||||
"&"
|
||||
"&&"
|
||||
"|"
|
||||
"||"
|
||||
"!"
|
||||
"!="
|
||||
"=="
|
||||
"*"
|
||||
"/"
|
||||
"%"
|
||||
"<"
|
||||
"<="
|
||||
">"
|
||||
">="
|
||||
"="
|
||||
"-="
|
||||
"+="
|
||||
"*="
|
||||
"/="
|
||||
"%="
|
||||
"->"
|
||||
"^"
|
||||
"^="
|
||||
"&="
|
||||
"|="
|
||||
"~"
|
||||
">>"
|
||||
">>>"
|
||||
"<<"
|
||||
"::"
|
||||
] @operator
|
||||
|
||||
; Types
|
||||
(interface_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(annotation_type_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(class_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(record_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(enum_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(constructor_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(compact_constructor_declaration
|
||||
name: (identifier) @type)
|
||||
|
||||
(type_identifier) @type
|
||||
|
||||
((type_identifier) @type.builtin
|
||||
(#eq? @type.builtin "var"))
|
||||
|
||||
((method_invocation
|
||||
object: (identifier) @type)
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
((method_reference
|
||||
.
|
||||
(identifier) @type)
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
((field_access
|
||||
object: (identifier) @type)
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
(scoped_identifier
|
||||
(identifier) @type
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
; Fields
|
||||
(field_declaration
|
||||
declarator: (variable_declarator
|
||||
name: (identifier) @variable.member))
|
||||
|
||||
(field_access
|
||||
field: (identifier) @variable.member)
|
||||
|
||||
[
|
||||
(boolean_type)
|
||||
(integral_type)
|
||||
(floating_point_type)
|
||||
(void_type)
|
||||
] @type.builtin
|
||||
|
||||
; Variables
|
||||
((identifier) @constant
|
||||
(#lua-match? @constant "^[A-Z_][A-Z%d_]+$"))
|
||||
|
||||
(this) @variable.builtin
|
||||
|
||||
; Annotations
|
||||
(annotation
|
||||
"@" @attribute
|
||||
name: (identifier) @attribute)
|
||||
|
||||
(marker_annotation
|
||||
"@" @attribute
|
||||
name: (identifier) @attribute)
|
||||
|
||||
; Literals
|
||||
(string_literal) @string
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
(character_literal) @character
|
||||
|
||||
[
|
||||
(hex_integer_literal)
|
||||
(decimal_integer_literal)
|
||||
(octal_integer_literal)
|
||||
(binary_integer_literal)
|
||||
] @number
|
||||
|
||||
[
|
||||
(decimal_floating_point_literal)
|
||||
(hex_floating_point_literal)
|
||||
] @number.float
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
] @boolean
|
||||
|
||||
(null_literal) @constant.builtin
|
||||
|
||||
; Keywords
|
||||
[
|
||||
"assert"
|
||||
"default"
|
||||
"extends"
|
||||
"implements"
|
||||
"instanceof"
|
||||
"@interface"
|
||||
"permits"
|
||||
"to"
|
||||
"with"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"record"
|
||||
"class"
|
||||
"enum"
|
||||
"interface"
|
||||
] @keyword.type
|
||||
|
||||
(synchronized_statement
|
||||
"synchronized" @keyword)
|
||||
|
||||
[
|
||||
"abstract"
|
||||
"final"
|
||||
"native"
|
||||
"non-sealed"
|
||||
"open"
|
||||
"private"
|
||||
"protected"
|
||||
"public"
|
||||
"sealed"
|
||||
"static"
|
||||
"strictfp"
|
||||
"transitive"
|
||||
] @keyword.modifier
|
||||
|
||||
(modifiers
|
||||
"synchronized" @keyword.modifier)
|
||||
|
||||
[
|
||||
"transient"
|
||||
"volatile"
|
||||
] @keyword.modifier
|
||||
|
||||
[
|
||||
"return"
|
||||
"yield"
|
||||
] @keyword.return
|
||||
|
||||
"new" @keyword.operator
|
||||
|
||||
; Conditionals
|
||||
[
|
||||
"if"
|
||||
"else"
|
||||
"switch"
|
||||
"case"
|
||||
"when"
|
||||
] @keyword.conditional
|
||||
|
||||
(ternary_expression
|
||||
[
|
||||
"?"
|
||||
":"
|
||||
] @keyword.conditional.ternary)
|
||||
|
||||
; Loops
|
||||
[
|
||||
"for"
|
||||
"while"
|
||||
"do"
|
||||
"continue"
|
||||
"break"
|
||||
] @keyword.repeat
|
||||
|
||||
; Includes
|
||||
[
|
||||
"exports"
|
||||
"import"
|
||||
"module"
|
||||
"opens"
|
||||
"package"
|
||||
"provides"
|
||||
"requires"
|
||||
"uses"
|
||||
] @keyword.import
|
||||
|
||||
(import_declaration
|
||||
(asterisk
|
||||
"*" @character.special))
|
||||
|
||||
; Punctuation
|
||||
[
|
||||
";"
|
||||
"."
|
||||
"..."
|
||||
","
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
"["
|
||||
"]"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
] @punctuation.bracket
|
||||
|
||||
(type_arguments
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(type_parameters
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(string_interpolation
|
||||
[
|
||||
"\\{"
|
||||
"}"
|
||||
] @punctuation.special)
|
||||
|
||||
; Exceptions
|
||||
[
|
||||
"throw"
|
||||
"throws"
|
||||
"finally"
|
||||
"try"
|
||||
"catch"
|
||||
] @keyword.exception
|
||||
|
||||
; Labels
|
||||
(labeled_statement
|
||||
(identifier) @label)
|
||||
|
||||
; Comments
|
||||
[
|
||||
(line_comment)
|
||||
(block_comment)
|
||||
] @comment @spell
|
||||
|
||||
((block_comment) @comment.documentation
|
||||
(#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$"))
|
||||
|
||||
((line_comment) @comment.documentation
|
||||
(#lua-match? @comment.documentation "^///[^/]"))
|
||||
|
||||
((line_comment) @comment.documentation
|
||||
(#lua-match? @comment.documentation "^///$"))
|
||||
605
internal/syntax/queries/javascript/highlights.scm
Normal file
605
internal/syntax/queries/javascript/highlights.scm
Normal file
@ -0,0 +1,605 @@
|
||||
; Types
|
||||
; Javascript
|
||||
; Variables
|
||||
;-----------
|
||||
(identifier) @variable
|
||||
|
||||
; Properties
|
||||
;-----------
|
||||
(property_identifier) @variable.member
|
||||
|
||||
(shorthand_property_identifier) @variable.member
|
||||
|
||||
(private_property_identifier) @variable.member
|
||||
|
||||
(object_pattern
|
||||
(shorthand_property_identifier_pattern) @variable)
|
||||
|
||||
(object_pattern
|
||||
(object_assignment_pattern
|
||||
(shorthand_property_identifier_pattern) @variable))
|
||||
|
||||
; Special identifiers
|
||||
;--------------------
|
||||
((identifier) @type
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
((identifier) @constant
|
||||
(#lua-match? @constant "^_*[A-Z][A-Z%d_]*$"))
|
||||
|
||||
((shorthand_property_identifier) @constant
|
||||
(#lua-match? @constant "^_*[A-Z][A-Z%d_]*$"))
|
||||
|
||||
((identifier) @variable.builtin
|
||||
(#any-of? @variable.builtin "arguments" "module" "console" "window" "document"))
|
||||
|
||||
((identifier) @type.builtin
|
||||
(#any-of? @type.builtin
|
||||
"Object" "Function" "Boolean" "Symbol" "Number" "Math" "Date" "String" "RegExp" "Map" "Set"
|
||||
"WeakMap" "WeakSet" "Promise" "Array" "Int8Array" "Uint8Array" "Uint8ClampedArray" "Int16Array"
|
||||
"Uint16Array" "Int32Array" "Uint32Array" "Float32Array" "Float64Array" "ArrayBuffer" "DataView"
|
||||
"Error" "EvalError" "InternalError" "RangeError" "ReferenceError" "SyntaxError" "TypeError"
|
||||
"URIError"))
|
||||
|
||||
(statement_identifier) @label
|
||||
|
||||
; Function and method definitions
|
||||
;--------------------------------
|
||||
(function_expression
|
||||
name: (identifier) @function)
|
||||
|
||||
(function_declaration
|
||||
name: (identifier) @function)
|
||||
|
||||
(generator_function
|
||||
name: (identifier) @function)
|
||||
|
||||
(generator_function_declaration
|
||||
name: (identifier) @function)
|
||||
|
||||
(method_definition
|
||||
name: [
|
||||
(property_identifier)
|
||||
(private_property_identifier)
|
||||
] @function.method)
|
||||
|
||||
(method_definition
|
||||
name: (property_identifier) @constructor
|
||||
(#eq? @constructor "constructor"))
|
||||
|
||||
(pair
|
||||
key: (property_identifier) @function.method
|
||||
value: (function_expression))
|
||||
|
||||
(pair
|
||||
key: (property_identifier) @function.method
|
||||
value: (arrow_function))
|
||||
|
||||
(assignment_expression
|
||||
left: (member_expression
|
||||
property: (property_identifier) @function.method)
|
||||
right: (arrow_function))
|
||||
|
||||
(assignment_expression
|
||||
left: (member_expression
|
||||
property: (property_identifier) @function.method)
|
||||
right: (function_expression))
|
||||
|
||||
(variable_declarator
|
||||
name: (identifier) @function
|
||||
value: (arrow_function))
|
||||
|
||||
(variable_declarator
|
||||
name: (identifier) @function
|
||||
value: (function_expression))
|
||||
|
||||
(assignment_expression
|
||||
left: (identifier) @function
|
||||
right: (arrow_function))
|
||||
|
||||
(assignment_expression
|
||||
left: (identifier) @function
|
||||
right: (function_expression))
|
||||
|
||||
; Function and method calls
|
||||
;--------------------------
|
||||
(call_expression
|
||||
function: (identifier) @function.call)
|
||||
|
||||
(call_expression
|
||||
function: (member_expression
|
||||
property: [
|
||||
(property_identifier)
|
||||
(private_property_identifier)
|
||||
] @function.method.call))
|
||||
|
||||
(call_expression
|
||||
function: (await_expression
|
||||
(identifier) @function.call))
|
||||
|
||||
(call_expression
|
||||
function: (await_expression
|
||||
(member_expression
|
||||
property: [
|
||||
(property_identifier)
|
||||
(private_property_identifier)
|
||||
] @function.method.call)))
|
||||
|
||||
; Builtins
|
||||
;---------
|
||||
((identifier) @module.builtin
|
||||
(#eq? @module.builtin "Intl"))
|
||||
|
||||
((identifier) @function.builtin
|
||||
(#any-of? @function.builtin
|
||||
"eval" "isFinite" "isNaN" "parseFloat" "parseInt" "decodeURI" "decodeURIComponent" "encodeURI"
|
||||
"encodeURIComponent" "require"))
|
||||
|
||||
; Constructor
|
||||
;------------
|
||||
(new_expression
|
||||
constructor: (identifier) @constructor)
|
||||
|
||||
; Decorators
|
||||
;----------
|
||||
(decorator
|
||||
"@" @attribute
|
||||
(identifier) @attribute)
|
||||
|
||||
(decorator
|
||||
"@" @attribute
|
||||
(call_expression
|
||||
(identifier) @attribute))
|
||||
|
||||
(decorator
|
||||
"@" @attribute
|
||||
(member_expression
|
||||
(property_identifier) @attribute))
|
||||
|
||||
(decorator
|
||||
"@" @attribute
|
||||
(call_expression
|
||||
(member_expression
|
||||
(property_identifier) @attribute)))
|
||||
|
||||
; Literals
|
||||
;---------
|
||||
[
|
||||
(this)
|
||||
(super)
|
||||
] @variable.builtin
|
||||
|
||||
((identifier) @variable.builtin
|
||||
(#eq? @variable.builtin "self"))
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
] @boolean
|
||||
|
||||
[
|
||||
(null)
|
||||
(undefined)
|
||||
] @constant.builtin
|
||||
|
||||
[
|
||||
(comment)
|
||||
(html_comment)
|
||||
] @comment @spell
|
||||
|
||||
((comment) @comment.documentation
|
||||
(#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$"))
|
||||
|
||||
(hash_bang_line) @keyword.directive
|
||||
|
||||
((string_fragment) @keyword.directive
|
||||
(#eq? @keyword.directive "use strict"))
|
||||
|
||||
(string) @string
|
||||
|
||||
(template_string) @string
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
(regex_pattern) @string.regexp
|
||||
|
||||
(regex_flags) @character.special
|
||||
|
||||
(regex
|
||||
"/" @punctuation.bracket) ; Regex delimiters
|
||||
|
||||
(number) @number
|
||||
|
||||
((identifier) @number
|
||||
(#any-of? @number "NaN" "Infinity"))
|
||||
|
||||
; Punctuation
|
||||
;------------
|
||||
[
|
||||
";"
|
||||
"."
|
||||
","
|
||||
":"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"--"
|
||||
"-"
|
||||
"-="
|
||||
"&&"
|
||||
"+"
|
||||
"++"
|
||||
"+="
|
||||
"&="
|
||||
"/="
|
||||
"**="
|
||||
"<<="
|
||||
"<"
|
||||
"<="
|
||||
"<<"
|
||||
"="
|
||||
"=="
|
||||
"==="
|
||||
"!="
|
||||
"!=="
|
||||
"=>"
|
||||
">"
|
||||
">="
|
||||
">>"
|
||||
"||"
|
||||
"%"
|
||||
"%="
|
||||
"*"
|
||||
"**"
|
||||
">>>"
|
||||
"&"
|
||||
"|"
|
||||
"^"
|
||||
"??"
|
||||
"*="
|
||||
">>="
|
||||
">>>="
|
||||
"^="
|
||||
"|="
|
||||
"&&="
|
||||
"||="
|
||||
"??="
|
||||
"..."
|
||||
] @operator
|
||||
|
||||
(binary_expression
|
||||
"/" @operator)
|
||||
|
||||
(ternary_expression
|
||||
[
|
||||
"?"
|
||||
":"
|
||||
] @keyword.conditional.ternary)
|
||||
|
||||
(unary_expression
|
||||
[
|
||||
"!"
|
||||
"~"
|
||||
"-"
|
||||
"+"
|
||||
] @operator)
|
||||
|
||||
(unary_expression
|
||||
[
|
||||
"delete"
|
||||
"void"
|
||||
] @keyword.operator)
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
(template_substitution
|
||||
[
|
||||
"${"
|
||||
"}"
|
||||
] @punctuation.special) @none
|
||||
|
||||
; Imports
|
||||
;----------
|
||||
(namespace_import
|
||||
"*" @character.special
|
||||
(identifier) @module)
|
||||
|
||||
(namespace_export
|
||||
"*" @character.special
|
||||
(identifier) @module)
|
||||
|
||||
(export_statement
|
||||
"*" @character.special)
|
||||
|
||||
; Keywords
|
||||
;----------
|
||||
[
|
||||
"if"
|
||||
"else"
|
||||
"switch"
|
||||
"case"
|
||||
] @keyword.conditional
|
||||
|
||||
[
|
||||
"import"
|
||||
"from"
|
||||
"as"
|
||||
"export"
|
||||
] @keyword.import
|
||||
|
||||
[
|
||||
"for"
|
||||
"of"
|
||||
"do"
|
||||
"while"
|
||||
"continue"
|
||||
] @keyword.repeat
|
||||
|
||||
[
|
||||
"break"
|
||||
"const"
|
||||
"debugger"
|
||||
"extends"
|
||||
"get"
|
||||
"let"
|
||||
"set"
|
||||
"static"
|
||||
"target"
|
||||
"var"
|
||||
"with"
|
||||
] @keyword
|
||||
|
||||
"class" @keyword.type
|
||||
|
||||
[
|
||||
"async"
|
||||
"await"
|
||||
] @keyword.coroutine
|
||||
|
||||
[
|
||||
"return"
|
||||
"yield"
|
||||
] @keyword.return
|
||||
|
||||
"function" @keyword.function
|
||||
|
||||
[
|
||||
"new"
|
||||
"delete"
|
||||
"in"
|
||||
"instanceof"
|
||||
"typeof"
|
||||
] @keyword.operator
|
||||
|
||||
[
|
||||
"throw"
|
||||
"try"
|
||||
"catch"
|
||||
"finally"
|
||||
] @keyword.exception
|
||||
|
||||
(export_statement
|
||||
"default" @keyword)
|
||||
|
||||
(switch_default
|
||||
"default" @keyword.conditional)
|
||||
|
||||
(jsx_element
|
||||
open_tag: (jsx_opening_element
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @tag.delimiter))
|
||||
|
||||
(jsx_element
|
||||
close_tag: (jsx_closing_element
|
||||
[
|
||||
"</"
|
||||
">"
|
||||
] @tag.delimiter))
|
||||
|
||||
(jsx_self_closing_element
|
||||
[
|
||||
"<"
|
||||
"/>"
|
||||
] @tag.delimiter)
|
||||
|
||||
(jsx_attribute
|
||||
(property_identifier) @tag.attribute)
|
||||
|
||||
(jsx_opening_element
|
||||
name: (identifier) @tag.builtin)
|
||||
|
||||
(jsx_closing_element
|
||||
name: (identifier) @tag.builtin)
|
||||
|
||||
(jsx_self_closing_element
|
||||
name: (identifier) @tag.builtin)
|
||||
|
||||
(jsx_opening_element
|
||||
((identifier) @tag
|
||||
(#lua-match? @tag "^[A-Z]")))
|
||||
|
||||
; Handle the dot operator effectively - <My.Component>
|
||||
(jsx_opening_element
|
||||
(member_expression
|
||||
(identifier) @tag.builtin
|
||||
(property_identifier) @tag))
|
||||
|
||||
(jsx_closing_element
|
||||
((identifier) @tag
|
||||
(#lua-match? @tag "^[A-Z]")))
|
||||
|
||||
; Handle the dot operator effectively - </My.Component>
|
||||
(jsx_closing_element
|
||||
(member_expression
|
||||
(identifier) @tag.builtin
|
||||
(property_identifier) @tag))
|
||||
|
||||
(jsx_self_closing_element
|
||||
((identifier) @tag
|
||||
(#lua-match? @tag "^[A-Z]")))
|
||||
|
||||
; Handle the dot operator effectively - <My.Component />
|
||||
(jsx_self_closing_element
|
||||
(member_expression
|
||||
(identifier) @tag.builtin
|
||||
(property_identifier) @tag))
|
||||
|
||||
(html_character_reference) @tag
|
||||
|
||||
(jsx_text) @none @spell
|
||||
|
||||
(html_character_reference) @character.special
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading)
|
||||
(#eq? @_tag "title"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading.1)
|
||||
(#eq? @_tag "h1"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading.2)
|
||||
(#eq? @_tag "h2"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading.3)
|
||||
(#eq? @_tag "h3"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading.4)
|
||||
(#eq? @_tag "h4"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading.5)
|
||||
(#eq? @_tag "h5"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading.6)
|
||||
(#eq? @_tag "h6"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.strong)
|
||||
(#any-of? @_tag "strong" "b"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.italic)
|
||||
(#any-of? @_tag "em" "i"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.strikethrough)
|
||||
(#any-of? @_tag "s" "del"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.underline)
|
||||
(#eq? @_tag "u"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.raw)
|
||||
(#any-of? @_tag "code" "kbd"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.link.label)
|
||||
(#eq? @_tag "a"))
|
||||
|
||||
((jsx_attribute
|
||||
(property_identifier) @_attr
|
||||
(string
|
||||
(string_fragment) @string.special.url))
|
||||
(#any-of? @_attr "href" "src"))
|
||||
|
||||
((jsx_element) @_jsx_element
|
||||
(#set! @_jsx_element bo.commentstring "{/* %s */}"))
|
||||
|
||||
((jsx_attribute) @_jsx_attribute
|
||||
(#set! @_jsx_attribute bo.commentstring "// %s"))
|
||||
|
||||
; Parameters
|
||||
(formal_parameters
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(formal_parameters
|
||||
(rest_pattern
|
||||
(identifier) @variable.parameter))
|
||||
|
||||
; ({ a }) => null
|
||||
(formal_parameters
|
||||
(object_pattern
|
||||
(shorthand_property_identifier_pattern) @variable.parameter))
|
||||
|
||||
; ({ a = b }) => null
|
||||
(formal_parameters
|
||||
(object_pattern
|
||||
(object_assignment_pattern
|
||||
(shorthand_property_identifier_pattern) @variable.parameter)))
|
||||
|
||||
; ({ a: b }) => null
|
||||
(formal_parameters
|
||||
(object_pattern
|
||||
(pair_pattern
|
||||
value: (identifier) @variable.parameter)))
|
||||
|
||||
; ([ a ]) => null
|
||||
(formal_parameters
|
||||
(array_pattern
|
||||
(identifier) @variable.parameter))
|
||||
|
||||
; ({ a } = { a }) => null
|
||||
(formal_parameters
|
||||
(assignment_pattern
|
||||
(object_pattern
|
||||
(shorthand_property_identifier_pattern) @variable.parameter)))
|
||||
|
||||
; ({ a = b } = { a }) => null
|
||||
(formal_parameters
|
||||
(assignment_pattern
|
||||
(object_pattern
|
||||
(object_assignment_pattern
|
||||
(shorthand_property_identifier_pattern) @variable.parameter))))
|
||||
|
||||
; a => null
|
||||
(arrow_function
|
||||
parameter: (identifier) @variable.parameter)
|
||||
|
||||
; optional parameters
|
||||
(formal_parameters
|
||||
(assignment_pattern
|
||||
left: (identifier) @variable.parameter))
|
||||
|
||||
; punctuation
|
||||
(optional_chain) @punctuation.delimiter
|
||||
38
internal/syntax/queries/json/highlights.scm
Normal file
38
internal/syntax/queries/json/highlights.scm
Normal file
@ -0,0 +1,38 @@
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
] @boolean
|
||||
|
||||
(null) @constant.builtin
|
||||
|
||||
(number) @number
|
||||
|
||||
(pair
|
||||
key: (string) @property)
|
||||
|
||||
(pair
|
||||
value: (string) @string)
|
||||
|
||||
(array
|
||||
(string) @string)
|
||||
|
||||
[
|
||||
","
|
||||
":"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
("\"" @conceal
|
||||
(#set! conceal ""))
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
((escape_sequence) @conceal
|
||||
(#eq? @conceal "\\\"")
|
||||
(#set! conceal "\""))
|
||||
443
internal/syntax/queries/python/highlights.scm
Normal file
443
internal/syntax/queries/python/highlights.scm
Normal file
@ -0,0 +1,443 @@
|
||||
; From tree-sitter-python licensed under MIT License
|
||||
; Copyright (c) 2016 Max Brunsfeld
|
||||
; Variables
|
||||
(identifier) @variable
|
||||
|
||||
; Reset highlighting in f-string interpolations
|
||||
(interpolation) @none
|
||||
|
||||
; Identifier naming conventions
|
||||
((identifier) @type
|
||||
(#lua-match? @type "^[A-Z].*[a-z]"))
|
||||
|
||||
((identifier) @constant
|
||||
(#lua-match? @constant "^[A-Z][A-Z_0-9]*$"))
|
||||
|
||||
((identifier) @constant.builtin
|
||||
(#lua-match? @constant.builtin "^__[a-zA-Z0-9_]*__$"))
|
||||
|
||||
((identifier) @constant.builtin
|
||||
(#any-of? @constant.builtin
|
||||
; https://docs.python.org/3/library/constants.html
|
||||
"NotImplemented" "Ellipsis" "quit" "exit" "copyright" "credits" "license"))
|
||||
|
||||
"_" @character.special ; match wildcard
|
||||
|
||||
((assignment
|
||||
left: (identifier) @type.definition
|
||||
(type
|
||||
(identifier) @_annotation))
|
||||
(#eq? @_annotation "TypeAlias"))
|
||||
|
||||
((assignment
|
||||
left: (identifier) @type.definition
|
||||
right: (call
|
||||
function: (identifier) @_func))
|
||||
(#any-of? @_func "TypeVar" "NewType"))
|
||||
|
||||
; Function definitions
|
||||
(function_definition
|
||||
name: (identifier) @function)
|
||||
|
||||
(type
|
||||
(identifier) @type)
|
||||
|
||||
(type
|
||||
(subscript
|
||||
(identifier) @type)) ; type subscript: Tuple[int]
|
||||
|
||||
((call
|
||||
function: (identifier) @_isinstance
|
||||
arguments: (argument_list
|
||||
(_)
|
||||
(identifier) @type))
|
||||
(#eq? @_isinstance "isinstance"))
|
||||
|
||||
; Literals
|
||||
(none) @constant.builtin
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
] @boolean
|
||||
|
||||
(integer) @number
|
||||
|
||||
(float) @number.float
|
||||
|
||||
(comment) @comment @spell
|
||||
|
||||
((module
|
||||
.
|
||||
(comment) @keyword.directive @nospell)
|
||||
(#lua-match? @keyword.directive "^#!/"))
|
||||
|
||||
(string) @string
|
||||
|
||||
[
|
||||
(escape_sequence)
|
||||
(escape_interpolation)
|
||||
] @string.escape
|
||||
|
||||
; doc-strings
|
||||
(expression_statement
|
||||
(string
|
||||
(string_content) @spell) @string.documentation)
|
||||
|
||||
; Tokens
|
||||
[
|
||||
"-"
|
||||
"-="
|
||||
":="
|
||||
"!="
|
||||
"*"
|
||||
"**"
|
||||
"**="
|
||||
"*="
|
||||
"/"
|
||||
"//"
|
||||
"//="
|
||||
"/="
|
||||
"&"
|
||||
"&="
|
||||
"%"
|
||||
"%="
|
||||
"^"
|
||||
"^="
|
||||
"+"
|
||||
"+="
|
||||
"<"
|
||||
"<<"
|
||||
"<<="
|
||||
"<="
|
||||
"<>"
|
||||
"="
|
||||
"=="
|
||||
">"
|
||||
">="
|
||||
">>"
|
||||
">>="
|
||||
"@"
|
||||
"@="
|
||||
"|"
|
||||
"|="
|
||||
"~"
|
||||
"->"
|
||||
] @operator
|
||||
|
||||
; Keywords
|
||||
[
|
||||
"and"
|
||||
"in"
|
||||
"is"
|
||||
"not"
|
||||
"or"
|
||||
"is not"
|
||||
"not in"
|
||||
"del"
|
||||
] @keyword.operator
|
||||
|
||||
[
|
||||
"def"
|
||||
"lambda"
|
||||
] @keyword.function
|
||||
|
||||
[
|
||||
"assert"
|
||||
"exec"
|
||||
"global"
|
||||
"nonlocal"
|
||||
"pass"
|
||||
"print"
|
||||
"with"
|
||||
"as"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"type"
|
||||
"class"
|
||||
] @keyword.type
|
||||
|
||||
[
|
||||
"async"
|
||||
"await"
|
||||
] @keyword.coroutine
|
||||
|
||||
[
|
||||
"return"
|
||||
"yield"
|
||||
] @keyword.return
|
||||
|
||||
(yield
|
||||
"from" @keyword.return)
|
||||
|
||||
(future_import_statement
|
||||
"from" @keyword.import
|
||||
"__future__" @module.builtin)
|
||||
|
||||
(import_from_statement
|
||||
"from" @keyword.import)
|
||||
|
||||
"import" @keyword.import
|
||||
|
||||
(aliased_import
|
||||
"as" @keyword.import)
|
||||
|
||||
(wildcard_import
|
||||
"*" @character.special)
|
||||
|
||||
(import_statement
|
||||
name: (dotted_name
|
||||
(identifier) @module))
|
||||
|
||||
(import_statement
|
||||
name: (aliased_import
|
||||
name: (dotted_name
|
||||
(identifier) @module)
|
||||
alias: (identifier) @module))
|
||||
|
||||
(import_from_statement
|
||||
module_name: (dotted_name
|
||||
(identifier) @module))
|
||||
|
||||
(import_from_statement
|
||||
module_name: (relative_import
|
||||
(dotted_name
|
||||
(identifier) @module)))
|
||||
|
||||
[
|
||||
"if"
|
||||
"elif"
|
||||
"else"
|
||||
"match"
|
||||
"case"
|
||||
] @keyword.conditional
|
||||
|
||||
[
|
||||
"for"
|
||||
"while"
|
||||
"break"
|
||||
"continue"
|
||||
] @keyword.repeat
|
||||
|
||||
[
|
||||
"try"
|
||||
"except"
|
||||
; "except*"
|
||||
"raise"
|
||||
"finally"
|
||||
] @keyword.exception
|
||||
|
||||
(raise_statement
|
||||
"from" @keyword.exception)
|
||||
|
||||
(try_statement
|
||||
(else_clause
|
||||
"else" @keyword.exception))
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
(interpolation
|
||||
"{" @punctuation.special
|
||||
"}" @punctuation.special)
|
||||
|
||||
(type_conversion) @function.macro
|
||||
|
||||
[
|
||||
","
|
||||
"."
|
||||
":"
|
||||
";"
|
||||
(ellipsis)
|
||||
] @punctuation.delimiter
|
||||
|
||||
((identifier) @type.builtin
|
||||
(#any-of? @type.builtin
|
||||
; https://docs.python.org/3/library/exceptions.html
|
||||
"BaseException" "Exception" "ArithmeticError" "BufferError" "LookupError" "AssertionError"
|
||||
"AttributeError" "EOFError" "FloatingPointError" "GeneratorExit" "ImportError"
|
||||
"ModuleNotFoundError" "IndexError" "KeyError" "KeyboardInterrupt" "MemoryError" "NameError"
|
||||
"NotImplementedError" "OSError" "OverflowError" "RecursionError" "ReferenceError" "RuntimeError"
|
||||
"StopIteration" "StopAsyncIteration" "SyntaxError" "IndentationError" "TabError" "SystemError"
|
||||
"SystemExit" "TypeError" "UnboundLocalError" "UnicodeError" "UnicodeEncodeError"
|
||||
"UnicodeDecodeError" "UnicodeTranslateError" "ValueError" "ZeroDivisionError" "EnvironmentError"
|
||||
"IOError" "WindowsError" "BlockingIOError" "ChildProcessError" "ConnectionError"
|
||||
"BrokenPipeError" "ConnectionAbortedError" "ConnectionRefusedError" "ConnectionResetError"
|
||||
"FileExistsError" "FileNotFoundError" "InterruptedError" "IsADirectoryError"
|
||||
"NotADirectoryError" "PermissionError" "ProcessLookupError" "TimeoutError" "Warning"
|
||||
"UserWarning" "DeprecationWarning" "PendingDeprecationWarning" "SyntaxWarning" "RuntimeWarning"
|
||||
"FutureWarning" "ImportWarning" "UnicodeWarning" "BytesWarning" "ResourceWarning"
|
||||
; https://docs.python.org/3/library/stdtypes.html
|
||||
"bool" "int" "float" "complex" "list" "tuple" "range" "str" "bytes" "bytearray" "memoryview"
|
||||
"set" "frozenset" "dict" "type" "object"))
|
||||
|
||||
; Normal parameters
|
||||
(parameters
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
; Lambda parameters
|
||||
(lambda_parameters
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(lambda_parameters
|
||||
(tuple_pattern
|
||||
(identifier) @variable.parameter))
|
||||
|
||||
; Default parameters
|
||||
(keyword_argument
|
||||
name: (identifier) @variable.parameter)
|
||||
|
||||
; Naming parameters on call-site
|
||||
(default_parameter
|
||||
name: (identifier) @variable.parameter)
|
||||
|
||||
(typed_parameter
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(typed_default_parameter
|
||||
name: (identifier) @variable.parameter)
|
||||
|
||||
; Variadic parameters *args, **kwargs
|
||||
(parameters
|
||||
(list_splat_pattern ; *args
|
||||
(identifier) @variable.parameter))
|
||||
|
||||
(parameters
|
||||
(dictionary_splat_pattern ; **kwargs
|
||||
(identifier) @variable.parameter))
|
||||
|
||||
; Typed variadic parameters
|
||||
(parameters
|
||||
(typed_parameter
|
||||
(list_splat_pattern ; *args: type
|
||||
(identifier) @variable.parameter)))
|
||||
|
||||
(parameters
|
||||
(typed_parameter
|
||||
(dictionary_splat_pattern ; *kwargs: type
|
||||
(identifier) @variable.parameter)))
|
||||
|
||||
; Lambda parameters
|
||||
(lambda_parameters
|
||||
(list_splat_pattern
|
||||
(identifier) @variable.parameter))
|
||||
|
||||
(lambda_parameters
|
||||
(dictionary_splat_pattern
|
||||
(identifier) @variable.parameter))
|
||||
|
||||
((identifier) @variable.builtin
|
||||
(#eq? @variable.builtin "self"))
|
||||
|
||||
((identifier) @variable.builtin
|
||||
(#eq? @variable.builtin "cls"))
|
||||
|
||||
; After @type.builtin bacause builtins (such as `type`) are valid as attribute name
|
||||
((attribute
|
||||
attribute: (identifier) @variable.member)
|
||||
(#lua-match? @variable.member "^[%l_].*$"))
|
||||
|
||||
; Class definitions
|
||||
(class_definition
|
||||
name: (identifier) @type)
|
||||
|
||||
(class_definition
|
||||
body: (block
|
||||
(function_definition
|
||||
name: (identifier) @function.method)))
|
||||
|
||||
(class_definition
|
||||
superclasses: (argument_list
|
||||
(identifier) @type))
|
||||
|
||||
((class_definition
|
||||
body: (block
|
||||
(expression_statement
|
||||
(assignment
|
||||
left: (identifier) @variable.member))))
|
||||
(#lua-match? @variable.member "^[%l_].*$"))
|
||||
|
||||
((class_definition
|
||||
body: (block
|
||||
(expression_statement
|
||||
(assignment
|
||||
left: (_
|
||||
(identifier) @variable.member)))))
|
||||
(#lua-match? @variable.member "^[%l_].*$"))
|
||||
|
||||
((class_definition
|
||||
(block
|
||||
(function_definition
|
||||
name: (identifier) @constructor)))
|
||||
(#any-of? @constructor "__new__" "__init__"))
|
||||
|
||||
; Function calls
|
||||
(call
|
||||
function: (identifier) @function.call)
|
||||
|
||||
(call
|
||||
function: (attribute
|
||||
attribute: (identifier) @function.method.call))
|
||||
|
||||
((call
|
||||
function: (identifier) @constructor)
|
||||
(#lua-match? @constructor "^%u"))
|
||||
|
||||
((call
|
||||
function: (attribute
|
||||
attribute: (identifier) @constructor))
|
||||
(#lua-match? @constructor "^%u"))
|
||||
|
||||
; Builtin functions
|
||||
((call
|
||||
function: (identifier) @function.builtin)
|
||||
(#any-of? @function.builtin
|
||||
"abs" "all" "any" "ascii" "bin" "bool" "breakpoint" "bytearray" "bytes" "callable" "chr"
|
||||
"classmethod" "compile" "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" "exec"
|
||||
"filter" "float" "format" "frozenset" "getattr" "globals" "hasattr" "hash" "help" "hex" "id"
|
||||
"input" "int" "isinstance" "issubclass" "iter" "len" "list" "locals" "map" "max" "memoryview"
|
||||
"min" "next" "object" "oct" "open" "ord" "pow" "print" "property" "range" "repr" "reversed"
|
||||
"round" "set" "setattr" "slice" "sorted" "staticmethod" "str" "sum" "super" "tuple" "type"
|
||||
"vars" "zip" "__import__"))
|
||||
|
||||
; Regex from the `re` module
|
||||
(call
|
||||
function: (attribute
|
||||
object: (identifier) @_re)
|
||||
arguments: (argument_list
|
||||
.
|
||||
(string
|
||||
(string_content) @string.regexp))
|
||||
(#eq? @_re "re"))
|
||||
|
||||
; Decorators
|
||||
((decorator
|
||||
"@" @attribute)
|
||||
(#set! priority 101))
|
||||
|
||||
(decorator
|
||||
(identifier) @attribute)
|
||||
|
||||
(decorator
|
||||
(attribute
|
||||
attribute: (identifier) @attribute))
|
||||
|
||||
(decorator
|
||||
(call
|
||||
(identifier) @attribute))
|
||||
|
||||
(decorator
|
||||
(call
|
||||
(attribute
|
||||
attribute: (identifier) @attribute)))
|
||||
|
||||
((decorator
|
||||
(identifier) @attribute.builtin)
|
||||
(#any-of? @attribute.builtin "classmethod" "property" "staticmethod"))
|
||||
309
internal/syntax/queries/ruby/highlights.scm
Normal file
309
internal/syntax/queries/ruby/highlights.scm
Normal file
@ -0,0 +1,309 @@
|
||||
; Variables
|
||||
[
|
||||
(identifier)
|
||||
(global_variable)
|
||||
] @variable
|
||||
|
||||
; Keywords
|
||||
[
|
||||
"alias"
|
||||
"begin"
|
||||
"do"
|
||||
"end"
|
||||
"ensure"
|
||||
"module"
|
||||
"rescue"
|
||||
"then"
|
||||
] @keyword
|
||||
|
||||
"class" @keyword.type
|
||||
|
||||
[
|
||||
"return"
|
||||
"yield"
|
||||
] @keyword.return
|
||||
|
||||
[
|
||||
"and"
|
||||
"or"
|
||||
"in"
|
||||
"not"
|
||||
] @keyword.operator
|
||||
|
||||
[
|
||||
"def"
|
||||
"undef"
|
||||
] @keyword.function
|
||||
|
||||
(method
|
||||
"end" @keyword.function)
|
||||
|
||||
[
|
||||
"case"
|
||||
"else"
|
||||
"elsif"
|
||||
"if"
|
||||
"unless"
|
||||
"when"
|
||||
"then"
|
||||
] @keyword.conditional
|
||||
|
||||
(if
|
||||
"end" @keyword.conditional)
|
||||
|
||||
[
|
||||
"for"
|
||||
"until"
|
||||
"while"
|
||||
"break"
|
||||
"redo"
|
||||
"retry"
|
||||
"next"
|
||||
] @keyword.repeat
|
||||
|
||||
(constant) @constant
|
||||
|
||||
((identifier) @keyword.modifier
|
||||
(#any-of? @keyword.modifier "private" "protected" "public"))
|
||||
|
||||
[
|
||||
"rescue"
|
||||
"ensure"
|
||||
] @keyword.exception
|
||||
|
||||
; Function calls
|
||||
"defined?" @function
|
||||
|
||||
(call
|
||||
receiver: (constant)? @type
|
||||
method: [
|
||||
(identifier)
|
||||
(constant)
|
||||
] @function.call)
|
||||
|
||||
(program
|
||||
(call
|
||||
(identifier) @keyword.import)
|
||||
(#any-of? @keyword.import "require" "require_relative" "load"))
|
||||
|
||||
; Function definitions
|
||||
(alias
|
||||
(identifier) @function)
|
||||
|
||||
(setter
|
||||
(identifier) @function)
|
||||
|
||||
(method
|
||||
name: [
|
||||
(identifier) @function
|
||||
(constant) @type
|
||||
])
|
||||
|
||||
(singleton_method
|
||||
name: [
|
||||
(identifier) @function
|
||||
(constant) @type
|
||||
])
|
||||
|
||||
(class
|
||||
name: (constant) @type)
|
||||
|
||||
(module
|
||||
name: (constant) @type)
|
||||
|
||||
(superclass
|
||||
(constant) @type)
|
||||
|
||||
; Identifiers
|
||||
[
|
||||
(class_variable)
|
||||
(instance_variable)
|
||||
] @variable.member
|
||||
|
||||
((identifier) @constant.builtin
|
||||
(#any-of? @constant.builtin
|
||||
"__callee__" "__dir__" "__id__" "__method__" "__send__" "__ENCODING__" "__FILE__" "__LINE__"))
|
||||
|
||||
((identifier) @function.builtin
|
||||
(#any-of? @function.builtin "attr_reader" "attr_writer" "attr_accessor" "module_function"))
|
||||
|
||||
((call
|
||||
!receiver
|
||||
method: (identifier) @function.builtin)
|
||||
(#any-of? @function.builtin "include" "extend" "prepend" "refine" "using"))
|
||||
|
||||
((identifier) @keyword.exception
|
||||
(#any-of? @keyword.exception "raise" "fail" "catch" "throw"))
|
||||
|
||||
((constant) @type
|
||||
(#not-lua-match? @type "^[A-Z0-9_]+$"))
|
||||
|
||||
[
|
||||
(self)
|
||||
(super)
|
||||
] @variable.builtin
|
||||
|
||||
(method_parameters
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(lambda_parameters
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(block_parameters
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(splat_parameter
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(hash_splat_parameter
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(optional_parameter
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(destructured_parameter
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(block_parameter
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
(keyword_parameter
|
||||
(identifier) @variable.parameter)
|
||||
|
||||
; TODO: Re-enable this once it is supported
|
||||
; ((identifier) @function
|
||||
; (#is-not? local))
|
||||
; Literals
|
||||
[
|
||||
(string_content)
|
||||
(heredoc_content)
|
||||
"\""
|
||||
"`"
|
||||
] @string
|
||||
|
||||
[
|
||||
(heredoc_beginning)
|
||||
(heredoc_end)
|
||||
] @label
|
||||
|
||||
[
|
||||
(bare_symbol)
|
||||
(simple_symbol)
|
||||
(delimited_symbol)
|
||||
(hash_key_symbol)
|
||||
] @string.special.symbol
|
||||
|
||||
(regex
|
||||
(string_content) @string.regexp)
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
(integer) @number
|
||||
|
||||
(float) @number.float
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
] @boolean
|
||||
|
||||
(nil) @constant.builtin
|
||||
|
||||
(comment) @comment @spell
|
||||
|
||||
((program
|
||||
.
|
||||
(comment) @keyword.directive @nospell)
|
||||
(#lua-match? @keyword.directive "^#!/"))
|
||||
|
||||
(program
|
||||
(comment)+ @comment.documentation
|
||||
(class))
|
||||
|
||||
(module
|
||||
(comment)+ @comment.documentation
|
||||
(body_statement
|
||||
(class)))
|
||||
|
||||
(class
|
||||
(comment)+ @comment.documentation
|
||||
(body_statement
|
||||
(method)))
|
||||
|
||||
(body_statement
|
||||
(comment)+ @comment.documentation
|
||||
(method))
|
||||
|
||||
; Operators
|
||||
[
|
||||
"!"
|
||||
"="
|
||||
"=="
|
||||
"==="
|
||||
"<=>"
|
||||
"=>"
|
||||
"->"
|
||||
">>"
|
||||
"<<"
|
||||
">"
|
||||
"<"
|
||||
">="
|
||||
"<="
|
||||
"**"
|
||||
"*"
|
||||
"/"
|
||||
"%"
|
||||
"+"
|
||||
"-"
|
||||
"&"
|
||||
"|"
|
||||
"^"
|
||||
"&&"
|
||||
"||"
|
||||
"||="
|
||||
"&&="
|
||||
"!="
|
||||
"%="
|
||||
"+="
|
||||
"-="
|
||||
"*="
|
||||
"/="
|
||||
"=~"
|
||||
"!~"
|
||||
"?"
|
||||
":"
|
||||
".."
|
||||
"..."
|
||||
] @operator
|
||||
|
||||
[
|
||||
","
|
||||
";"
|
||||
"."
|
||||
"&."
|
||||
"::"
|
||||
] @punctuation.delimiter
|
||||
|
||||
(regex
|
||||
"/" @punctuation.bracket)
|
||||
|
||||
(pair
|
||||
":" @punctuation.delimiter)
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
"%w("
|
||||
"%i("
|
||||
] @punctuation.bracket
|
||||
|
||||
(block_parameters
|
||||
"|" @punctuation.bracket)
|
||||
|
||||
(interpolation
|
||||
"#{" @punctuation.special
|
||||
"}" @punctuation.special)
|
||||
531
internal/syntax/queries/rust/highlights.scm
Normal file
531
internal/syntax/queries/rust/highlights.scm
Normal file
@ -0,0 +1,531 @@
|
||||
; Forked from https://github.com/tree-sitter/tree-sitter-rust
|
||||
; Copyright (c) 2017 Maxim Sokolov
|
||||
; Licensed under the MIT license.
|
||||
; Identifier conventions
|
||||
(shebang) @keyword.directive
|
||||
|
||||
(identifier) @variable
|
||||
|
||||
((identifier) @type
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
(const_item
|
||||
name: (identifier) @constant)
|
||||
|
||||
; Assume all-caps names are constants
|
||||
((identifier) @constant
|
||||
(#lua-match? @constant "^[A-Z][A-Z%d_]*$"))
|
||||
|
||||
; Other identifiers
|
||||
(type_identifier) @type
|
||||
|
||||
(primitive_type) @type.builtin
|
||||
|
||||
(field_identifier) @variable.member
|
||||
|
||||
(shorthand_field_identifier) @variable.member
|
||||
|
||||
(shorthand_field_initializer
|
||||
(identifier) @variable.member)
|
||||
|
||||
(mod_item
|
||||
name: (identifier) @module)
|
||||
|
||||
(self) @variable.builtin
|
||||
|
||||
"_" @character.special
|
||||
|
||||
(label
|
||||
[
|
||||
"'"
|
||||
(identifier)
|
||||
] @label)
|
||||
|
||||
; Function definitions
|
||||
(function_item
|
||||
(identifier) @function)
|
||||
|
||||
(function_signature_item
|
||||
(identifier) @function)
|
||||
|
||||
(parameter
|
||||
[
|
||||
(identifier)
|
||||
"_"
|
||||
] @variable.parameter)
|
||||
|
||||
(parameter
|
||||
(ref_pattern
|
||||
[
|
||||
(mut_pattern
|
||||
(identifier) @variable.parameter)
|
||||
(identifier) @variable.parameter
|
||||
]))
|
||||
|
||||
(closure_parameters
|
||||
(_) @variable.parameter)
|
||||
|
||||
; Function calls
|
||||
(call_expression
|
||||
function: (identifier) @function.call)
|
||||
|
||||
(call_expression
|
||||
function: (scoped_identifier
|
||||
(identifier) @function.call .))
|
||||
|
||||
(call_expression
|
||||
function: (field_expression
|
||||
field: (field_identifier) @function.call))
|
||||
|
||||
(generic_function
|
||||
function: (identifier) @function.call)
|
||||
|
||||
(generic_function
|
||||
function: (scoped_identifier
|
||||
name: (identifier) @function.call))
|
||||
|
||||
(generic_function
|
||||
function: (field_expression
|
||||
field: (field_identifier) @function.call))
|
||||
|
||||
; Assume other uppercase names are enum constructors
|
||||
((field_identifier) @constant
|
||||
(#lua-match? @constant "^[A-Z]"))
|
||||
|
||||
(enum_variant
|
||||
name: (identifier) @constant)
|
||||
|
||||
; Assume that uppercase names in paths are types
|
||||
(scoped_identifier
|
||||
path: (identifier) @module)
|
||||
|
||||
(scoped_identifier
|
||||
(scoped_identifier
|
||||
name: (identifier) @module))
|
||||
|
||||
(scoped_type_identifier
|
||||
path: (identifier) @module)
|
||||
|
||||
(scoped_type_identifier
|
||||
path: (identifier) @type
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
(scoped_type_identifier
|
||||
(scoped_identifier
|
||||
name: (identifier) @module))
|
||||
|
||||
((scoped_identifier
|
||||
path: (identifier) @type)
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
((scoped_identifier
|
||||
name: (identifier) @type)
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
((scoped_identifier
|
||||
name: (identifier) @constant)
|
||||
(#lua-match? @constant "^[A-Z][A-Z%d_]*$"))
|
||||
|
||||
((scoped_identifier
|
||||
path: (identifier) @type
|
||||
name: (identifier) @constant)
|
||||
(#lua-match? @type "^[A-Z]")
|
||||
(#lua-match? @constant "^[A-Z]"))
|
||||
|
||||
((scoped_type_identifier
|
||||
path: (identifier) @type
|
||||
name: (type_identifier) @constant)
|
||||
(#lua-match? @type "^[A-Z]")
|
||||
(#lua-match? @constant "^[A-Z]"))
|
||||
|
||||
[
|
||||
(crate)
|
||||
(super)
|
||||
] @module
|
||||
|
||||
(scoped_use_list
|
||||
path: (identifier) @module)
|
||||
|
||||
(scoped_use_list
|
||||
path: (scoped_identifier
|
||||
(identifier) @module))
|
||||
|
||||
(use_list
|
||||
(scoped_identifier
|
||||
(identifier) @module
|
||||
.
|
||||
(_)))
|
||||
|
||||
(use_list
|
||||
(identifier) @type
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
(use_as_clause
|
||||
alias: (identifier) @type
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
; Correct enum constructors
|
||||
(call_expression
|
||||
function: (scoped_identifier
|
||||
"::"
|
||||
name: (identifier) @constant)
|
||||
(#lua-match? @constant "^[A-Z]"))
|
||||
|
||||
; Assume uppercase names in a match arm are constants.
|
||||
((match_arm
|
||||
pattern: (match_pattern
|
||||
(identifier) @constant))
|
||||
(#lua-match? @constant "^[A-Z]"))
|
||||
|
||||
((match_arm
|
||||
pattern: (match_pattern
|
||||
(scoped_identifier
|
||||
name: (identifier) @constant)))
|
||||
(#lua-match? @constant "^[A-Z]"))
|
||||
|
||||
((identifier) @constant.builtin
|
||||
(#any-of? @constant.builtin "Some" "None" "Ok" "Err"))
|
||||
|
||||
; Macro definitions
|
||||
"$" @function.macro
|
||||
|
||||
(metavariable) @function.macro
|
||||
|
||||
(macro_definition
|
||||
"macro_rules!" @function.macro)
|
||||
|
||||
; Attribute macros
|
||||
(attribute_item
|
||||
(attribute
|
||||
(identifier) @function.macro))
|
||||
|
||||
(inner_attribute_item
|
||||
(attribute
|
||||
(identifier) @function.macro))
|
||||
|
||||
(attribute
|
||||
(scoped_identifier
|
||||
(identifier) @function.macro .))
|
||||
|
||||
; Derive macros (assume all arguments are types)
|
||||
; (attribute
|
||||
; (identifier) @_name
|
||||
; arguments: (attribute (attribute (identifier) @type))
|
||||
; (#eq? @_name "derive"))
|
||||
; Function-like macros
|
||||
(macro_invocation
|
||||
macro: (identifier) @function.macro)
|
||||
|
||||
(macro_invocation
|
||||
macro: (scoped_identifier
|
||||
(identifier) @function.macro .))
|
||||
|
||||
; Literals
|
||||
(boolean_literal) @boolean
|
||||
|
||||
(integer_literal) @number
|
||||
|
||||
(float_literal) @number.float
|
||||
|
||||
[
|
||||
(raw_string_literal)
|
||||
(string_literal)
|
||||
] @string
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
(char_literal) @character
|
||||
|
||||
; Keywords
|
||||
[
|
||||
"use"
|
||||
"mod"
|
||||
] @keyword.import
|
||||
|
||||
(use_as_clause
|
||||
"as" @keyword.import)
|
||||
|
||||
[
|
||||
"default"
|
||||
"impl"
|
||||
"let"
|
||||
"move"
|
||||
"unsafe"
|
||||
"where"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"enum"
|
||||
"struct"
|
||||
"union"
|
||||
"trait"
|
||||
"type"
|
||||
] @keyword.type
|
||||
|
||||
[
|
||||
"async"
|
||||
"await"
|
||||
"gen"
|
||||
] @keyword.coroutine
|
||||
|
||||
"try" @keyword.exception
|
||||
|
||||
[
|
||||
"ref"
|
||||
"pub"
|
||||
"raw"
|
||||
(mutable_specifier)
|
||||
"const"
|
||||
"static"
|
||||
"dyn"
|
||||
"extern"
|
||||
] @keyword.modifier
|
||||
|
||||
(lifetime
|
||||
"'" @keyword.modifier)
|
||||
|
||||
(lifetime
|
||||
(identifier) @attribute)
|
||||
|
||||
(lifetime
|
||||
(identifier) @attribute.builtin
|
||||
(#any-of? @attribute.builtin "static" "_"))
|
||||
|
||||
"fn" @keyword.function
|
||||
|
||||
[
|
||||
"return"
|
||||
"yield"
|
||||
] @keyword.return
|
||||
|
||||
(type_cast_expression
|
||||
"as" @keyword.operator)
|
||||
|
||||
(qualified_type
|
||||
"as" @keyword.operator)
|
||||
|
||||
(use_list
|
||||
(self) @module)
|
||||
|
||||
(scoped_use_list
|
||||
(self) @module)
|
||||
|
||||
(scoped_identifier
|
||||
[
|
||||
(crate)
|
||||
(super)
|
||||
(self)
|
||||
] @module)
|
||||
|
||||
(visibility_modifier
|
||||
[
|
||||
(crate)
|
||||
(super)
|
||||
(self)
|
||||
] @module)
|
||||
|
||||
[
|
||||
"if"
|
||||
"else"
|
||||
"match"
|
||||
] @keyword.conditional
|
||||
|
||||
[
|
||||
"break"
|
||||
"continue"
|
||||
"in"
|
||||
"loop"
|
||||
"while"
|
||||
] @keyword.repeat
|
||||
|
||||
"for" @keyword
|
||||
|
||||
(for_expression
|
||||
"for" @keyword.repeat)
|
||||
|
||||
; Operators
|
||||
[
|
||||
"!"
|
||||
"!="
|
||||
"%"
|
||||
"%="
|
||||
"&"
|
||||
"&&"
|
||||
"&="
|
||||
"*"
|
||||
"*="
|
||||
"+"
|
||||
"+="
|
||||
"-"
|
||||
"-="
|
||||
".."
|
||||
"..="
|
||||
"..."
|
||||
"/"
|
||||
"/="
|
||||
"<"
|
||||
"<<"
|
||||
"<<="
|
||||
"<="
|
||||
"="
|
||||
"=="
|
||||
">"
|
||||
">="
|
||||
">>"
|
||||
">>="
|
||||
"?"
|
||||
"@"
|
||||
"^"
|
||||
"^="
|
||||
"|"
|
||||
"|="
|
||||
"||"
|
||||
] @operator
|
||||
|
||||
(use_wildcard
|
||||
"*" @character.special)
|
||||
|
||||
(remaining_field_pattern
|
||||
".." @character.special)
|
||||
|
||||
(range_pattern
|
||||
[
|
||||
".."
|
||||
"..="
|
||||
"..."
|
||||
] @character.special)
|
||||
|
||||
; Punctuation
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
(closure_parameters
|
||||
"|" @punctuation.bracket)
|
||||
|
||||
(type_arguments
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(type_parameters
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(bracketed_type
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(for_lifetimes
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
[
|
||||
","
|
||||
"."
|
||||
":"
|
||||
"::"
|
||||
";"
|
||||
"->"
|
||||
"=>"
|
||||
] @punctuation.delimiter
|
||||
|
||||
(attribute_item
|
||||
"#" @punctuation.special)
|
||||
|
||||
(inner_attribute_item
|
||||
[
|
||||
"!"
|
||||
"#"
|
||||
] @punctuation.special)
|
||||
|
||||
(macro_invocation
|
||||
"!" @function.macro)
|
||||
|
||||
(never_type
|
||||
"!" @type.builtin)
|
||||
|
||||
(macro_invocation
|
||||
macro: (identifier) @_identifier @keyword.exception
|
||||
"!" @keyword.exception
|
||||
(#eq? @_identifier "panic"))
|
||||
|
||||
(macro_invocation
|
||||
macro: (identifier) @_identifier @keyword.exception
|
||||
"!" @keyword.exception
|
||||
(#contains? @_identifier "assert"))
|
||||
|
||||
(macro_invocation
|
||||
macro: (identifier) @_identifier @keyword.debug
|
||||
"!" @keyword.debug
|
||||
(#eq? @_identifier "dbg"))
|
||||
|
||||
; Comments
|
||||
[
|
||||
(line_comment)
|
||||
(block_comment)
|
||||
(outer_doc_comment_marker)
|
||||
(inner_doc_comment_marker)
|
||||
] @comment @spell
|
||||
|
||||
(line_comment
|
||||
(doc_comment)) @comment.documentation
|
||||
|
||||
(block_comment
|
||||
(doc_comment)) @comment.documentation
|
||||
|
||||
(call_expression
|
||||
function: (scoped_identifier
|
||||
path: (identifier) @_regex
|
||||
(#any-of? @_regex "Regex" "ByteRegexBuilder")
|
||||
name: (identifier) @_new
|
||||
(#eq? @_new "new"))
|
||||
arguments: (arguments
|
||||
(raw_string_literal
|
||||
(string_content) @string.regexp)))
|
||||
|
||||
(call_expression
|
||||
function: (scoped_identifier
|
||||
path: (scoped_identifier
|
||||
(identifier) @_regex
|
||||
(#any-of? @_regex "Regex" "ByteRegexBuilder") .)
|
||||
name: (identifier) @_new
|
||||
(#eq? @_new "new"))
|
||||
arguments: (arguments
|
||||
(raw_string_literal
|
||||
(string_content) @string.regexp)))
|
||||
|
||||
(call_expression
|
||||
function: (scoped_identifier
|
||||
path: (identifier) @_regex
|
||||
(#any-of? @_regex "RegexSet" "RegexSetBuilder")
|
||||
name: (identifier) @_new
|
||||
(#eq? @_new "new"))
|
||||
arguments: (arguments
|
||||
(array_expression
|
||||
(raw_string_literal
|
||||
(string_content) @string.regexp))))
|
||||
|
||||
(call_expression
|
||||
function: (scoped_identifier
|
||||
path: (scoped_identifier
|
||||
(identifier) @_regex
|
||||
(#any-of? @_regex "RegexSet" "RegexSetBuilder") .)
|
||||
name: (identifier) @_new
|
||||
(#eq? @_new "new"))
|
||||
arguments: (arguments
|
||||
(array_expression
|
||||
(raw_string_literal
|
||||
(string_content) @string.regexp))))
|
||||
757
internal/syntax/queries/tsx/highlights.scm
Normal file
757
internal/syntax/queries/tsx/highlights.scm
Normal file
@ -0,0 +1,757 @@
|
||||
(jsx_element
|
||||
open_tag: (jsx_opening_element
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @tag.delimiter))
|
||||
|
||||
(jsx_element
|
||||
close_tag: (jsx_closing_element
|
||||
[
|
||||
"</"
|
||||
">"
|
||||
] @tag.delimiter))
|
||||
|
||||
(jsx_self_closing_element
|
||||
[
|
||||
"<"
|
||||
"/>"
|
||||
] @tag.delimiter)
|
||||
|
||||
(jsx_attribute
|
||||
(property_identifier) @tag.attribute)
|
||||
|
||||
(jsx_opening_element
|
||||
name: (identifier) @tag.builtin)
|
||||
|
||||
(jsx_closing_element
|
||||
name: (identifier) @tag.builtin)
|
||||
|
||||
(jsx_self_closing_element
|
||||
name: (identifier) @tag.builtin)
|
||||
|
||||
(jsx_opening_element
|
||||
((identifier) @tag
|
||||
(#lua-match? @tag "^[A-Z]")))
|
||||
|
||||
; Handle the dot operator effectively - <My.Component>
|
||||
(jsx_opening_element
|
||||
(member_expression
|
||||
(identifier) @tag.builtin
|
||||
(property_identifier) @tag))
|
||||
|
||||
(jsx_closing_element
|
||||
((identifier) @tag
|
||||
(#lua-match? @tag "^[A-Z]")))
|
||||
|
||||
; Handle the dot operator effectively - </My.Component>
|
||||
(jsx_closing_element
|
||||
(member_expression
|
||||
(identifier) @tag.builtin
|
||||
(property_identifier) @tag))
|
||||
|
||||
(jsx_self_closing_element
|
||||
((identifier) @tag
|
||||
(#lua-match? @tag "^[A-Z]")))
|
||||
|
||||
; Handle the dot operator effectively - <My.Component />
|
||||
(jsx_self_closing_element
|
||||
(member_expression
|
||||
(identifier) @tag.builtin
|
||||
(property_identifier) @tag))
|
||||
|
||||
(html_character_reference) @tag
|
||||
|
||||
(jsx_text) @none @spell
|
||||
|
||||
(html_character_reference) @character.special
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading)
|
||||
(#eq? @_tag "title"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading.1)
|
||||
(#eq? @_tag "h1"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading.2)
|
||||
(#eq? @_tag "h2"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading.3)
|
||||
(#eq? @_tag "h3"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading.4)
|
||||
(#eq? @_tag "h4"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading.5)
|
||||
(#eq? @_tag "h5"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.heading.6)
|
||||
(#eq? @_tag "h6"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.strong)
|
||||
(#any-of? @_tag "strong" "b"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.italic)
|
||||
(#any-of? @_tag "em" "i"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.strikethrough)
|
||||
(#any-of? @_tag "s" "del"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.underline)
|
||||
(#eq? @_tag "u"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.raw)
|
||||
(#any-of? @_tag "code" "kbd"))
|
||||
|
||||
((jsx_element
|
||||
(jsx_opening_element
|
||||
name: (identifier) @_tag)
|
||||
(jsx_text) @markup.link.label)
|
||||
(#eq? @_tag "a"))
|
||||
|
||||
((jsx_attribute
|
||||
(property_identifier) @_attr
|
||||
(string
|
||||
(string_fragment) @string.special.url))
|
||||
(#any-of? @_attr "href" "src"))
|
||||
|
||||
((jsx_element) @_jsx_element
|
||||
(#set! @_jsx_element bo.commentstring "{/* %s */}"))
|
||||
|
||||
((jsx_attribute) @_jsx_attribute
|
||||
(#set! @_jsx_attribute bo.commentstring "// %s"))
|
||||
|
||||
; Types
|
||||
; Javascript
|
||||
; Variables
|
||||
;-----------
|
||||
(identifier) @variable
|
||||
|
||||
; Properties
|
||||
;-----------
|
||||
(property_identifier) @variable.member
|
||||
|
||||
(shorthand_property_identifier) @variable.member
|
||||
|
||||
(private_property_identifier) @variable.member
|
||||
|
||||
(object_pattern
|
||||
(shorthand_property_identifier_pattern) @variable)
|
||||
|
||||
(object_pattern
|
||||
(object_assignment_pattern
|
||||
(shorthand_property_identifier_pattern) @variable))
|
||||
|
||||
; Special identifiers
|
||||
;--------------------
|
||||
((identifier) @type
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
((identifier) @constant
|
||||
(#lua-match? @constant "^_*[A-Z][A-Z%d_]*$"))
|
||||
|
||||
((shorthand_property_identifier) @constant
|
||||
(#lua-match? @constant "^_*[A-Z][A-Z%d_]*$"))
|
||||
|
||||
((identifier) @variable.builtin
|
||||
(#any-of? @variable.builtin "arguments" "module" "console" "window" "document"))
|
||||
|
||||
((identifier) @type.builtin
|
||||
(#any-of? @type.builtin
|
||||
"Object" "Function" "Boolean" "Symbol" "Number" "Math" "Date" "String" "RegExp" "Map" "Set"
|
||||
"WeakMap" "WeakSet" "Promise" "Array" "Int8Array" "Uint8Array" "Uint8ClampedArray" "Int16Array"
|
||||
"Uint16Array" "Int32Array" "Uint32Array" "Float32Array" "Float64Array" "ArrayBuffer" "DataView"
|
||||
"Error" "EvalError" "InternalError" "RangeError" "ReferenceError" "SyntaxError" "TypeError"
|
||||
"URIError"))
|
||||
|
||||
(statement_identifier) @label
|
||||
|
||||
; Function and method definitions
|
||||
;--------------------------------
|
||||
(function_expression
|
||||
name: (identifier) @function)
|
||||
|
||||
(function_declaration
|
||||
name: (identifier) @function)
|
||||
|
||||
(generator_function
|
||||
name: (identifier) @function)
|
||||
|
||||
(generator_function_declaration
|
||||
name: (identifier) @function)
|
||||
|
||||
(method_definition
|
||||
name: [
|
||||
(property_identifier)
|
||||
(private_property_identifier)
|
||||
] @function.method)
|
||||
|
||||
(method_definition
|
||||
name: (property_identifier) @constructor
|
||||
(#eq? @constructor "constructor"))
|
||||
|
||||
(pair
|
||||
key: (property_identifier) @function.method
|
||||
value: (function_expression))
|
||||
|
||||
(pair
|
||||
key: (property_identifier) @function.method
|
||||
value: (arrow_function))
|
||||
|
||||
(assignment_expression
|
||||
left: (member_expression
|
||||
property: (property_identifier) @function.method)
|
||||
right: (arrow_function))
|
||||
|
||||
(assignment_expression
|
||||
left: (member_expression
|
||||
property: (property_identifier) @function.method)
|
||||
right: (function_expression))
|
||||
|
||||
(variable_declarator
|
||||
name: (identifier) @function
|
||||
value: (arrow_function))
|
||||
|
||||
(variable_declarator
|
||||
name: (identifier) @function
|
||||
value: (function_expression))
|
||||
|
||||
(assignment_expression
|
||||
left: (identifier) @function
|
||||
right: (arrow_function))
|
||||
|
||||
(assignment_expression
|
||||
left: (identifier) @function
|
||||
right: (function_expression))
|
||||
|
||||
; Function and method calls
|
||||
;--------------------------
|
||||
(call_expression
|
||||
function: (identifier) @function.call)
|
||||
|
||||
(call_expression
|
||||
function: (member_expression
|
||||
property: [
|
||||
(property_identifier)
|
||||
(private_property_identifier)
|
||||
] @function.method.call))
|
||||
|
||||
(call_expression
|
||||
function: (await_expression
|
||||
(identifier) @function.call))
|
||||
|
||||
(call_expression
|
||||
function: (await_expression
|
||||
(member_expression
|
||||
property: [
|
||||
(property_identifier)
|
||||
(private_property_identifier)
|
||||
] @function.method.call)))
|
||||
|
||||
; Builtins
|
||||
;---------
|
||||
((identifier) @module.builtin
|
||||
(#eq? @module.builtin "Intl"))
|
||||
|
||||
((identifier) @function.builtin
|
||||
(#any-of? @function.builtin
|
||||
"eval" "isFinite" "isNaN" "parseFloat" "parseInt" "decodeURI" "decodeURIComponent" "encodeURI"
|
||||
"encodeURIComponent" "require"))
|
||||
|
||||
; Constructor
|
||||
;------------
|
||||
(new_expression
|
||||
constructor: (identifier) @constructor)
|
||||
|
||||
; Decorators
|
||||
;----------
|
||||
(decorator
|
||||
"@" @attribute
|
||||
(identifier) @attribute)
|
||||
|
||||
(decorator
|
||||
"@" @attribute
|
||||
(call_expression
|
||||
(identifier) @attribute))
|
||||
|
||||
(decorator
|
||||
"@" @attribute
|
||||
(member_expression
|
||||
(property_identifier) @attribute))
|
||||
|
||||
(decorator
|
||||
"@" @attribute
|
||||
(call_expression
|
||||
(member_expression
|
||||
(property_identifier) @attribute)))
|
||||
|
||||
; Literals
|
||||
;---------
|
||||
[
|
||||
(this)
|
||||
(super)
|
||||
] @variable.builtin
|
||||
|
||||
((identifier) @variable.builtin
|
||||
(#eq? @variable.builtin "self"))
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
] @boolean
|
||||
|
||||
[
|
||||
(null)
|
||||
(undefined)
|
||||
] @constant.builtin
|
||||
|
||||
[
|
||||
(comment)
|
||||
(html_comment)
|
||||
] @comment @spell
|
||||
|
||||
((comment) @comment.documentation
|
||||
(#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$"))
|
||||
|
||||
(hash_bang_line) @keyword.directive
|
||||
|
||||
((string_fragment) @keyword.directive
|
||||
(#eq? @keyword.directive "use strict"))
|
||||
|
||||
(string) @string
|
||||
|
||||
(template_string) @string
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
(regex_pattern) @string.regexp
|
||||
|
||||
(regex_flags) @character.special
|
||||
|
||||
(regex
|
||||
"/" @punctuation.bracket) ; Regex delimiters
|
||||
|
||||
(number) @number
|
||||
|
||||
((identifier) @number
|
||||
(#any-of? @number "NaN" "Infinity"))
|
||||
|
||||
; Punctuation
|
||||
;------------
|
||||
[
|
||||
";"
|
||||
"."
|
||||
","
|
||||
":"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"--"
|
||||
"-"
|
||||
"-="
|
||||
"&&"
|
||||
"+"
|
||||
"++"
|
||||
"+="
|
||||
"&="
|
||||
"/="
|
||||
"**="
|
||||
"<<="
|
||||
"<"
|
||||
"<="
|
||||
"<<"
|
||||
"="
|
||||
"=="
|
||||
"==="
|
||||
"!="
|
||||
"!=="
|
||||
"=>"
|
||||
">"
|
||||
">="
|
||||
">>"
|
||||
"||"
|
||||
"%"
|
||||
"%="
|
||||
"*"
|
||||
"**"
|
||||
">>>"
|
||||
"&"
|
||||
"|"
|
||||
"^"
|
||||
"??"
|
||||
"*="
|
||||
">>="
|
||||
">>>="
|
||||
"^="
|
||||
"|="
|
||||
"&&="
|
||||
"||="
|
||||
"??="
|
||||
"..."
|
||||
] @operator
|
||||
|
||||
(binary_expression
|
||||
"/" @operator)
|
||||
|
||||
(ternary_expression
|
||||
[
|
||||
"?"
|
||||
":"
|
||||
] @keyword.conditional.ternary)
|
||||
|
||||
(unary_expression
|
||||
[
|
||||
"!"
|
||||
"~"
|
||||
"-"
|
||||
"+"
|
||||
] @operator)
|
||||
|
||||
(unary_expression
|
||||
[
|
||||
"delete"
|
||||
"void"
|
||||
] @keyword.operator)
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
(template_substitution
|
||||
[
|
||||
"${"
|
||||
"}"
|
||||
] @punctuation.special) @none
|
||||
|
||||
; Imports
|
||||
;----------
|
||||
(namespace_import
|
||||
"*" @character.special
|
||||
(identifier) @module)
|
||||
|
||||
(namespace_export
|
||||
"*" @character.special
|
||||
(identifier) @module)
|
||||
|
||||
(export_statement
|
||||
"*" @character.special)
|
||||
|
||||
; Keywords
|
||||
;----------
|
||||
[
|
||||
"if"
|
||||
"else"
|
||||
"switch"
|
||||
"case"
|
||||
] @keyword.conditional
|
||||
|
||||
[
|
||||
"import"
|
||||
"from"
|
||||
"as"
|
||||
"export"
|
||||
] @keyword.import
|
||||
|
||||
[
|
||||
"for"
|
||||
"of"
|
||||
"do"
|
||||
"while"
|
||||
"continue"
|
||||
] @keyword.repeat
|
||||
|
||||
[
|
||||
"break"
|
||||
"const"
|
||||
"debugger"
|
||||
"extends"
|
||||
"get"
|
||||
"let"
|
||||
"set"
|
||||
"static"
|
||||
"target"
|
||||
"var"
|
||||
"with"
|
||||
] @keyword
|
||||
|
||||
"class" @keyword.type
|
||||
|
||||
[
|
||||
"async"
|
||||
"await"
|
||||
] @keyword.coroutine
|
||||
|
||||
[
|
||||
"return"
|
||||
"yield"
|
||||
] @keyword.return
|
||||
|
||||
"function" @keyword.function
|
||||
|
||||
[
|
||||
"new"
|
||||
"delete"
|
||||
"in"
|
||||
"instanceof"
|
||||
"typeof"
|
||||
] @keyword.operator
|
||||
|
||||
[
|
||||
"throw"
|
||||
"try"
|
||||
"catch"
|
||||
"finally"
|
||||
] @keyword.exception
|
||||
|
||||
(export_statement
|
||||
"default" @keyword)
|
||||
|
||||
(switch_default
|
||||
"default" @keyword.conditional)
|
||||
|
||||
"require" @keyword.import
|
||||
|
||||
(import_require_clause
|
||||
source: (string) @string.special.url)
|
||||
|
||||
[
|
||||
"declare"
|
||||
"implements"
|
||||
"type"
|
||||
"override"
|
||||
"module"
|
||||
"asserts"
|
||||
"infer"
|
||||
"is"
|
||||
"using"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"namespace"
|
||||
"interface"
|
||||
"enum"
|
||||
] @keyword.type
|
||||
|
||||
[
|
||||
"keyof"
|
||||
"satisfies"
|
||||
] @keyword.operator
|
||||
|
||||
(as_expression
|
||||
"as" @keyword.operator)
|
||||
|
||||
(mapped_type_clause
|
||||
"as" @keyword.operator)
|
||||
|
||||
[
|
||||
"abstract"
|
||||
"private"
|
||||
"protected"
|
||||
"public"
|
||||
"readonly"
|
||||
] @keyword.modifier
|
||||
|
||||
; types
|
||||
(type_identifier) @type
|
||||
|
||||
(predefined_type) @type.builtin
|
||||
|
||||
(import_statement
|
||||
"type"
|
||||
(import_clause
|
||||
(named_imports
|
||||
(import_specifier
|
||||
name: (identifier) @type))))
|
||||
|
||||
(template_literal_type) @string
|
||||
|
||||
(non_null_expression
|
||||
"!" @operator)
|
||||
|
||||
; punctuation
|
||||
(type_arguments
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(type_parameters
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(object_type
|
||||
[
|
||||
"{|"
|
||||
"|}"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(union_type
|
||||
"|" @punctuation.delimiter)
|
||||
|
||||
(intersection_type
|
||||
"&" @punctuation.delimiter)
|
||||
|
||||
(type_annotation
|
||||
":" @punctuation.delimiter)
|
||||
|
||||
(type_predicate_annotation
|
||||
":" @punctuation.delimiter)
|
||||
|
||||
(index_signature
|
||||
":" @punctuation.delimiter)
|
||||
|
||||
(omitting_type_annotation
|
||||
"-?:" @punctuation.delimiter)
|
||||
|
||||
(adding_type_annotation
|
||||
"+?:" @punctuation.delimiter)
|
||||
|
||||
(opting_type_annotation
|
||||
"?:" @punctuation.delimiter)
|
||||
|
||||
"?." @punctuation.delimiter
|
||||
|
||||
(abstract_method_signature
|
||||
"?" @punctuation.special)
|
||||
|
||||
(method_signature
|
||||
"?" @punctuation.special)
|
||||
|
||||
(method_definition
|
||||
"?" @punctuation.special)
|
||||
|
||||
(property_signature
|
||||
"?" @punctuation.special)
|
||||
|
||||
(optional_parameter
|
||||
"?" @punctuation.special)
|
||||
|
||||
(optional_type
|
||||
"?" @punctuation.special)
|
||||
|
||||
(public_field_definition
|
||||
[
|
||||
"?"
|
||||
"!"
|
||||
] @punctuation.special)
|
||||
|
||||
(flow_maybe_type
|
||||
"?" @punctuation.special)
|
||||
|
||||
(template_type
|
||||
[
|
||||
"${"
|
||||
"}"
|
||||
] @punctuation.special)
|
||||
|
||||
(conditional_type
|
||||
[
|
||||
"?"
|
||||
":"
|
||||
] @keyword.conditional.ternary)
|
||||
|
||||
; Parameters
|
||||
(required_parameter
|
||||
pattern: (identifier) @variable.parameter)
|
||||
|
||||
(optional_parameter
|
||||
pattern: (identifier) @variable.parameter)
|
||||
|
||||
(required_parameter
|
||||
(rest_pattern
|
||||
(identifier) @variable.parameter))
|
||||
|
||||
; ({ a }) => null
|
||||
(required_parameter
|
||||
(object_pattern
|
||||
(shorthand_property_identifier_pattern) @variable.parameter))
|
||||
|
||||
; ({ a = b }) => null
|
||||
(required_parameter
|
||||
(object_pattern
|
||||
(object_assignment_pattern
|
||||
(shorthand_property_identifier_pattern) @variable.parameter)))
|
||||
|
||||
; ({ a: b }) => null
|
||||
(required_parameter
|
||||
(object_pattern
|
||||
(pair_pattern
|
||||
value: (identifier) @variable.parameter)))
|
||||
|
||||
; ([ a ]) => null
|
||||
(required_parameter
|
||||
(array_pattern
|
||||
(identifier) @variable.parameter))
|
||||
|
||||
; a => null
|
||||
(arrow_function
|
||||
parameter: (identifier) @variable.parameter)
|
||||
|
||||
; global declaration
|
||||
(ambient_declaration
|
||||
"global" @module)
|
||||
|
||||
; function signatures
|
||||
(ambient_declaration
|
||||
(function_signature
|
||||
name: (identifier) @function))
|
||||
|
||||
; method signatures
|
||||
(method_signature
|
||||
name: (_) @function.method)
|
||||
|
||||
(abstract_method_signature
|
||||
name: (property_identifier) @function.method)
|
||||
|
||||
; property signatures
|
||||
(property_signature
|
||||
name: (property_identifier) @function.method
|
||||
type: (type_annotation
|
||||
[
|
||||
(union_type
|
||||
(parenthesized_type
|
||||
(function_type)))
|
||||
(function_type)
|
||||
]))
|
||||
599
internal/syntax/queries/typescript/highlights.scm
Normal file
599
internal/syntax/queries/typescript/highlights.scm
Normal file
@ -0,0 +1,599 @@
|
||||
; Types
|
||||
; Javascript
|
||||
; Variables
|
||||
;-----------
|
||||
(identifier) @variable
|
||||
|
||||
; Properties
|
||||
;-----------
|
||||
(property_identifier) @variable.member
|
||||
|
||||
(shorthand_property_identifier) @variable.member
|
||||
|
||||
(private_property_identifier) @variable.member
|
||||
|
||||
(object_pattern
|
||||
(shorthand_property_identifier_pattern) @variable)
|
||||
|
||||
(object_pattern
|
||||
(object_assignment_pattern
|
||||
(shorthand_property_identifier_pattern) @variable))
|
||||
|
||||
; Special identifiers
|
||||
;--------------------
|
||||
((identifier) @type
|
||||
(#lua-match? @type "^[A-Z]"))
|
||||
|
||||
((identifier) @constant
|
||||
(#lua-match? @constant "^_*[A-Z][A-Z%d_]*$"))
|
||||
|
||||
((shorthand_property_identifier) @constant
|
||||
(#lua-match? @constant "^_*[A-Z][A-Z%d_]*$"))
|
||||
|
||||
((identifier) @variable.builtin
|
||||
(#any-of? @variable.builtin "arguments" "module" "console" "window" "document"))
|
||||
|
||||
((identifier) @type.builtin
|
||||
(#any-of? @type.builtin
|
||||
"Object" "Function" "Boolean" "Symbol" "Number" "Math" "Date" "String" "RegExp" "Map" "Set"
|
||||
"WeakMap" "WeakSet" "Promise" "Array" "Int8Array" "Uint8Array" "Uint8ClampedArray" "Int16Array"
|
||||
"Uint16Array" "Int32Array" "Uint32Array" "Float32Array" "Float64Array" "ArrayBuffer" "DataView"
|
||||
"Error" "EvalError" "InternalError" "RangeError" "ReferenceError" "SyntaxError" "TypeError"
|
||||
"URIError"))
|
||||
|
||||
(statement_identifier) @label
|
||||
|
||||
; Function and method definitions
|
||||
;--------------------------------
|
||||
(function_expression
|
||||
name: (identifier) @function)
|
||||
|
||||
(function_declaration
|
||||
name: (identifier) @function)
|
||||
|
||||
(generator_function
|
||||
name: (identifier) @function)
|
||||
|
||||
(generator_function_declaration
|
||||
name: (identifier) @function)
|
||||
|
||||
(method_definition
|
||||
name: [
|
||||
(property_identifier)
|
||||
(private_property_identifier)
|
||||
] @function.method)
|
||||
|
||||
(method_definition
|
||||
name: (property_identifier) @constructor
|
||||
(#eq? @constructor "constructor"))
|
||||
|
||||
(pair
|
||||
key: (property_identifier) @function.method
|
||||
value: (function_expression))
|
||||
|
||||
(pair
|
||||
key: (property_identifier) @function.method
|
||||
value: (arrow_function))
|
||||
|
||||
(assignment_expression
|
||||
left: (member_expression
|
||||
property: (property_identifier) @function.method)
|
||||
right: (arrow_function))
|
||||
|
||||
(assignment_expression
|
||||
left: (member_expression
|
||||
property: (property_identifier) @function.method)
|
||||
right: (function_expression))
|
||||
|
||||
(variable_declarator
|
||||
name: (identifier) @function
|
||||
value: (arrow_function))
|
||||
|
||||
(variable_declarator
|
||||
name: (identifier) @function
|
||||
value: (function_expression))
|
||||
|
||||
(assignment_expression
|
||||
left: (identifier) @function
|
||||
right: (arrow_function))
|
||||
|
||||
(assignment_expression
|
||||
left: (identifier) @function
|
||||
right: (function_expression))
|
||||
|
||||
; Function and method calls
|
||||
;--------------------------
|
||||
(call_expression
|
||||
function: (identifier) @function.call)
|
||||
|
||||
(call_expression
|
||||
function: (member_expression
|
||||
property: [
|
||||
(property_identifier)
|
||||
(private_property_identifier)
|
||||
] @function.method.call))
|
||||
|
||||
(call_expression
|
||||
function: (await_expression
|
||||
(identifier) @function.call))
|
||||
|
||||
(call_expression
|
||||
function: (await_expression
|
||||
(member_expression
|
||||
property: [
|
||||
(property_identifier)
|
||||
(private_property_identifier)
|
||||
] @function.method.call)))
|
||||
|
||||
; Builtins
|
||||
;---------
|
||||
((identifier) @module.builtin
|
||||
(#eq? @module.builtin "Intl"))
|
||||
|
||||
((identifier) @function.builtin
|
||||
(#any-of? @function.builtin
|
||||
"eval" "isFinite" "isNaN" "parseFloat" "parseInt" "decodeURI" "decodeURIComponent" "encodeURI"
|
||||
"encodeURIComponent" "require"))
|
||||
|
||||
; Constructor
|
||||
;------------
|
||||
(new_expression
|
||||
constructor: (identifier) @constructor)
|
||||
|
||||
; Decorators
|
||||
;----------
|
||||
(decorator
|
||||
"@" @attribute
|
||||
(identifier) @attribute)
|
||||
|
||||
(decorator
|
||||
"@" @attribute
|
||||
(call_expression
|
||||
(identifier) @attribute))
|
||||
|
||||
(decorator
|
||||
"@" @attribute
|
||||
(member_expression
|
||||
(property_identifier) @attribute))
|
||||
|
||||
(decorator
|
||||
"@" @attribute
|
||||
(call_expression
|
||||
(member_expression
|
||||
(property_identifier) @attribute)))
|
||||
|
||||
; Literals
|
||||
;---------
|
||||
[
|
||||
(this)
|
||||
(super)
|
||||
] @variable.builtin
|
||||
|
||||
((identifier) @variable.builtin
|
||||
(#eq? @variable.builtin "self"))
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
] @boolean
|
||||
|
||||
[
|
||||
(null)
|
||||
(undefined)
|
||||
] @constant.builtin
|
||||
|
||||
[
|
||||
(comment)
|
||||
(html_comment)
|
||||
] @comment @spell
|
||||
|
||||
((comment) @comment.documentation
|
||||
(#lua-match? @comment.documentation "^/[*][*][^*].*[*]/$"))
|
||||
|
||||
(hash_bang_line) @keyword.directive
|
||||
|
||||
((string_fragment) @keyword.directive
|
||||
(#eq? @keyword.directive "use strict"))
|
||||
|
||||
(string) @string
|
||||
|
||||
(template_string) @string
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
(regex_pattern) @string.regexp
|
||||
|
||||
(regex_flags) @character.special
|
||||
|
||||
(regex
|
||||
"/" @punctuation.bracket) ; Regex delimiters
|
||||
|
||||
(number) @number
|
||||
|
||||
((identifier) @number
|
||||
(#any-of? @number "NaN" "Infinity"))
|
||||
|
||||
; Punctuation
|
||||
;------------
|
||||
[
|
||||
";"
|
||||
"."
|
||||
","
|
||||
":"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"--"
|
||||
"-"
|
||||
"-="
|
||||
"&&"
|
||||
"+"
|
||||
"++"
|
||||
"+="
|
||||
"&="
|
||||
"/="
|
||||
"**="
|
||||
"<<="
|
||||
"<"
|
||||
"<="
|
||||
"<<"
|
||||
"="
|
||||
"=="
|
||||
"==="
|
||||
"!="
|
||||
"!=="
|
||||
"=>"
|
||||
">"
|
||||
">="
|
||||
">>"
|
||||
"||"
|
||||
"%"
|
||||
"%="
|
||||
"*"
|
||||
"**"
|
||||
">>>"
|
||||
"&"
|
||||
"|"
|
||||
"^"
|
||||
"??"
|
||||
"*="
|
||||
">>="
|
||||
">>>="
|
||||
"^="
|
||||
"|="
|
||||
"&&="
|
||||
"||="
|
||||
"??="
|
||||
"..."
|
||||
] @operator
|
||||
|
||||
(binary_expression
|
||||
"/" @operator)
|
||||
|
||||
(ternary_expression
|
||||
[
|
||||
"?"
|
||||
":"
|
||||
] @keyword.conditional.ternary)
|
||||
|
||||
(unary_expression
|
||||
[
|
||||
"!"
|
||||
"~"
|
||||
"-"
|
||||
"+"
|
||||
] @operator)
|
||||
|
||||
(unary_expression
|
||||
[
|
||||
"delete"
|
||||
"void"
|
||||
] @keyword.operator)
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
(template_substitution
|
||||
[
|
||||
"${"
|
||||
"}"
|
||||
] @punctuation.special) @none
|
||||
|
||||
; Imports
|
||||
;----------
|
||||
(namespace_import
|
||||
"*" @character.special
|
||||
(identifier) @module)
|
||||
|
||||
(namespace_export
|
||||
"*" @character.special
|
||||
(identifier) @module)
|
||||
|
||||
(export_statement
|
||||
"*" @character.special)
|
||||
|
||||
; Keywords
|
||||
;----------
|
||||
[
|
||||
"if"
|
||||
"else"
|
||||
"switch"
|
||||
"case"
|
||||
] @keyword.conditional
|
||||
|
||||
[
|
||||
"import"
|
||||
"from"
|
||||
"as"
|
||||
"export"
|
||||
] @keyword.import
|
||||
|
||||
[
|
||||
"for"
|
||||
"of"
|
||||
"do"
|
||||
"while"
|
||||
"continue"
|
||||
] @keyword.repeat
|
||||
|
||||
[
|
||||
"break"
|
||||
"const"
|
||||
"debugger"
|
||||
"extends"
|
||||
"get"
|
||||
"let"
|
||||
"set"
|
||||
"static"
|
||||
"target"
|
||||
"var"
|
||||
"with"
|
||||
] @keyword
|
||||
|
||||
"class" @keyword.type
|
||||
|
||||
[
|
||||
"async"
|
||||
"await"
|
||||
] @keyword.coroutine
|
||||
|
||||
[
|
||||
"return"
|
||||
"yield"
|
||||
] @keyword.return
|
||||
|
||||
"function" @keyword.function
|
||||
|
||||
[
|
||||
"new"
|
||||
"delete"
|
||||
"in"
|
||||
"instanceof"
|
||||
"typeof"
|
||||
] @keyword.operator
|
||||
|
||||
[
|
||||
"throw"
|
||||
"try"
|
||||
"catch"
|
||||
"finally"
|
||||
] @keyword.exception
|
||||
|
||||
(export_statement
|
||||
"default" @keyword)
|
||||
|
||||
(switch_default
|
||||
"default" @keyword.conditional)
|
||||
|
||||
"require" @keyword.import
|
||||
|
||||
(import_require_clause
|
||||
source: (string) @string.special.url)
|
||||
|
||||
[
|
||||
"declare"
|
||||
"implements"
|
||||
"type"
|
||||
"override"
|
||||
"module"
|
||||
"asserts"
|
||||
"infer"
|
||||
"is"
|
||||
"using"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"namespace"
|
||||
"interface"
|
||||
"enum"
|
||||
] @keyword.type
|
||||
|
||||
[
|
||||
"keyof"
|
||||
"satisfies"
|
||||
] @keyword.operator
|
||||
|
||||
(as_expression
|
||||
"as" @keyword.operator)
|
||||
|
||||
(mapped_type_clause
|
||||
"as" @keyword.operator)
|
||||
|
||||
[
|
||||
"abstract"
|
||||
"private"
|
||||
"protected"
|
||||
"public"
|
||||
"readonly"
|
||||
] @keyword.modifier
|
||||
|
||||
; types
|
||||
(type_identifier) @type
|
||||
|
||||
(predefined_type) @type.builtin
|
||||
|
||||
(import_statement
|
||||
"type"
|
||||
(import_clause
|
||||
(named_imports
|
||||
(import_specifier
|
||||
name: (identifier) @type))))
|
||||
|
||||
(template_literal_type) @string
|
||||
|
||||
(non_null_expression
|
||||
"!" @operator)
|
||||
|
||||
; punctuation
|
||||
(type_arguments
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(type_parameters
|
||||
[
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(object_type
|
||||
[
|
||||
"{|"
|
||||
"|}"
|
||||
] @punctuation.bracket)
|
||||
|
||||
(union_type
|
||||
"|" @punctuation.delimiter)
|
||||
|
||||
(intersection_type
|
||||
"&" @punctuation.delimiter)
|
||||
|
||||
(type_annotation
|
||||
":" @punctuation.delimiter)
|
||||
|
||||
(type_predicate_annotation
|
||||
":" @punctuation.delimiter)
|
||||
|
||||
(index_signature
|
||||
":" @punctuation.delimiter)
|
||||
|
||||
(omitting_type_annotation
|
||||
"-?:" @punctuation.delimiter)
|
||||
|
||||
(adding_type_annotation
|
||||
"+?:" @punctuation.delimiter)
|
||||
|
||||
(opting_type_annotation
|
||||
"?:" @punctuation.delimiter)
|
||||
|
||||
"?." @punctuation.delimiter
|
||||
|
||||
(abstract_method_signature
|
||||
"?" @punctuation.special)
|
||||
|
||||
(method_signature
|
||||
"?" @punctuation.special)
|
||||
|
||||
(method_definition
|
||||
"?" @punctuation.special)
|
||||
|
||||
(property_signature
|
||||
"?" @punctuation.special)
|
||||
|
||||
(optional_parameter
|
||||
"?" @punctuation.special)
|
||||
|
||||
(optional_type
|
||||
"?" @punctuation.special)
|
||||
|
||||
(public_field_definition
|
||||
[
|
||||
"?"
|
||||
"!"
|
||||
] @punctuation.special)
|
||||
|
||||
(flow_maybe_type
|
||||
"?" @punctuation.special)
|
||||
|
||||
(template_type
|
||||
[
|
||||
"${"
|
||||
"}"
|
||||
] @punctuation.special)
|
||||
|
||||
(conditional_type
|
||||
[
|
||||
"?"
|
||||
":"
|
||||
] @keyword.conditional.ternary)
|
||||
|
||||
; Parameters
|
||||
(required_parameter
|
||||
pattern: (identifier) @variable.parameter)
|
||||
|
||||
(optional_parameter
|
||||
pattern: (identifier) @variable.parameter)
|
||||
|
||||
(required_parameter
|
||||
(rest_pattern
|
||||
(identifier) @variable.parameter))
|
||||
|
||||
; ({ a }) => null
|
||||
(required_parameter
|
||||
(object_pattern
|
||||
(shorthand_property_identifier_pattern) @variable.parameter))
|
||||
|
||||
; ({ a = b }) => null
|
||||
(required_parameter
|
||||
(object_pattern
|
||||
(object_assignment_pattern
|
||||
(shorthand_property_identifier_pattern) @variable.parameter)))
|
||||
|
||||
; ({ a: b }) => null
|
||||
(required_parameter
|
||||
(object_pattern
|
||||
(pair_pattern
|
||||
value: (identifier) @variable.parameter)))
|
||||
|
||||
; ([ a ]) => null
|
||||
(required_parameter
|
||||
(array_pattern
|
||||
(identifier) @variable.parameter))
|
||||
|
||||
; a => null
|
||||
(arrow_function
|
||||
parameter: (identifier) @variable.parameter)
|
||||
|
||||
; global declaration
|
||||
(ambient_declaration
|
||||
"global" @module)
|
||||
|
||||
; function signatures
|
||||
(ambient_declaration
|
||||
(function_signature
|
||||
name: (identifier) @function))
|
||||
|
||||
; method signatures
|
||||
(method_signature
|
||||
name: (_) @function.method)
|
||||
|
||||
(abstract_method_signature
|
||||
name: (property_identifier) @function.method)
|
||||
|
||||
; property signatures
|
||||
(property_signature
|
||||
name: (property_identifier) @function.method
|
||||
type: (type_annotation
|
||||
[
|
||||
(union_type
|
||||
(parenthesized_type
|
||||
(function_type)))
|
||||
(function_type)
|
||||
]))
|
||||
108
internal/syntax/query_assets.go
Normal file
108
internal/syntax/query_assets.go
Normal file
@ -0,0 +1,108 @@
|
||||
package syntax
|
||||
|
||||
import _ "embed"
|
||||
|
||||
//go:embed queries/go/highlights.scm
|
||||
var goHighlightsQuery string
|
||||
|
||||
//go:embed queries/javascript/highlights.scm
|
||||
var javascriptHighlightsQuery string
|
||||
|
||||
//go:embed queries/python/highlights.scm
|
||||
var pythonHighlightsQuery string
|
||||
|
||||
//go:embed queries/rust/highlights.scm
|
||||
var rustHighlightsQuery string
|
||||
|
||||
//go:embed queries/typescript/highlights.scm
|
||||
var typescriptHighlightsQuery string
|
||||
|
||||
//go:embed queries/tsx/highlights.scm
|
||||
var tsxHighlightsQuery string
|
||||
|
||||
//go:embed queries/bash/highlights.scm
|
||||
var bashHighlightsQuery string
|
||||
|
||||
//go:embed queries/json/highlights.scm
|
||||
var jsonHighlightsQuery string
|
||||
|
||||
//go:embed queries/css/highlights.scm
|
||||
var cssHighlightsQuery string
|
||||
|
||||
//go:embed queries/html/highlights.scm
|
||||
var htmlHighlightsQuery string
|
||||
|
||||
//go:embed queries/c/highlights.scm
|
||||
var cHighlightsQuery string
|
||||
|
||||
//go:embed queries/cpp/highlights.scm
|
||||
var cppHighlightsQuery string
|
||||
|
||||
//go:embed queries/java/highlights.scm
|
||||
var javaHighlightsQuery string
|
||||
|
||||
//go:embed queries/csharp/highlights.scm
|
||||
var csharpHighlightsQuery string
|
||||
|
||||
//go:embed queries/ruby/highlights.scm
|
||||
var rubyHighlightsQuery string
|
||||
|
||||
func loadGoHighlightsQuery() ([]byte, error) {
|
||||
return []byte(goHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadJavaScriptHighlightsQuery() ([]byte, error) {
|
||||
return []byte(javascriptHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadPythonHighlightsQuery() ([]byte, error) {
|
||||
return []byte(pythonHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadRustHighlightsQuery() ([]byte, error) {
|
||||
return []byte(rustHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadTypeScriptHighlightsQuery() ([]byte, error) {
|
||||
return []byte(typescriptHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadTSXHighlightsQuery() ([]byte, error) {
|
||||
return []byte(tsxHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadBashHighlightsQuery() ([]byte, error) {
|
||||
return []byte(bashHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadJSONHighlightsQuery() ([]byte, error) {
|
||||
return []byte(jsonHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadCSSHighlightsQuery() ([]byte, error) {
|
||||
return []byte(cssHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadHTMLHighlightsQuery() ([]byte, error) {
|
||||
return []byte(htmlHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadCHighlightsQuery() ([]byte, error) {
|
||||
return []byte(cHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadCppHighlightsQuery() ([]byte, error) {
|
||||
return []byte(cppHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadJavaHighlightsQuery() ([]byte, error) {
|
||||
return []byte(javaHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadCSharpHighlightsQuery() ([]byte, error) {
|
||||
return []byte(csharpHighlightsQuery), nil
|
||||
}
|
||||
|
||||
func loadRubyHighlightsQuery() ([]byte, error) {
|
||||
return []byte(rubyHighlightsQuery), nil
|
||||
}
|
||||
101
internal/syntax/query_assets_test.go
Normal file
101
internal/syntax/query_assets_test.go
Normal file
@ -0,0 +1,101 @@
|
||||
package syntax
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
sitter "github.com/tree-sitter/go-tree-sitter"
|
||||
ts_bash "github.com/tree-sitter/tree-sitter-bash/bindings/go"
|
||||
ts_csharp "github.com/tree-sitter/tree-sitter-c-sharp/bindings/go"
|
||||
ts_c "github.com/tree-sitter/tree-sitter-c/bindings/go"
|
||||
ts_cpp "github.com/tree-sitter/tree-sitter-cpp/bindings/go"
|
||||
ts_css "github.com/tree-sitter/tree-sitter-css/bindings/go"
|
||||
ts_go "github.com/tree-sitter/tree-sitter-go/bindings/go"
|
||||
ts_html "github.com/tree-sitter/tree-sitter-html/bindings/go"
|
||||
ts_java "github.com/tree-sitter/tree-sitter-java/bindings/go"
|
||||
ts_js "github.com/tree-sitter/tree-sitter-javascript/bindings/go"
|
||||
ts_json "github.com/tree-sitter/tree-sitter-json/bindings/go"
|
||||
ts_python "github.com/tree-sitter/tree-sitter-python/bindings/go"
|
||||
ts_ruby "github.com/tree-sitter/tree-sitter-ruby/bindings/go"
|
||||
ts_rust "github.com/tree-sitter/tree-sitter-rust/bindings/go"
|
||||
ts_ts "github.com/tree-sitter/tree-sitter-typescript/bindings/go"
|
||||
)
|
||||
|
||||
func TestEmbeddedQueriesLoadAndCompile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
loadQuery func() ([]byte, error)
|
||||
expectNonNil bool
|
||||
}{
|
||||
{name: "go", loadQuery: loadGoHighlightsQuery, expectNonNil: true},
|
||||
{name: "javascript", loadQuery: loadJavaScriptHighlightsQuery, expectNonNil: true},
|
||||
{name: "typescript", loadQuery: loadTypeScriptHighlightsQuery, expectNonNil: true},
|
||||
{name: "tsx", loadQuery: loadTSXHighlightsQuery, expectNonNil: true},
|
||||
{name: "python", loadQuery: loadPythonHighlightsQuery, expectNonNil: true},
|
||||
{name: "rust", loadQuery: loadRustHighlightsQuery, expectNonNil: true},
|
||||
{name: "bash", loadQuery: loadBashHighlightsQuery, expectNonNil: true},
|
||||
{name: "json", loadQuery: loadJSONHighlightsQuery, expectNonNil: true},
|
||||
{name: "css", loadQuery: loadCSSHighlightsQuery, expectNonNil: true},
|
||||
{name: "html", loadQuery: loadHTMLHighlightsQuery, expectNonNil: true},
|
||||
{name: "c", loadQuery: loadCHighlightsQuery, expectNonNil: true},
|
||||
{name: "cpp", loadQuery: loadCppHighlightsQuery, expectNonNil: true},
|
||||
{name: "java", loadQuery: loadJavaHighlightsQuery, expectNonNil: true},
|
||||
{name: "csharp", loadQuery: loadCSharpHighlightsQuery, expectNonNil: true},
|
||||
{name: "ruby", loadQuery: loadRubyHighlightsQuery, expectNonNil: true},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
b, err := tc.loadQuery()
|
||||
if err != nil {
|
||||
t.Fatalf("failed loading embedded query: %v", err)
|
||||
}
|
||||
if tc.expectNonNil && b == nil {
|
||||
t.Fatalf("expected non-nil embedded query bytes")
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmbeddedQueriesCompileKnownGood(t *testing.T) {
|
||||
compileTests := []struct {
|
||||
name string
|
||||
loadQuery func() ([]byte, error)
|
||||
newLanguage func() *sitter.Language
|
||||
}{
|
||||
{name: "go", loadQuery: loadGoHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_go.Language()) }},
|
||||
{name: "javascript", loadQuery: loadJavaScriptHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_js.Language()) }},
|
||||
{name: "typescript", loadQuery: loadTypeScriptHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_ts.LanguageTypescript()) }},
|
||||
{name: "tsx", loadQuery: loadTSXHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_ts.LanguageTSX()) }},
|
||||
{name: "python", loadQuery: loadPythonHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_python.Language()) }},
|
||||
{name: "rust", loadQuery: loadRustHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_rust.Language()) }},
|
||||
{name: "bash", loadQuery: loadBashHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_bash.Language()) }},
|
||||
{name: "json", loadQuery: loadJSONHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_json.Language()) }},
|
||||
{name: "css", loadQuery: loadCSSHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_css.Language()) }},
|
||||
{name: "html", loadQuery: loadHTMLHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_html.Language()) }},
|
||||
{name: "c", loadQuery: loadCHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_c.Language()) }},
|
||||
{name: "cpp", loadQuery: loadCppHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_cpp.Language()) }},
|
||||
{name: "java", loadQuery: loadJavaHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_java.Language()) }},
|
||||
{name: "csharp", loadQuery: loadCSharpHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_csharp.Language()) }},
|
||||
{name: "ruby", loadQuery: loadRubyHighlightsQuery, newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_ruby.Language()) }},
|
||||
}
|
||||
|
||||
for _, tc := range compileTests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
b, err := tc.loadQuery()
|
||||
if err != nil {
|
||||
t.Fatalf("failed loading embedded query: %v", err)
|
||||
}
|
||||
lang := tc.newLanguage()
|
||||
if lang == nil {
|
||||
t.Fatalf("language handle is nil")
|
||||
}
|
||||
|
||||
q, qErr := sitter.NewQuery(lang, string(b))
|
||||
if qErr != nil {
|
||||
t.Fatalf("embedded query failed to compile: %v", qErr)
|
||||
}
|
||||
q.Close()
|
||||
})
|
||||
}
|
||||
}
|
||||
291
internal/syntax/registry.go
Normal file
291
internal/syntax/registry.go
Normal file
@ -0,0 +1,291 @@
|
||||
package syntax
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
sitter "github.com/tree-sitter/go-tree-sitter"
|
||||
ts_bash "github.com/tree-sitter/tree-sitter-bash/bindings/go"
|
||||
ts_csharp "github.com/tree-sitter/tree-sitter-c-sharp/bindings/go"
|
||||
ts_c "github.com/tree-sitter/tree-sitter-c/bindings/go"
|
||||
ts_cpp "github.com/tree-sitter/tree-sitter-cpp/bindings/go"
|
||||
ts_css "github.com/tree-sitter/tree-sitter-css/bindings/go"
|
||||
ts_go "github.com/tree-sitter/tree-sitter-go/bindings/go"
|
||||
ts_html "github.com/tree-sitter/tree-sitter-html/bindings/go"
|
||||
ts_java "github.com/tree-sitter/tree-sitter-java/bindings/go"
|
||||
ts_js "github.com/tree-sitter/tree-sitter-javascript/bindings/go"
|
||||
ts_json "github.com/tree-sitter/tree-sitter-json/bindings/go"
|
||||
ts_python "github.com/tree-sitter/tree-sitter-python/bindings/go"
|
||||
ts_ruby "github.com/tree-sitter/tree-sitter-ruby/bindings/go"
|
||||
ts_rust "github.com/tree-sitter/tree-sitter-rust/bindings/go"
|
||||
ts_ts "github.com/tree-sitter/tree-sitter-typescript/bindings/go"
|
||||
)
|
||||
|
||||
type languagePack struct {
|
||||
// languagePack.id is the stable registry identifier (for example, "go").
|
||||
id string
|
||||
// languagePack.filetypes are normalized aliases resolved from buffer filetype.
|
||||
filetypes []string
|
||||
// languagePack.extensions are normalized filename extensions (for example, ".go").
|
||||
extensions []string
|
||||
|
||||
// languagePack.newLanguage constructs the tree-sitter language handle.
|
||||
newLanguage func() *sitter.Language
|
||||
// languagePack.loadQuery returns highlights query source for this language.
|
||||
loadQuery func() ([]byte, error)
|
||||
}
|
||||
|
||||
// resolvedLanguage stores compiled runtime assets for one language.
|
||||
//
|
||||
// Instances are cached in languageRegistry.compiledByLang and reused by all
|
||||
// buffers that resolve to the same language id.
|
||||
type resolvedLanguage struct {
|
||||
id string
|
||||
language *sitter.Language
|
||||
query *sitter.Query
|
||||
}
|
||||
|
||||
// languageRegistry maps buffer metadata to language packs and lazily compiles
|
||||
// tree-sitter language/query assets.
|
||||
type languageRegistry struct {
|
||||
packs []languagePack
|
||||
byFiletype map[string]languagePack
|
||||
byExtension map[string]languagePack
|
||||
compiledByLang map[string]*resolvedLanguage
|
||||
}
|
||||
|
||||
// newLanguageRegistry constructs the default in-process language registry.
|
||||
//
|
||||
// It registers built-in packs and prepares lookup maps for filetype and
|
||||
// extension resolution.
|
||||
func newLanguageRegistry() *languageRegistry {
|
||||
r := &languageRegistry{
|
||||
packs: []languagePack{},
|
||||
byFiletype: map[string]languagePack{},
|
||||
byExtension: map[string]languagePack{},
|
||||
compiledByLang: map[string]*resolvedLanguage{},
|
||||
}
|
||||
|
||||
r.register(languagePack{
|
||||
id: "go",
|
||||
filetypes: []string{"go", "golang"},
|
||||
extensions: []string{".go"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_go.Language()) },
|
||||
loadQuery: loadGoHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "javascript",
|
||||
filetypes: []string{"javascript", "js", "jsx"},
|
||||
extensions: []string{".js", ".mjs", ".cjs", ".jsx"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_js.Language()) },
|
||||
loadQuery: loadJavaScriptHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "typescript",
|
||||
filetypes: []string{"typescript", "ts"},
|
||||
extensions: []string{".ts"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_ts.LanguageTypescript()) },
|
||||
loadQuery: loadTypeScriptHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "tsx",
|
||||
filetypes: []string{"tsx"},
|
||||
extensions: []string{".tsx"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_ts.LanguageTSX()) },
|
||||
loadQuery: loadTSXHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "python",
|
||||
filetypes: []string{"python", "py"},
|
||||
extensions: []string{".py"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_python.Language()) },
|
||||
loadQuery: loadPythonHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "rust",
|
||||
filetypes: []string{"rust", "rs"},
|
||||
extensions: []string{".rs"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_rust.Language()) },
|
||||
loadQuery: loadRustHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "bash",
|
||||
filetypes: []string{"bash", "sh", "shell"},
|
||||
extensions: []string{".sh", ".bash"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_bash.Language()) },
|
||||
loadQuery: loadBashHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "json",
|
||||
filetypes: []string{"json"},
|
||||
extensions: []string{".json"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_json.Language()) },
|
||||
loadQuery: loadJSONHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "css",
|
||||
filetypes: []string{"css"},
|
||||
extensions: []string{".css"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_css.Language()) },
|
||||
loadQuery: loadCSSHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "html",
|
||||
filetypes: []string{"html"},
|
||||
extensions: []string{".html", ".htm"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_html.Language()) },
|
||||
loadQuery: loadHTMLHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "c",
|
||||
filetypes: []string{"c"},
|
||||
extensions: []string{".c", ".h"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_c.Language()) },
|
||||
loadQuery: loadCHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "cpp",
|
||||
filetypes: []string{"cpp", "c++", "hpp"},
|
||||
extensions: []string{".cc", ".cpp", ".cxx", ".hpp", ".hh", ".hxx"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_cpp.Language()) },
|
||||
loadQuery: loadCppHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "java",
|
||||
filetypes: []string{"java"},
|
||||
extensions: []string{".java"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_java.Language()) },
|
||||
loadQuery: loadJavaHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "csharp",
|
||||
filetypes: []string{"csharp", "cs"},
|
||||
extensions: []string{".cs"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_csharp.Language()) },
|
||||
loadQuery: loadCSharpHighlightsQuery,
|
||||
})
|
||||
|
||||
r.register(languagePack{
|
||||
id: "ruby",
|
||||
filetypes: []string{"ruby", "rb"},
|
||||
extensions: []string{".rb"},
|
||||
newLanguage: func() *sitter.Language { return sitter.NewLanguage(ts_ruby.Language()) },
|
||||
loadQuery: loadRubyHighlightsQuery,
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// register adds a language pack and indexes it by normalized keys.
|
||||
func (r *languageRegistry) register(pack languagePack) {
|
||||
r.packs = append(r.packs, pack)
|
||||
|
||||
for _, ft := range pack.filetypes {
|
||||
n := normalizeKey(ft)
|
||||
if n != "" {
|
||||
r.byFiletype[n] = pack
|
||||
}
|
||||
}
|
||||
|
||||
for _, ext := range pack.extensions {
|
||||
n := normalizeExtension(ext)
|
||||
if n != "" {
|
||||
r.byExtension[n] = pack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resolve returns compiled language/query assets for a buffer identity.
|
||||
//
|
||||
// Resolution is filetype-first, extension-second. Results are compiled once
|
||||
// per language id and cached in compiledByLang.
|
||||
func (r *languageRegistry) resolve(filetype, filename string) (*resolvedLanguage, bool, error) {
|
||||
pack, ok := r.resolvePack(filetype, filename)
|
||||
if !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
if cached, ok := r.compiledByLang[pack.id]; ok {
|
||||
return cached, true, nil
|
||||
}
|
||||
|
||||
lang := pack.newLanguage()
|
||||
if lang == nil {
|
||||
return nil, false, fmt.Errorf("language %q did not provide a language handle", pack.id)
|
||||
}
|
||||
|
||||
qBytes, err := pack.loadQuery()
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("load query for %q: %w", pack.id, err)
|
||||
}
|
||||
|
||||
q, qErr := sitter.NewQuery(lang, string(qBytes))
|
||||
if qErr != nil {
|
||||
return nil, false, fmt.Errorf("compile query for %q: %w", pack.id, qErr)
|
||||
}
|
||||
|
||||
resolved := &resolvedLanguage{id: pack.id, language: lang, query: q}
|
||||
r.compiledByLang[pack.id] = resolved
|
||||
return resolved, true, nil
|
||||
}
|
||||
|
||||
// resolvePack finds a registered language pack using normalized buffer
|
||||
// metadata without compiling queries.
|
||||
func (r *languageRegistry) resolvePack(filetype, filename string) (languagePack, bool) {
|
||||
if p, ok := r.byFiletype[normalizeKey(filetype)]; ok {
|
||||
return p, true
|
||||
}
|
||||
|
||||
if p, ok := r.byExtension[extensionOf(filename)]; ok {
|
||||
return p, true
|
||||
}
|
||||
|
||||
return languagePack{}, false
|
||||
}
|
||||
|
||||
// normalizeKey canonicalizes filetype-like keys for registry lookups.
|
||||
func normalizeKey(s string) string {
|
||||
s = strings.TrimSpace(strings.ToLower(s))
|
||||
s = strings.TrimPrefix(s, ".")
|
||||
return s
|
||||
}
|
||||
|
||||
// normalizeExtension canonicalizes extension keys and guarantees a leading
|
||||
// dot for non-empty values.
|
||||
func normalizeExtension(ext string) string {
|
||||
ext = strings.TrimSpace(strings.ToLower(ext))
|
||||
if ext == "" {
|
||||
return ""
|
||||
}
|
||||
if !strings.HasPrefix(ext, ".") {
|
||||
ext = "." + ext
|
||||
}
|
||||
return ext
|
||||
}
|
||||
|
||||
// extensionOf extracts a normalized extension from a filename.
|
||||
// Returns empty string when no usable extension is present.
|
||||
func extensionOf(filename string) string {
|
||||
name := strings.TrimSpace(strings.ToLower(filename))
|
||||
if name == "" {
|
||||
return ""
|
||||
}
|
||||
i := strings.LastIndex(name, ".")
|
||||
if i <= 0 || i == len(name)-1 {
|
||||
return ""
|
||||
}
|
||||
return name[i:]
|
||||
}
|
||||
45
internal/syntax/registry_test.go
Normal file
45
internal/syntax/registry_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
package syntax
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLanguageRegistryResolveByFiletype(t *testing.T) {
|
||||
r := newLanguageRegistry()
|
||||
|
||||
res, ok, err := r.resolve("go", "")
|
||||
if err != nil {
|
||||
t.Fatalf("resolve error: %v", err)
|
||||
}
|
||||
if !ok || res == nil {
|
||||
t.Fatalf("expected go to resolve")
|
||||
}
|
||||
if res.id != "go" {
|
||||
t.Fatalf("expected go id, got %q", res.id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLanguageRegistryResolveByExtension(t *testing.T) {
|
||||
r := newLanguageRegistry()
|
||||
|
||||
res, ok, err := r.resolve("", "main.js")
|
||||
if err != nil {
|
||||
t.Fatalf("resolve error: %v", err)
|
||||
}
|
||||
if !ok || res == nil {
|
||||
t.Fatalf("expected javascript to resolve")
|
||||
}
|
||||
if res.id != "javascript" {
|
||||
t.Fatalf("expected javascript id, got %q", res.id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLanguageRegistryUnknown(t *testing.T) {
|
||||
r := newLanguageRegistry()
|
||||
|
||||
res, ok, err := r.resolve("txt", "notes.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error for unknown language, got: %v", err)
|
||||
}
|
||||
if ok || res != nil {
|
||||
t.Fatalf("expected unknown language to not resolve")
|
||||
}
|
||||
}
|
||||
520
internal/syntax/treesitter.go
Normal file
520
internal/syntax/treesitter.go
Normal file
@ -0,0 +1,520 @@
|
||||
package syntax
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/style"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
sitter "github.com/tree-sitter/go-tree-sitter"
|
||||
)
|
||||
|
||||
type TreeSitterEngine struct {
|
||||
styles style.Styles
|
||||
registry *languageRegistry
|
||||
|
||||
cache map[*core.Buffer]*bufferCache
|
||||
}
|
||||
|
||||
type bufferCache struct {
|
||||
built bool
|
||||
lines map[int][]lipgloss.Style
|
||||
count int
|
||||
|
||||
parser *sitter.Parser
|
||||
tree *sitter.Tree
|
||||
source []byte
|
||||
dirtyAll bool
|
||||
dirty []lineRange
|
||||
|
||||
langID string
|
||||
language *sitter.Language
|
||||
query *sitter.Query
|
||||
}
|
||||
|
||||
type lineRange struct {
|
||||
start int
|
||||
end int
|
||||
}
|
||||
|
||||
type captureRange struct {
|
||||
startRow uint
|
||||
startCol uint
|
||||
endRow uint
|
||||
endCol uint
|
||||
name string
|
||||
}
|
||||
|
||||
// NewTreeSitterEngine: Creates a new tree sitter engine with the styles
|
||||
// provided attached.
|
||||
//
|
||||
// Currently, this engine only support GoLang. But more languages can be
|
||||
// added with easy.
|
||||
func NewTreeSitterEngine(styles style.Styles) *TreeSitterEngine {
|
||||
return &TreeSitterEngine{
|
||||
styles: styles,
|
||||
registry: newLanguageRegistry(),
|
||||
cache: map[*core.Buffer]*bufferCache{},
|
||||
}
|
||||
}
|
||||
|
||||
func (e *TreeSitterEngine) PrepareBuffer(buf *core.Buffer) {
|
||||
// Cannot prepare a nil buffer
|
||||
if buf == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get the buffers cache and return if we are already "built" (ready to render).
|
||||
bc := e.getCache(buf)
|
||||
if bc.count != buf.LineCount() {
|
||||
bc.dirtyAll = true
|
||||
}
|
||||
if bc.dirtyAll {
|
||||
bc.built = false
|
||||
}
|
||||
if bc.built {
|
||||
return
|
||||
}
|
||||
|
||||
// If we do no support the buffer, load empty styles into the cache
|
||||
lang, ok, err := e.resolveBufferLanguage(buf, bc)
|
||||
if err != nil || !ok {
|
||||
bc.lines = map[int][]lipgloss.Style{}
|
||||
bc.built = true
|
||||
return
|
||||
}
|
||||
_ = lang
|
||||
|
||||
e.buildFullBuffer(buf, bc)
|
||||
}
|
||||
|
||||
func (e *TreeSitterEngine) LineStyleMap(buf *core.Buffer, line int) []lipgloss.Style {
|
||||
if buf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.PrepareBuffer(buf)
|
||||
bc := e.getCache(buf)
|
||||
|
||||
if s, ok := bc.lines[line]; ok {
|
||||
return s
|
||||
}
|
||||
|
||||
runes := []rune(buf.Line(line))
|
||||
out := make([]lipgloss.Style, len(runes))
|
||||
for i := range out {
|
||||
out[i] = e.styles.LineStyle
|
||||
}
|
||||
bc.lines[line] = out
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *TreeSitterEngine) ApplyEdit(buf *core.Buffer, edit *core.BufferEdit) {
|
||||
if buf == nil || edit == nil {
|
||||
return
|
||||
}
|
||||
|
||||
bc := e.getCache(buf)
|
||||
lang, ok, err := e.resolveBufferLanguage(buf, bc)
|
||||
if err != nil || !ok {
|
||||
bc.built = false
|
||||
bc.dirtyAll = true
|
||||
return
|
||||
}
|
||||
_ = lang
|
||||
|
||||
if bc.parser == nil {
|
||||
bc.parser = sitter.NewParser()
|
||||
bc.parser.SetLanguage(bc.language)
|
||||
}
|
||||
|
||||
if bc.tree == nil || len(bc.source) == 0 {
|
||||
bc.dirtyAll = true
|
||||
return
|
||||
}
|
||||
|
||||
bc.tree.Edit(&sitter.InputEdit{
|
||||
StartByte: edit.StartByte,
|
||||
OldEndByte: edit.OldEndByte,
|
||||
NewEndByte: edit.NewEndByte,
|
||||
StartPosition: sitter.NewPoint(edit.StartPoint.Row, edit.StartPoint.Column),
|
||||
OldEndPosition: sitter.NewPoint(edit.OldEndPoint.Row, edit.OldEndPoint.Column),
|
||||
NewEndPosition: sitter.NewPoint(edit.NewEndPoint.Row, edit.NewEndPoint.Column),
|
||||
})
|
||||
|
||||
newSource := buildBufferSource(buf)
|
||||
newTree := bc.parser.Parse(newSource, bc.tree)
|
||||
if newTree == nil {
|
||||
bc.dirtyAll = true
|
||||
return
|
||||
}
|
||||
|
||||
changed := bc.tree.ChangedRanges(newTree)
|
||||
|
||||
newLineCount := buf.LineCount()
|
||||
if newLineCount != bc.count {
|
||||
bc.dirtyAll = true
|
||||
bc.dirty = nil
|
||||
} else {
|
||||
startRow := int(edit.StartPoint.Row)
|
||||
endRow := int(max(edit.OldEndPoint.Row, edit.NewEndPoint.Row))
|
||||
addDirtyRange(bc, startRow, endRow)
|
||||
for _, r := range changed {
|
||||
addDirtyRange(bc, int(r.StartPoint.Row), int(r.EndPoint.Row))
|
||||
}
|
||||
}
|
||||
|
||||
bc.source = newSource
|
||||
bc.tree.Close()
|
||||
bc.tree = newTree
|
||||
bc.built = false
|
||||
}
|
||||
|
||||
// TreeSitterEngine.InvalidateBuffer: Deletes the entire buffers cache from the engine. If the
|
||||
// buffer provided is nil, this function does nothing.
|
||||
func (e *TreeSitterEngine) InvalidateBuffer(buf *core.Buffer) {
|
||||
if buf == nil {
|
||||
return
|
||||
}
|
||||
bc := e.getCache(buf)
|
||||
bc.built = false
|
||||
bc.dirtyAll = true
|
||||
bc.dirty = nil
|
||||
}
|
||||
|
||||
// TreeSitterEngine.InvalidateLines: Deletes lines between start and end (inclusive) from the
|
||||
// buffers cache. Then marks the cache as "unbuilt." If the buffer provided is nil, this function
|
||||
// does nothing.
|
||||
func (e *TreeSitterEngine) InvalidateLines(buf *core.Buffer, startLine, endLine int) {
|
||||
if buf == nil {
|
||||
return
|
||||
}
|
||||
bc := e.getCache(buf)
|
||||
addDirtyRange(bc, startLine, endLine)
|
||||
bc.built = false
|
||||
}
|
||||
|
||||
// TreeSitterEngine.supportsBuffer: Returns whether the buffer can be parsed and highlighted
|
||||
// by the engine. When false, there should be a fallback.
|
||||
func (e *TreeSitterEngine) resolveBufferLanguage(buf *core.Buffer, bc *bufferCache) (*resolvedLanguage, bool, error) {
|
||||
if e.registry == nil {
|
||||
e.registry = newLanguageRegistry()
|
||||
}
|
||||
|
||||
resolved, ok, err := e.registry.resolve(buf.Filetype, buf.Filename)
|
||||
if err != nil || !ok {
|
||||
return nil, ok, err
|
||||
}
|
||||
|
||||
if bc.langID != resolved.id {
|
||||
bc.langID = resolved.id
|
||||
bc.language = resolved.language
|
||||
bc.query = resolved.query
|
||||
if bc.parser != nil {
|
||||
bc.parser.SetLanguage(bc.language)
|
||||
}
|
||||
bc.dirtyAll = true
|
||||
bc.built = false
|
||||
}
|
||||
|
||||
return resolved, true, nil
|
||||
}
|
||||
|
||||
// TreeSitterEngine.getCache: Returns the buffers cache. If the cache does not exist, a new one
|
||||
// is created and applied to the engines cache map.
|
||||
func (e *TreeSitterEngine) getCache(buf *core.Buffer) *bufferCache {
|
||||
if bc, ok := e.cache[buf]; ok {
|
||||
return bc
|
||||
}
|
||||
bc := &bufferCache{lines: map[int][]lipgloss.Style{}}
|
||||
e.cache[buf] = bc
|
||||
return bc
|
||||
}
|
||||
|
||||
func (e *TreeSitterEngine) buildFullBuffer(buf *core.Buffer, bc *bufferCache) {
|
||||
lineCount := buf.LineCount()
|
||||
|
||||
// Load the lines into memory. There is no method for this due to the buffers
|
||||
// internal implementation using a gap buffer. So the "Lines" property is of
|
||||
// type []*GapBuffer.
|
||||
lines := make([]string, lineCount)
|
||||
for i := range lineCount {
|
||||
lines[i] = buf.Line(i)
|
||||
}
|
||||
|
||||
fullRebuild := bc.dirtyAll || len(bc.lines) == 0 || len(bc.dirty) == 0
|
||||
if fullRebuild {
|
||||
bc.lines = map[int][]lipgloss.Style{}
|
||||
for i := range lineCount {
|
||||
bc.lines[i] = defaultLineStyles(lines[i], e.styles.LineStyle)
|
||||
}
|
||||
} else {
|
||||
dirty := normalizedDirtyRanges(bc.dirty, lineCount)
|
||||
for _, r := range dirty {
|
||||
for i := r.start; i <= r.end; i++ {
|
||||
bc.lines[i] = defaultLineStyles(lines[i], e.styles.LineStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
source := buildBufferSource(buf)
|
||||
useCurrentTree := bc.tree != nil && bytes.Equal(bc.source, source)
|
||||
|
||||
if bc.parser == nil {
|
||||
bc.parser = sitter.NewParser()
|
||||
bc.parser.SetLanguage(bc.language)
|
||||
}
|
||||
|
||||
if !useCurrentTree {
|
||||
var baseTree *sitter.Tree
|
||||
if bc.tree != nil {
|
||||
baseTree = bc.tree
|
||||
}
|
||||
|
||||
tree := bc.parser.Parse(source, baseTree)
|
||||
if tree == nil {
|
||||
bc.built = true
|
||||
return
|
||||
}
|
||||
|
||||
if bc.tree != nil {
|
||||
bc.tree.Close()
|
||||
}
|
||||
bc.tree = tree
|
||||
bc.source = source
|
||||
}
|
||||
|
||||
root := bc.tree.RootNode()
|
||||
cursor := sitter.NewQueryCursor()
|
||||
defer cursor.Close()
|
||||
|
||||
var captures []captureRange
|
||||
|
||||
if fullRebuild {
|
||||
iter := cursor.Captures(bc.query, root, source)
|
||||
captures = append(captures, collectCaptures(iter, bc.query)...)
|
||||
} else {
|
||||
dirty := normalizedDirtyRanges(bc.dirty, lineCount)
|
||||
for _, r := range dirty {
|
||||
queryStart := max(0, r.start-1)
|
||||
queryEnd := min(lineCount-1, r.end+1)
|
||||
|
||||
rangeCursor := sitter.NewQueryCursor()
|
||||
rangeCursor.SetPointRange(
|
||||
sitter.NewPoint(uint(queryStart), 0),
|
||||
sitter.NewPoint(uint(queryEnd+1), 0),
|
||||
)
|
||||
iter := rangeCursor.Captures(bc.query, root, source)
|
||||
captures = append(captures, collectCaptures(iter, bc.query)...)
|
||||
rangeCursor.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the captures in order of their character occurrence in the file
|
||||
sort.Slice(captures, func(i, j int) bool {
|
||||
if captures[i].startRow == captures[j].startRow {
|
||||
if captures[i].startCol == captures[j].startCol {
|
||||
if captures[i].endRow == captures[j].endRow {
|
||||
return captures[i].endCol > captures[j].endCol
|
||||
}
|
||||
return captures[i].endRow > captures[j].endRow
|
||||
}
|
||||
return captures[i].startCol < captures[j].startCol
|
||||
}
|
||||
return captures[i].startRow < captures[j].startRow
|
||||
})
|
||||
|
||||
// Basically, this code works by rewriting the same range and the last capture wins.
|
||||
// This is a great spot for optimization: No need to draw many times, just pick the best one.
|
||||
// Or maybe when we sort, if we find ones that are the same, remove the first one, and then
|
||||
// we just keep the last one. Then this code can stay the same but will not suffer so many
|
||||
// rewrites.
|
||||
targetDirty := normalizedDirtyRanges(bc.dirty, lineCount)
|
||||
for _, c := range captures {
|
||||
sty := style.CaptureStyle(e.styles.LineStyle, c.name)
|
||||
for row := c.startRow; row <= c.endRow; row++ {
|
||||
if int(row) >= len(lines) {
|
||||
break
|
||||
}
|
||||
if !fullRebuild && !rowInRanges(int(row), targetDirty) {
|
||||
continue
|
||||
}
|
||||
|
||||
lineBytes := []byte(lines[row])
|
||||
startByteCol := uint(0)
|
||||
if row == c.startRow {
|
||||
startByteCol = c.startCol
|
||||
}
|
||||
endByteCol := uint(len(lineBytes))
|
||||
if row == c.endRow {
|
||||
endByteCol = min(c.endCol, uint(len(lineBytes)))
|
||||
}
|
||||
|
||||
startRune := byteColToRuneIndex(lineBytes, int(startByteCol))
|
||||
endRune := byteColToRuneIndex(lineBytes, int(endByteCol))
|
||||
|
||||
rowStyles := bc.lines[int(row)]
|
||||
if startRune < 0 {
|
||||
startRune = 0
|
||||
}
|
||||
if endRune > len(rowStyles) {
|
||||
endRune = len(rowStyles)
|
||||
}
|
||||
if startRune >= endRune {
|
||||
continue
|
||||
}
|
||||
|
||||
for i := startRune; i < endRune; i++ {
|
||||
rowStyles[i] = sty
|
||||
}
|
||||
bc.lines[int(row)] = rowStyles
|
||||
}
|
||||
}
|
||||
|
||||
bc.dirtyAll = false
|
||||
bc.dirty = nil
|
||||
bc.count = lineCount
|
||||
bc.built = true
|
||||
}
|
||||
|
||||
func addDirtyRange(bc *bufferCache, start, end int) {
|
||||
if bc == nil {
|
||||
return
|
||||
}
|
||||
if end < start {
|
||||
start, end = end, start
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end < 0 {
|
||||
end = 0
|
||||
}
|
||||
bc.dirty = append(bc.dirty, lineRange{start: start, end: end})
|
||||
bc.dirty = mergeRanges(bc.dirty)
|
||||
}
|
||||
|
||||
func normalizedDirtyRanges(ranges []lineRange, lineCount int) []lineRange {
|
||||
if lineCount <= 0 || len(ranges) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
clamped := make([]lineRange, 0, len(ranges))
|
||||
for _, r := range ranges {
|
||||
start := max(0, r.start)
|
||||
end := min(lineCount-1, r.end)
|
||||
if start > end {
|
||||
continue
|
||||
}
|
||||
clamped = append(clamped, lineRange{start: start, end: end})
|
||||
}
|
||||
|
||||
return mergeRanges(clamped)
|
||||
}
|
||||
|
||||
func mergeRanges(ranges []lineRange) []lineRange {
|
||||
if len(ranges) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sort.Slice(ranges, func(i, j int) bool {
|
||||
if ranges[i].start == ranges[j].start {
|
||||
return ranges[i].end < ranges[j].end
|
||||
}
|
||||
return ranges[i].start < ranges[j].start
|
||||
})
|
||||
|
||||
merged := make([]lineRange, 0, len(ranges))
|
||||
cur := ranges[0]
|
||||
for i := 1; i < len(ranges); i++ {
|
||||
n := ranges[i]
|
||||
if n.start <= cur.end+1 {
|
||||
if n.end > cur.end {
|
||||
cur.end = n.end
|
||||
}
|
||||
continue
|
||||
}
|
||||
merged = append(merged, cur)
|
||||
cur = n
|
||||
}
|
||||
merged = append(merged, cur)
|
||||
return merged
|
||||
}
|
||||
|
||||
func rowInRanges(row int, ranges []lineRange) bool {
|
||||
for _, r := range ranges {
|
||||
if row >= r.start && row <= r.end {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func defaultLineStyles(line string, base lipgloss.Style) []lipgloss.Style {
|
||||
runes := []rune(line)
|
||||
row := make([]lipgloss.Style, len(runes))
|
||||
for i := range row {
|
||||
row[i] = base
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
func collectCaptures(iter sitter.QueryCaptures, query *sitter.Query) []captureRange {
|
||||
if query == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
names := query.CaptureNames()
|
||||
out := []captureRange{}
|
||||
for match, captureIdx := iter.Next(); match != nil; match, captureIdx = iter.Next() {
|
||||
capture := match.Captures[captureIdx]
|
||||
if int(capture.Index) >= len(names) {
|
||||
continue
|
||||
}
|
||||
name := names[capture.Index]
|
||||
if name == "spell" {
|
||||
continue
|
||||
}
|
||||
|
||||
node := capture.Node
|
||||
start := node.StartPosition()
|
||||
end := node.EndPosition()
|
||||
out = append(out, captureRange{
|
||||
startRow: start.Row,
|
||||
startCol: start.Column,
|
||||
endRow: end.Row,
|
||||
endCol: end.Column,
|
||||
name: name,
|
||||
})
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func buildBufferSource(buf *core.Buffer) []byte {
|
||||
lineCount := buf.LineCount()
|
||||
if lineCount == 0 {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
lines := make([]string, lineCount)
|
||||
for i := range lineCount {
|
||||
lines[i] = buf.Line(i)
|
||||
}
|
||||
|
||||
return []byte(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
func byteColToRuneIndex(line []byte, byteCol int) int {
|
||||
if byteCol <= 0 {
|
||||
return 0
|
||||
}
|
||||
if byteCol >= len(line) {
|
||||
return len([]rune(string(line)))
|
||||
}
|
||||
|
||||
prefix := line[:byteCol]
|
||||
return len([]rune(string(prefix)))
|
||||
}
|
||||
242
internal/syntax/treesitter_behavior_test.go
Normal file
242
internal/syntax/treesitter_behavior_test.go
Normal file
@ -0,0 +1,242 @@
|
||||
package syntax
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/style"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
func TestTreeSitterEngineHighlightsGoKeywordAndString(t *testing.T) {
|
||||
b := core.NewBufferBuilder().
|
||||
WithFilename("sample.go").
|
||||
WithFiletype("go").
|
||||
WithLines([]string{
|
||||
"package main",
|
||||
"func main() {",
|
||||
" s := \"hi\"",
|
||||
"}",
|
||||
}).
|
||||
Build()
|
||||
buf := &b
|
||||
|
||||
engine := NewTreeSitterEngine(style.DefaultStyles())
|
||||
engine.PrepareBuffer(buf)
|
||||
|
||||
base := engine.styles.LineStyle
|
||||
|
||||
line0 := buf.Line(0)
|
||||
map0 := engine.LineStyleMap(buf, 0)
|
||||
if len(map0) != len([]rune(line0)) {
|
||||
t.Fatalf("line 0 style map length mismatch")
|
||||
}
|
||||
if len(map0) == 0 || styleEquivalent(map0[0], base) {
|
||||
t.Fatalf("expected 'package' keyword to be highlighted")
|
||||
}
|
||||
|
||||
line2 := buf.Line(2)
|
||||
stringStart := strings.Index(line2, "\"hi\"")
|
||||
if stringStart < 0 {
|
||||
t.Fatalf("test setup failed: string literal not found")
|
||||
}
|
||||
map2 := engine.LineStyleMap(buf, 2)
|
||||
if styleEquivalent(map2[stringStart+1], base) {
|
||||
t.Fatalf("expected string contents to be highlighted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeSitterEngineHighlightsMultilineRawString(t *testing.T) {
|
||||
b := core.NewBufferBuilder().
|
||||
WithFilename("sample.go").
|
||||
WithFiletype("go").
|
||||
WithLines([]string{
|
||||
"package main",
|
||||
"func main() {",
|
||||
" s := `hello",
|
||||
"world`",
|
||||
" println(s)",
|
||||
"}",
|
||||
}).
|
||||
Build()
|
||||
buf := &b
|
||||
|
||||
engine := NewTreeSitterEngine(style.DefaultStyles())
|
||||
engine.PrepareBuffer(buf)
|
||||
|
||||
base := engine.styles.LineStyle
|
||||
map3 := engine.LineStyleMap(buf, 3)
|
||||
if len(map3) == 0 {
|
||||
t.Fatalf("expected style map on multiline raw string line")
|
||||
}
|
||||
if styleEquivalent(map3[0], base) {
|
||||
t.Fatalf("expected multiline raw string line to be highlighted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeSitterEngineApplyEditUpdatesStyleCategory(t *testing.T) {
|
||||
b := core.NewBufferBuilder().
|
||||
WithFilename("sample.go").
|
||||
WithFiletype("go").
|
||||
WithLines([]string{
|
||||
"package main",
|
||||
"func main() {",
|
||||
" x := 123",
|
||||
"}",
|
||||
}).
|
||||
Build()
|
||||
buf := &b
|
||||
|
||||
engine := NewTreeSitterEngine(style.DefaultStyles())
|
||||
engine.PrepareBuffer(buf)
|
||||
|
||||
oldLine := buf.Line(2)
|
||||
oldIdx := strings.Index(oldLine, "123")
|
||||
if oldIdx < 0 {
|
||||
t.Fatalf("test setup failed: number not found")
|
||||
}
|
||||
oldMap := engine.LineStyleMap(buf, 2)
|
||||
oldStyle := oldMap[oldIdx]
|
||||
|
||||
var edit *core.BufferEdit
|
||||
buf.OnChange = func(change core.BufferChange) {
|
||||
edit = change.Edit
|
||||
}
|
||||
|
||||
buf.SetLine(2, " x := \"abc\"")
|
||||
if edit == nil {
|
||||
t.Fatalf("expected edit metadata from SetLine")
|
||||
}
|
||||
|
||||
engine.ApplyEdit(buf, edit)
|
||||
engine.PrepareBuffer(buf)
|
||||
|
||||
newLine := buf.Line(2)
|
||||
newIdx := strings.Index(newLine, "abc")
|
||||
if newIdx < 0 {
|
||||
t.Fatalf("test setup failed: string not found")
|
||||
}
|
||||
newMap := engine.LineStyleMap(buf, 2)
|
||||
newStyle := newMap[newIdx]
|
||||
|
||||
if styleEquivalent(newStyle, engine.styles.LineStyle) {
|
||||
t.Fatalf("expected updated string to be highlighted")
|
||||
}
|
||||
if styleEquivalent(oldStyle, newStyle) {
|
||||
t.Fatalf("expected style category to change from number to string")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeSitterEngineApplyEditLineCountChangeForcesFullDirty(t *testing.T) {
|
||||
b := core.NewBufferBuilder().
|
||||
WithFilename("sample.go").
|
||||
WithFiletype("go").
|
||||
WithLines([]string{"package main", "func main() {}"}).
|
||||
Build()
|
||||
buf := &b
|
||||
|
||||
engine := NewTreeSitterEngine(style.DefaultStyles())
|
||||
engine.PrepareBuffer(buf)
|
||||
bc := engine.getCache(buf)
|
||||
|
||||
var edit *core.BufferEdit
|
||||
buf.OnChange = func(change core.BufferChange) {
|
||||
edit = change.Edit
|
||||
}
|
||||
|
||||
buf.InsertLine(1, "var x = 1")
|
||||
if edit == nil {
|
||||
t.Fatalf("expected edit metadata from InsertLine")
|
||||
}
|
||||
|
||||
engine.ApplyEdit(buf, edit)
|
||||
if !bc.dirtyAll {
|
||||
t.Fatalf("expected line count change to set dirtyAll")
|
||||
}
|
||||
|
||||
engine.PrepareBuffer(buf)
|
||||
if !bc.built {
|
||||
t.Fatalf("expected cache rebuilt after prepare")
|
||||
}
|
||||
if bc.count != buf.LineCount() {
|
||||
t.Fatalf("expected cache line count to match buffer")
|
||||
}
|
||||
if bc.dirtyAll {
|
||||
t.Fatalf("expected dirtyAll to clear after rebuild")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeSitterEngineUnsupportedBufferFallsBackToDefaultStyles(t *testing.T) {
|
||||
b := core.NewBufferBuilder().
|
||||
WithFilename("notes.txt").
|
||||
WithFiletype("txt").
|
||||
WithLines([]string{"just text", "with no language"}).
|
||||
Build()
|
||||
buf := &b
|
||||
|
||||
engine := NewTreeSitterEngine(style.DefaultStyles())
|
||||
engine.PrepareBuffer(buf)
|
||||
|
||||
base := engine.styles.LineStyle
|
||||
line := buf.Line(0)
|
||||
m := engine.LineStyleMap(buf, 0)
|
||||
if len(m) != len([]rune(line)) {
|
||||
t.Fatalf("style map length mismatch on fallback buffer")
|
||||
}
|
||||
for i := range m {
|
||||
if !styleEquivalent(m[i], base) {
|
||||
t.Fatalf("expected default style for unsupported filetype at rune %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeSitterEngineLastLineEditDoesNotPanicAndRebuilds(t *testing.T) {
|
||||
b := core.NewBufferBuilder().
|
||||
WithFilename("sample.go").
|
||||
WithFiletype("go").
|
||||
WithLines([]string{"package main", "func main() {", " return", "}"}).
|
||||
Build()
|
||||
buf := &b
|
||||
|
||||
engine := NewTreeSitterEngine(style.DefaultStyles())
|
||||
engine.PrepareBuffer(buf)
|
||||
bc := engine.getCache(buf)
|
||||
|
||||
var edit *core.BufferEdit
|
||||
buf.OnChange = func(change core.BufferChange) {
|
||||
edit = change.Edit
|
||||
}
|
||||
|
||||
buf.SetLine(3, "// end")
|
||||
if edit == nil {
|
||||
t.Fatalf("expected edit metadata for last line change")
|
||||
}
|
||||
|
||||
engine.ApplyEdit(buf, edit)
|
||||
engine.PrepareBuffer(buf)
|
||||
|
||||
if !bc.built {
|
||||
t.Fatalf("expected cache built after last-line edit")
|
||||
}
|
||||
if len(bc.dirty) != 0 {
|
||||
t.Fatalf("expected dirty ranges cleared after rebuild")
|
||||
}
|
||||
}
|
||||
|
||||
func styleEquivalent(a, b lipgloss.Style) bool {
|
||||
return styleSignature(a) == styleSignature(b)
|
||||
}
|
||||
|
||||
func styleSignature(s lipgloss.Style) string {
|
||||
return fmt.Sprintf(
|
||||
"fg=%v,bg=%v,bold=%v,italic=%v,underline=%v,reverse=%v",
|
||||
s.GetForeground(),
|
||||
s.GetBackground(),
|
||||
s.GetBold(),
|
||||
s.GetItalic(),
|
||||
s.GetUnderline(),
|
||||
s.GetReverse(),
|
||||
)
|
||||
}
|
||||
37
internal/syntax/treesitter_bench_test.go
Normal file
37
internal/syntax/treesitter_bench_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package syntax
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/style"
|
||||
)
|
||||
|
||||
func BenchmarkTreeSitterPrepareAndIncrementalEdit(b *testing.B) {
|
||||
lines := make([]string, 0, 2000)
|
||||
lines = append(lines, "package main", "", "func main() {")
|
||||
for i := 0; i < 1990; i++ {
|
||||
lines = append(lines, fmt.Sprintf(" v%d := %d", i, i))
|
||||
}
|
||||
lines = append(lines, "}")
|
||||
|
||||
bld := core.NewBufferBuilder().WithFilename("bench.go").WithFiletype("go").WithLines(lines).Build()
|
||||
buf := &bld
|
||||
eng := NewTreeSitterEngine(style.DefaultStyles())
|
||||
|
||||
eng.PrepareBuffer(buf)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lineIdx := 10 + (i % 1000)
|
||||
buf.SetLine(lineIdx, fmt.Sprintf(" v%d := %d", lineIdx, i))
|
||||
|
||||
oldSource := buildBufferSource(buf)
|
||||
_ = oldSource
|
||||
|
||||
// Synthetic direct invalidate path benchmark (current API entrypoints)
|
||||
eng.InvalidateLines(buf, lineIdx, lineIdx)
|
||||
eng.PrepareBuffer(buf)
|
||||
}
|
||||
}
|
||||
84
internal/syntax/treesitter_engine_test.go
Normal file
84
internal/syntax/treesitter_engine_test.go
Normal file
@ -0,0 +1,84 @@
|
||||
package syntax
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/style"
|
||||
)
|
||||
|
||||
func TestTreeSitterEngineApplyEditMarksDirtyWithoutFullInvalidation(t *testing.T) {
|
||||
b := core.NewBufferBuilder().
|
||||
WithFilename("x.go").
|
||||
WithFiletype("go").
|
||||
WithLines([]string{"package main", "func main() {", "println(1)", "}"}).
|
||||
Build()
|
||||
buf := &b
|
||||
|
||||
engine := NewTreeSitterEngine(style.DefaultStyles())
|
||||
engine.PrepareBuffer(buf)
|
||||
|
||||
bc := engine.getCache(buf)
|
||||
if !bc.built {
|
||||
t.Fatalf("expected cache to be built after prepare")
|
||||
}
|
||||
|
||||
var edit *core.BufferEdit
|
||||
buf.OnChange = func(change core.BufferChange) {
|
||||
edit = change.Edit
|
||||
}
|
||||
|
||||
buf.SetLine(2, "println(22)")
|
||||
if edit == nil {
|
||||
t.Fatalf("expected setline to emit edit metadata")
|
||||
}
|
||||
|
||||
engine.ApplyEdit(buf, edit)
|
||||
|
||||
if bc.built {
|
||||
t.Fatalf("expected cache to become unbuilt after apply edit")
|
||||
}
|
||||
if bc.dirtyAll {
|
||||
t.Fatalf("expected non-structural edit to avoid dirtyAll")
|
||||
}
|
||||
if len(bc.dirty) == 0 {
|
||||
t.Fatalf("expected dirty ranges after apply edit")
|
||||
}
|
||||
|
||||
engine.PrepareBuffer(buf)
|
||||
if !bc.built {
|
||||
t.Fatalf("expected cache rebuilt after prepare")
|
||||
}
|
||||
if len(bc.dirty) != 0 {
|
||||
t.Fatalf("expected dirty ranges cleared after rebuild")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeSitterEngineInvalidateLinesAndBuffer(t *testing.T) {
|
||||
b := core.NewBufferBuilder().
|
||||
WithFilename("x.go").
|
||||
WithFiletype("go").
|
||||
WithLines([]string{"package main", "func main() {}"}).
|
||||
Build()
|
||||
buf := &b
|
||||
|
||||
engine := NewTreeSitterEngine(style.DefaultStyles())
|
||||
engine.PrepareBuffer(buf)
|
||||
|
||||
bc := engine.getCache(buf)
|
||||
engine.InvalidateLines(buf, 1, 1)
|
||||
if bc.built {
|
||||
t.Fatalf("expected invalidate lines to unset built")
|
||||
}
|
||||
if bc.dirtyAll {
|
||||
t.Fatalf("expected line invalidation to avoid dirtyAll")
|
||||
}
|
||||
if len(bc.dirty) == 0 {
|
||||
t.Fatalf("expected dirty line ranges")
|
||||
}
|
||||
|
||||
engine.InvalidateBuffer(buf)
|
||||
if !bc.dirtyAll {
|
||||
t.Fatalf("expected invalidate buffer to set dirtyAll")
|
||||
}
|
||||
}
|
||||
35
internal/syntax/treesitter_fuzz_test.go
Normal file
35
internal/syntax/treesitter_fuzz_test.go
Normal file
@ -0,0 +1,35 @@
|
||||
package syntax
|
||||
|
||||
import "testing"
|
||||
|
||||
func FuzzByteColToRuneIndexInvariants(f *testing.F) {
|
||||
f.Add("abc", 0)
|
||||
f.Add("aéb", 1)
|
||||
f.Add("こんにちは", 5)
|
||||
f.Add("", 0)
|
||||
|
||||
f.Fuzz(func(t *testing.T, s string, col int) {
|
||||
line := []byte(s)
|
||||
idx := byteColToRuneIndex(line, col)
|
||||
|
||||
runes := []rune(s)
|
||||
if idx < 0 || idx > len(runes) {
|
||||
t.Fatalf("rune index out of bounds: idx=%d len=%d", idx, len(runes))
|
||||
}
|
||||
|
||||
if col <= 0 && idx != 0 {
|
||||
t.Fatalf("expected idx 0 for non-positive col, got %d", idx)
|
||||
}
|
||||
|
||||
if col >= len(line) && idx != len(runes) {
|
||||
t.Fatalf("expected idx len(runes) for col>=len(bytes), got %d", idx)
|
||||
}
|
||||
|
||||
if col > 0 && col < len(line) {
|
||||
expected := len([]rune(string(line[:col])))
|
||||
if idx != expected {
|
||||
t.Fatalf("expected idx %d got %d", expected, idx)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
50
internal/syntax/treesitter_internal_test.go
Normal file
50
internal/syntax/treesitter_internal_test.go
Normal file
@ -0,0 +1,50 @@
|
||||
package syntax
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMergeRanges(t *testing.T) {
|
||||
in := []lineRange{{start: 5, end: 8}, {start: 1, end: 2}, {start: 2, end: 4}, {start: 10, end: 10}}
|
||||
out := mergeRanges(in)
|
||||
|
||||
if len(out) != 2 {
|
||||
t.Fatalf("expected 2 merged ranges, got %d", len(out))
|
||||
}
|
||||
if out[0].start != 1 || out[0].end != 8 {
|
||||
t.Fatalf("unexpected first merged range: %+v", out[0])
|
||||
}
|
||||
if out[1].start != 10 || out[1].end != 10 {
|
||||
t.Fatalf("unexpected second merged range: %+v", out[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizedDirtyRanges(t *testing.T) {
|
||||
ranges := []lineRange{{start: -2, end: 1}, {start: 3, end: 99}}
|
||||
out := normalizedDirtyRanges(ranges, 5)
|
||||
|
||||
if len(out) != 2 {
|
||||
t.Fatalf("expected 2 normalized ranges, got %d", len(out))
|
||||
}
|
||||
if out[0].start != 0 || out[0].end != 1 {
|
||||
t.Fatalf("unexpected first normalized range: %+v", out[0])
|
||||
}
|
||||
if out[1].start != 3 || out[1].end != 4 {
|
||||
t.Fatalf("unexpected second normalized range: %+v", out[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteColToRuneIndexUTF8(t *testing.T) {
|
||||
line := []byte("aéb")
|
||||
|
||||
if got := byteColToRuneIndex(line, 0); got != 0 {
|
||||
t.Fatalf("expected 0, got %d", got)
|
||||
}
|
||||
if got := byteColToRuneIndex(line, 1); got != 1 {
|
||||
t.Fatalf("expected 1, got %d", got)
|
||||
}
|
||||
if got := byteColToRuneIndex(line, 3); got != 2 {
|
||||
t.Fatalf("expected 2, got %d", got)
|
||||
}
|
||||
if got := byteColToRuneIndex(line, len(line)); got != 3 {
|
||||
t.Fatalf("expected 3, got %d", got)
|
||||
}
|
||||
}
|
||||
112
internal/syntax/treesitter_sequences_test.go
Normal file
112
internal/syntax/treesitter_sequences_test.go
Normal file
@ -0,0 +1,112 @@
|
||||
package syntax
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"git.gophernest.net/azpect/TextEditor/internal/core"
|
||||
"git.gophernest.net/azpect/TextEditor/internal/style"
|
||||
)
|
||||
|
||||
type seqOp func(*core.Buffer, *core.Window)
|
||||
|
||||
func TestTreeSitterEngineEditSequences(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
lines []string
|
||||
opList []seqOp
|
||||
}{
|
||||
{
|
||||
name: "setline and insertline",
|
||||
lines: []string{"package main", "func main() {", " x := 1", "}"},
|
||||
opList: []seqOp{
|
||||
func(b *core.Buffer, _ *core.Window) { b.SetLine(2, " x := 2") },
|
||||
func(b *core.Buffer, _ *core.Window) { b.InsertLine(3, " println(x)") },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delete middle line",
|
||||
lines: []string{"package main", "func main() {", " x := 1", " y := 2", "}"},
|
||||
opList: []seqOp{
|
||||
func(b *core.Buffer, _ *core.Window) { b.DeleteLine(3) },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiline string evolve",
|
||||
lines: []string{"package main", "func main() {", " s := `a", "b`", " _ = s", "}"},
|
||||
opList: []seqOp{
|
||||
func(b *core.Buffer, _ *core.Window) { b.SetLine(2, " s := `alpha") },
|
||||
func(b *core.Buffer, _ *core.Window) { b.SetLine(3, "beta`") },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "undo redo sequence",
|
||||
lines: []string{"package main", "func main() {", " v := 3", "}"},
|
||||
opList: []seqOp{
|
||||
func(b *core.Buffer, w *core.Window) {
|
||||
b.UndoStack.BeginBlock(w.Cursor)
|
||||
b.SetLine(2, " v := 9")
|
||||
b.UndoStack.EndBlock(core.Position{Line: 2, Col: 8})
|
||||
},
|
||||
func(b *core.Buffer, w *core.Window) { _ = b.Undo(w) },
|
||||
func(b *core.Buffer, w *core.Window) { _ = b.Redo(w) },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
b := core.NewBufferBuilder().
|
||||
WithFilename("seq.go").
|
||||
WithFiletype("go").
|
||||
WithLines(tc.lines).
|
||||
Build()
|
||||
buf := &b
|
||||
|
||||
w := core.NewWindowBuilder().WithBuffer(buf).WithDimensions(120, 40).Build()
|
||||
win := &w
|
||||
|
||||
engine := NewTreeSitterEngine(style.DefaultStyles())
|
||||
|
||||
buf.OnChange = func(change core.BufferChange) {
|
||||
if change.Edit != nil {
|
||||
engine.ApplyEdit(buf, change.Edit)
|
||||
} else {
|
||||
engine.InvalidateBuffer(buf)
|
||||
}
|
||||
}
|
||||
|
||||
engine.PrepareBuffer(buf)
|
||||
assertEngineInvariants(t, engine, buf, "initial")
|
||||
|
||||
for i, op := range tc.opList {
|
||||
op(buf, win)
|
||||
engine.PrepareBuffer(buf)
|
||||
assertEngineInvariants(t, engine, buf, fmt.Sprintf("after op %d", i+1))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertEngineInvariants(t *testing.T, engine *TreeSitterEngine, buf *core.Buffer, phase string) {
|
||||
t.Helper()
|
||||
|
||||
bc := engine.getCache(buf)
|
||||
if !bc.built {
|
||||
t.Fatalf("%s: expected built cache", phase)
|
||||
}
|
||||
if bc.dirtyAll {
|
||||
t.Fatalf("%s: expected dirtyAll=false after prepare", phase)
|
||||
}
|
||||
if len(bc.dirty) != 0 {
|
||||
t.Fatalf("%s: expected no pending dirty ranges", phase)
|
||||
}
|
||||
|
||||
for i := 0; i < buf.LineCount(); i++ {
|
||||
line := buf.Line(i)
|
||||
m := engine.LineStyleMap(buf, i)
|
||||
if len(m) != len([]rune(line)) {
|
||||
t.Fatalf("%s: line %d style length mismatch: got %d want %d", phase, i, len(m), len([]rune(line)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,43 +1,7 @@
|
||||
package theme
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
"github.com/alecthomas/chroma/v2/styles"
|
||||
)
|
||||
|
||||
//go:embed themes/*
|
||||
var themeFS embed.FS
|
||||
|
||||
// RegisterAll: Registers all XML theme files embedded in the themes/ directory
|
||||
// with chroma's style registry. After calling this, styles.Get() will recognize
|
||||
// any theme defined in those files.
|
||||
// RegisterAll is retained as a no-op for compatibility while Chroma-based
|
||||
// theme loading is removed.
|
||||
func RegisterAll() error {
|
||||
entries, err := themeFS.ReadDir("themes")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read embedded themes directory: %w", err)
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
f, err := themeFS.Open("themes/" + entry.Name())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open theme %s: %w", entry.Name(), err)
|
||||
}
|
||||
|
||||
style, err := chroma.NewXMLStyle(f)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse theme %s: %w", entry.Name(), err)
|
||||
}
|
||||
|
||||
styles.Register(style)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
373
main.go
Normal file
373
main.go
Normal file
@ -0,0 +1,373 @@
|
||||
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