emacs-tree-sitter / elisp-tree-sitter

Emacs Lisp bindings for tree-sitter
https://emacs-tree-sitter.github.io
MIT License
826 stars 74 forks source link

Integration with expand-region #20

Open ubolonton opened 4 years ago

ubolonton commented 4 years ago

The basic idea is simple: walking up the syntax tree from node at point.

Below is a simple implementation that expands region to the next bigger node:

(defun tree-sitter-mark-bigger-node ()
  (interactive)
  (let* ((p (point))
         (m (or (mark) p))
         (beg (min p m))
         (end (max p m))
         (root (ts-root-node tree-sitter-tree))
         (node (ts-get-descendant-for-position-range root beg end))
         (node-beg (ts-node-start-position node))
         (node-end (ts-node-end-position node)))
    ;; Node fits the region exactly. Try its parent node instead.
    (when (and (= beg node-beg) (= end node-end))
      (when-let ((node (ts-get-parent node)))
        (setq node-beg (ts-node-start-position node)
              node-end (ts-node-end-position node))))
    (set-mark node-end)
    (goto-char node-beg)))

(setq er/try-expand-list (append er/try-expand-list
                                 '(tree-sitter-mark-bigger-node)))
skrytebane commented 1 year ago

Hi! This seems to work fine. Here's a small adjustment to use non-obsolete tree-sitter functions, etc.

(defun tree-sitter-mark-bigger-node ()
  (interactive)
  (let* ((root (tsc-root-node tree-sitter-tree))
         (node (tsc-get-descendant-for-position-range root (region-beginning) (region-end)))
         (node-start (tsc-node-start-position node))
         (node-end (tsc-node-end-position node)))
    ;; Node fits the region exactly. Try its parent node instead.
    (when (and (= (region-beginning) node-start) (= (region-end) node-end))
      (when-let ((node (tsc-get-parent node)))
        (setq node-start (tsc-node-start-position node)
              node-end (tsc-node-end-position node))))
    (set-mark node-end)
    (goto-char node-start)))
c-alpha commented 1 month ago

Updated again to use Emacs 31 APIs:

  (defun er/mark-ts-node ()
    (interactive)
    (when (and (boundp 'treesit-primary-parser) treesit-primary-parser)
      (let* ((node (if (use-region-p)
                       (treesit-node-on (region-beginning) (region-end))
                     (treesit-node-at (point))))
             (node-start (treesit-node-start node))
             (node-end (treesit-node-end node)))
        ;; when the node fits the region exactly, try its parent node instead
        (when (and (= (region-beginning) node-start)
                   (= (region-end) node-end))
          (when-let ((node (treesit-node-parent node)))
            (setq node-start (treesit-node-start node)
                  node-end (treesit-node-end node))))
        (goto-char node-start)
        (set-mark node-end))))
jimeh commented 1 month ago

@c-alpha that's excellent. Can I be an annoying random person on the internet making demands, and kindly ask what contract region command would look like? I often find myself going a step too far and need to contract back down again... lol

c-alpha commented 1 month ago

You don't need specific code for contracting. Just put the marking function on er/try-expand-list, and expand-region will take care of the rest.

c-alpha commented 1 month ago

This has just been added to magnars/expand-region.el as commit 541d971f7c77ca5c0f66c88bcbfeb0d165883023. Now we'll have to wait for the next release of expand-region (or install HEAD via quelpa).