branch: elpa/swift-mode
commit 95b74fbf1bed063816f6702ee0c800888c062171
Author: taku0 <mxxouy6x3m_git...@tatapa.org>
Commit: taku0 <mxxouy6x3m_git...@tatapa.org>

    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))

Reply via email to