Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for builtin treesit using helix queries #93

Merged
merged 11 commits into from Aug 7, 2023
Merged

Conversation

meain
Copy link
Owner

@meain meain commented May 21, 2023

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 #76

TODOs

  • Update melpa with queries-dir Update recipe for evil-textobj-tree-sitter melpa/melpa#8669
  • Update readme with mention about treesit and updated installation instructions
  • Mention about usage and state of helix grammars
  • Ensure we have enough tests for the treesit side
  • Add script to auto convert helix queries to make it work for treesit
  • Fix CI to load treesit grammars

@d1egoaz
Copy link

d1egoaz commented May 22, 2023

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
Copy link
Owner Author

meain commented May 22, 2023

@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
Copy link

d1egoaz commented May 22, 2023

@meain Thanks, the new config fixes the issue.

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

  • If my cursor is in the if statement, (thing-at-point 'function) works as expected, however for 'conditional, or 'loop it returns nil.
func (f *Feed) xxx() *Entry {
	x := 1
	for _, entry := range f.Entries {
		if entry.Category.Term == "yyy" {
			return &entry
		}
	}
	return nil
}

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.

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

@meain
Copy link
Owner Author

meain commented May 23, 2023

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
Copy link

d1egoaz commented May 23, 2023

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
Copy link

jasonjckn commented May 28, 2023

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
Copy link

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
Copy link

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
Copy link

in golang, vaf doesn't quite get the right bounds either
Screenshot 2023-05-28 at 11 26 28 AM

@d1egoaz
Copy link

d1egoaz commented May 28, 2023

@jasonjckn vaf works for me 🤔

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

image

@jasonjckn
Copy link

jasonjckn commented May 29, 2023

@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
Copy link
Owner Author

meain commented May 30, 2023

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
Copy link
Owner Author

meain commented Jun 4, 2023

@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
Copy link
Contributor

p00f commented Jul 15, 2023

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

@meain
Copy link
Owner Author

meain commented Jul 18, 2023

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
Copy link
Contributor

p00f commented Jul 18, 2023

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

@meain
Copy link
Owner Author

meain commented Jul 18, 2023

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

;; Package-Requires: ((emacs "25.1") (tree-sitter "0.15.0"))

@p00f
Copy link
Contributor

p00f commented Jul 18, 2023

Ah

@Stebalien
Copy link

Stebalien commented Aug 3, 2023

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.

Emacs treesit implementation doesn't support #eq? and #equal is the
replacement for it.
@meain
Copy link
Owner Author

meain commented Aug 7, 2023

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

@meain meain merged commit 0877337 into master Aug 7, 2023
2 checks passed
@meain meain deleted the treesit branch August 7, 2023 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants