From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Evgeni Kolev Newsgroups: gmane.emacs.bugs Subject: bug#60407: [PATCH] Update go-ts-mode to use Imenu facility Date: Thu, 5 Jan 2023 09:24:55 +0200 Message-ID: References: <83y1qm39o3.fsf@gnu.org> Mime-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="11056"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Eli Zaretskii , Yuan Fu , 60407@debbugs.gnu.org To: Randy Taylor Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Thu Jan 05 08:26:19 2023 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pDKdm-0002gM-Qk for geb-bug-gnu-emacs@m.gmane-mx.org; Thu, 05 Jan 2023 08:26:19 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pDKda-000749-Jx; Thu, 05 Jan 2023 02:26:06 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pDKdW-00071x-Tz for bug-gnu-emacs@gnu.org; Thu, 05 Jan 2023 02:26:05 -0500 Original-Received: from debbugs.gnu.org ([209.51.188.43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1pDKdW-0007uK-LV for bug-gnu-emacs@gnu.org; Thu, 05 Jan 2023 02:26:02 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1pDKdW-00074t-6x for bug-gnu-emacs@gnu.org; Thu, 05 Jan 2023 02:26:02 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Evgeni Kolev Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 05 Jan 2023 07:26:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 60407 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 60407-submit@debbugs.gnu.org id=B60407.167290353127163 (code B ref 60407); Thu, 05 Jan 2023 07:26:02 +0000 Original-Received: (at 60407) by debbugs.gnu.org; 5 Jan 2023 07:25:31 +0000 Original-Received: from localhost ([127.0.0.1]:50102 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1pDKd0-000742-FL for submit@debbugs.gnu.org; Thu, 05 Jan 2023 02:25:31 -0500 Original-Received: from mail-yw1-f175.google.com ([209.85.128.175]:45016) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1pDKcx-00073m-IS for 60407@debbugs.gnu.org; Thu, 05 Jan 2023 02:25:28 -0500 Original-Received: by mail-yw1-f175.google.com with SMTP id 00721157ae682-4b6255ce5baso61875167b3.11 for <60407@debbugs.gnu.org>; Wed, 04 Jan 2023 23:25:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=uknwCbvvNZAthCUyZzzH7jtN/kIBW6jwtyVYIrAvs4Y=; b=WXzbpEzFzNSw3LhCb2Pmq6VI+bJ8OVckAoWB2vthwNl4jeH6dY/lFmpw6L8SiSJkxa ZXeW3M0pOCyVM7WJjLK0Ny0fLvCwDSnPzHp4B84GaPxYtZti+gwAC7daiImUmxEwYEiW Pj7haF+Ed+cZgCDBH7VvH0NFf5bqhCNDqMgbmhvy1VtLDtrlFuFF/2INNkVoYe1d7fwb JacP/njqbLmGRY1cqJoG6FN3ts/erMmJXHS3GQkahfssFPSXYMQYD5wpTmM/HC4XL3dU cNsFoljlzzNo+nLbWSAwmjfbHKnSNDCfvIwssXJ3fZTzAXj7CkFQCe6SmyQNWwxwYyTw EEVg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uknwCbvvNZAthCUyZzzH7jtN/kIBW6jwtyVYIrAvs4Y=; b=pUNmsBndy9qMrrXsBHCc0/hnJ57fuIMIvEk5H2MB+hE6wojvx+7oBK1NCV+1HFlVYW RUjqtYU2/NpqsMWEcRDib6YBWd9QqI66JgI6E+VWBs3ruvOvQptKqEsJQC40gXsmqAl8 r+1qjKi37viqURqYgwCJWe4CwLLHJMlQlt/1sBVOiF3hHwF6y4s/Xc66w6AGftsHw1uN d8LqAcJsPgu4k/NfjJp9Q8X7AMKn9moW7p32Swa3H8aseWgRhh33wL+c7LFgkRAkftoJ tFUS7gmtlceotLnDZZb8GwRlVoyEVX7b2gVIP0lcHgDETmlWV3D1eq/pFgmvZG6Z7UTJ XBcw== X-Gm-Message-State: AFqh2kpj6VjGeUR1ph+DehbnWMAVIeCTBlVegOIS1qCWUEPOxQXhkIlJ WiopB86bfJZ6iXelJCEzjagY4S8wPBxYGgdhkxw= X-Google-Smtp-Source: AMrXdXtvJhSpSsZ7/cs7wsskM/Q1K9xbKLednu0hmkRN1iw2VqJVryzr5OiI6zBHBzvRIrdYlILSok2ouqUEEsYFmxQ= X-Received: by 2002:a0d:df89:0:b0:4b6:dce2:93 with SMTP id i131-20020a0ddf89000000b004b6dce20093mr655392ywe.164.1672903521509; Wed, 04 Jan 2023 23:25:21 -0800 (PST) In-Reply-To: X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:252559 Archived-At: Hi Randy, I'm providing the updated patch - I've addressed your comments and also added Electric Pair mode settings (variable electric-indent-chars). Thanks for your feedback. Again, please let me know if the patch can be improved. The patch is below. commit c38df01b088c3fdc0b86340a230b7397b5c81317 Author: Evgeni Kolev Date: Thu Dec 29 17:49:40 2022 +0200 Improve go-ts-mode Imenu, navigation and electric pair configuration 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. Variable electric-indent-chars is set in order to improve integration with Electric Pair mode. * 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) (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, navigation, add Electric Pair mode settings. diff --git a/lisp/progmodes/go-ts-mode.el b/lisp/progmodes/go-ts-mode.el index 1d6a8a30db5..64e761d2f72 100644 --- a/lisp/progmodes/go-ts-mode.el +++ b/lisp/progmodes/go-ts-mode.el @@ -36,6 +36,7 @@ (declare-function treesit-node-child-by-field-name "treesit.c") (declare-function treesit-node-start "treesit.c") (declare-function treesit-node-type "treesit.c") +(declare-function treesit-search-subtree "treesit.c") (defcustom go-ts-mode-indent-offset 4 "Number of spaces for each indentation step in `go-ts-mode'." @@ -173,44 +174,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,14 +191,30 @@ 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 treesit-simple-indent-rules go-ts-mode--indent-rules) + ;; Electric + (setq-local electric-indent-chars + (append "{}()" electric-indent-chars)) + ;; Font-lock. (setq-local treesit-font-lock-settings go-ts-mode--font-lock-settings) (setq-local treesit-font-lock-feature-list @@ -247,6 +226,54 @@ 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 "receive= r")) + (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))) + +(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, struct or alias." + (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)) + (not (go-ts-mode--alias-node-p node)))) + ;; go.mod support. (defvar go-mod-ts-mode--syntax-table On Tue, Jan 3, 2023 at 4:31 PM Randy Taylor wrote: > > On Tuesday, January 3rd, 2023 at 04:01, Evgeni Kolev = 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 =3D 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 =3D int shows up in both categories: A= lias 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 sim= ilar)? > > > > > 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-n= ame, go-ts-mode--interface-node-p) > Whenever it fits on a single line, you can combine them like that. Same f= or 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.e= l > > 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 (tr= eesit-buffer-root-node)) - (func-tree (treesit-induce-sparse-tree - node "f= unction_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-inde= x` (("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" "\\\\`typ= e_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 "receive= r")) > > + (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 fi= x the issue mentioned above. > > > + > > ;; go.mod support. > > > > (defvar go-mod-ts-mode--syntax-table > >