unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Randy Taylor <dev@rjt.dev>
To: Eli Zaretskii <eliz@gnu.org>
Cc: 60025@debbugs.gnu.org, theo@thornhill.no, casouri@gmail.com
Subject: bug#60025: [PATCH] Add go-ts-mode and go-mod-ts-mode
Date: Wed, 14 Dec 2022 16:21:40 +0000	[thread overview]
Message-ID: <5HEX4A0JH8fd_VGG1FbzJ8QtBZXlHLKtuAGgc_TUHvzKCzU2Q3PYbbi3rUWeLOBoJzg0qsQgVhwGZCF86A2Xg--I2ep5d1KjKoUoHrxXpqA=@rjt.dev> (raw)
In-Reply-To: <83edt2nqgm.fsf@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 970 bytes --]

On Wednesday, December 14th, 2022 at 07:02, Eli Zaretskii <eliz@gnu.org> wrote:
> 
> > Date: Tue, 13 Dec 2022 19:39:18 +0000
> 
> > From: Randy Taylor dev@rjt.dev
> > Cc: 60025@debbugs.gnu.org, theo@thornhill.no, casouri@gmail.com
> > 
> > +** New major mode 'go-mod-ts-mode'.
> > +A major mode based on the tree-sitter library for editing go.mod
> > +files. It includes support for font-locking and indentation. This
> > +mode will be auto-enabled for go.mod files.
> 
> 
> Please quote go.mod as 'go.mod' (we quote file names and symbols in
> NEWS).
> 
> Otherwise, this LGTM, thanks.
> 

Thanks, here's the new patch. I double-quoted it since that's what other file names and extensions have in NEWS - hopefully that's right. Assuming that's OK, should be good to install.

Yuan, did you see my original post in this thread? I'm wondering how to go about tackling that indentation problem (I'm probably missing something very simple and obvious).

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-go-ts-mode-and-go-mod-ts-mode-Bug-60025.patch --]
[-- Type: text/x-patch; name=0001-Add-go-ts-mode-and-go-mod-ts-mode-Bug-60025.patch, Size: 14956 bytes --]

From 364343de36e332a2bed4527c844782b8f1e508be Mon Sep 17 00:00:00 2001
From: Randy Taylor <dev@rjt.dev>
Date: Sun, 11 Dec 2022 18:41:16 -0500
Subject: [PATCH] Add go-ts-mode and go-mod-ts-mode (Bug#60025)

* admin/notes/tree-sitter/build-module/batch.sh:
* admin/notes/tree-sitter/build-module/build.sh: Add go-mod support.
* etc/NEWS: Mention them.
* lisp/progmodes/eglot.el (eglot-server-programs): Add them.
* lisp/progmodes/go-ts-mode.el: New major modes with
tree-sitter support.
---
 admin/notes/tree-sitter/build-module/batch.sh |   1 +
 admin/notes/tree-sitter/build-module/build.sh |   5 +
 etc/NEWS                                      |  11 +
 lisp/progmodes/eglot.el                       |   3 +-
 lisp/progmodes/go-ts-mode.el                  | 331 ++++++++++++++++++
 5 files changed, 350 insertions(+), 1 deletion(-)
 create mode 100644 lisp/progmodes/go-ts-mode.el

diff --git a/admin/notes/tree-sitter/build-module/batch.sh b/admin/notes/tree-sitter/build-module/batch.sh
index c395449977..c322d405f2 100755
--- a/admin/notes/tree-sitter/build-module/batch.sh
+++ b/admin/notes/tree-sitter/build-module/batch.sh
@@ -8,6 +8,7 @@ languages=
     'c-sharp'
     'dockerfile'
     'go'
+    'go-mod'
     'html'
     'javascript'
     'json'
diff --git a/admin/notes/tree-sitter/build-module/build.sh b/admin/notes/tree-sitter/build-module/build.sh
index 4195ea58c3..6d22127bd1 100755
--- a/admin/notes/tree-sitter/build-module/build.sh
+++ b/admin/notes/tree-sitter/build-module/build.sh
@@ -26,6 +26,11 @@ grammardir=
     "cmake")
         org="uyha"
         ;;
+    "go-mod")
+        # The parser is called "gomod".
+        lang="gomod"
+        namespace="camdencheek"
+        ;;
     "typescript")
         sourcedir="tree-sitter-typescript/typescript/src"
         grammardir="tree-sitter-typescript/typescript"
diff --git a/etc/NEWS b/etc/NEWS
index 6cb531cd0f..02c8d19778 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -3078,6 +3078,17 @@ A major mode based on the tree-sitter library for editing CMake files.
 It includes support for font-locking, indentation, Imenu, and
 which-func.
 
