From db244a6f3071dcf633eb77a871c6f79b49a8bfc8 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 4 Jan 2023 08:53:08 -0500 Subject: [PATCH 01/14] Prototype of treesit.el support for ruby This commit is a prototype exploring what it would mean to make the minor mode work with the built in `treesit.el` treesitter support in Emacs 29. I've modified many of the functions and fold rules specific to the ruby language, and have tested locally with a few ruby code files to confirm things are working The actual work consists mostly of ... * swapping out functions for their `treesit.el` equivalents * replacing symbols for node types with strings * Modifying `(alist-get...` usages to support string keys --- ts-fold-parsers.el | 16 +++---- ts-fold.el | 117 ++++++++++++++++++++++----------------------- 2 files changed, 65 insertions(+), 68 deletions(-) diff --git a/ts-fold-parsers.el b/ts-fold-parsers.el index d7087b9..464cfdf 100644 --- a/ts-fold-parsers.el +++ b/ts-fold-parsers.el @@ -211,14 +211,14 @@ (defun ts-fold-parsers-ruby () "Rule set for Ruby." - '((class . ts-fold-range-ruby-class-def) - (method . ts-fold-range-ruby-class-def) - (array . ts-fold-range-seq) - (do . (ts-fold-range-seq 1 -2)) ; match with `end` - (do_block . (ts-fold-range-seq 1 -2)) ; match with `end`, in spec file - (then . ts-fold-range-ruby-if) ; `if` and `elsif` block - (else . (ts-fold-range-ruby-if 4 0)) ; `else` block - (comment + '(("class" . ts-fold-range-ruby-class-def) + ("method" . ts-fold-range-ruby-class-def) + ("array" . ts-fold-range-seq) + ("do" . (ts-fold-range-seq 1 -2)) ; match with `end` + ("do_block" . (ts-fold-range-seq 1 -2)) ; match with `end`, in spec file + ("then" . ts-fold-range-ruby-if) ; `if` and `elsif` block + ("else" . (ts-fold-range-ruby-if 4 0)) ; `else` block + ("comment" . (lambda (node offset) (ts-fold-range-line-comment node offset "#"))))) diff --git a/ts-fold.el b/ts-fold.el index 47852ab..1739a37 100644 --- a/ts-fold.el +++ b/ts-fold.el @@ -41,7 +41,6 @@ (require 'subr-x) (require 's) -(require 'tree-sitter) (require 'ts-fold-util) (require 'ts-fold-parsers) @@ -82,7 +81,7 @@ (php-mode . ,(ts-fold-parsers-php)) (python-mode . ,(ts-fold-parsers-python)) (rjsx-mode . ,(ts-fold-parsers-javascript)) - (ruby-mode . ,(ts-fold-parsers-ruby)) + (ruby-ts-mode . ,(ts-fold-parsers-ruby)) (rust-mode . ,(ts-fold-parsers-rust)) (rustic-mode . ,(ts-fold-parsers-rust)) (sh-mode . ,(ts-fold-parsers-bash)) @@ -200,19 +199,19 @@ Return nil if no valid node is found. This function is borrowed from `tree-sitter-node-at-point'." (let* ((pos (or pos (point))) (mode-ranges (alist-get major-mode ts-fold-range-alist)) - (root (tsc-root-node tree-sitter-tree)) - (node (tsc-get-descendant-for-position-range root pos pos)) + (root (treesit-buffer-root-node)) + (node (treesit-node-descendant-for-range root pos pos)) ;; Used for looping (current node)) - (while (and current (not (alist-get (tsc-node-type current) mode-ranges))) - (setq current (tsc-get-parent current))) + (while (and current (not (alist-get (treesit-node-type current) mode-ranges nil t #'equal))) + (setq current (treesit-node-parent current))) current)) (defun ts-fold--get-fold-range (node) "Return the beginning (as buffer position) of fold for NODE. Return nil if there is no fold to be made." (when-let* ((fold-alist (alist-get major-mode ts-fold-range-alist)) - (fold-func (alist-get (tsc-node-type node) fold-alist))) + (fold-func (alist-get (treesit-node-type node) fold-alist nil t #'equal))) (cond ((functionp fold-func) (funcall fold-func node (cons 0 0))) ((listp fold-func) (funcall (nth 0 fold-func) node (cons (nth 1 fold-func) (nth 2 fold-func)))) (t (user-error "Bad folding function for node"))))) @@ -257,9 +256,9 @@ Return nil otherwise." (defmacro ts-fold--ensure-ts (&rest body) "Run BODY only if `tree-sitter-mode` is enabled." (declare (indent 0)) - `(if (bound-and-true-p tree-sitter-mode) + `(if (treesit-buffer-root-node) (progn ,@body) - (user-error "Ignored, tree-sitter-mode is not enabled in the current buffer"))) + (user-error "Ignored, cannot parse current buffer with treesit"))) ;;;###autoload (defun ts-fold-close (&optional node) @@ -294,8 +293,8 @@ If the current node is not folded or not foldable, do nothing." (interactive) (ts-fold--ensure-ts (when-let* ((node (ts-fold--foldable-node-at-pos)) - (beg (tsc-node-start-position node)) - (end (tsc-node-end-position node))) + (beg (treesit-node-start node)) + (end (treesit-node-end node))) (thread-last (overlays-in beg end) (seq-filter (lambda (ov) (eq (overlay-get ov 'invisible) 'ts-fold))) (mapc #'delete-overlay))))) @@ -305,14 +304,12 @@ If the current node is not folded or not foldable, do nothing." "Fold all foldable syntax nodes in the buffer." (interactive) (ts-fold--ensure-ts - (let* ((node (tsc-root-node tree-sitter-tree)) - (patterns (seq-mapcat (lambda (fold-range) `((,(car fold-range)) @name)) - (alist-get major-mode ts-fold-range-alist) - 'vector)) - (query (tsc-make-query tree-sitter-language patterns)) - (nodes-to-fold (tsc-query-captures query node #'ignore))) + (let* ((node (treesit-buffer-root-node)) + (patterns (mapconcat (lambda (fold-range) (concat "(" (car fold-range) ") " "@name")) + (alist-get major-mode ts-fold-range-alist) " ")) + (query (treesit-query-compile (treesit-node-language node) patterns)) + (nodes-to-fold (treesit-query-capture node query nil nil t))) (thread-last nodes-to-fold - (mapcar #'cdr) (mapc #'ts-fold-close))))) ;;;###autoload @@ -356,7 +353,7 @@ If the current syntax node is not foldable, do nothing." "Return previous/next sibling node starting from NODE. If NEXT is non-nil, return next sibling. Otherwirse, return previouse sibling." - (if next (tsc-get-next-sibling node) (tsc-get-prev-sibling node))) + (if next (treesit-node-next-sibling node) (treesit-node-prev-sibling node))) (defun ts-fold--continuous-node-prefix (node prefix next) "Iterate through node starting from NODE and compare node-text to PREFIX; @@ -365,11 +362,11 @@ then return the last iterated node. Argument NEXT is a boolean type. If non-nil iterate forward; otherwise iterate in backward direction." (let ((iter-node node) (last-node node) - (last-line (car (tsc-node-start-point node))) line text break + (last-line (treesit-node-start node)) line text break (line-range 1) (last-line-range 1) max-line-range) (while (and iter-node (not break)) - (setq text (tsc-node-text iter-node) - line (car (tsc-node-start-point iter-node)) + (setq text (treesit-node-text iter-node) + line (treesit-node-start iter-node) line-range (1+ (s-count-matches "\n" text)) max-line-range (max line-range last-line-range)) (if (and (ts-fold--in-range-p line (- last-line max-line-range) (+ last-line max-line-range)) @@ -388,8 +385,8 @@ in backward direction." "Return the fold range in sequence starting from NODE. Argument OFFSET can be used to tweak the final beginning and end position." - (let ((beg (1+ (tsc-node-start-position node))) - (end (1- (tsc-node-end-position node)))) + (let ((beg (1+ (treesit-node-start node))) + (end (1- (treesit-node-end node)))) (ts-fold--cons-add (cons beg end) offset))) (defun ts-fold-range-line-comment (node offset prefix) @@ -402,8 +399,8 @@ Argument PREFIX is the comment prefix in string." (when-let* ((first-node (ts-fold--continuous-node-prefix node prefix nil)) (last-node (ts-fold--continuous-node-prefix node prefix t)) (prefix-len (length prefix)) - (beg (+ (tsc-node-start-position first-node) prefix-len)) - (end (tsc-node-end-position last-node))) + (beg (+ (treesit-node-start first-node) prefix-len)) + (end (treesit-node-end last-node))) (ts-fold--cons-add (cons beg end) offset))) (defun ts-fold-range-block-comment (node offset) @@ -436,8 +433,8 @@ For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." (let* ((named-node (tsc-get-child-by-field node :condition)) (else (tsc-get-child-by-field node :alternative)) - (beg (tsc-node-end-position named-node)) - (end (1- (tsc-node-start-position else)))) + (beg (treesit-node-end named-node)) + (end (1- (treesit-node-start else)))) (ts-fold--cons-add (cons beg end) offset))) (defun ts-fold-range-c-preproc-ifdef (node offset) @@ -447,8 +444,8 @@ For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." (when-let* ((named-node (tsc-get-child-by-field node :name)) (else (tsc-get-child-by-field node :alternative)) - (beg (tsc-node-end-position named-node)) - (end (1- (tsc-node-start-position else)))) + (beg (treesit-node-end named-node)) + (end (1- (treesit-node-start else)))) (ts-fold--cons-add (cons beg end) offset))) (defun ts-fold-range-c-preproc-elif (node offset) @@ -458,8 +455,8 @@ For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." (when-let* ((named-node (tsc-get-child-by-field node :condition)) (else (tsc-get-child-by-field node :alternative)) - (beg (tsc-node-end-position named-node)) - (end (1- (tsc-node-start-position else)))) + (beg (treesit-node-end named-node)) + (end (1- (treesit-node-start else)))) (ts-fold--cons-add (cons beg end) offset))) (defun ts-fold-range-c-preproc-else (node offset) @@ -469,8 +466,8 @@ For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." (when-let* ((target "#else") (len (length target)) - (beg (+ (tsc-node-start-position node) len)) - (end (tsc-node-end-position node))) + (beg (+ (treesit-node-start node) len)) + (end (treesit-node-end node))) (ts-fold--cons-add (cons beg end) offset))) (defun ts-fold-range-html (node offset) @@ -478,9 +475,9 @@ more information." For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." - (let* ((beg (tsc-node-end-position (tsc-get-nth-child node 0))) + (let* ((beg (treesit-node-end (tsc-get-nth-child node 0))) (end-node (tsc-get-nth-child node (1- (tsc-count-children node)))) - (end (tsc-node-start-position end-node))) + (end (treesit-node-start end-node))) (ts-fold--cons-add (cons beg end) offset))) ;;+ OCaml @@ -494,9 +491,9 @@ more information." (when-let* ((text (tsc-node-text node)) (beg (if (string-prefix-p "(* " text) - (+ 2 (tsc-node-start-position node)) - (+ 3 (tsc-node-start-position node)))) - (end (- (tsc-node-end-position node) 2))) + (+ 2 (treesit-node-start node)) + (+ 3 (treesit-node-start node)))) + (end (- (treesit-node-end node) 2))) (ts-fold--cons-add (cons beg end) offset)))) (defun ts-fold-range-ocaml-module-definition (node offset) @@ -509,8 +506,8 @@ more information." ((module-binding (tsc-get-nth-named-child node 0)) (body (tsc-get-child-by-field module-binding :body)) ;; body is struct ... end - (beg (+ 6 (tsc-node-start-position body))) - (end (- (tsc-node-end-position node) 3))) + (beg (+ 6 (treesit-node-start body))) + (end (- (treesit-node-end node) 3))) (ts-fold--cons-add (cons beg end) offset)))) (defun ts-fold-range-ocaml-type-definition (node offset) @@ -525,12 +522,12 @@ more information." (text (tsc-node-text (tsc-get-nth-child body 0))) (beg (if (string-equal "{" text) - (1+ (tsc-node-start-position body)) - (tsc-node-end-position (tsc-get-prev-sibling body)))) + (1+ (treesit-node-start body)) + (treesit-node-end (tsc-get-prev-sibling body)))) (end (if (string-equal "{" text) - (1- (tsc-node-end-position node)) - (tsc-node-end-position node)))) + (1- (treesit-node-end node)) + (treesit-node-end node)))) (ts-fold--cons-add (cons beg end) offset)))) (defun ts-fold-range-ocaml-value-definition (node offset) @@ -542,8 +539,8 @@ more information." (when-let* ((let-binding (tsc-get-nth-named-child node 0)) (body (tsc-get-child-by-field let-binding :body)) - (beg (tsc-node-end-position (tsc-get-prev-sibling body))) - (end (tsc-node-end-position node))) + (beg (treesit-node-end (tsc-get-prev-sibling body))) + (end (treesit-node-end node))) (ts-fold--cons-add (cons beg end) offset)))) ;;- OCaml @@ -558,8 +555,8 @@ more information." (tsc-get-child-by-field node :parameters) (tsc-get-child-by-field node :name))) ;; the colon is an anonymous node after return_type or parameters node - (beg (tsc-node-end-position (tsc-get-next-sibling named-node))) - (end (tsc-node-end-position node))) + (beg (treesit-node-end (tsc-get-next-sibling named-node))) + (end (treesit-node-end node))) (ts-fold--cons-add (cons beg end) offset))) (defun ts-fold-range-ruby-class-def (node offset) @@ -567,11 +564,11 @@ more information." For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." - (when-let* ((named-node (or (tsc-get-child-by-field node :superclass) - (tsc-get-child-by-field node :parameters) - (tsc-get-child-by-field node :name))) - (beg (tsc-node-end-position named-node)) - (end (tsc-node-end-position node))) + (when-let* ((named-node (or (treesit-node-child-by-field-name node "superclass") + (treesit-node-child-by-field-name node "parameters") + (treesit-node-child-by-field-name node "name"))) + (beg (treesit-node-end named-node)) + (end (treesit-node-end node))) (ts-fold--cons-add (cons beg (- end 3)) offset))) (defun ts-fold-range-ruby-if (node offset) @@ -579,8 +576,8 @@ more information." For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." - (when-let ((beg (tsc-node-start-position node)) - (end (tsc-node-end-position node))) + (when-let ((beg (treesit-node-start node)) + (end (treesit-node-end node))) (ts-fold--cons-add (cons beg end) offset))) (defun ts-fold-range-rust-macro (node offset) @@ -591,8 +588,8 @@ more information." (when-let* ((children (tsc-count-children node)) (last_bracket (tsc-get-nth-child node (- children 1))) (first_bracket (tsc-get-nth-child node 2)) - (beg (tsc-node-start-position first_bracket)) - (end (1+ (tsc-node-start-position last_bracket)))) + (beg (treesit-node-start first_bracket)) + (end (1+ (treesit-node-start last_bracket)))) (ts-fold--cons-add (cons beg end) offset))) (defun ts-fold-range-elixir (node offset) @@ -603,8 +600,8 @@ more information." (when-let* ((children (tsc-count-children node)) (end_child (tsc-get-nth-child node (- children 1))) (do_child (tsc-get-nth-child node 1)) - (beg (tsc-node-start-position do_child)) - (end (tsc-node-start-position end_child))) + (beg (treesit-node-start do_child)) + (end (treesit-node-start end_child))) (ts-fold--cons-add (cons beg end) offset))) (defun ts-fold-range-julia (node offset) From 708c5f714957077eddd18194ed8ee5bb7f417d03 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 8 Jan 2023 00:18:30 -0500 Subject: [PATCH 02/14] Getting all the languages to work --- ts-fold-parsers.el | 16 ++++----- ts-fold-util.el | 19 +++++++++++ ts-fold.el | 82 ++++++++++++++++++++++++---------------------- 3 files changed, 69 insertions(+), 48 deletions(-) diff --git a/ts-fold-parsers.el b/ts-fold-parsers.el index 464cfdf..d7087b9 100644 --- a/ts-fold-parsers.el +++ b/ts-fold-parsers.el @@ -211,14 +211,14 @@ (defun ts-fold-parsers-ruby () "Rule set for Ruby." - '(("class" . ts-fold-range-ruby-class-def) - ("method" . ts-fold-range-ruby-class-def) - ("array" . ts-fold-range-seq) - ("do" . (ts-fold-range-seq 1 -2)) ; match with `end` - ("do_block" . (ts-fold-range-seq 1 -2)) ; match with `end`, in spec file - ("then" . ts-fold-range-ruby-if) ; `if` and `elsif` block - ("else" . (ts-fold-range-ruby-if 4 0)) ; `else` block - ("comment" + '((class . ts-fold-range-ruby-class-def) + (method . ts-fold-range-ruby-class-def) + (array . ts-fold-range-seq) + (do . (ts-fold-range-seq 1 -2)) ; match with `end` + (do_block . (ts-fold-range-seq 1 -2)) ; match with `end`, in spec file + (then . ts-fold-range-ruby-if) ; `if` and `elsif` block + (else . (ts-fold-range-ruby-if 4 0)) ; `else` block + (comment . (lambda (node offset) (ts-fold-range-line-comment node offset "#"))))) diff --git a/ts-fold-util.el b/ts-fold-util.el index 07880a5..956bfe7 100644 --- a/ts-fold-util.el +++ b/ts-fold-util.el @@ -46,6 +46,25 @@ (push ov lst))) lst)) +;; +;; (@* "Treesitter") +;; + +(defun ts-fold--get-nth-child (node index) + (aref (treesit-node-children node) index)) + +(defun ts-fold--get-nth-named-child (node index) + (aref (treesit-node-children node t) index)) + +;; +;; (@* "Folds" ) +;; + +(defun ts-fold--get-fold-func (node) + (let ((fold-alist (alist-get major-mode ts-fold-range-alist)) + (node-symbol (intern (treesit-node-type node)))) + (alist-get node-symbol fold-alist))) + ;; ;; (@* "Face" ) ;; diff --git a/ts-fold.el b/ts-fold.el index 1739a37..3805b6d 100644 --- a/ts-fold.el +++ b/ts-fold.el @@ -88,6 +88,8 @@ (scala-mode . ,(ts-fold-parsers-scala)) (swift-mode . ,(ts-fold-parsers-swift)) (tuareg-mode . ,(ts-fold-parsers-ocaml)) + (typescript-ts-mode . ,(ts-fold-parsers-typescript)) + (tsx-ts-mode . ,(ts-fold-parsers-typescript)) (typescript-mode . ,(ts-fold-parsers-typescript))) "An alist of (major-mode . (foldable-node-type . function)). @@ -152,9 +154,9 @@ the fold in a cons cell. See `ts-fold-range-python' for an example." (ts-fold-open-all))) (defun ts-fold--tree-sitter-trigger () - "Turn `ts-fold-mode' on and off alongside `tree-sitter-mode' + "Turn `ts-fold-mode' on and off alongside `treesit' when in a mode ts-fold can act on." - (if (and tree-sitter-mode (ts-fold-usable-mode-p)) + (if (and (treesit-buffer-root-node) (ts-fold-usable-mode-p)) (ts-fold-mode 1) (ts-fold-mode -1))) @@ -175,12 +177,12 @@ when in a mode ts-fold can act on." :global t (if global-ts-fold-mode (progn - (add-hook 'tree-sitter-mode-hook #'ts-fold--tree-sitter-trigger) + (add-hook 'prog-mode-hook #'ts-fold--tree-sitter-trigger) ;; try to turn on in all buffers. (dolist (buf (buffer-list)) (with-current-buffer buf (ts-fold--tree-sitter-trigger)))) - (remove-hook 'tree-sitter-mode-hook #'ts-fold--tree-sitter-trigger))) + (remove-hook 'prog-mode-hook #'ts-fold--tree-sitter-trigger))) (defun ts-fold-usable-mode-p (&optional mode) "Return non-nil if `ts-fold' has defined folds for MODE." @@ -203,7 +205,7 @@ This function is borrowed from `tree-sitter-node-at-point'." (node (treesit-node-descendant-for-range root pos pos)) ;; Used for looping (current node)) - (while (and current (not (alist-get (treesit-node-type current) mode-ranges nil t #'equal))) + (while (and current (not (ts-fold--get-fold-func current))) (setq current (treesit-node-parent current))) current)) @@ -211,7 +213,7 @@ This function is borrowed from `tree-sitter-node-at-point'." "Return the beginning (as buffer position) of fold for NODE. Return nil if there is no fold to be made." (when-let* ((fold-alist (alist-get major-mode ts-fold-range-alist)) - (fold-func (alist-get (treesit-node-type node) fold-alist nil t #'equal))) + (fold-func (ts-fold--get-fold-func node))) (cond ((functionp fold-func) (funcall fold-func node (cons 0 0))) ((listp fold-func) (funcall (nth 0 fold-func) node (cons (nth 1 fold-func) (nth 2 fold-func)))) (t (user-error "Bad folding function for node"))))) @@ -379,7 +381,7 @@ in backward direction." (defun ts-fold--one-liner-node (node) "Helper function to check if NODE is on one line only." - (= (car (aref (tsc-node-range node) 2)) (car (aref (tsc-node-range node) 3)))) + (= (car (aref (treesit-node-start node) 2)) (car (aref (treesit-node-end node) 3)))) (defun ts-fold-range-seq (node offset) "Return the fold range in sequence starting from NODE. @@ -415,7 +417,7 @@ more information." For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." - (let ((text (tsc-node-text node))) + (let ((text (treesit-node-text node))) (if (and (string-match-p "\n" text) (string-prefix-p "/*" text)) (ts-fold-range-block-comment node offset) (if (string-prefix-p "///" text) @@ -431,8 +433,8 @@ more information." For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." - (let* ((named-node (tsc-get-child-by-field node :condition)) - (else (tsc-get-child-by-field node :alternative)) + (let* ((named-node (treesit-node-child-by-field-name node "condition")) + (else (treesit-node-child-by-field-name node "alternative")) (beg (treesit-node-end named-node)) (end (1- (treesit-node-start else)))) (ts-fold--cons-add (cons beg end) offset))) @@ -442,8 +444,8 @@ more information." For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." - (when-let* ((named-node (tsc-get-child-by-field node :name)) - (else (tsc-get-child-by-field node :alternative)) + (when-let* ((named-node (treesit-node-child-by-field-name node "name")) + (else (treesit-node-child-by-field-name node "alternative")) (beg (treesit-node-end named-node)) (end (1- (treesit-node-start else)))) (ts-fold--cons-add (cons beg end) offset))) @@ -453,8 +455,8 @@ more information." For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." - (when-let* ((named-node (tsc-get-child-by-field node :condition)) - (else (tsc-get-child-by-field node :alternative)) + (when-let* ((named-node (treesit-node-child-by-field-name node "condition")) + (else (treesit-node-child-by-field-name node "alternative")) (beg (treesit-node-end named-node)) (end (1- (treesit-node-start else)))) (ts-fold--cons-add (cons beg end) offset))) @@ -475,8 +477,8 @@ more information." For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." - (let* ((beg (treesit-node-end (tsc-get-nth-child node 0))) - (end-node (tsc-get-nth-child node (1- (tsc-count-children node)))) + (let* ((beg (treesit-node-end (ts-fold--get-nth-child node 0))) + (end-node (ts-fold--get-nth-child node (1- (treesit-node-child-count node)))) (end (treesit-node-start end-node))) (ts-fold--cons-add (cons beg end) offset))) @@ -489,7 +491,7 @@ For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." (unless (ts-fold--one-liner-node node) (when-let* - ((text (tsc-node-text node)) + ((text (treesit-node-text node)) (beg (if (string-prefix-p "(* " text) (+ 2 (treesit-node-start node)) (+ 3 (treesit-node-start node)))) @@ -503,8 +505,8 @@ For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." (unless (ts-fold--one-liner-node node) (when-let* - ((module-binding (tsc-get-nth-named-child node 0)) - (body (tsc-get-child-by-field module-binding :body)) + ((module-binding (ts-fold--get-nth-child node 0)) + (body (treesit-node-child-by-field-name module-binding "body")) ;; body is struct ... end (beg (+ 6 (treesit-node-start body))) (end (- (treesit-node-end node) 3))) @@ -517,13 +519,13 @@ For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." (unless (ts-fold--one-liner-node node) (when-let* - ((type-definition (tsc-get-nth-named-child node 0)) - (body (tsc-get-child-by-field type-definition :body)) - (text (tsc-node-text (tsc-get-nth-child body 0))) + ((type-definition (ts-fold--get-nth-named-child node 0)) + (body (treesit-node-child-by-field-name type-definition "body")) + (text (treesit-node-text (ts-fold--get-nth-child body 0))) (beg (if (string-equal "{" text) (1+ (treesit-node-start body)) - (treesit-node-end (tsc-get-prev-sibling body)))) + (treesit-node-end (treesit-node-prev-sibling body)))) (end (if (string-equal "{" text) (1- (treesit-node-end node)) @@ -537,9 +539,9 @@ For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." (unless (ts-fold--one-liner-node node) (when-let* - ((let-binding (tsc-get-nth-named-child node 0)) - (body (tsc-get-child-by-field let-binding :body)) - (beg (treesit-node-end (tsc-get-prev-sibling body))) + ((let-binding (ts-fold--get-nth-named-child node 0)) + (body (treesit-node-child-by-field-name let-binding "body")) + (beg (treesit-node-end (treesit-node-prev-sibling body))) (end (treesit-node-end node))) (ts-fold--cons-add (cons beg end) offset)))) @@ -550,12 +552,12 @@ more information." For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." - (when-let* ((named-node (or (tsc-get-child-by-field node :superclasses) - (tsc-get-child-by-field node :return_type) - (tsc-get-child-by-field node :parameters) - (tsc-get-child-by-field node :name))) + (when-let* ((named-node (or (treesit-node-child-by-field-name node "superclasses") + (treesit-node-child-by-field-name node "return_type") + (treesit-node-child-by-field-name node "parameters") + (treesit-node-child-by-field-name node "name"))) ;; the colon is an anonymous node after return_type or parameters node - (beg (treesit-node-end (tsc-get-next-sibling named-node))) + (beg (treesit-node-end (treesit-node-next-sibling named-node))) (end (treesit-node-end node))) (ts-fold--cons-add (cons beg end) offset))) @@ -585,9 +587,9 @@ more information." For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." - (when-let* ((children (tsc-count-children node)) - (last_bracket (tsc-get-nth-child node (- children 1))) - (first_bracket (tsc-get-nth-child node 2)) + (when-let* ((children (treesit-node-child-count node)) + (last_bracket (ts-fold--get-nth-child node (- children 1))) + (first_bracket (ts-fold--get-nth-child node 2)) (beg (treesit-node-start first_bracket)) (end (1+ (treesit-node-start last_bracket)))) (ts-fold--cons-add (cons beg end) offset))) @@ -597,9 +599,9 @@ more information." For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." - (when-let* ((children (tsc-count-children node)) - (end_child (tsc-get-nth-child node (- children 1))) - (do_child (tsc-get-nth-child node 1)) + (when-let* ((children (treesit-node-child-count node)) + (end_child (ts-fold--get-nth-child node (- children 1))) + (do_child (ts-fold--get-nth-child node 1)) (beg (treesit-node-start do_child)) (end (treesit-node-start end_child))) (ts-fold--cons-add (cons beg end) offset))) @@ -610,9 +612,9 @@ more information." It excludes the NODE's first child and the `end' keyword. For argument OFFSET, see function `ts-fold-range-seq' for more information." - (let* ((identifier (tsc-get-nth-named-child node 0)) - (end-position (byte-to-position (aref (tsc-node-range identifier) 1))) - (start-position (byte-to-position (aref (tsc-node-range node) 0))) + (let* ((identifier (ts-fold--get-nth-named-child node 0)) + (end-position (treesit-node-end identifier)) + (start-position (treesit-node-start node)) (fold-begin (1- (- end-position start-position)))) (ts-fold-range-seq node (ts-fold--cons-add (cons fold-begin -2) offset)))) From de098df56abed7b3531f3488273572bed1a670ca Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 8 Jan 2023 09:29:45 -0500 Subject: [PATCH 03/14] Removing old treesitter interface from indicators --- ts-fold-indicators.el | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ts-fold-indicators.el b/ts-fold-indicators.el index 467c253..862f0b4 100644 --- a/ts-fold-indicators.el +++ b/ts-fold-indicators.el @@ -278,16 +278,15 @@ Argument FOLDED holds folding state; it's a boolean." "Refresh indicators for all folding range." (when ts-fold-indicators-mode (ts-fold--ensure-ts - (when-let* ((node (tsc-root-node tree-sitter-tree)) + (when-let* ((node (treesit-buffer-root-node)) (patterns (seq-mapcat (lambda (fold-range) `((,(car fold-range)) @name)) (alist-get major-mode ts-fold-range-alist) 'vector)) (query (ignore-errors - (tsc-make-query tree-sitter-language patterns))) - (nodes-to-fold (tsc-query-captures query node #'ignore))) + treesit-query-compile (treesit-node-language node) patterns)) + (nodes-to-fold (treesit-query-capture node query nil nil t))) (ts-fold-indicators--remove-overlays) (thread-last nodes-to-fold - (mapcar #'cdr) (mapc #'ts-fold-indicators--create)))))) (defun ts-fold-indicators--remove-overlays () From f51f807a27c7f400980a7cf9ccfbb74dad9626e3 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 8 Jan 2023 09:31:57 -0500 Subject: [PATCH 04/14] Remove all final treesitter emacs library references --- README.md | 2 ++ ts-fold-parsers.el | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e6817bd..7d6bcee 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +**This is a WIP port of the original ts-fold library to work with Emacs 29's built in treesit.el. It is not fully tested, and will likely be eventually folded into the original library sometime after the Emacs 29 release** + [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![JCS-ELPA](https://raw.githubusercontent.com/jcs-emacs/badges/master/elpa/v/ts-fold.svg)](https://jcs-emacs.github.io/jcs-elpa/#/ts-fold) diff --git a/ts-fold-parsers.el b/ts-fold-parsers.el index d7087b9..3484401 100644 --- a/ts-fold-parsers.el +++ b/ts-fold-parsers.el @@ -192,7 +192,7 @@ (compound_statement . ts-fold-range-seq) (comment . (lambda (node offset) - (if (string-prefix-p "#" (tsc-node-text node)) + (if (string-prefix-p "#" (treesit-node-text node)) (ts-fold-range-line-comment node offset "#") (ts-fold-range-c-like-comment node offset)))))) From 3bd91f1ac6ef732c861f6b436cbfd79149b33172 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Thu, 2 Feb 2023 09:35:58 -0500 Subject: [PATCH 05/14] Add ts- modes --- ts-fold.el | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ts-fold.el b/ts-fold.el index 3805b6d..9ac8083 100644 --- a/ts-fold.el +++ b/ts-fold.el @@ -60,7 +60,9 @@ (defcustom ts-fold-range-alist `((agda-mode . ,(ts-fold-parsers-agda)) (c-mode . ,(ts-fold-parsers-c)) + (c-ts-mode . ,(ts-fold-parsers-c)) (c++-mode . ,(ts-fold-parsers-c++)) + (c++-ts-mode . ,(ts-fold-parsers-c++)) (caml-mode . ,(ts-fold-parsers-ocaml)) (csharp-mode . ,(ts-fold-parsers-csharp)) (css-mode . ,(ts-fold-parsers-css)) @@ -71,15 +73,18 @@ (java-mode . ,(ts-fold-parsers-java)) (javascript-mode . ,(ts-fold-parsers-javascript)) (js-mode . ,(ts-fold-parsers-javascript)) + (js-ts-mode . ,(ts-fold-parsers-javascript)) (js2-mode . ,(ts-fold-parsers-javascript)) (js3-mode . ,(ts-fold-parsers-javascript)) (json-mode . ,(ts-fold-parsers-json)) + (json-ts-mode . ,(ts-fold-parsers-json)) (jsonc-mode . ,(ts-fold-parsers-json)) (julia-mode . ,(ts-fold-parsers-julia)) (nix-mode . ,(ts-fold-parsers-nix)) (ocaml-mode . ,(ts-fold-parsers-ocaml)) (php-mode . ,(ts-fold-parsers-php)) (python-mode . ,(ts-fold-parsers-python)) + (python-ts-mode . ,(ts-fold-parsers-python)) (rjsx-mode . ,(ts-fold-parsers-javascript)) (ruby-ts-mode . ,(ts-fold-parsers-ruby)) (rust-mode . ,(ts-fold-parsers-rust)) @@ -90,7 +95,9 @@ (tuareg-mode . ,(ts-fold-parsers-ocaml)) (typescript-ts-mode . ,(ts-fold-parsers-typescript)) (tsx-ts-mode . ,(ts-fold-parsers-typescript)) - (typescript-mode . ,(ts-fold-parsers-typescript))) + (typescript-mode . ,(ts-fold-parsers-typescript)) + (typescript-ts-mode . ,(ts-fold-parsers-typescript)) + ) "An alist of (major-mode . (foldable-node-type . function)). FUNCTION is used to determine where the beginning and end for FOLDABLE-NODE-TYPE From ad62bc734ebd48288e00afd258e10d129b22757a Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Thu, 2 Feb 2023 09:36:16 -0500 Subject: [PATCH 06/14] Check that `treesit-buffer-root-node` is defined Not sure why this is needed but I get startup errors without these. Perhaps an ordering issue. --- ts-fold.el | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ts-fold.el b/ts-fold.el index 9ac8083..86305a4 100644 --- a/ts-fold.el +++ b/ts-fold.el @@ -163,7 +163,9 @@ the fold in a cons cell. See `ts-fold-range-python' for an example." (defun ts-fold--tree-sitter-trigger () "Turn `ts-fold-mode' on and off alongside `treesit' when in a mode ts-fold can act on." - (if (and (treesit-buffer-root-node) (ts-fold-usable-mode-p)) + (if (and (functionp 'treesit-buffer-root-node) + (treesit-buffer-root-node) + (ts-fold-usable-mode-p)) (ts-fold-mode 1) (ts-fold-mode -1))) @@ -264,10 +266,13 @@ Return nil otherwise." (defmacro ts-fold--ensure-ts (&rest body) "Run BODY only if `tree-sitter-mode` is enabled." - (declare (indent 0)) - `(if (treesit-buffer-root-node) - (progn ,@body) - (user-error "Ignored, cannot parse current buffer with treesit"))) + (declare (indent 0) + (debug (&rest form))) + `(if (and (functionp 'treesit-buffer-root-node) (treesit-buffer-root-node)) + (progn + (message "treesit is enabled!") + ,@body) + (message "ts-fold: ignoring, because cannot parse current buffer with treesit"))) ;;;###autoload (defun ts-fold-close (&optional node) From b9d44b6e0c5db725bd35fe6f6daaca2e31f59f16 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Thu, 2 Feb 2023 09:36:54 -0500 Subject: [PATCH 07/14] Fix ts-fold-close-all `(car fold-range)` is not a sequence, it's a symbol, at least in my C++ buffers. This patch converts it using `symbol-name`. --- ts-fold.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts-fold.el b/ts-fold.el index 86305a4..6514c29 100644 --- a/ts-fold.el +++ b/ts-fold.el @@ -319,7 +319,7 @@ If the current node is not folded or not foldable, do nothing." (interactive) (ts-fold--ensure-ts (let* ((node (treesit-buffer-root-node)) - (patterns (mapconcat (lambda (fold-range) (concat "(" (car fold-range) ") " "@name")) + (patterns (mapconcat (lambda (fold-range) (concat "(" (symbol-name (car fold-range)) ") " "@name")) (alist-get major-mode ts-fold-range-alist) " ")) (query (treesit-query-compile (treesit-node-language node) patterns)) (nodes-to-fold (treesit-query-capture node query nil nil t))) From ae7643ee3d473a7e37237a60d7ade50115ffd9ad Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Thu, 2 Feb 2023 09:40:29 -0500 Subject: [PATCH 08/14] Remove debug message --- ts-fold.el | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ts-fold.el b/ts-fold.el index 6514c29..c77e5c3 100644 --- a/ts-fold.el +++ b/ts-fold.el @@ -269,9 +269,7 @@ Return nil otherwise." (declare (indent 0) (debug (&rest form))) `(if (and (functionp 'treesit-buffer-root-node) (treesit-buffer-root-node)) - (progn - (message "treesit is enabled!") - ,@body) + (progn ,@body) (message "ts-fold: ignoring, because cannot parse current buffer with treesit"))) ;;;###autoload From e838f1c51edff99dfe50f12e63dd367689f26328 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Thu, 2 Feb 2023 10:11:23 -0500 Subject: [PATCH 09/14] Fix ts-fold-indicators: treesit-query-compile needs list, not vector For simplicity I just converted the `patterns` vector to a list, but there are probably simpler ways to do this. --- ts-fold-indicators.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ts-fold-indicators.el b/ts-fold-indicators.el index 862f0b4..0fb3ee0 100644 --- a/ts-fold-indicators.el +++ b/ts-fold-indicators.el @@ -283,7 +283,8 @@ Argument FOLDED holds folding state; it's a boolean." (alist-get major-mode ts-fold-range-alist) 'vector)) (query (ignore-errors - treesit-query-compile (treesit-node-language node) patterns)) + (treesit-query-compile (treesit-node-language node) + (append patterns nil)))) (nodes-to-fold (treesit-query-capture node query nil nil t))) (ts-fold-indicators--remove-overlays) (thread-last nodes-to-fold From 526987c4e5c110cff3b53af4007738489df272e3 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Thu, 2 Feb 2023 11:44:35 -0500 Subject: [PATCH 10/14] Handle errors from treesit-buffer-root-node It seems `treesit-buffer-root-node` returns an error when there is no parser for a given buffer. In my case, this happens when trying to get doc for an elisp variable; `ts-fold--tree-sitter-trigger` runs in an elisp-mode "*temp*" buffer, and `treesit-buffer-root-node` raises an error. If I just ignore the error around that call, `ts-fold--tree-sitter-trigger` still runs `(ts-fold-mode -1)`. Seems like the simplest thing is to just do nothing in that case. To repro the problem, enable ts-fold mode globally and do `C-H v emacs-version`. --- ts-fold.el | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ts-fold.el b/ts-fold.el index c77e5c3..d3c5253 100644 --- a/ts-fold.el +++ b/ts-fold.el @@ -163,11 +163,12 @@ the fold in a cons cell. See `ts-fold-range-python' for an example." (defun ts-fold--tree-sitter-trigger () "Turn `ts-fold-mode' on and off alongside `treesit' when in a mode ts-fold can act on." - (if (and (functionp 'treesit-buffer-root-node) - (treesit-buffer-root-node) - (ts-fold-usable-mode-p)) - (ts-fold-mode 1) - (ts-fold-mode -1))) + (condition-case nil + (if (and (treesit-buffer-root-node) + (ts-fold-usable-mode-p)) + (ts-fold-mode 1) + (ts-fold-mode -1)) + (error nil))) ;;;###autoload (define-minor-mode ts-fold-mode From 38f646cedb8b3d86b08c40a7c0bf3abdb479e955 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Fri, 3 Feb 2023 13:24:32 -0500 Subject: [PATCH 11/14] Remove duplicate parser entry --- ts-fold.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ts-fold.el b/ts-fold.el index d3c5253..10f6fc1 100644 --- a/ts-fold.el +++ b/ts-fold.el @@ -93,8 +93,7 @@ (scala-mode . ,(ts-fold-parsers-scala)) (swift-mode . ,(ts-fold-parsers-swift)) (tuareg-mode . ,(ts-fold-parsers-ocaml)) - (typescript-ts-mode . ,(ts-fold-parsers-typescript)) - (tsx-ts-mode . ,(ts-fold-parsers-typescript)) + (tsx-ts-mode . ,(ts-fold-parsers-typescript)) (typescript-mode . ,(ts-fold-parsers-typescript)) (typescript-ts-mode . ,(ts-fold-parsers-typescript)) ) From 3c0f46fdd2a8355a3384a4dfc36691be59d45fb6 Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Fri, 3 Feb 2023 13:24:46 -0500 Subject: [PATCH 12/14] Add YAML support This is not perfect; when folding a node with children, it shows the first character of the node, then the fold indicator, then the colon. Ideally it would show the whole name of the key, and then the fold indicator. Not sure how to do that. --- ts-fold-parsers.el | 6 ++++++ ts-fold.el | 2 ++ 2 files changed, 8 insertions(+) diff --git a/ts-fold-parsers.el b/ts-fold-parsers.el index 3484401..84d701a 100644 --- a/ts-fold-parsers.el +++ b/ts-fold-parsers.el @@ -258,5 +258,11 @@ "Rule set for TypeScript." (append (ts-fold-parsers-javascript))) +(defun ts-fold-parsers-yaml () + "Rule set for YAML." + '((block_mapping_pair . ts-fold-range-seq) + (block_sequence . ts-fold-range-seq))) + + (provide 'ts-fold-parsers) ;;; ts-fold-parsers.el ends here diff --git a/ts-fold.el b/ts-fold.el index 10f6fc1..8c605dc 100644 --- a/ts-fold.el +++ b/ts-fold.el @@ -96,6 +96,8 @@ (tsx-ts-mode . ,(ts-fold-parsers-typescript)) (typescript-mode . ,(ts-fold-parsers-typescript)) (typescript-ts-mode . ,(ts-fold-parsers-typescript)) + (yaml-mode . ,(ts-fold-parsers-yaml)) + (yaml-ts-mode . ,(ts-fold-parsers-yaml)) ) "An alist of (major-mode . (foldable-node-type . function)). From 59da0279f95f2688a348c0cd98ae14cabef7accb Mon Sep 17 00:00:00 2001 From: Gary Oberbrunner Date: Fri, 3 Feb 2023 13:41:37 -0500 Subject: [PATCH 13/14] Fix yaml support - Use a better ts-fold-range function for yaml - Remove "block_sequence" fold-parser; don't think that's useful. - Fix ts-fold--get-nth-child: `(treesit-node-children) is a list, not an array. --- ts-fold-parsers.el | 4 ++-- ts-fold-util.el | 4 ++-- ts-fold.el | 13 +++++++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ts-fold-parsers.el b/ts-fold-parsers.el index 84d701a..fe9b796 100644 --- a/ts-fold-parsers.el +++ b/ts-fold-parsers.el @@ -52,6 +52,7 @@ (declare-function ts-fold-range-ruby-if "ts-fold.el") (declare-function ts-fold-range-rust-macro "ts-fold.el") (declare-function ts-fold-range-elixir "ts-fold.el") +(declare-function ts-fold-range-yaml-object "ts-fold.el") ;; ;; (@* "Parsers" ) @@ -260,8 +261,7 @@ (defun ts-fold-parsers-yaml () "Rule set for YAML." - '((block_mapping_pair . ts-fold-range-seq) - (block_sequence . ts-fold-range-seq))) + '((block_mapping_pair . ts-fold-range-yaml-object))) (provide 'ts-fold-parsers) diff --git a/ts-fold-util.el b/ts-fold-util.el index 956bfe7..3508ddc 100644 --- a/ts-fold-util.el +++ b/ts-fold-util.el @@ -51,10 +51,10 @@ ;; (defun ts-fold--get-nth-child (node index) - (aref (treesit-node-children node) index)) + (nth index (treesit-node-children node))) (defun ts-fold--get-nth-named-child (node index) - (aref (treesit-node-children node t) index)) + (nth index (treesit-node-children node t))) ;; ;; (@* "Folds" ) diff --git a/ts-fold.el b/ts-fold.el index 8c605dc..e30b2b8 100644 --- a/ts-fold.el +++ b/ts-fold.el @@ -630,5 +630,18 @@ information." (fold-begin (1- (- end-position start-position)))) (ts-fold-range-seq node (ts-fold--cons-add (cons fold-begin -2) offset)))) +(defun ts-fold-range-yaml-object (node offset) + "Define fold range for YAML object. + +For arguments NODE and OFFSET, see function `ts-fold-range-seq' for +more information. + +Excludes the NODE's first child, which is the key." + (let* ((beg (treesit-node-end (ts-fold--get-nth-child node 0))) + (end (treesit-node-end node))) + (ts-fold--cons-add (cons beg end) offset))) + + + (provide 'ts-fold) ;;; ts-fold.el ends here From a7bb833c66ae5bdd6143628140f9679629171b8d Mon Sep 17 00:00:00 2001 From: Arto Young Date: Mon, 13 Mar 2023 23:58:17 +0800 Subject: [PATCH 14/14] Fix folding of c-preproc-if Some c-preproc-if have no 'else' branch and 'alternative' node. For example: CODE: #if THREAD_SIZE >= PAGE_SIZE void __init __weak thread_stack_cache_init(void) { } #endif TREE: (preproc_if #if condition: (binary_expression left: (identifier) operator: >= right: (identifier)) \n (declaration type: (primitive_type) declarator: (identifier) type: ;) (function_definition type: (type_identifier) declarator: (function_declarator declarator: (identifier) parameters: (parameter_list ( (parameter_declaration type: (primitive_type)) ))) body: (compound_statement { })) #endif) So we use last child node(i.e. "#endif") instead of 'alternative' node in this case. --- ts-fold.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ts-fold.el b/ts-fold.el index e30b2b8..6db8c30 100644 --- a/ts-fold.el +++ b/ts-fold.el @@ -446,7 +446,8 @@ more information." For arguments NODE and OFFSET, see function `ts-fold-range-seq' for more information." (let* ((named-node (treesit-node-child-by-field-name node "condition")) - (else (treesit-node-child-by-field-name node "alternative")) + (else (or (treesit-node-child-by-field-name node "alternative") + (treesit-node-child node -1))) (beg (treesit-node-end named-node)) (end (1- (treesit-node-start else)))) (ts-fold--cons-add (cons beg end) offset)))