]> git.eshelyaron.com Git - emacs.git/commitdiff
ruby-ts-mode: Fix/simplify Imenu index generation
authorDmitry Gutov <dgutov@yandex.ru>
Mon, 27 Mar 2023 00:49:13 +0000 (03:49 +0300)
committerDmitry Gutov <dgutov@yandex.ru>
Mon, 27 Mar 2023 00:49:13 +0000 (03:49 +0300)
* lisp/progmodes/ruby-ts-mode.el (ruby-ts--full-name): Drop '#'
from the end of resulting string when the node is not a method.
Support 'singleton_method' nodes.
(ruby-ts--imenu-helper): Simplify, to create a "flat" list of
entries, rather than a nested one.  The previous implementation
had problems (like producing a nested structure of full-qualified
names, thus creating a lot of textual repetition), seems easier to
just follow ruby-mode's example here, at least for Emacs 29's
release.

* test/lisp/progmodes/ruby-ts-mode-tests.el (ruby-ts-imenu-index):
New test.

lisp/progmodes/ruby-ts-mode.el
test/lisp/progmodes/ruby-ts-mode-tests.el

index 0915c29881d099de09c24625b539413c49b74178..91d65a2777b751c165b6fd5a79b32dbd1937d4d6 100644 (file)
@@ -883,32 +883,24 @@ a statement container is a node that matches
   "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
@@ -916,8 +908,14 @@ NODE into a list of imenu ( name . pos ) nodes"
 (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."
index e0d9f1b5c504a4ad0ef9de7b0c689681a490fa2f..11125dc5cd3a2467ad23d1dde0c1f0aa627977d3 100644 (file)
@@ -281,6 +281,31 @@ The whitespace before and including \"|\" on each line is removed."
        (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)