junyi-hou / tree-sitter-fold

Code-folding using tree-sitter
8 stars 4 forks source link

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.

~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.

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~.

  1. make sure ~tree-sitter CLI~ is installed and up-to-date. To check, run

    +begin_src shell

    tree-sitter --version

    +end_src

    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.

  2. add new grammar repos to ~tree-sitter-langs-git-dir~. In the case of ~R~, run

    +begin_src emacs-lisp

    ;; 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")))

    +end_src

  3. compile the new grammar parser by running

    +begin_src emacs-lisp

    ;; 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)))

    +end_src

  4. register the respective major mode with the newly compiled grammar

    +begin_src emacs-lisp

    (unless (assq 'ess-r-mode tree-sitter-major-mode-language-alist) (add-to-list 'tree-sitter-major-mode-language-alist '(ess-r-mode . r)))

    +end_src

  5. require the new grammar

    +begin_src emacs-lisp

    (tree-sitter-require 'r)

    +end_src

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