Migrate nvim-treesitter to main branch
The master branch is archived and its parsers no longer match nvim
head. Main is a smaller rewrite with no `configs.setup`; parsers
install via `.install()`, highlights move to a native FileType
autocmd, incremental selection becomes a small custom module, and
textobjects-main rebinds via per-module require calls. The setup
moves to its own treesitter.fnl since the new shape is sizeable.

Assisted-by: Claude Opus 4.7 via Claude Code
change vtrkxsxsytoznpzsxyklkvywwlloowqv
commit 228a7dc1b36d723deb5736f4065ed3ce9158ec44
author Alpha Chen <alpha@kejadlen.dev>
date
parent rxrylyus
diff --git a/.config/nvim/fnl/init.fnl b/.config/nvim/fnl/init.fnl
index 71cafe4f..b0201a7d 100644
--- a/.config/nvim/fnl/init.fnl
+++ b/.config/nvim/fnl/init.fnl
@@ -131,9 +131,11 @@
                 :version :v3.9.1}
                {:src "https://github.com/neovim/nvim-lspconfig.git"
                 :version (vim.version.range :v2.8.0)}
-               "https://github.com/nvim-treesitter/nvim-treesitter.git"
+               {:src "https://github.com/nvim-treesitter/nvim-treesitter.git"
+                :version :main}
                "https://github.com/nvim-treesitter/nvim-treesitter-context.git"
-               "https://github.com/nvim-treesitter/nvim-treesitter-textobjects.git"
+               {:src "https://github.com/nvim-treesitter/nvim-treesitter-textobjects.git"
+                :version :main}
                ;; here for completeness, but actually added in init.lua for bootstrapping purposes
                ; "https://github.com/rktjmp/hotpot.nvim.git"
                "https://github.com/suy/vim-context-commentstring.git"
@@ -197,72 +199,7 @@
 (set vim.g.netrw_home "~/.nvim_tmp")
 
 ;;; treesitter
