;;
;; 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.
;;; 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:
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)))
((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
(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")
(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
(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