Please use the ~switch-to-treesit~ branch for ~emacs-29~ and above with the builtin tree-sitter library ~treesit~.
~tree-sitter-fold~ builds on top of [[https://github.com/ubolonton/emacs-tree-sitter][emacs-tree-sitter]] to provide code folding base on the tree-sitter syntax tree.
Installation
with ~straight.el~ and ~use-package~:
(use-package tree-sitter-fold :straight (host github repo "junyi-hou/tree-sitter-fold"))
manual:
git clone https://github.com/junyi-hou/tree-sitter-fold /path/to/lib
then in ~emacs~:
(add-to-list 'load-path "/path/to/lib") (require 'tree-sitter-fold)
Usage
~tree-sitter-fold-close~: fold the current syntax node.
~tree-sitter-fold-open~: open all folds inside the current syntax node.
~tree-sitter-fold-open-recursively~: open the outmost fold of the current syntax node. Keep the sub-folds close.
~tree-sitter-fold-close-all~: close all foldable syntax nodes in the current buffer.
~tree-sitter-fold-open-all~: open all folded syntax nodes in the current buffer.
~tree-sitter-fold~ allows user-defined rules of how to fold syntax nodes. Two variables determine which node is foldable and how ~tree-sitter-fold~ should fold those nodes. I only implement folding mechanisms for ~python~. Contribution on other types of folds and for other major modes are welcome.
** ~tree-sitter-fold-foldable-node-alist~
This is an alist whose keys are ~major-mode~ symbols, and the corresponding values are lists of ~tree-sitter-node-type~ considered foldable in the major mode. ~tree-sitter-fold-close~ will fold only if the node are one of the types defined in this list under the current major mode.
** ~tree-sitter-fold-range-alist~
This variable determines how ~tree-sitter-fold~ should fold each of the nodes defined in ~tree-sitter-fold-foldable-node-alist~. It is a nested alist, with the first key being the major mode, and second key being the ~tree-sitter-node-type~. The value should be a function that takes a ~tree-sitter-node~ as the single argument, and return a cons cell of buffer position as the range should be folded by ~tree-sitter-fold~. See ~tree-sitter-fold-range-python~ for an example.
Supported languages
python
R (need to compile [[https://github.com/r-lib/tree-sitter-r][tree-sitter-r]] grammar, see [[Note on installing additional grammars]])
Nix (need to compile [[https://github.com/cstrahan/tree-sitter-nix][tree-sitter-nix]], see [[Note no installing additional grammars]])
Go (thanks to [[https://github.com/jakejx][jakejx]])
contribution of other languages are welcome!
Contribution
Enable ~tree-sitter-mode~ first, then ~tree-sitter-query-builder~ is useful to test out queries that determine what syntax nodes should be foldable and how to fold them. emacs-tree-sitter has [[https://ubolonton.github.io/emacs-tree-sitter/syntax-highlighting/queries/][an excellent documentation]] on how to write ~tree-sitter~ queries.
Following the following steps to build additional grammar for ~emacs-tree-sitter~.
make sure ~tree-sitter CLI~ is installed and up-to-date. To check, run
tree-sitter --version
The new ABI requires ~0.19.3~ or newer version of ~tree-sitter~. ~Node.js~ is also required to generate grammar parsers. See [[https://github.com/tree-sitter/tree-sitter/blob/master/cli/README.md][here]] for detailed instructions of how to install tree-sitter CLI tools.
add new grammar repos to ~tree-sitter-langs-git-dir~. In the case of ~R~, run
;; make sure I do not re-add the submodule multiple times (unless (f-exists-p (concat tree-sitter-langs-git-dir "/repos/r")) (let ((default-directory tree-sitter-langs-git-dir)) (tree-sitter-langs--call "git" "submodule" "add" "https://github.com/r-lib/tree-sitter-r" "repos/r")))
compile the new grammar parser by running
;; compile only if haven't done so (unless (--filter (string= (f-base it) "r") (f-entries (tree-sitter-langs--bin-dir))) ;; do not display compile information in a new buffer (cl-letf (((symbol-function 'tree-sitter-langs--buffer) (lambda (&rest _) nil))) (tree-sitter-langs-compile 'r)))
register the respective major mode with the newly compiled grammar
(unless (assq 'ess-r-mode tree-sitter-major-mode-language-alist) (add-to-list 'tree-sitter-major-mode-language-alist '(ess-r-mode . r)))
require the new grammar
(tree-sitter-require 'r)
I have those things together in a function ~gatsby:install-and-load-tree-sitter-r~ at [[https://github.com/junyi-hou/dotfiles/blob/main/main.org#rtree-sitter][here]] and hook it to ~ess-r-mode-hook~.
MIT