-(let [{: treesitter} vim
-      {: setup} (require :nvim-treesitter.configs)]
-  (setup {:ensure_installed [:fennel
-                             :hcl
-                             :lua
-                             :python
-                             :query
-                             :ruby
-                             :rust
-                             :terraform
-                             :typescript
-                             :yaml]
-          :sync_install false
-          :highlight {:enable true :additional_vim_regex_highlighting false}
-          ;; disabling since this is super annoying in Ruby
-          ; :indent {:enable true}
-          :incremental_selection {:enable true
-                                  :keymaps {:init_selection :gnn
-                                            :node_incremental :grn
-                                            :scope_incremental :grc
-                                            :node_decremental :grm}}
-          :textobjects {:move {:enable true
-                               :set_jumps true
-                               :goto_next_start {"]a" "@parameter.inner"
-                                                 "]b" "@block.outer"
-                                                 "]c" "@class.inner"
-                                                 "]f" "@function.outer"}
-                               :goto_next_end {"]A" "@parameter.inner"
-                                               "]B" "@block.outer"
-                                               "]C" "@class.outer"
-                                               "]F" "@function.outer"}
-                               :goto_previous_start {"[a" "@parameter.inner"
-                                                     "[b" "@block.outer"
-                                                     "[c" "@class.outer"
-                                                     "[f" "@function.outer"}
-                               :goto_previous_end {"[A" "@parameter.inner"
-                                                   "[B" "@block.outer"
-                                                   "[C" "@class.outer"
-                                                   "[F" "@function.outer"}}
-                        :select {:enable true
-                                 :lookahead true
-                                 :keymaps {:af "@function.outer"
-                                           :if "@function.inner"
-                                           :ac "@class.outer"
-                                           :ic "@class.inner"
-                                           :ab "@block.outer"
-                                           :ib "@block.inner"
-                                           :aa "@parameter.outer"
-                                           :ia "@parameter.inner
-                                           "}}
-                        :swap {:enable true
-                               :swap_next {:<leader>a "@parameter.inner"}
-                               :swap_previous {:<leader>A "@parameter.inner"}}}})
-  (treesitter.language.register :yaml :yaml.ansible)
-  (treesitter.query.set :python :folds "[
-  (function_definition)
-  (class_definition)
-  (block)
-] @fold
-[
-  (import_statement)
-  (import_from_statement)
-]+ @fold"))
-
-(let [{: setup} (require :treesitter-context)]
-  (setup))
+(require :treesitter)
 
 ;; https://neovim.io/doc/user/lsp.html#vim.lsp.foldexpr()
 (set vim.o.foldmethod :expr)
diff --git a/.config/nvim/fnl/treesitter.fnl b/.config/nvim/fnl/treesitter.fnl
new file mode 100644
index 00000000..a473f95d
--- /dev/null
+++ b/.config/nvim/fnl/treesitter.fnl
@@ -0,0 +1,120 @@
+(local {:api {:nvim_buf_set_mark nvim-buf-set-mark
+              :nvim_create_autocmd nvim-create-autocmd
+              :nvim_get_current_buf nvim-get-current-buf}
+        : keymap
+        : treesitter} vim)
+
+;;; parsers
+;; Async install at startup; subsequent calls no-op for already-installed parsers.
+(let [ts (require :nvim-treesitter)]
+  (ts.install [:fennel :hcl :lua :python :query :ruby :rust :terraform
+               :typescript :yaml]))
+
+;;; highlighting
+;; Native treesitter highlight per filetype with a parser; pcall covers filetypes without one.
+(nvim-create-autocmd :FileType
+                     {:callback #(pcall treesitter.start $1.buf)})
+
+;;; custom queries
+(treesitter.language.register :yaml :yaml.ansible)
+(treesitter.query.set :python :folds "[
+  (function_definition)
+  (class_definition)
+  (block)
+] @fold
+[
+  (import_statement)
+  (import_from_statement)
+]+ @fold")
+
+;;; incremental selection
+;; Replaces the `incremental_selection` module from nvim-treesitter master.
+;; Maintains a per-buffer stack of nodes; each expansion pushes the parent.
+(let [stacks {}
+      get-stack #(or (. stacks $1) [])
+      select-node (fn [node]
+                    (let [(srow scol erow ecol) (node:range)]
+                      (nvim-buf-set-mark 0 :< (+ srow 1) scol {})
+                      (nvim-buf-set-mark 0 :> (+ erow 1)
+                                         (math.max (- ecol 1) 0) {})
+                      (vim.cmd "normal! gv")))
+      push (fn [bufnr node]
+             (let [stack (get-stack bufnr)]
+               (table.insert stack node)
+               (tset stacks bufnr stack)
+               (select-node node)))
+      expand (fn []
+               (let [bufnr (nvim-get-current-buf)
+                     stack (get-stack bufnr)
+                     current (. stack (length stack))
+                     parent (and current (current:parent))]
+                 (when parent (push bufnr parent))))
+      shrink (fn []
+               (let [bufnr (nvim-get-current-buf)
+                     stack (get-stack bufnr)]
+                 (when (> (length stack) 1)
+                   (table.remove stack)
+                   (tset stacks bufnr stack)
+                   (select-node (. stack (length stack))))))]
+  (keymap.set :n :gnn
+              #(let [bufnr (nvim-get-current-buf)
+                     node (treesitter.get_node)]
+                 (when node
+                   (tset stacks bufnr [])
+                   (push bufnr node))))
+  (keymap.set :x :grn expand)
+  (keymap.set :x :grc expand)
+  (keymap.set :x :grm shrink))
+
+;;; textobjects (nvim-treesitter-textobjects main branch)
+(let [{: setup} (require :nvim-treesitter-textobjects)
+      ts-select (require :nvim-treesitter-textobjects.select)
+      ts-swap (require :nvim-treesitter-textobjects.swap)
+      ts-move (require :nvim-treesitter-textobjects.move)
+      bind-each (fn [modes specs action]
+                  (each [_ [lhs query] (ipairs specs)]
+                    (keymap.set modes lhs #(action query :textobjects))))]
+  (setup {:select {:lookahead true} :move {:set_jumps true}})
+
+  (bind-each [:x :o]
+             [[:af "@function.outer"]
+              [:if "@function.inner"]
+              [:ac "@class.outer"]
+              [:ic "@class.inner"]
+              [:ab "@block.outer"]
+              [:ib "@block.inner"]
+              [:aa "@parameter.outer"]
+              [:ia "@parameter.inner"]]
+             ts-select.select_textobject)
+
+  (keymap.set :n :<leader>a #(ts-swap.swap_next "@parameter.inner"))
+  (keymap.set :n :<leader>A #(ts-swap.swap_previous "@parameter.inner"))
+
+  (bind-each [:n :x :o]
+             [["]a" "@parameter.inner"]
+              ["]b" "@block.outer"]
+              ["]c" "@class.inner"]
+              ["]f" "@function.outer"]]
+             ts-move.goto_next_start)
+  (bind-each [:n :x :o]
+             [["]A" "@parameter.inner"]
+              ["]B" "@block.outer"]
+              ["]C" "@class.outer"]
+              ["]F" "@function.outer"]]
+             ts-move.goto_next_end)
+  (bind-each [:n :x :o]
+             [["[a" "@parameter.inner"]
+              ["[b" "@block.outer"]
+              ["[c" "@class.outer"]
+              ["[f" "@function.outer"]]
+             ts-move.goto_previous_start)
+  (bind-each [:n :x :o]
+             [["[A" "@parameter.inner"]
+              ["[B" "@block.outer"]
+              ["[C" "@class.outer"]
+              ["[F" "@function.outer"]]
+             ts-move.goto_previous_end))
+
+;;; treesitter-context
+(let [{: setup} (require :treesitter-context)]
+  (setup))
diff --git a/.config/nvim/nvim-pack-lock.json b/.config/nvim/nvim-pack-lock.json
index 2cf49c57..f6946a86 100644
--- a/.config/nvim/nvim-pack-lock.json
+++ b/.config/nvim/nvim-pack-lock.json
@@ -35,16 +35,18 @@
       "version": "2.8.0 - 2.8.0"
     },
     "nvim-treesitter": {
-      "rev": "cf12346a3414fa1b06af75c79faebe7f76df080a",
-      "src": "https://github.com/nvim-treesitter/nvim-treesitter.git"
+      "rev": "4916d6592ede8c07973490d9322f187e07dfefac",
+      "src": "https://github.com/nvim-treesitter/nvim-treesitter.git",
+      "version": "'main'"
     },
     "nvim-treesitter-context": {
       "rev": "b0c45cefe2c8f7b55fc46f34e563bc428ef99636",
       "src": "https://github.com/nvim-treesitter/nvim-treesitter-context.git"
     },
     "nvim-treesitter-textobjects": {
-      "rev": "71385f1",
-      "src": "https://github.com/nvim-treesitter/nvim-treesitter-textobjects.git"
+      "rev": "851e865342e5a4cb1ae23d31caf6e991e1c99f1e",
+      "src": "https://github.com/nvim-treesitter/nvim-treesitter-textobjects.git",
+      "version": "'main'"
     },
     "vim-abolish": {
       "rev": "dcbfe06",