]> git.eshelyaron.com Git - emacs.git/commitdiff
Add go-work-ts-mode for Go workspace files
authorGabriel Santos <gabrielsantosdesouza@disroot.org>
Thu, 21 Nov 2024 02:07:28 +0000 (23:07 -0300)
committerEshel Yaron <me@eshelyaron.com>
Wed, 12 Feb 2025 10:52:57 +0000 (11:52 +0100)
* lisp/progmodes/eglot.el (eglot-server-programs): Add go-work-ts-mode.
* lisp/progmodes/go-ts-mode.el
(Commentary): Add the repositories for the grammars.
(go-work-ts-mode--indent-rules, go-work-ts-mode--keywords)
(go-work-ts-mode--font-lock-settings): New variables.
(go-work-ts-mode--directive-matcher, go-work-ts-mode): New functions.
(go-mod-ts-mode--directive-matcher): Rename from
go-mod-ts-mode--in-directive-p.  Be more specific on the directive
location (modules).  Replace mention of nil with function.  Use member
instead of pcase to check node types.

* admin/notes/tree-sitter/build-module/batch.sh
* admin/notes/tree-sitter/build-module/build.sh: Add go-work support.

* test/lisp/progmodes/go-ts-mode-resources/font-lock-package.go:
* test/lisp/progmodes/go-ts-mode-resources/indent-mod.erts:
* test/lisp/progmodes/go-ts-mode-resources/indent-work.erts:
New files for testing indentation and font-locking for Go
module and workspace files.
* test/lisp/progmodes/go-ts-mode-tests.el: Add tests for Go module and
workspace files.  (Bug#74461)

* etc/NEWS: Announce go-work-ts-mode.

(cherry picked from commit aade1b707c6b4932ed023f387d49324c6a7123eb)

admin/notes/tree-sitter/build-module/batch.sh
admin/notes/tree-sitter/build-module/build.sh
lisp/progmodes/eglot.el
lisp/progmodes/go-ts-mode.el
test/lisp/progmodes/go-ts-mode-resources/font-lock-package.go [new file with mode: 0644]
test/lisp/progmodes/go-ts-mode-resources/indent-mod.erts [new file with mode: 0644]
test/lisp/progmodes/go-ts-mode-resources/indent-work.erts [new file with mode: 0644]
test/lisp/progmodes/go-ts-mode-tests.el

index 012b5882e83e971d3211e615dd646d921c5fbe32..1b5214267f5295beaa44538baafe8320c7447d1b 100755 (executable)
@@ -11,6 +11,7 @@ languages=(
     'elixir'
     'go'
     'go-mod'
+    'go-work'
     'heex'
     'html'
     'java'
index 9a567bb094ddd506d940f1632caa531258116530..4f3c6da3c5f53362f362cae49890df92d6a84144 100755 (executable)
@@ -39,6 +39,11 @@ case "${lang}" in
         lang="gomod"
         org="camdencheek"
         ;;
+    "go-work")
+        # The parser is called "gowork".
+        lang="gowork"
+        org="omertuc"
+        ;;
     "heex")
         org="phoenixframework"
         ;;
index b5f3afa5d544d6e025fa84d3978df5ed7e1a967d..747fe0976428d6a6c7a9bc3944d3ff743291e5ac 100644 (file)
@@ -277,7 +277,7 @@ automatically)."
     (elm-mode . ("elm-language-server"))
     (mint-mode . ("mint" "ls"))
     ((kotlin-mode kotlin-ts-mode) . ("kotlin-language-server"))
