[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bug#60407: [PATCH] Update go-ts-mode to use Imenu facility
From: |
Randy Taylor |
Subject: |
bug#60407: [PATCH] Update go-ts-mode to use Imenu facility |
Date: |
Tue, 03 Jan 2023 14:30:59 +0000 |
On Tuesday, January 3rd, 2023 at 04:01, Evgeni Kolev <evgenysw@gmail.com> wrote:
>
> Hi Randy, thank you for your feedback!
>
> I'm providing an updated patch below. I tested with "type Quack int"
> and other cases such as:
> type MyInt = int
> type Option func(s string)
> type List[T any] struct {
> head, tail *element[T]
> }
> type Number interface {
> int64 | float64
> }
>
> After experimenting, I decided to add additional Imenu categories:
> "Alias" and "Type".
> So the final list of newly added categories is "Method", "Struct",
> "Interface", "Alias" and "Type".
Sounds good to me.
It looks like the alias type MyInt = int shows up in both categories: Alias and
Type.
It should probably belong just in the Alias category.
>
> Structs and interfaces are technically a private case for "Type", but
> are put in their own Imenu category.
> Hence the "Type" category now holds all types except structs,
> interfaces and aliases (aliases have their own tree sitter type
> defined in Go's grammar.js).
>
> For reference, eglot's Imenu uses category "Class" instead of "Type".
> But I decided to not replicate this behavior - "Class" is not a widely
> used Go concept.
> However, I decided to replicate another eglot behaviour - prefixing
> the method names with the type of the receiver (for example,
> "(rect).area" for "func (r rect) area() float64...").
Great! I was going to suggest that but forgot.
>
> I've also addressed the other comments. I'm a bit unsure how the git
> commit should be formatted - the part of the message which describes
> changed functions/files.
>
> Please let me know if the patch can be improved. The patch is below.
Comments below.
>
> commit a96e70052a79881ac666ab699ffd63ed916eaf83
> Author: Evgeni Kolev evgenysw@gmail.com
>
> Date: Thu Dec 29 17:49:40 2022 +0200
>
> Improve go-ts-mode Imenu
Maybe this should also say "and add navigation support" (or something similar)?
>
> The Imenu items are extended to support "Method", "Struct",
> "Interface", "Alias" and "Type".
>
> go-ts-mode is updated to use the Imenu facility added in commit
> b39dc7ab27a696a8607ab859aeff3c71509231f5.
>
> * lisp/progmodes/go-ts-mode.el (go-ts-mode--imenu-1) (go-ts-mode--imenu):
> Remove functions.
> (go-ts-mode--defun-name) (go-ts-mode--interface-node-p)
I'm no commit format expert, but I think this can be (go-ts-mode--defun-name,
go-ts-mode--interface-node-p)
Whenever it fits on a single line, you can combine them like that. Same for the
line below.
> (go-ts-mode--struct-node-p) (go-ts-mode--other-type-node-p)
> (go-ts-mode--alias-node-p): New functions.
> (go-ts-mode): Improve Imenu settings.
I think the (go-ts-mode) part should mention that navigation support was added.
>
> diff --git a/lisp/progmodes/go-ts-mode.el b/lisp/progmodes/go-ts-mode.el
> index 1d6a8a30db5..d91b555e03e 100644
> --- a/lisp/progmodes/go-ts-mode.el
> +++ b/lisp/progmodes/go-ts-mode.el
> @@ -173,44 +173,6 @@ go-ts-mode--font-lock-settings
> '((ERROR) @font-lock-warning-face))
> "Tree-sitter font-lock settings for `go-ts-mode'.") -(defun go-ts-mode--imenu
> () - "Return Imenu alist for the current buffer." - (let* ((node
> (treesit-buffer-root-node)) - (func-tree (treesit-induce-sparse-tree - node
> "function_declaration" nil 1000)) - (type-tree (treesit-induce-sparse-tree -
> node "type_spec" nil 1000)) - (func-index (go-ts-mode--imenu-1 func-tree)) -
> (type-index (go-ts-mode--imenu-1 type-tree))) - (append - (when func-index`
> (("Function" . ,func-index)))
> - (when type-index `(("Type" . ,type-index)))))) - -(defun
> go-ts-mode--imenu-1 (node) - "Helper for` go-ts-mode--imenu'.
> -Find string representation for NODE and set marker, then recurse
> -the subtrees."
> - (let* ((ts-node (car node))
> - (children (cdr node))
> - (subtrees (mapcan #'go-ts-mode--imenu-1
> - children))
> - (name (when ts-node
> - (treesit-node-text
> - (pcase (treesit-node-type ts-node)
> - ("function_declaration"
> - (treesit-node-child-by-field-name ts-node "name"))
> - ("type_spec"
> - (treesit-node-child-by-field-name ts-node "name"))))))
> - (marker (when ts-node
> - (set-marker (make-marker)
> - (treesit-node-start ts-node)))))
> - (cond
> - ((or (null ts-node) (null name)) subtrees)
> - (subtrees
> - `((,name ,(cons name marker) ,@subtrees))) - (t -` ((,name . ,marker))))))
> -
> ;;;###autoload
> (add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode))
>
> @@ -228,9 +190,21 @@ go-ts-mode
> (setq-local comment-end "")
> (setq-local comment-start-skip (rx "//" (* (syntax whitespace))))
>
> + ;; Navigation.
> + (setq-local treesit-defun-type-regexp
> + (regexp-opt '("method_declaration"
> + "function_declaration"
> + "type_declaration")))
> + (setq-local treesit-defun-name-function #'go-ts-mode--defun-name)
> +
> ;; Imenu.
> - (setq-local imenu-create-index-function #'go-ts-mode--imenu)
> - (setq-local which-func-functions nil)
> + (setq-local treesit-simple-imenu-settings
> + `(("Function" "\\\\`function_declaration\\'" nil nil)
> + ("Method" "\\`method_declaration\\\\'" nil nil) + ("Struct"
> "\\\\`type_declaration\\'"
> go-ts-mode--struct-node-p nil)
> + ("Interface" "\\`type_declaration\\\\'" go-ts-mode--interface-node-p nil) +
> ("Type" "\\\\`type_declaration\\'"
> go-ts-mode--other-type-node-p nil)
> + ("Alias" "\\`type_declaration\\'"
> go-ts-mode--alias-node-p nil)))
>
> ;; Indent.
> (setq-local indent-tabs-mode t
> @@ -247,6 +221,53 @@ go-ts-mode
>
> (treesit-major-mode-setup)))
>
> +(defun go-ts-mode--defun-name (node)
> + "Return the defun name of NODE.
> +Return nil if there is no name or if NODE is not a defun node."
> + (pcase (treesit-node-type node)
> + ("function_declaration"
> + (treesit-node-text
> + (treesit-node-child-by-field-name
> + node "name")
> + t))
> + ("method_declaration"
> + (let* ((receiver-node (treesit-node-child-by-field-name node "receiver"))
> + (type-node (treesit-search-subtree receiver-node
> "type_identifier"))
> + (name-node (treesit-node-child-by-field-name node "name")))
> + (concat
> + "(" (treesit-node-text type-node) ")."
> + (treesit-node-text name-node))))
> + ("type_declaration"
> + (treesit-node-text
> + (treesit-node-child-by-field-name
> + (treesit-node-child node 0 t) "name")
> + t))))
> +
> +(defun go-ts-mode--interface-node-p (node)
> + "Return t if NODE is an interface."
> + (and
> + (string-equal "type_declaration" (treesit-node-type node))
> + (treesit-search-subtree node "interface_type" nil nil 2)))
I think you need to add (declare-function treesit-search-subtree "treesit.c")
after the last one at the top of the file.
> +
> +(defun go-ts-mode--struct-node-p (node)
> + "Return t if NODE is a struct."
> + (and
> + (string-equal "type_declaration" (treesit-node-type node))
> + (treesit-search-subtree node "struct_type" nil nil 2)))
> +
> +(defun go-ts-mode--alias-node-p (node)
> + "Return t if NODE is a type alias."
> + (and
> + (string-equal "type_declaration" (treesit-node-type node))
> + (treesit-search-subtree node "type_alias" nil nil 1)))
> +
> +(defun go-ts-mode--other-type-node-p (node)
> + "Return t if NODE is a type, other than interface or struct."
> + (and
> + (string-equal "type_declaration" (treesit-node-type node))
> + (not (go-ts-mode--interface-node-p node))
> + (not (go-ts-mode--struct-node-p node))))
Here I guess we just need a (not alias) (and the docstring updated) to fix the
issue mentioned above.
> +
> ;; go.mod support.
>
> (defvar go-mod-ts-mode--syntax-table
>
bug#60407: [PATCH] Update go-ts-mode to use Imenu facility, Randy Taylor, 2023/01/01