"Return the fully qualified name of NODE."
(let* ((name (ruby-ts--get-name node))
(delimiter "#"))
+ (when (equal (treesit-node-type node) "singleton_method")
+ (setq delimiter "."
+ name (treesit-node-text (treesit-node-child-by-field-name node "name"))))
(while (setq node (treesit-parent-until node #'ruby-ts--class-or-module-p))
- (setq name (concat (ruby-ts--get-name node) delimiter name))
+ (if name
+ (setq name (concat (ruby-ts--get-name node) delimiter name))
+ (setq name (ruby-ts--get-name node)))
(setq delimiter "::"))
name))
-(defun ruby-ts--imenu-helper (node)
- "Convert a treesit sparse tree NODE in an imenu list.
-Helper for `ruby-ts--imenu' which converts a treesit sparse
-NODE into a list of imenu ( name . pos ) nodes"
- (let* ((ts-node (car node))
- (subtrees (mapcan #'ruby-ts--imenu-helper (cdr node)))
- (name (when ts-node
- (ruby-ts--full-name ts-node)))
- (marker (when ts-node
- (set-marker (make-marker)
- (treesit-node-start ts-node)))))
- (cond
- ((or (null ts-node) (null name)) subtrees)
- ;; Don't include the anonymous "class" and "module" nodes
- ((string-match-p "(\"\\(class\\|module\\)\")"
- (treesit-node-string ts-node))
- nil)
- (subtrees
- `((,name ,(cons name marker) ,@subtrees)))
- (t
- `((,name . ,marker))))))
+(defun ruby-ts--imenu-helper (tree)
+ "Convert a treesit sparse tree NODE in a flat imenu list."
+ (if (cdr tree)
+ ;; We only use the "leaf" values in the tree. It does include a
+ ;; leaf node for every class or module body.
+ (cl-mapcan #'ruby-ts--imenu-helper (cdr tree))
+ (list (cons (ruby-ts--full-name (car tree))
+ (treesit-node-start (car tree))))))
;; For now, this is going to work like ruby-mode and return a list of
;; class, modules, def (methods), and alias. It is likely that this
(defun ruby-ts--imenu ()
"Return Imenu alist for the current buffer."
(let* ((root (treesit-buffer-root-node))
- (nodes (treesit-induce-sparse-tree root "^\\(method\\|alias\\|class\\|module\\)$")))
- (ruby-ts--imenu-helper nodes)))
+ (tree (treesit-induce-sparse-tree root
+ (rx bol (or "singleton_method"
+ "method"
+ "alias"
+ "class"
+ "module")
+ eol))))
+ (ruby-ts--imenu-helper tree)))
(defun ruby-ts--arrow-up-start (arg)
"Move to the start ARG levels up or out."
(file-truename
(expand-file-name (format "ruby-mode-resources/%s" ,file))))))
+(ert-deftest ruby-ts-imenu-index ()
+ (ruby-ts-with-temp-buffer
+ (ruby-ts-test-string
+ "module Foo
+ | class Blub
+ | def hi
+ | 'Hi!'
+ | end
+ |
+ | def bye
+ | 'Bye!'
+ | end
+ |
+ | private def self.hiding
+ | 'You can't see me'
+ | end
+ | end
+ |end")
+ (should (equal (mapcar #'car (ruby-ts--imenu))
+ '("Foo"
+ "Foo::Blub"
+ "Foo::Blub#hi"
+ "Foo::Blub#bye"
+ "Foo::Blub.hiding")))))
+
(defmacro ruby-ts-deftest-indent (file)
`(ert-deftest ,(intern (format "ruby-ts-indent-test/%s" file)) ()
;; :tags '(:expensive-test)