-    ((go-mode go-dot-mod-mode go-dot-work-mode go-ts-mode go-mod-ts-mode)
+    ((go-mode go-dot-mod-mode go-dot-work-mode go-ts-mode go-mod-ts-mode go-work-ts-mode)
      . ("gopls"))
     ((R-mode ess-r-mode) . ("R" "--slave" "-e"
                             "languageserver::run()"))
index 8cb2d8f336265ad9a4802680cdc23f513a1e57ff..469df9904c431eee4ca77ae0926cb93ebf11a6bb 100644 (file)
@@ -26,6 +26,8 @@
 ;;
 ;; go-ts-mode is known to work with the following languages and version:
 ;; - tree-sitter-go: v0.23.4-1-g12fe553
+;; - tree-sitter-go-mod: v1.1.0-3b01edce
+;; - tree-sitter-go-work: 949a8a47
 ;;
 ;; We try our best to make builtin modes work with latest grammar
 ;; versions, so a more recent grammar version has a good chance to work.
@@ -33,6 +35,9 @@
 
 ;;; Commentary:
 ;;
+;; Go uses tabs as a convention for indentation:
+;; https://go.dev/doc/effective_go#formatting
+;; so `indent-tabs-mode' is enabled for the modes.
 
 ;;; Code:
 
@@ -478,7 +483,7 @@ be run."
                    default-directory
                    (go-ts-mode--get-test-flags))))
 
-;; go.mod support.
+;;;; go.mod support.
 
 (defvar go-mod-ts-mode--syntax-table
   (let ((table (make-syntax-table)))
@@ -495,12 +500,12 @@ be run."
      ((parent-is "replace_directive") parent-bol go-ts-mode-indent-offset)
      ((parent-is "require_directive") parent-bol go-ts-mode-indent-offset)
      ((parent-is "retract_directive") parent-bol go-ts-mode-indent-offset)
-     ((go-mod-ts-mode--in-directive-p) no-indent go-ts-mode-indent-offset)
+     ((go-mod-ts-mode--directive-matcher) no-indent go-ts-mode-indent-offset)
      (no-node no-indent 0)))
   "Tree-sitter indent rules for `go-mod-ts-mode'.")
 
-(defun go-mod-ts-mode--in-directive-p ()
-  "Return non-nil if point is inside a directive.
+(defun go-mod-ts-mode--directive-matcher ()
+  "Return a function for determining if point is inside a Go module directive.
 When entering an empty directive or adding a new entry to one, no node
 will be present meaning none of the indentation rules will match,
 because there is no parent to match against.  This function determines
@@ -510,12 +515,12 @@ what the parent of the node would be if it were a node."
       (save-excursion
         (backward-up-list)
         (back-to-indentation)
-        (pcase (treesit-node-type (treesit-node-at (point)))
-          ("exclude" t)
-          ("module" t)
-          ("replace" t)
-          ("require" t)
-          ("retract" t))))))
+        (member (treesit-node-type (treesit-node-at (point)))
+                '("exclude"
+                  "module"
+                  "replace"
+                  "require"
+                  "retract"))))))
 
 (defvar go-mod-ts-mode--keywords
   '("exclude" "go" "module" "replace" "require" "retract")
@@ -582,6 +587,94 @@ what the parent of the node would be if it were a node."
 (if (treesit-ready-p 'gomod)
     (add-to-list 'auto-mode-alist '("/go\\.mod\\'" . go-mod-ts-mode)))
 
+;;;; go.work support.
+
+(defvar go-work-ts-mode--indent-rules
+  `((gowork
+     ((node-is ")") parent-bol 0)
+     ((parent-is "replace_directive") parent-bol go-ts-mode-indent-offset)
+     ((parent-is "use_directive") parent-bol go-ts-mode-indent-offset)
+     ((go-work-ts-mode--directive-matcher) no-indent go-ts-mode-indent-offset)
+     (no-node no-indent 0)))
+  "Tree-sitter indent rules for `go-work-ts-mode'.")
+
+(defun go-work-ts-mode--directive-matcher ()
+  "Return a function for determining if point is inside a Go workspace directive.
+When entering an empty directive or adding a new entry to one, no node
+will be present meaning none of the indentation rules will match,
+because there is no parent to match against.  This function determines
+what the parent of the node would be if it were a node."
+  (lambda (node _ _ &rest _)
+    (unless (treesit-node-type node)
+      (save-excursion
+        (backward-up-list)
+        (back-to-indentation)
+        (member (treesit-node-type (treesit-node-at (point)))
+                '("replace"
+                  "use"))))))
+
+(defvar go-work-ts-mode--keywords
+  '("go" "replace" "use")
+  "go.work keywords for tree-sitter font-locking.")
+
+(defvar go-work-ts-mode--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'gowork
+   :feature 'bracket
+   '((["(" ")"]) @font-lock-bracket-face)
+
+   :language 'gowork
+   :feature 'comment
+   '((comment) @font-lock-comment-face)
+
+   :language 'gowork
+   :feature 'keyword
+   `([,@go-work-ts-mode--keywords] @font-lock-keyword-face)
+
+   :language 'gowork
+   :feature 'number
+   '([(go_version) (version)] @font-lock-number-face)
+
+   :language 'gowork
+   :feature 'operator
+   '((["=>"]) @font-lock-operator-face)
+
+   :language 'gowork
+   :feature 'error
+   :override t
+   '((ERROR) @font-lock-warning-face))
+  "Tree-sitter font-lock settings for `go-work-ts-mode'.")
+
+;;;###autoload
+(define-derived-mode go-work-ts-mode prog-mode "Go Work"
+  "Major mode for editing go.work files, powered by tree-sitter."
+  :group 'go
+
+  (when (treesit-ready-p 'gowork)
+    (setq treesit-primary-parser (treesit-parser-create 'gowork))
+
+    ;; 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-work-ts-mode--indent-rules)
+
+    ;; Font-lock.
+    (setq-local treesit-font-lock-settings go-work-ts-mode--font-lock-settings)
+    (setq-local treesit-font-lock-feature-list
+                '((comment)
+                  (keyword)
+                  (number)
+                  (bracket error operator)))
+
+    (treesit-major-mode-setup)))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("/go\\.work\\'" . go-work-ts-mode))
+
 (provide 'go-ts-mode)
 
 ;;; go-ts-mode.el ends here
diff --git a/test/lisp/progmodes/go-ts-mode-resources/font-lock-package.go b/test/lisp/progmodes/go-ts-mode-resources/font-lock-package.go
new file mode 100644 (file)
index 0000000..7bee684
--- /dev/null
@@ -0,0 +1,4 @@
+replace gnu.org/go/package1 v1.0.0 => gnu.org/go/package2 v1.0.0
+// ^ font-lock-keyword-face
+//                             ^ font-lock-number-face
+//                                 ^ font-lock-operator-face
diff --git a/test/lisp/progmodes/go-ts-mode-resources/indent-mod.erts b/test/lisp/progmodes/go-ts-mode-resources/indent-mod.erts
new file mode 100644 (file)
index 0000000..2f7bfd9
--- /dev/null
@@ -0,0 +1,16 @@
+Code:
+  (lambda ()
+    (go-mod-ts-mode)
+    (indent-region (point-min) (point-max)))
+
+Point-Char: |
+
+Name: Basic
+
+=-=
+require (
+       gnu.org/go/package1 v1.0.0
+       gnu.org/go/package2 v1.0.0
+)
+
+=-=-=
diff --git a/test/lisp/progmodes/go-ts-mode-resources/indent-work.erts b/test/lisp/progmodes/go-ts-mode-resources/indent-work.erts
new file mode 100644 (file)
index 0000000..b210974
--- /dev/null
@@ -0,0 +1,16 @@
+Code:
+  (lambda ()
+    (go-work-ts-mode)
+    (indent-region (point-min) (point-max)))
+
+Point-Char: |
+
+Name: Basic
+
+=-=
+use (
+       ./package1
+       ./package2
+)
+
+=-=-=
index 2837d5d23d25461c5df321d5050394242f712320..7a4d74537992ac7e24e2cef6b1cae8be4a9d4de9 100644 (file)
@@ -23,6 +23,8 @@
 (require 'ert-x)
 (require 'treesit)
 
+;; go-ts-mode
+
 (ert-deftest go-ts-mode-test-indentation ()
   (skip-unless (treesit-ready-p 'go))
   (ert-test-erts-file (ert-resource-file "indent.erts")))
   (let ((treesit-font-lock-level 4))
     (ert-font-lock-test-file (ert-resource-file "font-lock.go") 'go-ts-mode)))
 
+;; go-mod-ts-mode
+
+(ert-deftest go-work-ts-mode-test-indentation ()
+  (skip-unless (treesit-ready-p 'gomod))
+  (ert-test-erts-file (ert-resource-file "indent-mod.erts")))
+
+(ert-deftest go-mod-ts-test-font-lock ()
+  (skip-unless (treesit-ready-p 'gomod))
+  (let ((treesit-font-lock-level 4))
+    (ert-font-lock-test-file (ert-resource-file "font-lock-package.go") 'go-mod-ts-mode)))
+
+;; go-work-ts-mode
+
+(ert-deftest go-work-ts-mode-test-indentation ()
+  (skip-unless (treesit-ready-p 'gowork))
+  (ert-test-erts-file (ert-resource-file "indent-work.erts")))
+
+(ert-deftest go-work-ts-test-font-lock ()
+  (skip-unless (treesit-ready-p 'gowork))
+  (let ((treesit-font-lock-level 4))
+    (ert-font-lock-test-file (ert-resource-file "font-lock-package.go") 'go-work-ts-mode)))
+
 (provide 'go-ts-mode-tests)
 ;;; go-ts-mode-tests.el ends here