|
| 1 | +;;; lsp-dart-test-output.el --- Test output features and decorations -*- lexical-binding: t; -*- |
| 2 | +;; |
| 3 | +;; Copyright (C) 2020 Eric Dallo |
| 4 | +;; |
| 5 | +;; This program is free software; you can redistribute it and/or modify |
| 6 | +;; it under the terms of the GNU General Public License as published by |
| 7 | +;; the Free Software Foundation, either version 3 of the License, or |
| 8 | +;; (at your option) any later version. |
| 9 | + |
| 10 | +;; This program is distributed in the hope that it will be useful, |
| 11 | +;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | +;; GNU General Public License for more details. |
| 14 | + |
| 15 | +;; You should have received a copy of the GNU General Public License |
| 16 | +;; along with this program. If not, see <https://www.gnu.org/licenses/>. |
| 17 | +;; |
| 18 | +;;; Commentary: |
| 19 | +;; |
| 20 | +;; Test output features and decorations |
| 21 | +;; |
| 22 | +;;; Code: |
| 23 | + |
| 24 | + |
| 25 | +(require 'rx) |
| 26 | + |
| 27 | +(require 'lsp-dart-protocol) |
| 28 | +(require 'lsp-dart-utils) |
| 29 | + |
| 30 | +(defcustom lsp-dart-test-pop-to-buffer-on-run 'display-only |
| 31 | + "Controls whether to pop to the tests buffer on run. |
| 32 | +
|
| 33 | +When set to nil the buffer will only be created, and not displayed. |
| 34 | +When set to `display-only' the buffer will be displayed, but it will |
| 35 | +not become focused, otherwise the buffer is displayed and focused." |
| 36 | + :group 'lsp-dart |
| 37 | + :type '(choice (const :tag "Create the buffer, but don't display it" nil) |
| 38 | + (const :tag "Create and display the buffer, but don't focus it" display-only) |
| 39 | + (const :tag "Create, display, and focus the buffer" t))) |
| 40 | + |
| 41 | + |
| 42 | +;;; Internal |
| 43 | + |
| 44 | +(defconst lsp-dart-test-output--passed-icon "★") |
| 45 | +(defconst lsp-dart-test-output--success-icon "✔") |
| 46 | +(defconst lsp-dart-test-output--skipped-icon "•") |
| 47 | +(defconst lsp-dart-test-output--error-icon "✖") |
| 48 | + |
| 49 | +(defvar lsp-dart-test-output--tests-count 0) |
| 50 | +(defvar lsp-dart-test-output--tests-passed 0) |
| 51 | + |
| 52 | +(defconst lsp-dart-test-output--buffer-name "*LSP Dart tests*") |
| 53 | + |
| 54 | +(defconst lsp-dart-test-output--exception-re |
| 55 | + (rx (or (and (zero-or-more any) |
| 56 | + (or "exception" "EXCEPTION") |
| 57 | + (zero-or-more any)) |
| 58 | + "<asynchronous suspension>" |
| 59 | + (and "#" |
| 60 | + (one-or-more |
| 61 | + any))))) |
| 62 | + |
| 63 | +(defconst lsp-dart-test-output--expected-actual-re |
| 64 | + (rx (or (and (zero-or-more blank) |
| 65 | + "Expected:" |
| 66 | + (zero-or-more any)) |
| 67 | + (and (zero-or-more blank) |
| 68 | + "Actual:" |
| 69 | + (zero-or-more any))))) |
| 70 | + |
| 71 | +(defconst lsp-dart-test--font-lock |
| 72 | + `((,lsp-dart-test-output--exception-re . 'error) |
| 73 | + (,lsp-dart-test-output--expected-actual-re . 'warning))) |
| 74 | + |
| 75 | +(defvar lsp-dart-test--output-font-lock |
| 76 | + '((lsp-dart-test--font-lock))) |
| 77 | + |
| 78 | +(lsp-defun lsp-dart-test-output--get-icon ((&TestDoneNotification :result :skipped)) |
| 79 | + "Return the icon for test done notification." |
| 80 | + (if (string= result "success") |
| 81 | + (if skipped |
| 82 | + lsp-dart-test-output--skipped-icon |
| 83 | + lsp-dart-test-output--success-icon) |
| 84 | + lsp-dart-test-output--error-icon)) |
| 85 | + |
| 86 | +(lsp-defun lsp-dart-test-output--get-face ((&TestDoneNotification :result :skipped)) |
| 87 | + "Return the icon for test done notification." |
| 88 | + (if (string= result "success") |
| 89 | + (if skipped |
| 90 | + 'homoglyph |
| 91 | + 'success) |
| 92 | + 'error)) |
| 93 | + |
| 94 | +(defun lsp-dart-test-output--send (message &rest args) |
| 95 | + "Send MESSAGE with ARGS to test buffer." |
| 96 | + (let* ((inhibit-read-only t)) |
| 97 | + (with-current-buffer (lsp-dart-test-output--get-buffer-create) |
| 98 | + (save-excursion |
| 99 | + (goto-char (point-max)) |
| 100 | + (insert (apply #'format (concat message "\n") args)))))) |
| 101 | + |
| 102 | +(defun lsp-dart-test-output--get-buffer-create () |
| 103 | + "Create a buffer for test display." |
| 104 | + (let ((buffer (get-buffer-create lsp-dart-test-output--buffer-name))) |
| 105 | + (with-current-buffer buffer |
| 106 | + (setq-local default-directory (or (lsp-dart-get-project-root) default-directory)) |
| 107 | + (unless (derived-mode-p 'lsp-dart-test-output-content-mode) |
| 108 | + (lsp-dart-test-output-content-mode)) |
| 109 | + (current-buffer)))) |
| 110 | + |
| 111 | +(defun lsp-dart-test-output--show-buffer () |
| 112 | + "Show test buffer." |
| 113 | + (let ((test-buffer (lsp-dart-test-output--get-buffer-create)) |
| 114 | + (inhibit-read-only t)) |
| 115 | + (with-current-buffer test-buffer |
| 116 | + (erase-buffer)) |
| 117 | + (pcase lsp-dart-test-pop-to-buffer-on-run |
| 118 | + (`display-only |
| 119 | + (let ((orig-buffer (current-buffer))) |
| 120 | + (display-buffer test-buffer) |
| 121 | + (set-buffer orig-buffer))) |
| 122 | + ((pred identity) (pop-to-buffer test-buffer))))) |
| 123 | + |
| 124 | +(defun lsp-dart-test-output--handle-run-started () |
| 125 | + "Handle test run started." |
| 126 | + (lsp-dart-test-output--send "Running tests...\n") |
| 127 | + (lsp-dart-test-output--show-buffer)) |
| 128 | + |
| 129 | +(defun lsp-dart-test-output--handle-all-start (_notification) |
| 130 | + "Handle all start notification." |
| 131 | + (setq lsp-dart-test-output--tests-count 0) |
| 132 | + (setq lsp-dart-test-output--tests-passed 0)) |
| 133 | + |
| 134 | +(lsp-defun lsp-dart-test-output--handle-start ((&TestStartNotification :test (&Test :group-i-ds))) |
| 135 | + (unless (seq-empty-p group-i-ds) |
| 136 | + (setq lsp-dart-test-output--tests-count (1+ lsp-dart-test-output--tests-count)))) |
| 137 | + |
| 138 | +(lsp-defun lsp-dart-test-output--handle-done ((notification &as &TestDoneNotification :result :time :hidden) test-name test-start-time) |
| 139 | + "Handle test done notification." |
| 140 | + (unless hidden |
| 141 | + (when (string= result "success") |
| 142 | + (setq lsp-dart-test-output--tests-passed (1+ lsp-dart-test-output--tests-passed)))) |
| 143 | + (let* ((formatted-time (propertize (format "(%s ms)" |
| 144 | + (- time test-start-time)) |
| 145 | + 'font-lock-face 'font-lock-comment-face)) |
| 146 | + (text (propertize (concat (lsp-dart-test-output--get-icon notification) |
| 147 | + " " |
| 148 | + test-name) |
| 149 | + 'font-lock-face (lsp-dart-test-output--get-face notification)))) |
| 150 | + (lsp-dart-test-output--send "%s %s" text formatted-time))) |
| 151 | + |
| 152 | +(lsp-defun lsp-dart-test-output--handle-all-done ((&DoneNotification :success)) |
| 153 | + "Handle all tests done notification." |
| 154 | + (if success |
| 155 | + (lsp-dart-test-output--send (propertize (format "\n%s All ran tests passed %s" lsp-dart-test-output--passed-icon lsp-dart-test-output--passed-icon) |
| 156 | + 'font-lock-face 'success)) |
| 157 | + (lsp-dart-test-output--send (propertize (format "\n● %s/%s tests passed" lsp-dart-test-output--tests-passed lsp-dart-test-output--tests-count) |
| 158 | + 'font-lock-face font-lock-warning-face)))) |
| 159 | + |
| 160 | +(lsp-defun lsp-dart-test-output--handle-print ((&PrintNotification :message)) |
| 161 | + "Handle test print notification." |
| 162 | + (lsp-dart-test-output--send "%s" message)) |
| 163 | + |
| 164 | +(lsp-defun lsp-dart-test-output--handle-error ((&ErrorNotification :error :stack-trace)) |
| 165 | + "Handle test error notification." |
| 166 | + (lsp-dart-test-output--send "%s" error) |
| 167 | + (lsp-dart-test-output--send "%s" stack-trace)) |
| 168 | + |
| 169 | +(define-derived-mode lsp-dart-test-output-content-mode special-mode lsp-dart-test-output--buffer-name |
| 170 | + "Major mode for buffer running tests." |
| 171 | + (setq font-lock-defaults lsp-dart-test--output-font-lock)) |
| 172 | + |
| 173 | +(add-hook 'lsp-dart-test-run-started-hook #'lsp-dart-test-output--handle-run-started) |
| 174 | +(add-hook 'lsp-dart-test-all-start-notification-hook #'lsp-dart-test-output--handle-all-start) |
| 175 | +(add-hook 'lsp-dart-test-start-notification-hook #'lsp-dart-test-output--handle-start) |
| 176 | +(add-hook 'lsp-dart-test-done-notification-hook #'lsp-dart-test-output--handle-done) |
| 177 | +(add-hook 'lsp-dart-test-all-done-notification-hook #'lsp-dart-test-output--handle-all-done) |
| 178 | +(add-hook 'lsp-dart-test-print-notification-hook #'lsp-dart-test-output--handle-print) |
| 179 | +(add-hook 'lsp-dart-test-error-notification-hook #'lsp-dart-test-output--handle-error) |
| 180 | + |
| 181 | +(provide 'lsp-dart-test-output) |
| 182 | +;;; lsp-dart-test-output.el ends here |
0 commit comments