From d6ce4e34460b0ddc12937772dd66e48b524e3489 Mon Sep 17 00:00:00 2001 From: Ankit R Gadiya Date: Tue, 14 May 2024 00:14:03 +0530 Subject: [PATCH] Add commands to run unit tests in 'go-ts-mode' * lisp/progmodes/go-ts-mode.el (go-ts-mode-build-tags): New variable. (go-ts-mode-map): Add new bindings. (go-ts-mode--get-build-tags-flag, go-ts-mode--compile-test) (go-ts-mode--find-defun-at, go-ts-mode--get-function-regexp) (go-ts-mode--get-functions-in-range) (go-ts-mode--get-test-regexp-at-point) (go-ts-mode-test-function-at-point, go-ts-mode-test-this-file) (go-ts-mode-test-this-package): New functions. * etc/NEWS: Mention the change. (Bug#70939) (cherry picked from commit f249c81f868e8fea9d2a05ce258b3ebefba6620f) --- etc/NEWS | 19 ++++++++ lisp/progmodes/go-ts-mode.el | 88 +++++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/etc/NEWS b/etc/NEWS index e10514dd953..a3b72666dab 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -94,6 +94,25 @@ Advanced" node in the EWW manual. By customizing 'shr-image-zoom-levels', you can change the list of zoom levels that SHR cycles through when calling 'shr-zoom-image'. +** Go-ts mode + ++++ +*** New unit test commands. +Three new commands are now available to run unit tests. + +The 'go-ts-mode-test-function-at-point' command runs the unit test at +point. If a region is active, it runs all the unit tests under the +region. It is bound to 'C-c C-t t' in 'go-ts-mode'. + +The 'go-ts-mode-test-this-file' command runs all unit tests in the current +file. It is bound to 'C-c C-t f' in 'go-ts-mode'. + +The 'go-ts-mode-test-this-package' command runs all unit tests under the +package of the current buffer. It is bound to 'C-c C-t p' in 'go-ts-mode'. + +The 'go-ts-mode-build-tags' variable is available to set a list of build +tags for the test commands. + ** Emacs Lisp mode --- diff --git a/lisp/progmodes/go-ts-mode.el b/lisp/progmodes/go-ts-mode.el index 2d3e6aac090..3fe427fa911 100644 --- a/lisp/progmodes/go-ts-mode.el +++ b/lisp/progmodes/go-ts-mode.el @@ -46,6 +46,12 @@ :safe 'integerp :group 'go) +(defcustom go-ts-mode-build-tags nil + "List of Go build tags for the test commands." + :version "30.1" + :type '(repeat string) + :group 'go) + (defvar go-ts-mode--syntax-table (let ((table (make-syntax-table))) (modify-syntax-entry ?+ "." table) @@ -242,7 +248,10 @@ (defvar-keymap go-ts-mode-map :doc "Keymap used in Go mode, powered by tree-sitter" :parent prog-mode-map - "C-c C-d" #'go-ts-mode-docstring) + "C-c C-d" #'go-ts-mode-docstring + "C-c C-t t" #'go-ts-mode-test-function-at-point + "C-c C-t f" #'go-ts-mode-test-this-file + "C-c C-t p" #'go-ts-mode-test-this-package) ;;;###autoload (define-derived-mode go-ts-mode prog-mode "Go" @@ -375,6 +384,83 @@ comment already exists, jump to it." (<= (treesit-node-start node) point (treesit-node-end node)) (string-equal "comment" (treesit-node-type node))))) +(defun go-ts-mode--get-build-tags-flag () + "Return the compile flag for build tags. +This function respects the `go-ts-mode-build-tags' variable for +specifying build tags." + (if go-ts-mode-build-tags + (format "-tags %s" (string-join go-ts-mode-build-tags ",")) + "")) + +(defun go-ts-mode--compile-test (regexp) + "Compile the tests matching REGEXP. +This function respects the `go-ts-mode-build-tags' variable for +specifying build tags." + (compile (format "go test -v %s -run '%s'" + (go-ts-mode--get-build-tags-flag) + regexp))) + +(defun go-ts-mode--find-defun-at (start) + "Return the first defun node from START." + (let ((thing (or treesit-defun-type-regexp 'defun))) + (or (treesit-thing-at start thing) + (treesit-thing-next start thing)))) + +(defun go-ts-mode--get-function-regexp (name) + (if name + (format "^%s$" name) + (error "No test function found"))) + +(defun go-ts-mode--get-functions-in-range (start end) + "Return a list with the names of all defuns in the range START to END." + (let* ((node (go-ts-mode--find-defun-at start)) + (name (treesit-defun-name node)) + (node-start (treesit-node-start node)) + (node-end (treesit-node-end node))) + (cond ((or (not node) + (> start node-end) + (< end node-start)) + nil) + ((or (not (equal (treesit-node-type node) "function_declaration")) + (not (string-prefix-p "Test" name))) + (go-ts-mode--get-functions-in-range (treesit-node-end node) end)) + (t + (cons (go-ts-mode--get-function-regexp name) + (go-ts-mode--get-functions-in-range (treesit-node-end node) end)))))) + +(defun go-ts-mode--get-test-regexp-at-point () + "Return a regular expression for the tests at point. +If region is active, the regexp will include all the functions under the +region." + (if-let ((range (if (region-active-p) + (list (region-beginning) (region-end)) + (list (point) (point)))) + (funcs (apply #'go-ts-mode--get-functions-in-range range))) + (string-join funcs "|") + (error "No test function found"))) + +(defun go-ts-mode-test-function-at-point () + "Run the unit test at point. +If the point is anywhere in the test function, that function will be +run. If the region is selected, all the functions under the region will +be run." + (interactive) + (go-ts-mode--compile-test (go-ts-mode--get-test-regexp-at-point))) + +(defun go-ts-mode-test-this-file () + "Run all the unit tests in the current file." + (interactive) + (if-let ((defuns (go-ts-mode--get-functions-in-range (point-min) (point-max)))) + (go-ts-mode--compile-test (string-join defuns "|")) + (error "No test functions found in the current file"))) + +(defun go-ts-mode-test-this-package () + "Run all the unit tests under the current package." + (interactive) + (compile (format "go test -v %s -run %s" + (go-ts-mode--get-build-tags-flag) + default-directory))) + ;; go.mod support. (defvar go-mod-ts-mode--syntax-table -- 2.39.2