+** New major mode 'go-ts-mode'.
+A major mode based on the tree-sitter library for editing programs in
+the Go language.  It includes support for font-locking, indentation,
+Imenu, and which-func.  This mode will be auto-enabled for files with
+the ".go" extension.
+
+** New major mode 'go-mod-ts-mode'.
+A major mode based on the tree-sitter library for editing "go.mod"
+files.  It includes support for font-locking and indentation.  This
+mode will be auto-enabled for "go.mod" files.
+
 \f
 * Incompatible Lisp Changes in Emacs 29.1
 
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 9c5a361df7..02bb6bb665 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -211,7 +211,8 @@ eglot-server-programs
                                 (elm-mode . ("elm-language-server"))
                                 (mint-mode . ("mint" "ls"))
                                 (kotlin-mode . ("kotlin-language-server"))
-                                ((go-mode go-dot-mod-mode go-dot-work-mode) . ("gopls"))
+                                ((go-mode go-dot-mod-mode go-dot-work-mode go-ts-mode go-mod-ts-mode)
+                                 . ("gopls"))
                                 ((R-mode ess-r-mode) . ("R" "--slave" "-e"
                                                         "languageserver::run()"))
                                 ((java-mode java-ts-mode) . ("jdtls"))
diff --git a/lisp/progmodes/go-ts-mode.el b/lisp/progmodes/go-ts-mode.el
new file mode 100644
index 0000000000..b86ab7ab85
--- /dev/null
+++ b/lisp/progmodes/go-ts-mode.el
@@ -0,0 +1,331 @@
+;;; go-ts-mode.el --- tree-sitter support for Go  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author     : Randy Taylor <dev@rjt.dev>
+;; Maintainer : Randy Taylor <dev@rjt.dev>
+;; Created    : December 2022
+;; Keywords   : go languages tree-sitter
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+
+;;; Code:
+
+(require 'treesit)
+(eval-when-compile (require 'rx))
+
+(declare-function treesit-parser-create "treesit.c")
+(declare-function treesit-induce-sparse-tree "treesit.c")
+(declare-function treesit-node-child "treesit.c")
+(declare-function treesit-node-child-by-field-name "treesit.c")
+(declare-function treesit-node-start "treesit.c")
+(declare-function treesit-node-type "treesit.c")
+
+(defcustom go-ts-mode-indent-offset 4
+  "Number of spaces for each indentation step in `go-ts-mode'."
+  :version "29.1"
+  :type 'integer
+  :safe 'integerp
+  :group 'go)
+
+(defvar go-ts-mode--syntax-table
+  (let ((table (make-syntax-table)))
+    (modify-syntax-entry ?+   "."      table)
+    (modify-syntax-entry ?-   "."      table)
+    (modify-syntax-entry ?=   "."      table)
+    (modify-syntax-entry ?%   "."      table)
+    (modify-syntax-entry ?&   "."      table)
+    (modify-syntax-entry ?|   "."      table)
+    (modify-syntax-entry ?^   "."      table)
+    (modify-syntax-entry ?!   "."      table)
+    (modify-syntax-entry ?<   "."      table)
+    (modify-syntax-entry ?>   "."      table)
+    (modify-syntax-entry ?\\  "\\"     table)
+    (modify-syntax-entry ?/   ". 124b" table)
+    (modify-syntax-entry ?*   ". 23"   table)
+    (modify-syntax-entry ?\n  "> b"    table)
+    table)
+  "Syntax table for `go-ts-mode'.")
+
+(defvar go-ts-mode--indent-rules
+  `((go
+     ((node-is ")") parent-bol 0)
+     ((node-is "]") parent-bol 0)
+     ((node-is "}") parent-bol 0)
+     ((node-is "labeled_statement") no-indent)
+     ((parent-is "argument_list") parent-bol go-ts-mode-indent-offset)
+     ((parent-is "block") parent-bol go-ts-mode-indent-offset)
+     ((parent-is "const_declaration") parent-bol go-ts-mode-indent-offset)
+     ((parent-is "default_case") parent-bol go-ts-mode-indent-offset)
+     ((parent-is "expression_case") parent-bol go-ts-mode-indent-offset)
+     ((parent-is "expression_switch_statement") parent-bol 0)
+     ((parent-is "field_declaration_list") parent-bol go-ts-mode-indent-offset)
+     ((parent-is "import_spec_list") parent-bol go-ts-mode-indent-offset)
+     ((parent-is "labeled_statement") parent-bol go-ts-mode-indent-offset)
+     ((parent-is "literal_value") parent-bol go-ts-mode-indent-offset)
+     ((parent-is "type_spec") parent-bol go-ts-mode-indent-offset)
+     ((parent-is "var_declaration") parent-bol go-ts-mode-indent-offset)
+     (no-node parent-bol 0)))
+  "Tree-sitter indent rules for `go-ts-mode'.")
+
+(defvar go-ts-mode--keywords
+  '("break" "case" "chan" "const" "continue" "default" "defer" "else"
+    "fallthrough" "for" "func" "go" "goto" "if" "import" "interface" "map"
+    "package" "range" "return" "select" "struct" "switch" "type" "var")
+  "Go keywords for tree-sitter font-locking.")
+
+(defvar go-ts-mode--operators
+  '("+" "&" "+=" "&=" "&&" "==" "!=" "-" "|" "-=" "|=" "||" "<" "<="
+    "*" "^" "*=" "^=" "<-" ">" ">=" "/" "<<" "/=" "<<=" "++" "=" ":=" "%"
+    ">>" "%=" ">>=" "--" "!"  "..."  "&^" "&^=" "~")
+  "Go operators for tree-sitter font-locking.")
+
+(defvar go-ts-mode--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'go
+   :feature 'bracket
+   '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face)
+
+   :language 'go
+   :feature 'comment
+   '((comment) @font-lock-comment-face)
+
+   :language 'go
+   :feature 'constant
+   '([(false) (iota) (nil) (true)] @font-lock-constant-face
+     (const_declaration
+      (const_spec name: (identifier) @font-lock-constant-face)))
+
+   :language 'go
+   :feature 'delimiter
+   '((["," "." ";" ":"]) @font-lock-delimiter-face)
+
+   :language 'go
+   :feature 'function
+   '((call_expression
+      function: (identifier) @font-lock-function-name-face)
+     (call_expression
+      function: (selector_expression
+                 field: (field_identifier) @font-lock-function-name-face))
+     (function_declaration
+      name: (identifier) @font-lock-function-name-face)
+     (method_declaration
+      name: (field_identifier) @font-lock-function-name-face))
+
+   :language 'go
+   :feature 'keyword
+   `([,@go-ts-mode--keywords] @font-lock-keyword-face)
+
+   :language 'go
+   :feature 'label
+   '((label_name) @font-lock-constant-face)
+
+   :language 'go
+   :feature 'number
+   '([(float_literal)
+      (imaginary_literal)
+      (int_literal)] @font-lock-number-face)
+
+   :language 'go
+   :feature 'string
+   '([(interpreted_string_literal)
+      (raw_string_literal)
+      (rune_literal)] @font-lock-string-face)
+
+   :language 'go
+   :feature 'type
+   '([(package_identifier) (type_identifier)] @font-lock-type-face)
+
+   :language 'go
+   :feature 'variable
+   '((identifier) @font-lock-variable-name-face)
+
+   :language 'go
+   :feature 'escape-sequence
+   :override t
+   '((escape_sequence) @font-lock-escape-face)
+
+   :language 'go
+   :feature 'property
+   :override t
+   '((field_identifier) @font-lock-property-face
+     (keyed_element (_ (identifier) @font-lock-property-face)))
+
+   :language 'go
+   :feature 'error
+   :override t
+   '((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))
+
+;;;###autoload
+(define-derived-mode go-ts-mode prog-mode "Go"
+  "Major mode for editing Go, powered by tree-sitter."
+  :group 'go
+  :syntax-table go-ts-mode--syntax-table
+
+  (when (treesit-ready-p 'go)
+    (treesit-parser-create 'go)
+
+    ;; Comments.
+    (setq-local comment-start "// ")
+    (setq-local comment-end "")
+    (setq-local comment-start-skip (rx "//" (* (syntax whitespace))))
+
+    ;; Imenu.
+    (setq-local imenu-create-index-function #'go-ts-mode--imenu)
+    (setq-local which-func-functions nil)
+
+    ;; Indent.
+    (setq-local indent-tabs-mode t
+                treesit-simple-indent-rules go-ts-mode--indent-rules)
+
+    ;; Font-lock.
+    (setq-local treesit-font-lock-settings go-ts-mode--font-lock-settings)
+    (setq-local treesit-font-lock-feature-list
+                '(( comment)
+                  ( keyword string type)
+                  ( constant escape-sequence function label number
+                    property variable)
+                  ( bracket delimiter error operator)))
+
+    (treesit-major-mode-setup)))
+
+;; go.mod support.
+
+(defvar go-mod-ts-mode--syntax-table
+  (let ((table (make-syntax-table)))
+    (modify-syntax-entry ?/   ". 124b" table)
+    (modify-syntax-entry ?\n  "> b"    table)
+    table)
+  "Syntax table for `go-mod-ts-mode'.")
+
+(defvar go-mod-ts-mode--indent-rules
+  `((gomod
+     ((node-is ")") parent-bol 0)
+     ((parent-is "require_directive") parent-bol go-ts-mode-indent-offset)
+     (no-node parent-bol 0)))
+  "Tree-sitter indent rules for `go-mod-ts-mode'.")
+
+(defvar go-mod-ts-mode--keywords
+  '("exclude" "go" "module" "replace" "require" "retract")
+  "go.mod keywords for tree-sitter font-locking.")
+
+(defvar go-mod-ts-mode--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'gomod
+   :feature 'bracket
+   '((["(" ")"]) @font-lock-bracket-face)
+
+   :language 'gomod
+   :feature 'comment
+   '((comment) @font-lock-comment-face)
+
+   :language 'gomod
+   :feature 'keyword
+   `([,@go-mod-ts-mode--keywords] @font-lock-keyword-face)
+
+   :language 'gomod
+   :feature 'number
+   '([(go_version) (version)] @font-lock-number-face)
+
+   :language 'gomod
+   :feature 'operator
+   '((["=>"]) @font-lock-operator-face)
+
+   :language 'gomod
+   :feature 'error
+   :override t
+   '((ERROR) @font-lock-warning-face))
+  "Tree-sitter font-lock settings for `go-mod-ts-mode'.")
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("/go\\.mod\\'" . go-mod-ts-mode))
+
+;;;###autoload
+(define-derived-mode go-mod-ts-mode prog-mode "Go Mod"
+  "Major mode for editing go.mod files, powered by tree-sitter."
+  :group 'go
+  :syntax-table go-mod-ts-mode--syntax-table
+
+  (when (treesit-ready-p 'gomod)
+    (treesit-parser-create 'gomod)
+
+    ;; Comments.
+    (setq-local comment-start "// ")
+    (setq-local comment-end "")
+    (setq-local comment-start-skip (rx "//" (* (syntax whitespace))))
+
+    ;; Indent.
+    (setq-local indent-tabs-mode t
+                treesit-simple-indent-rules go-mod-ts-mode--indent-rules)
+
+    ;; Font-lock.
+    (setq-local treesit-font-lock-settings go-mod-ts-mode--font-lock-settings)
+    (setq-local treesit-font-lock-feature-list
+                '((comment)
+                  (keyword)
+                  (number)
+                  (bracket error operator)))
+
+    (treesit-major-mode-setup)))
+
+(provide 'go-ts-mode)
+
+;;; go-ts-mode.el ends here
-- 
2.39.0


  reply	other threads:[~2022-12-14 16:21 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-13  2:13 bug#60025: [PATCH] Add go-ts-mode and go-mod-ts-mode Randy Taylor
2022-12-13 12:25 ` Eli Zaretskii
2022-12-13 19:39   ` Randy Taylor
2022-12-14 12:02     ` Eli Zaretskii
2022-12-14 16:21       ` Randy Taylor [this message]
2022-12-14 19:55         ` Yuan Fu
2022-12-14 20:54           ` Randy Taylor
2022-12-14 21:27             ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-14 21:56               ` Randy Taylor
2022-12-15  2:15               ` Randy Taylor
2022-12-15  7:20                 ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-15 16:40                   ` Randy Taylor
2022-12-15 18:06                     ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-15 19:45                       ` Randy Taylor
2022-12-15 19:59                         ` Theodor Thornhill via Bug reports for GNU Emacs, the Swiss army knife of text editors
2022-12-15 20:09                           ` Randy Taylor
2022-12-15 22:22                             ` Randy Taylor
2022-12-14  2:22 ` Yuan Fu
2022-12-16  1:22 ` Yuan Fu
2022-12-16  2:05   ` Randy Taylor

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='5HEX4A0JH8fd_VGG1FbzJ8QtBZXlHLKtuAGgc_TUHvzKCzU2Q3PYbbi3rUWeLOBoJzg0qsQgVhwGZCF86A2Xg--I2ep5d1KjKoUoHrxXpqA=@rjt.dev' \
    --to=dev@rjt.dev \
    --cc=60025@debbugs.gnu.org \
    --cc=casouri@gmail.com \
    --cc=eliz@gnu.org \
    --cc=theo@thornhill.no \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).