meain / evil-textobj-tree-sitter

Tree-sitter powered textobjects for evil mode in Emacs
Apache License 2.0
199 stars 14 forks source link

Add support for builtin treesit using helix queries #93

Closed meain closed 1 year ago

meain commented 1 year ago

This branch, currently experimental, adds evil-texobj-tree-sitter with the builtin treesit using texobject queries from helix. Although neovim has more complete queries, the builtin treesit cannot parse most of them. This still gets you most of the way there, but will only miss a few QOL stuff like not including brackets when selecting inside a function etc. The idea is to keep this open and dogfood it for a while and merge it in eventually.

(use-package evil-textobj-tree-sitter
  :straight (evil-textobj-tree-sitter
             :host github
             :repo "meain/evil-textobj-tree-sitter"
             :files (:defaults "queries" "treesit-queries")
             :branch "treesit")))

You can use this branch with straight like above. For more information refer https://github.com/meain/evil-textobj-tree-sitter/issues/76

TODOs

d1egoaz commented 1 year ago

Hey @meain thanks for doing this, I can't wait for the built-in treesit support.

I was trying this branch with:

(use-package evil-textobj-tree-sitter
  :after (evil treesit)
  :straight (evil-textobj-tree-sitter
             :host github
             :repo "meain/evil-textobj-tree-sitter"
             :branch "treesit")
  :config
  ;; bind `function.outer`(entire function block) to `f` for use in things like `vaf`, `yaf`
  (define-key evil-outer-text-objects-map "f" (evil-textobj-tree-sitter-get-textobj "function.outer"))
  ;; bind `function.inner`(function block without name and args) to `f` for use in things like `vif`, `yif`
  (define-key evil-inner-text-objects-map "f" (evil-textobj-tree-sitter-get-textobj "function.inner"))
...

Then, in a go-ts-mode or rust-ts-mode buffer I run (thing-at-point 'function) or (thing-at-point 'loop) and I got this errors:

Debugger entered--Lisp error: (wrong-type-argument treesit-query-p nil)
  evil-textobj-tree-sitter--treesit-get-nodes(nil)
  evil-textobj-tree-sitter--get-nodes((function.outer) nil)
  evil-textobj-tree-sitter--get-within((function.outer) 1 nil)
  evil-textobj-tree-sitter--thing-at-point-bounds("function.outer")
  #f(compiled-function () #<bytecode 0x1bec2b3405c72eb8>)()
  bounds-of-thing-at-point(function)
  thing-at-point(function)
  eval-expression((thing-at-point 'function) nil nil 127)
  funcall-interactively(eval-expression (thing-at-point 'function) nil nil 127)
  command-execute(eval-expression)
Debugger entered--Lisp error: (wrong-type-argument treesit-query-p nil)
  evil-textobj-tree-sitter--treesit-get-nodes(nil)
  evil-textobj-tree-sitter--get-nodes((loop.outer) nil)
  evil-textobj-tree-sitter--get-within((loop.outer) 1 nil)
  evil-textobj-tree-sitter--thing-at-point-bounds("loop.outer")
  #f(compiled-function () #<bytecode -0x1487bd632c2abf5>)()
  bounds-of-thing-at-point(loop)
  thing-at-point(loop)
  eval-expression((thing-at-point 'loop) nil nil 127)
  funcall-interactively(eval-expression (thing-at-point 'loop) nil nil 127)
  command-execute(eval-expression)

(emacs-version)

GNU Emacs 30.0.50 (build 3, aarch64-apple-darwin22.4.0, NS appkit-2299.50 Version 13.3.1 (Build 22E261)) of 2023-05-09
meain commented 1 year ago

@d1egoaz Thanks for trying it out. I had missed including :files (:defaults "queries" "treesit-queries") bit in the config. We need to explicit specify it to load the query directories as they are not elisp files. I've updated the original comment. You'll just have to update your :straight block to include the :files section like above.

PS: You might have to delete ~/.config/emacs/straight/build/evil-textobj-tree-sitter for straight to pick up the change and rebuild it.

d1egoaz commented 1 year ago

@meain Thanks, the new config fixes the issue.

I'm now having a different issue, and I wonder what the issue is.

loop is defined in https://github.com/meain/evil-textobj-tree-sitter/blob/master/queries/go/textobjects.scm#L49-L51 but it isn't in https://github.com/helix-editor/helix/blob/master/runtime/queries/go/textobjects.scm nor in https://github.com/tree-sitter/tree-sitter-go/blob/master/grammar.js not sure if it's because loop, conditional, etc, needs to be defined some where.

meain commented 1 year ago

If my cursor is in the if statement, (thing-at-point 'function) works as expected, however for 'conditional, or 'loop it returns nil

You were on the right track with "... but it isn't in helix-editor/helix@master/runtime/queries/go/textobjects.scm...". The helix queries (which we use for treesit) are still in the early stages and does not provide loop or conditional textobjects.

If the cursor is in the x := 1, calling (thing-at-point 'function) also return nil.

Hmm, somehow I am not able to repro this. Where on this line is the cursor when you call the function?

d1egoaz commented 1 year ago

Thanks!!

If the cursor is in the x := 1, calling (thing-at-point 'function) also return nil.

Hmm, somehow I am not able to repro this. Where on this line is the cursor when you call the function?

Ohh sorry, I meant to say (thing-at-point 'assignment))

Which I think the answer is the same as your previous answer.

Thanks!

jasonjckn commented 1 year ago

I ran a couple tests on some julia code, and 'vaf' almost seemed to find the correct bounds but offset by ~10 each time.

my config was

(use-package evil-textobj-tree-sitter)
(define-key evil-outer-text-objects-map "f" (evil-textobj-tree-sitter-get-textobj "function.outer"))
(define-key evil-inner-text-objects-map "f" (evil-textobj-tree-sitter-get-textobj "function.inner"))
Screenshot 2023-05-28 at 11 01 30 AM

Thanks for all your work!


EDIT: weird, I can't reproduce the julia one after restarting emacs, maybe the treesit parser was in a funky state? the bounds look more like the golang one now (i.e. just 1 past the end of the function. )

jasonjckn commented 1 year ago

Also, i tried it on some elisp, and vaf, gave me this error,

evil-textobj-tree-sitter--elisp-tree-sitter-get-nodes: Wrong type argument: user-ptrp, nil

I notice combobulate has a more graceful error message here

treesit-node-on: No available parser for this buffer
jasonjckn commented 1 year ago

In (defcustom evil-textobj-tree-sitter-major-mode-language-alist nil

can you add clojure please

i.e. modes = clojure-mode clojurec-mode clojurescript-mode clojure-ts-mode lang = clojure


emacs-lisp-mode wouldn't hurt too.

jasonjckn commented 1 year ago

in golang, vaf doesn't quite get the right bounds either

Screenshot 2023-05-28 at 11 26 28 AM
d1egoaz commented 1 year ago

@jasonjckn vaf works for me 🤔

I wonder if you have an additional minor/mode applied, might want to check with emacs -Q

image
jasonjckn commented 1 year ago

@jasonjckn vaf works for me 🤔

I wonder if you have an additional minor/mode applied, might want to check with emacs -Q

image

I deleted everything in my config except doom itself, and i'm still able to reproduce :( doom-config.zip

EDIT: I tried your config @d1egoaz and couldn't reproduce, so it must be specific to doom's configuration of evil, or something that ships in stock doom modules.

Screenshot 2023-05-28 at 6 56 47 PM
meain commented 1 year ago

so it must be specific to doom's configuration of evil

That is my best guess too. Since I don't use doom I don't have much clue of what exactly might be going on unfortunately but I'll maybe dig in if I find some time.

meain commented 1 year ago

@jasonjckn I was cleaning up a few things and the problem you had might not have been a doom thing. The package was doing some extra stuff to handle non ascii or multibye buffers which is not needed for treesit and that was messing up the locations. Feel free to give the new changes a shot.

p00f commented 1 year ago

Cask in this branch still lists tree-sitter as a dependency

meain commented 1 year ago

The deps in Cask are just for development and CI and not really used anywhere else. I am keeping tree-sitter in there as we still support it. @p00f

p00f commented 1 year ago

@meain I thought package managers pulled in dependencies from Cask?

meain commented 1 year ago

@p00f IIUC they pull them from the file header (which I forgot to remove).

https://github.com/meain/evil-textobj-tree-sitter/blob/e8bb9d63deeb2953ff9600e1633de667b3d7673e/evil-textobj-tree-sitter-core.el#L6

p00f commented 1 year ago

Ah

Stebalien commented 1 year ago

When trying to get an inner/outer function/class/argument in rust, I'm getting the following error:

let*: Query pattern is malformed: "Invalid predicate", "eq?", "Currently Emacs only supports equal',match', and `pred' predicates"

This appears to be due this section at the bottom of treesit-queries/rust/textobjects.scm:

(; #[test]
 (attribute_item
   (attribute
     (identifier) @_test_attribute))
 ; allow other attributes like #[should_panic] and comments
 [
   (attribute_item)
   (line_comment)
 ]*
 ; the test function
 (function_item
   body: (_) @test.inner) @test.outer
 (#eq? @_test_attribute "test"))

Removing that section fixes the issue.

meain commented 1 year ago

@Stebalien thanks for bringing up the issue. I missed the fact that treesit implementation used #equal instead of #eq? which is used by tree-sitter. I've fixed in the latest commit.