branch: elpa/swift-mode
commit 95b74fbf1bed063816f6702ee0c800888c062171
Author: taku0 <[email protected]>
Commit: taku0 <[email protected]>
Handle output of Swift Testing
https://github.com/swift-emacs/swift-mode/issues/191
---
swift-mode-repl.el | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++
swift-mode.el | 4 +-
2 files changed, 138 insertions(+), 1 deletion(-)
diff --git a/swift-mode-repl.el b/swift-mode-repl.el
index fb59655e08..72de42c020 100644
--- a/swift-mode-repl.el
+++ b/swift-mode-repl.el
@@ -35,6 +35,7 @@
(require 'seq)
(require 'subr-x)
(require 'wid-edit)
+(require 'compile)
;;;###autoload
(defgroup swift-mode:repl nil
@@ -104,6 +105,19 @@ The string is split by spaces, then unquoted."
:type 'string
:safe #'stringp)
+(defcustom swift-mode:swift-testing-command-regexp "\\<swift test\\>"
+ "Regexp to of command line of Swift Testing.
+
+When the command of `compile' matches this regexp, its
+`compilation-error-regexp-alist' is overridden."
+ :type 'string
+ :safe #'stringp)
+
+(defcustom swift-mode:resolve-swift-test-file-function
+ #'swift-mode:resolve-swift-test-file
+ "Function to resolve Swift Testing files."
+ :type 'function)
+
(defvar swift-mode:repl-buffer nil
"Stores the name of the current swift REPL buffer, or nil.")
@@ -959,6 +973,127 @@ the value of `swift-mode:ios-project-scheme' is used."
codesigning-folder-path
product-bundle-identifier))))
+(defvar swift-mode:compilation-swift-files nil
+ "Hash table from Swift filenames in the project to its absolute path.")
+
+(defun swift-mode:setup-swift-testing-buffer ()
+ "Prepare *compilation* buffer for Swift Testing.
+
+Initialize a hash table from names of swift files to its absolute path.
+
+Adds `ansi-color-compilation-filter' to `compilation-filter-hook'.
+
+Overrides `compilation-error-regexp-alist'."
+ (setq-local swift-mode:compilation-swift-files nil)
+ (when (fboundp 'ansi-color-compilation-filter)
+ (add-hook 'compilation-filter-hook #'ansi-color-compilation-filter nil t))
+ (setq-local compilation-error-regexp-alist '(swift-testing)))
+
+(defvar swift-mode:original-compilation-process-setup-function nil
+ "`compilation-process-setup-function' before
`swift-mode:setup-swift-testing'.
+
+It is called from `swift-mode:compilation-process-setup-function'.")
+
+(defun swift-mode:swift-testing-compilation-process-setup-function ()
+ "`compilation-process-setup-function' for Swift Testing.
+
+Call original `compilation-process-setup-function' and prepare *compilation*
+buffer.
+
+See also `swift-mode:setup-swift-testing-buffer'."
+ (when swift-mode:original-compilation-process-setup-function
+ (funcall swift-mode:original-compilation-process-setup-function))
+ (when (string-match-p swift-mode:swift-testing-command-regexp
+ (car compilation-arguments))
+ (swift-mode:setup-swift-testing-buffer)))
+
+(defun swift-mode:find-test-sources (project-directory target)
+ "Return a list of the absolute paths of test sources.
+
+The manifest file is searched from the PROJECT-DIRECTORY, defaults to
+`default-directory', or its ancestors.
+
+If TARGET is non-nil, return only sources of that target."
+ (let* ((description (swift-mode:describe-package project-directory))
+ (targets (cdr (assoc 'targets description)))
+ (test-targets (seq-filter
+ (lambda (module)
+ (and (equal "test" (cdr (assoc 'type module)))
+ (or (null target)
+ (equal target (cdr (assoc 'name module))))))
+ targets))
+ (project-path (cdr (assoc 'path description)))
+ target-path)
+ (seq-mapcat
+ (lambda (target)
+ (setq target-path
+ (expand-file-name (cdr (assoc 'path target)) project-path))
+ (mapcar (lambda (source)
+ (expand-file-name source target-path))
+ (cdr (assoc 'sources target))))
+ test-targets)))
+
+(defun swift-mode:resolve-swift-test-file (file)
+ "Return full path of Swift Testing FILE in the project if any.
+
+If FILE is an absolute path, return it as is, even if it doesn't exist.
+
+If FILE doesn't exist in the project, return nil."
+ (if (file-name-absolute-p file)
+ (list file)
+ (when (null swift-mode:compilation-swift-files)
+ (setq-local swift-mode:compilation-swift-files
+ (make-hash-table :test 'equal))
+ (dolist (path (swift-mode:find-test-sources
+ (or compilation-directory default-directory)
+ ;; TODO
+ nil))
+ (puthash (file-name-nondirectory path)
+ path
+ swift-mode:compilation-swift-files)))
+ (let ((path (gethash (file-name-nondirectory file)
+ swift-mode:compilation-swift-files)))
+ (and path (list path)))))
+
+(defun swift-mode:setup-swift-testing ()
+ "Setup `compilation-process-setup-function' for Swift Testing.
+
+Save original `compilation-process-setup-function' and set
+`compilation-process-setup-function' to
+`swift-mode:swift-testing-compilation-process-setup-function'.
+
+Also add an entry to `compilation-error-regexp-alist-alist'."
+ (unless swift-mode:original-compilation-process-setup-function
+ (setq swift-mode:original-compilation-process-setup-function
+ (or compilation-process-setup-function #'ignore))
+ (setq compilation-process-setup-function
+ #'swift-mode:swift-testing-compilation-process-setup-function)
+ (add-to-list 'compilation-error-regexp-alist-alist
+ `(swift-testing
+ ,(rx bol
+ (zero-or-one (seq (* not-newline) (any " \t")))
+ (group
+ (group (+ (not (any " \t\n"))) ".swift")
+ ":"
+ (group (+ digit))
+ (zero-or-one
+ ":"
+ (group (+ digit))))
+ ": ")
+ ;; filename
+ ,(lambda ()
+ (save-match-data
+ (funcall swift-mode:resolve-swift-test-file-function
+ (match-string 2))))
+ ;; line
+ 3
+ ;; column
+ 4
+ ;; type
+ nil
+ ;; hyperlink
+ 1))))
+
(provide 'swift-mode-repl)
;;; swift-mode-repl.el ends here
diff --git a/swift-mode.el b/swift-mode.el
index 1368c4c98e..8d1e72bf28 100644
--- a/swift-mode.el
+++ b/swift-mode.el
@@ -239,7 +239,9 @@ Signal `scan-error' if it hits opening parentheses."
(when (equal (with-current-buffer (current-buffer) major-mode)
'swift-mode)
(swift-mode:current-defun-name))))
- (setq-local add-log-current-defun-function #'swift-mode:current-defun-name))
+ (setq-local add-log-current-defun-function #'swift-mode:current-defun-name)
+
+ (swift-mode:setup-swift-testing))
;;;###autoload (add-to-list 'auto-mode-alist
;;;###autoload '("\\.swift\\(interface\\)?\\'" . swift-mode))