diff options
| author | João Távora | 2017-09-30 17:32:53 +0100 |
|---|---|---|
| committer | João Távora | 2017-10-03 14:18:55 +0100 |
| commit | f6e909b41e927a6715bad5fc5386257f29e7c0bb (patch) | |
| tree | efc4ed256ed6f6ff96326285cc1abd6348330343 | |
| parent | 22a7372faba317a3589c49fef912e542f3197f0d (diff) | |
| download | emacs-f6e909b41e927a6715bad5fc5386257f29e7c0bb.tar.gz emacs-f6e909b41e927a6715bad5fc5386257f29e7c0bb.zip | |
Flymake backends can report multiple times per check
Rewrote a significant part of the Flymake backend API. Flymake now
ignores the return value of backend functions: a function can either
returns or errors. If it doesn't error, a backend is no longer
constrained to call REPORT-FN exactly once. It may do so any number
of times, cumulatively reporting diagnostics. Flymake keeps track of
outdated REPORT-FN instances and disconsiders obsolete reports.
Backends should avoid reporting obsolete data by cancelling any
ongoing processing at every renewed call to the backend function.
Consolidated flymake.el internal data structures to require less
buffer-local variables. Adjusted Flymake's mode-line indicator to the
new semantics.
Adapted and simplified the implementation of elisp and legacy
backends, fixing potential race conditions when calling backends in
rapid succession.
Added a new test for a backend that calls REPORT-FN multiple
times. Simplify test infrastructure.
* lisp/progmodes/flymake-elisp.el (flymake-elisp-checkdoc)
(flymake-elisp-byte-compile): Error instead of returning nil
if not in emacs-lisp-mode.
(flymake-elisp--byte-compile-process): New buffer-local variable.
(flymake-elisp-byte-compile): Mark (and kill) previous process
obsolete process before starting a new one. Don't report if
obsolete process.
* lisp/progmodes/flymake-proc.el
(flymake-proc--current-process): New buffer-local variable.
(flymake-proc--processes): Remove.
(flymake-proc--process-filter): Don't bind
flymake-proc--report-fn.
(flymake-proc--process-sentinel): Rewrite. Don't report if
obsolete process.
(flymake-proc-legacy-flymake): Rewrite. Mark (and kill)
previous process obsolete process before starting a new
one. Integrate flymake-proc--start-syntax-check-process
helper.
(flymake-proc--start-syntax-check-process): Delete.
(flymake-proc-stop-all-syntax-checks): Don't use
flymake-proc--processes, iterate buffers.
(flymake-proc-compile):
* lisp/progmodes/flymake.el (subr-x): Require it
explicitly.
(flymake-diagnostic-functions): Reword docstring.
(flymake--running-backends, flymake--disabled-backends)
(flymake--diagnostics-table): Delete.
(flymake--backend-state): New buffer-local variable and new defstruct.
(flymake--with-backend-state, flymake--collect)
(flymake-running-backends, flymake-disabled-backends)
(flymake-reporting-backends): New helpers.
(flymake-is-running): Use flymake-running-backends.
(flymake--handle-report): Rewrite.
(flymake-make-report-fn): Ensure REPORT-FN runs in the correct
buffer or not at all.
(flymake--disable-backend, flymake--run-backend): Rewrite.
(flymake-start): Rewrite.
(flymake-mode): Set flymake--backend-state.
(flymake--mode-line-format): Rewrite.
* test/lisp/progmodes/flymake-tests.el
(flymake-tests--wait-for-backends): New helper.
(flymake-tests--call-with-fixture): Use it.
(included-c-header-files): Fix whitespace.
(flymake-tests--diagnose-words): New helper.
(dummy-backends): Rewrite for new semantics. Use cl-letf.
(flymake-tests--assert-set): Use quote.
(recurrent-backend): New test.
| -rw-r--r-- | lisp/progmodes/flymake-elisp.el | 94 | ||||
| -rw-r--r-- | lisp/progmodes/flymake-proc.el | 247 | ||||
| -rw-r--r-- | lisp/progmodes/flymake.el | 492 | ||||
| -rw-r--r-- | test/lisp/progmodes/flymake-tests.el | 246 |
4 files changed, 609 insertions, 470 deletions
diff --git a/lisp/progmodes/flymake-elisp.el b/lisp/progmodes/flymake-elisp.el index 7797d278e3f..f54badfc833 100644 --- a/lisp/progmodes/flymake-elisp.el +++ b/lisp/progmodes/flymake-elisp.el | |||
| @@ -48,14 +48,15 @@ | |||
| 48 | (defun flymake-elisp-checkdoc (report-fn) | 48 | (defun flymake-elisp-checkdoc (report-fn) |
| 49 | "A flymake backend for `checkdoc'. | 49 | "A flymake backend for `checkdoc'. |
| 50 | Calls REPORT-FN directly." | 50 | Calls REPORT-FN directly." |
| 51 | (when (derived-mode-p 'emacs-lisp-mode) | 51 | (unless (derived-mode-p 'emacs-lisp-mode) |
| 52 | (funcall report-fn | 52 | (error "Can only work on `emacs-lisp-mode' buffers")) |
| 53 | (cl-loop for (text start end _unfixable) in | 53 | (funcall report-fn |
| 54 | (flymake-elisp--checkdoc-1) | 54 | (cl-loop for (text start end _unfixable) in |
| 55 | collect | 55 | (flymake-elisp--checkdoc-1) |
| 56 | (flymake-make-diagnostic | 56 | collect |
| 57 | (current-buffer) | 57 | (flymake-make-diagnostic |
| 58 | start end :note text))))) | 58 | (current-buffer) |
| 59 | start end :note text)))) | ||
| 59 | 60 | ||
| 60 | (defun flymake-elisp--byte-compile-done (report-fn | 61 | (defun flymake-elisp--byte-compile-done (report-fn |
| 61 | origin-buffer | 62 | origin-buffer |
| @@ -94,40 +95,59 @@ Calls REPORT-FN directly." | |||
| 94 | (kill-buffer output-buffer) | 95 | (kill-buffer output-buffer) |
| 95 | (ignore-errors (delete-file temp-file)))) | 96 | (ignore-errors (delete-file temp-file)))) |
| 96 | 97 | ||
| 98 | (defvar-local flymake-elisp--byte-compile-process nil | ||
| 99 | "Buffer-local process started for byte-compiling the buffer.") | ||
| 100 | |||
| 97 | (defun flymake-elisp-byte-compile (report-fn) | 101 | (defun flymake-elisp-byte-compile (report-fn) |
| 98 | "A flymake backend for elisp byte compilation. | 102 | "A Flymake backend for elisp byte compilation. |
| 99 | Spawn an Emacs process that byte-compiles a file representing the | 103 | Spawn an Emacs process that byte-compiles a file representing the |
| 100 | current buffer state and calls REPORT-FN when done." | 104 | current buffer state and calls REPORT-FN when done." |
| 101 | (interactive (list (lambda (stuff) | 105 | (interactive (list (lambda (stuff) |
| 102 | (message "aha %s" stuff)))) | 106 | (message "aha %s" stuff)))) |
| 103 | (when (derived-mode-p 'emacs-lisp-mode) | 107 | (unless (derived-mode-p 'emacs-lisp-mode) |
| 104 | (let ((temp-file (make-temp-file "flymake-elisp-byte-compile")) | 108 | (error "Can only work on `emacs-lisp-mode' buffers")) |
| 105 | (origin-buffer (current-buffer))) | 109 | (when flymake-elisp--byte-compile-process |
| 106 | (save-restriction | 110 | (process-put flymake-elisp--byte-compile-process 'flymake-elisp--obsolete t) |
| 107 | (widen) | 111 | (when (process-live-p flymake-elisp--byte-compile-process) |
| 108 | (write-region (point-min) (point-max) temp-file nil 'nomessage)) | 112 | (kill-process flymake-elisp--byte-compile-process))) |
| 109 | (let* ((output-buffer (generate-new-buffer " *flymake-elisp-byte-compile*"))) | 113 | (let ((temp-file (make-temp-file "flymake-elisp-byte-compile")) |
| 110 | (make-process | 114 | (origin-buffer (current-buffer))) |
| 111 | :name "flymake-elisp-byte-compile" | 115 | (save-restriction |
| 112 | :buffer output-buffer | 116 | (widen) |
| 113 | :command (list (expand-file-name invocation-name invocation-directory) | 117 | (write-region (point-min) (point-max) temp-file nil 'nomessage)) |
| 114 | "-Q" | 118 | (let* ((output-buffer (generate-new-buffer " *flymake-elisp-byte-compile*"))) |
| 115 | "--batch" | 119 | (setq |
| 116 | ;; "--eval" "(setq load-prefer-newer t)" ; for testing | 120 | flymake-elisp--byte-compile-process |
| 117 | "-L" default-directory | 121 | (make-process |
| 118 | "-l" "flymake-elisp" | 122 | :name "flymake-elisp-byte-compile" |
| 119 | "-f" "flymake-elisp--batch-byte-compile" | 123 | :buffer output-buffer |
| 120 | temp-file) | 124 | :command (list (expand-file-name invocation-name invocation-directory) |
| 121 | :connection-type 'pipe | 125 | "-Q" |
| 122 | :sentinel | 126 | "--batch" |
| 123 | (lambda (proc _event) | 127 | ;; "--eval" "(setq load-prefer-newer t)" ; for testing |
| 124 | (unless (process-live-p proc) | 128 | "-L" default-directory |
| 125 | (flymake-elisp--byte-compile-done report-fn | 129 | "-l" "flymake-elisp" |
| 126 | origin-buffer | 130 | "-f" "flymake-elisp--batch-byte-compile" |
| 127 | output-buffer | 131 | temp-file) |
| 128 | temp-file)))) | 132 | :connection-type 'pipe |
| 129 | :stderr null-device | 133 | :sentinel |
| 130 | :noquery t)))) | 134 | (lambda (proc _event) |
| 135 | (unless (process-live-p proc) | ||
| 136 | (unwind-protect | ||
| 137 | (cond | ||
| 138 | ((zerop (process-exit-status proc)) | ||
| 139 | (flymake-elisp--byte-compile-done report-fn | ||
| 140 | origin-buffer | ||
| 141 | output-buffer | ||
| 142 | temp-file)) | ||
| 143 | ((process-get proc 'flymake-elisp--obsolete) | ||
| 144 | (flymake-log 3 "proc %s considered obsolete" proc)) | ||
| 145 | (t | ||
| 146 | (funcall report-fn | ||
| 147 | :panic | ||
| 148 | :explanation (format "proc %s died violently" proc))))))))) | ||
| 149 | :stderr null-device | ||
| 150 | :noquery t))) | ||
| 131 | 151 | ||
| 132 | (defun flymake-elisp--batch-byte-compile (&optional file) | 152 | (defun flymake-elisp--batch-byte-compile (&optional file) |
| 133 | "Helper for `flymake-elisp-byte-compile'. | 153 | "Helper for `flymake-elisp-byte-compile'. |
diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el index 3ab5523128f..b0ee7d65025 100644 --- a/lisp/progmodes/flymake-proc.el +++ b/lisp/progmodes/flymake-proc.el | |||
| @@ -109,12 +109,9 @@ NAME is the file name function to use, default `flymake-proc-get-real-file-name' | |||
| 109 | (const :tag "flymake-proc-get-real-file-name" nil) | 109 | (const :tag "flymake-proc-get-real-file-name" nil) |
| 110 | function)))) | 110 | function)))) |
| 111 | 111 | ||
| 112 | (defvar-local flymake-proc--process nil | 112 | (defvar-local flymake-proc--current-process nil |
| 113 | "Currently active flymake process for a buffer, if any.") | 113 | "Currently active flymake process for a buffer, if any.") |
| 114 | 114 | ||
| 115 | (defvar flymake-proc--processes nil | ||
| 116 | "List of currently active flymake processes.") | ||
| 117 | |||
| 118 | (defvar flymake-proc--report-fn nil | 115 | (defvar flymake-proc--report-fn nil |
| 119 | "If bound, function used to report back to flymake's UI.") | 116 | "If bound, function used to report back to flymake's UI.") |
| 120 | 117 | ||
| @@ -543,9 +540,7 @@ Create parent directories as needed." | |||
| 543 | "Parse STRING and collect diagnostics info." | 540 | "Parse STRING and collect diagnostics info." |
| 544 | (flymake-log 3 "received %d byte(s) of output from process %d" | 541 | (flymake-log 3 "received %d byte(s) of output from process %d" |
| 545 | (length string) (process-id proc)) | 542 | (length string) (process-id proc)) |
| 546 | (let ((output-buffer (process-get proc 'flymake-proc--output-buffer)) | 543 | (let ((output-buffer (process-get proc 'flymake-proc--output-buffer))) |
| 547 | (flymake-proc--report-fn | ||
| 548 | (process-get proc 'flymake-proc--report-fn))) | ||
| 549 | (when (and (buffer-live-p (process-buffer proc)) | 544 | (when (and (buffer-live-p (process-buffer proc)) |
| 550 | output-buffer) | 545 | output-buffer) |
| 551 | (with-current-buffer output-buffer | 546 | (with-current-buffer output-buffer |
| @@ -578,49 +573,55 @@ Create parent directories as needed." | |||
| 578 | 573 | ||
| 579 | (defun flymake-proc--process-sentinel (proc _event) | 574 | (defun flymake-proc--process-sentinel (proc _event) |
| 580 | "Sentinel for syntax check buffers." | 575 | "Sentinel for syntax check buffers." |
| 581 | (when (memq (process-status proc) '(signal exit)) | 576 | (let (debug |
| 582 | (let* ((exit-status (process-exit-status proc)) | 577 | (pid (process-id proc)) |
| 583 | (command (process-command proc)) | 578 | (source-buffer (process-buffer proc))) |
| 584 | (source-buffer (process-buffer proc)) | 579 | (unwind-protect |
| 585 | (flymake-proc--report-fn (process-get proc | 580 | (when (buffer-live-p source-buffer) |
| 586 | 'flymake-proc--report-fn)) | 581 | (with-current-buffer source-buffer |
| 587 | (cleanup-f (flymake-proc--get-cleanup-function | 582 | (cond ((process-get proc 'flymake-proc--obsolete) |
| 588 | (buffer-file-name source-buffer))) | 583 | (flymake-log 3 "proc %s considered obsolete" |
| 589 | (diagnostics (process-get | 584 | pid)) |
| 590 | proc | 585 | ((process-get proc 'flymake-proc--interrupted) |
| 591 | 'flymake-proc--collected-diagnostics)) | 586 | (flymake-log 3 "proc %s interrupted by user" |
| 592 | (interrupted (process-get proc 'flymake-proc--interrupted)) | 587 | pid)) |
| 593 | (panic nil) | 588 | ((not (process-live-p proc)) |
| 594 | (output-buffer (process-get proc 'flymake-proc--output-buffer))) | 589 | (let* ((exit-status (process-exit-status proc)) |
| 595 | (flymake-log 2 "process %d exited with code %d" | 590 | (command (process-command proc)) |
| 596 | (process-id proc) exit-status) | 591 | (diagnostics (process-get |
| 597 | (condition-case-unless-debug err | 592 | proc |
| 598 | (progn | 593 | 'flymake-proc--collected-diagnostics))) |
| 599 | (flymake-log 3 "cleaning up using %s" cleanup-f) | 594 | (flymake-log 2 "process %d exited with code %d" |
| 600 | (with-current-buffer source-buffer | 595 | pid exit-status) |
| 601 | (funcall cleanup-f) | 596 | (cond |
| 602 | (cond ((equal 0 exit-status) | 597 | ((equal 0 exit-status) |
| 603 | (funcall flymake-proc--report-fn diagnostics)) | 598 | (funcall flymake-proc--report-fn diagnostics |
| 604 | (interrupted | 599 | :explanation (format "a gift from %s" (process-id proc)) |
| 605 | (flymake-proc--panic :stopped interrupted)) | 600 | )) |
| 606 | (diagnostics | 601 | (diagnostics |
| 607 | ;; non-zero exit but some diagnostics is quite | 602 | ;; non-zero exit but some diagnostics is quite |
| 608 | ;; normal... | 603 | ;; normal... |
| 609 | (funcall flymake-proc--report-fn diagnostics)) | 604 | (funcall flymake-proc--report-fn diagnostics |
| 610 | ((null diagnostics) | 605 | :explanation (format "a gift from %s" (process-id proc)))) |
| 611 | ;; ...but no diagnostics is strange, so panic. | 606 | ((null diagnostics) |
| 612 | (setq panic t) | 607 | ;; ...but no diagnostics is strange, so panic. |
| 613 | (flymake-proc--panic | 608 | (setq debug debug-on-error) |
| 614 | :configuration-error | 609 | (flymake-proc--panic |
| 615 | (format "Command %s errored, but no diagnostics" | 610 | :configuration-error |
| 616 | command)))))) | 611 | (format "Command %s errored, but no diagnostics" |
| 617 | (delete-process proc) | 612 | command))))))))) |
| 618 | (setq flymake-proc--processes | 613 | (let ((output-buffer (process-get proc 'flymake-proc--output-buffer))) |
| 619 | (delq proc flymake-proc--processes)) | 614 | (cond (debug |
| 620 | (if panic | 615 | (flymake-log 3 "Output buffer %s kept alive for debugging" |
| 621 | (flymake-log 1 "Output buffer %s kept alive for debugging" | 616 | output-buffer)) |
| 622 | output-buffer) | 617 | (t |
| 623 | (kill-buffer output-buffer)))))) | 618 | (when (buffer-live-p source-buffer) |
| 619 | (with-current-buffer source-buffer | ||
| 620 | (let ((cleanup-f (flymake-proc--get-cleanup-function | ||
| 621 | (buffer-file-name)))) | ||
| 622 | (flymake-log 3 "cleaning up using %s" cleanup-f) | ||
| 623 | (funcall cleanup-f)))) | ||
| 624 | (kill-buffer output-buffer))))))) | ||
| 624 | 625 | ||
| 625 | (defun flymake-proc--panic (problem explanation) | 626 | (defun flymake-proc--panic (problem explanation) |
| 626 | "Tell flymake UI about a fatal PROBLEM with this backend. | 627 | "Tell flymake UI about a fatal PROBLEM with this backend. |
| @@ -729,87 +730,85 @@ can also be executed interactively independently of | |||
| 729 | diags | 730 | diags |
| 730 | (append args '(:force t)))) | 731 | (append args '(:force t)))) |
| 731 | t)) | 732 | t)) |
| 732 | (cond | 733 | (let ((proc flymake-proc--current-process) |
| 733 | ((process-live-p flymake-proc--process) | 734 | (flymake-proc--report-fn report-fn)) |
| 734 | (when interactive | 735 | (when (processp proc) |
| 735 | (user-error | 736 | (process-put proc 'flymake-proc--obsolete t) |
| 736 | "There's already a flymake process running in this buffer"))) | 737 | (flymake-log 3 "marking %s obsolete" (process-id proc)) |
| 737 | ((and buffer-file-name | 738 | (when (process-live-p proc) |
| 738 | ;; Since we write temp files in current dir, there's no point | 739 | (when interactive |
| 739 | ;; trying if the directory is read-only (bug#8954). | 740 | (user-error |
| 740 | (file-writable-p (file-name-directory buffer-file-name)) | 741 | "There's already a flymake process running in this buffer") |
| 741 | (or (not flymake-proc-compilation-prevents-syntax-check) | 742 | (kill-process proc)))) |
| 742 | (not (flymake-proc--compilation-is-running)))) | 743 | (when |
| 743 | (let ((init-f (flymake-proc--get-init-function buffer-file-name))) | 744 | ;; A number of situations make us not want to error right away |
| 744 | (unless init-f (error "Can find a suitable init function")) | 745 | ;; (and disable ourselves), in case the situation changes in |
| 745 | (flymake-proc--clear-buildfile-cache) | 746 | ;; the near future. |
| 746 | (flymake-proc--clear-project-include-dirs-cache) | 747 | (and buffer-file-name |
| 747 | 748 | ;; Since we write temp files in current dir, there's no point | |
| 748 | (let* ((flymake-proc--report-fn report-fn) | 749 | ;; trying if the directory is read-only (bug#8954). |
| 749 | (cleanup-f (flymake-proc--get-cleanup-function buffer-file-name)) | 750 | (file-writable-p (file-name-directory buffer-file-name)) |
| 750 | (cmd-and-args (funcall init-f)) | 751 | (or (not flymake-proc-compilation-prevents-syntax-check) |
| 751 | (cmd (nth 0 cmd-and-args)) | 752 | (not (flymake-proc--compilation-is-running)))) |
| 752 | (args (nth 1 cmd-and-args)) | 753 | (let ((init-f (flymake-proc--get-init-function buffer-file-name))) |
| 753 | (dir (nth 2 cmd-and-args))) | 754 | (unless init-f (error "Can find a suitable init function")) |
| 754 | (cond ((not cmd-and-args) | 755 | (flymake-proc--clear-buildfile-cache) |
| 755 | (progn | 756 | (flymake-proc--clear-project-include-dirs-cache) |
| 756 | (flymake-log 0 "init function %s for %s failed, cleaning up" | 757 | |
| 757 | init-f buffer-file-name) | 758 | (let* ((cleanup-f (flymake-proc--get-cleanup-function buffer-file-name)) |
| 758 | (funcall cleanup-f))) | 759 | (cmd-and-args (funcall init-f)) |
| 759 | (t | 760 | (cmd (nth 0 cmd-and-args)) |
| 760 | (setq flymake-last-change-time nil) | 761 | (args (nth 1 cmd-and-args)) |
| 761 | (flymake-proc--start-syntax-check-process cmd | 762 | (dir (nth 2 cmd-and-args)) |
| 762 | args | 763 | (success nil)) |
| 763 | dir) | 764 | (unwind-protect |
| 764 | t))))))) | 765 | (cond |
| 766 | ((not cmd-and-args) | ||
| 767 | (flymake-log 0 "init function %s for %s failed, cleaning up" | ||
| 768 | init-f buffer-file-name)) | ||
| 769 | (t | ||
| 770 | (setq flymake-last-change-time nil) | ||
| 771 | (setq proc | ||
| 772 | (let ((default-directory (or dir default-directory))) | ||
| 773 | (when dir | ||
| 774 | (flymake-log 3 "starting process on dir %s" dir)) | ||
| 775 | (make-process | ||
| 776 | :name "flymake-proc" | ||
| 777 | :buffer (current-buffer) | ||
| 778 | :command (cons cmd args) | ||
| 779 | :noquery t | ||
| 780 | :filter | ||
| 781 | (lambda (proc string) | ||
| 782 | (let ((flymake-proc--report-fn report-fn)) | ||
| 783 | (flymake-proc--process-filter proc string))) | ||
| 784 | :sentinel | ||
| 785 | (lambda (proc event) | ||
| 786 | (let ((flymake-proc--report-fn report-fn)) | ||
| 787 | (flymake-proc--process-sentinel proc event)))))) | ||
| 788 | (process-put proc 'flymake-proc--output-buffer | ||
| 789 | (generate-new-buffer | ||
| 790 | (format " *flymake output for %s*" (current-buffer)))) | ||
| 791 | (setq flymake-proc--current-process proc) | ||
| 792 | (flymake-log 2 "started process %d, command=%s, dir=%s" | ||
| 793 | (process-id proc) (process-command proc) | ||
| 794 | default-directory) | ||
| 795 | (setq success t))) | ||
| 796 | (unless success | ||
| 797 | (funcall cleanup-f)))))))) | ||
| 765 | 798 | ||
| 766 | (define-obsolete-function-alias 'flymake-start-syntax-check | 799 | (define-obsolete-function-alias 'flymake-start-syntax-check |
| 767 | 'flymake-proc-legacy-flymake "26.1") | 800 | 'flymake-proc-legacy-flymake "26.1") |
| 768 | 801 | ||
| 769 | (defun flymake-proc--start-syntax-check-process (cmd args dir) | ||
| 770 | "Start syntax check process." | ||
| 771 | (condition-case-unless-debug err | ||
| 772 | (let* ((process | ||
| 773 | (let ((default-directory (or dir default-directory))) | ||
| 774 | (when dir | ||
| 775 | (flymake-log 3 "starting process on dir %s" dir)) | ||
| 776 | (make-process :name "flymake-proc" | ||
| 777 | :buffer (current-buffer) | ||
| 778 | :command (cons cmd args) | ||
| 779 | :noquery t | ||
| 780 | :filter 'flymake-proc--process-filter | ||
| 781 | :sentinel 'flymake-proc--process-sentinel)))) | ||
| 782 | (process-put process 'flymake-proc--output-buffer | ||
| 783 | (generate-new-buffer | ||
| 784 | (format " *flymake output for %s*" (current-buffer)))) | ||
| 785 | (process-put process 'flymake-proc--report-fn | ||
| 786 | flymake-proc--report-fn) | ||
| 787 | |||
| 788 | (setq-local flymake-proc--process process) | ||
| 789 | (push process flymake-proc--processes) | ||
| 790 | |||
| 791 | (setq flymake-is-running t) | ||
| 792 | (setq flymake-last-change-time nil) | ||
| 793 | |||
| 794 | (flymake-log 2 "started process %d, command=%s, dir=%s" | ||
| 795 | (process-id process) (process-command process) | ||
| 796 | default-directory) | ||
| 797 | process) | ||
| 798 | (error | ||
| 799 | (flymake-proc--panic :make-process-error | ||
| 800 | (format-message | ||
| 801 | "Failed to launch syntax check process `%s' with args %s: %s" | ||
| 802 | cmd args (error-message-string err))) | ||
| 803 | (funcall (flymake-proc--get-cleanup-function buffer-file-name))))) | ||
| 804 | |||
| 805 | (defun flymake-proc-stop-all-syntax-checks (&optional reason) | 802 | (defun flymake-proc-stop-all-syntax-checks (&optional reason) |
| 806 | "Kill all syntax check processes." | 803 | "Kill all syntax check processes." |
| 807 | (interactive (list "Interrupted by user")) | 804 | (interactive (list "Interrupted by user")) |
| 808 | (mapc (lambda (proc) | 805 | (dolist (buf (buffer-list)) |
| 809 | (kill-process proc) | 806 | (with-current-buffer buf |
| 810 | (process-put proc 'flymake-proc--interrupted reason) | 807 | (let (p flymake-proc--current-process) |
| 811 | (flymake-log 2 "killed process %d" (process-id proc))) | 808 | (when (process-live-p p) |
| 812 | flymake-proc--processes)) | 809 | (kill-process p) |
| 810 | (process-put p 'flymake-proc--interrupted reason) | ||
| 811 | (flymake-log 2 "killed process %d" (process-id p))))))) | ||
| 813 | 812 | ||
| 814 | (defun flymake-proc--compilation-is-running () | 813 | (defun flymake-proc--compilation-is-running () |
| 815 | (and (boundp 'compilation-in-progress) | 814 | (and (boundp 'compilation-in-progress) |
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 1068b3889da..d1673977762 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el | |||
| @@ -36,7 +36,7 @@ | |||
| 36 | (require 'thingatpt) ; end-of-thing | 36 | (require 'thingatpt) ; end-of-thing |
| 37 | (require 'warnings) ; warning-numeric-level, display-warning | 37 | (require 'warnings) ; warning-numeric-level, display-warning |
| 38 | (require 'compile) ; for some faces | 38 | (require 'compile) ; for some faces |
| 39 | (eval-when-compile (require 'subr-x)) ; when-let*, if-let* | 39 | (require 'subr-x) ; when-let*, if-let*, hash-table-keys, hash-table-values |
| 40 | 40 | ||
| 41 | (defgroup flymake nil | 41 | (defgroup flymake nil |
| 42 | "Universal on-the-fly syntax checker." | 42 | "Universal on-the-fly syntax checker." |
| @@ -315,42 +315,39 @@ about where and how to annotate problems diagnosed in a buffer. | |||
| 315 | 315 | ||
| 316 | Whenever Flymake or the user decides to re-check the buffer, each | 316 | Whenever Flymake or the user decides to re-check the buffer, each |
| 317 | function is called with a common calling convention, a single | 317 | function is called with a common calling convention, a single |
| 318 | REPORT-FN argument, detailed below. Backend functions are first | 318 | REPORT-FN argument, detailed below. Backend functions are |
| 319 | expected to quickly and inexpensively announce the feasibility of | 319 | expected to initiate the buffer check, but aren't required to |
| 320 | checking the buffer via the return value (i.e. they aren't | 320 | complete it check before exiting: if the computation involved is |
| 321 | required to immediately start checking the buffer): | 321 | expensive, especially for large buffers, that task can be |
| 322 | 322 | scheduled for the future using asynchronous processes or other | |
| 323 | * If the backend function returns nil, Flymake forgets about this | 323 | asynchronous mechanisms. |
| 324 | backend for the current check, but will call it again for the | 324 | |
| 325 | next one; | 325 | In any case, backend functions are expected to return quickly or |
| 326 | 326 | signal an error, in which case the backend is disabled. Flymake | |
| 327 | * If the backend function returns non-nil, Flymake expects this | 327 | will not try disabled backends again for any future checks of |
| 328 | backend to check the buffer and call its REPORT-FN callback | 328 | this buffer. Certain commands, like turning `flymake-mode' off |
| 329 | function exactly once. If the computation involved is | 329 | and on again, reset the list of disabled backends. |
| 330 | inexpensive, the backend function may do so synchronously, | 330 | |
| 331 | before returning. If it is not, it should do so after | 331 | If the function returns, Flymake considers the backend to be |
| 332 | returning, using idle timers, asynchronous processes or other | 332 | \"running\". If it has not done so already, the backend is |
| 333 | asynchronous mechanisms. | 333 | expected to call the function REPORT-FN with a single argument |
| 334 | 334 | ACTION followed by an optional list of keyword arguments and | |
| 335 | * If the backend function signals an error, it is disabled, | ||
| 336 | i.e. Flymake will not use it again for the current or any | ||
| 337 | future checks of this buffer. Certain commands, like turning | ||
| 338 | `flymake-mode' on and off again, resets the list of disabled | ||
| 339 | backends. | ||
| 340 | |||
| 341 | Backends are required to call REPORT-FN with a single argument | ||
| 342 | ACTION followed by an optional list of keywords parameters and | ||
| 343 | their values (:KEY1 VALUE1 :KEY2 VALUE2...). | 335 | their values (:KEY1 VALUE1 :KEY2 VALUE2...). |
| 344 | 336 | ||
| 345 | The possible values for ACTION are. | 337 | The possible values for ACTION are. |
| 346 | 338 | ||
| 347 | * A (possibly empty) list of objects created with | 339 | * A (possibly empty) list of diagnostic objects created with |
| 348 | `flymake-make-diagnostic', causing Flymake to annotate the | 340 | `flymake-make-diagnostic', causing Flymake to annotate the |
| 349 | buffer with this information and consider the backend has | 341 | buffer with this information. |
| 350 | having finished its check normally. | ||
| 351 | 342 | ||
| 352 | * The symbol `:progress', signalling that the backend is still | 343 | A backend may call REPORT-FN repeatedly in this manner, but |
| 353 | working and will call REPORT-FN again in the future. | 344 | only until Flymake considers that the most recently requested |
| 345 | buffer check is now obsolete because, say, buffer contents have | ||
| 346 | changed in the meantime. The backend is only given notice of | ||
| 347 | this via a renewed call to the backend function. Thus, to | ||
| 348 | prevent making obsolete reports and wasting resources, backend | ||
| 349 | functions should first cancel any ongoing processing from | ||
| 350 | previous calls. | ||
| 354 | 351 | ||
| 355 | * The symbol `:panic', signalling that the backend has | 352 | * The symbol `:panic', signalling that the backend has |
| 356 | encountered an exceptional situation and should be disabled. | 353 | encountered an exceptional situation and should be disabled. |
| @@ -360,8 +357,8 @@ The recognized optional keyword arguments are: | |||
| 360 | * ‘:explanation’: value should give user-readable details of | 357 | * ‘:explanation’: value should give user-readable details of |
| 361 | the situation encountered, if any. | 358 | the situation encountered, if any. |
| 362 | 359 | ||
| 363 | * ‘:force’: value should be a boolean forcing the Flymake UI | 360 | * ‘:force’: value should be a boolean suggesting that the Flymake |
| 364 | to consider the report even if was somehow unexpected.") | 361 | considers the report even if was somehow unexpected.") |
| 365 | 362 | ||
| 366 | (defvar flymake-diagnostic-types-alist | 363 | (defvar flymake-diagnostic-types-alist |
| 367 | `((:error | 364 | `((:error |
| @@ -493,122 +490,189 @@ associated `flymake-category' return DEFAULT." | |||
| 493 | ;; third-party compatibility. | 490 | ;; third-party compatibility. |
| 494 | (define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1") | 491 | (define-obsolete-function-alias 'flymake-display-warning 'message-box "26.1") |
| 495 | 492 | ||
| 496 | (defvar-local flymake--running-backends nil | 493 | (defvar-local flymake--backend-state nil |
| 497 | "List of currently active flymake backends. | 494 | "Buffer-local hash table of a Flymake backend's state. |
| 498 | An active backend is a member of `flymake-diagnostic-functions' | 495 | The keys to this hash table are functions as found in |
| 499 | that has been invoked but hasn't reported any final status yet.") | 496 | `flymake-diagnostic-functions'. The values are structures |
| 500 | 497 | of the type `flymake--backend-state', with these slots | |
| 501 | (defvar-local flymake--disabled-backends nil | 498 | |
| 502 | "List of currently disabled flymake backends. | 499 | `running', a symbol to keep track of a backend's replies via its |
| 503 | A backend is disabled if it reported `:panic'.") | 500 | REPORT-FN argument. A backend is running if this key is |
| 504 | 501 | present. If the key is absent if the backend isn't expecting any | |
| 505 | (defvar-local flymake--diagnostics-table nil | 502 | replies from the backend. |
| 506 | "Hash table of all diagnostics indexed by backend.") | 503 | |
| 504 | `diags', a (possibly empty) list of diagnostic objects created | ||
| 505 | with `flymake-make-diagnostic'. This key is absent if the | ||
| 506 | backend hasn't reported anything yet. | ||
| 507 | |||
| 508 | `reported-p', a boolean indicating if the backend has replied | ||
| 509 | since it last was contacted. | ||
| 510 | |||
| 511 | `disabled', a string with the explanation for a previous | ||
| 512 | exceptional situation reported by the backend. If this key is | ||
| 513 | present the backend is disabled.") | ||
| 514 | |||
| 515 | (cl-defstruct (flymake--backend-state | ||
| 516 | (:constructor flymake--make-backend-state)) | ||
| 517 | running reported-p disabled diags) | ||
| 518 | |||
| 519 | (defmacro flymake--with-backend-state (backend state-var &rest body) | ||
| 520 | "Bind BACKEND's STATE-VAR to its state, run BODY." | ||
| 521 | (declare (indent 2) (debug (sexp sexp &rest form))) | ||
| 522 | (let ((b (make-symbol "b"))) | ||
| 523 | `(let* ((,b ,backend) | ||
| 524 | (,state-var | ||
| 525 | (or (gethash ,b flymake--backend-state) | ||
| 526 | (puthash ,b (flymake--make-backend-state) | ||
| 527 | flymake--backend-state)))) | ||
| 528 | ,@body))) | ||
| 507 | 529 | ||
| 508 | (defun flymake-is-running () | 530 | (defun flymake-is-running () |
| 509 | "Tell if flymake has running backends in this buffer" | 531 | "Tell if flymake has running backends in this buffer" |
| 510 | flymake--running-backends) | 532 | (flymake-running-backends)) |
| 511 | 533 | ||
| 512 | (defun flymake--disable-backend (backend action &optional explanation) | 534 | (cl-defun flymake--handle-report (backend token action &key explanation force) |
| 513 | (cl-pushnew backend flymake--disabled-backends) | 535 | "Handle reports from BACKEND identified by TOKEN. |
| 514 | (flymake-log :warning "Disabled the backend %s due to reports of %s (%s)" | 536 | |
| 515 | backend action explanation)) | 537 | BACKEND, ACTION and EXPLANATION, and FORCE conform to the calling |
| 516 | 538 | convention described in `flymake-diagnostic-functions' (which | |
| 517 | (cl-defun flymake--handle-report (backend action &key explanation force) | 539 | see). Optional FORCE says to handle a report even if TOKEN was |
| 518 | "Handle reports from flymake backend identified by BACKEND. | 540 | not expected." |
| 519 | 541 | (let* ((state (gethash backend flymake--backend-state)) | |
| 520 | BACKEND, ACTION and EXPLANATION conform to the calling convention | 542 | (first-report (not (flymake--backend-state-reported-p state)))) |
| 521 | described in `flymake-diagnostic-functions' (which see). Optional | 543 | (setf (flymake--backend-state-reported-p state) t) |
| 522 | FORCE says to handle a report even if it was not expected." | 544 | (let (expected-token |
| 523 | (cond | 545 | new-diags) |
| 524 | ((and (not (memq backend flymake--running-backends)) | 546 | (cond |
| 525 | (not force)) | 547 | ((null state) |
| 526 | (flymake-error "Ignoring unexpected report from backend %s" backend)) | 548 | (flymake-error |
| 527 | ((eq action :progress) | 549 | "Unexpected report from unknown backend %s" backend)) |
| 528 | (flymake-log 3 "Backend %s reports progress: %s" backend explanation)) | 550 | ((flymake--backend-state-disabled state) |
| 529 | ((eq :panic action) | 551 | (flymake-error |
| 530 | (flymake--disable-backend backend action explanation)) | 552 | "Unexpected report from disabled backend %s" backend)) |
| 531 | ((listp action) | 553 | ((progn |
| 532 | (let ((diagnostics action)) | 554 | (setq expected-token (flymake--backend-state-running state)) |
| 533 | (save-restriction | 555 | (null expected-token)) |
| 534 | (widen) | 556 | ;; should never happen |
| 535 | (flymake-delete-own-overlays | 557 | (flymake-error "Unexpected report from stopped backend %s" backend)) |
| 536 | (lambda (ov) | 558 | ((and (not (eq expected-token token)) |
| 537 | (eq backend | 559 | (not force)) |
| 538 | (flymake--diag-backend | 560 | (flymake-error "Obsolete report from backend %s with explanation %s" |
| 539 | (overlay-get ov 'flymake--diagnostic))))) | 561 | backend explanation)) |
| 540 | (puthash backend diagnostics flymake--diagnostics-table) | 562 | ((eq :panic action) |
| 541 | (mapc (lambda (diag) | 563 | (flymake--disable-backend backend explanation)) |
| 542 | (flymake--highlight-line diag) | 564 | ((not (listp action)) |
| 543 | (setf (flymake--diag-backend diag) backend)) | 565 | (flymake--disable-backend backend |
| 544 | diagnostics) | 566 | (format "Unknown action %S" action)) |
| 545 | (when flymake-check-start-time | 567 | (flymake-error "Expected report, but got unknown key %s" action)) |
| 546 | (flymake-log 2 "backend %s reported %d diagnostics in %.2f second(s)" | 568 | (t |
| 547 | backend | 569 | (setq new-diags action) |
| 548 | (length diagnostics) | 570 | (save-restriction |
| 549 | (- (float-time) flymake-check-start-time)))))) | 571 | (widen) |
| 550 | (t | 572 | ;; only delete overlays if this is the first report |
| 551 | (flymake--disable-backend "?" | 573 | (when first-report |
| 552 | :strange | 574 | (flymake-delete-own-overlays |
| 553 | (format "unknown action %s (%s)" | 575 | (lambda (ov) |
| 554 | action explanation)))) | 576 | (eq backend |
| 555 | (unless (eq action :progress) | 577 | (flymake--diag-backend |
| 556 | (flymake--stop-backend backend))) | 578 | (overlay-get ov 'flymake--diagnostic)))))) |
| 557 | 579 | (mapc (lambda (diag) | |
| 558 | (defun flymake-make-report-fn (backend) | 580 | (flymake--highlight-line diag) |
| 581 | (setf (flymake--diag-backend diag) backend)) | ||
| 582 | new-diags) | ||
| 583 | (setf (flymake--backend-state-diags state) | ||
| 584 | (append new-diags (flymake--backend-state-diags state))) | ||
| 585 | (when flymake-check-start-time | ||
| 586 | (flymake-log :debug "backend %s reported %d diagnostics in %.2f second(s)" | ||
| 587 | backend | ||
| 588 | (length new-diags) | ||
| 589 | (- (float-time) flymake-check-start-time))))))))) | ||
| 590 | |||
| 591 | (defun flymake-make-report-fn (backend &optional token) | ||
| 559 | "Make a suitable anonymous report function for BACKEND. | 592 | "Make a suitable anonymous report function for BACKEND. |
| 560 | BACKEND is used to help flymake distinguish diagnostic | 593 | BACKEND is used to help flymake distinguish different diagnostic |
| 561 | sources." | 594 | sources. If provided, TOKEN helps flymake distinguish between |
| 562 | (lambda (&rest args) | 595 | different runs of the same backend." |
| 563 | (apply #'flymake--handle-report backend args))) | 596 | (let ((buffer (current-buffer))) |
| 564 | 597 | (lambda (&rest args) | |
| 565 | (defun flymake--stop-backend (backend) | 598 | (when (buffer-live-p buffer) |
| 566 | "Stop the backend BACKEND." | 599 | (with-current-buffer buffer |
| 567 | (setq flymake--running-backends (delq backend flymake--running-backends))) | 600 | (apply #'flymake--handle-report backend token args)))))) |
| 601 | |||
| 602 | (defun flymake--collect (fn) | ||
| 603 | (let (retval) | ||
| 604 | (maphash (lambda (backend state) | ||
| 605 | (when (funcall fn state) (push backend retval))) | ||
| 606 | flymake--backend-state) | ||
| 607 | retval)) | ||
| 608 | |||
| 609 | (defun flymake-running-backends () | ||
| 610 | "Compute running Flymake backends in current buffer." | ||
| 611 | (flymake--collect #'flymake--backend-state-running)) | ||
| 612 | |||
| 613 | (defun flymake-disabled-backends () | ||
| 614 | "Compute disabled Flymake backends in current buffer." | ||
| 615 | (flymake--collect #'flymake--backend-state-disabled)) | ||
| 616 | |||
| 617 | (defun flymake-reporting-backends () | ||
| 618 | "Compute reporting Flymake backends in current buffer." | ||
| 619 | (flymake--collect #'flymake--backend-state-reported-p)) | ||
| 620 | |||
| 621 | (defun flymake--disable-backend (backend &optional explanation) | ||
| 622 | "Disable BACKEND because EXPLANATION. | ||
| 623 | If is is running also stop it." | ||
| 624 | (flymake-log :warning "Disabling backend %s because %s" backend explanation) | ||
| 625 | (flymake--with-backend-state backend state | ||
| 626 | (setf (flymake--backend-state-running state) nil | ||
| 627 | (flymake--backend-state-disabled state) explanation | ||
| 628 | (flymake--backend-state-reported-p state) t))) | ||
| 568 | 629 | ||
| 569 | (defun flymake--run-backend (backend) | 630 | (defun flymake--run-backend (backend) |
| 570 | "Run the backend BACKEND." | 631 | "Run the backend BACKEND, reenabling if necessary." |
| 571 | (push backend flymake--running-backends) | 632 | (flymake-log :debug "Running backend %s" backend) |
| 572 | (remhash backend flymake--diagnostics-table) | 633 | (let ((run-token (cl-gensym "backend-token"))) |
| 573 | ;; FIXME: Should use `condition-case-unless-debug' here, but that | 634 | (flymake--with-backend-state backend state |
| 574 | ;; won't let me catch errors from inside `ert-deftest' where | 635 | (setf (flymake--backend-state-running state) run-token |
| 575 | ;; `debug-on-error' is always t | 636 | (flymake--backend-state-disabled state) nil |
| 576 | (condition-case err | 637 | (flymake--backend-state-diags state) nil |
| 577 | (unless (funcall backend | 638 | (flymake--backend-state-reported-p state) nil)) |
| 578 | (flymake-make-report-fn backend)) | 639 | ;; FIXME: Should use `condition-case-unless-debug' here, for don't |
| 579 | (flymake--stop-backend backend)) | 640 | ;; for two reasons: (1) that won't let me catch errors from inside |
| 580 | (error | 641 | ;; `ert-deftest' where `debug-on-error' appears to be always |
| 581 | (flymake--disable-backend backend :error | 642 | ;; t. (2) In cases where the user is debugging elisp somewhere |
| 582 | err) | 643 | ;; else, and using flymake, the presence of a frequently |
| 583 | (flymake--stop-backend backend)))) | 644 | ;; misbehaving backend in the global hook (most likely the legacy |
| 584 | 645 | ;; backend) will trigger an annoying backtrace. | |
| 585 | (defun flymake-start (&optional deferred interactive) | 646 | ;; |
| 647 | (condition-case err | ||
| 648 | (funcall backend | ||
| 649 | (flymake-make-report-fn backend run-token)) | ||
| 650 | (error | ||
| 651 | (flymake--disable-backend backend err))))) | ||
| 652 | |||
| 653 | (defun flymake-start (&optional deferred force) | ||
| 586 | "Start a syntax check. | 654 | "Start a syntax check. |
| 587 | Start it immediately, or after current command if DEFERRED is | 655 | Start it immediately, or after current command if DEFERRED is |
| 588 | non-nil. With optional INTERACTIVE or interactively, clear any | 656 | non-nil. With optional FORCE run even disabled backends. |
| 589 | stale information about running and automatically disabled | 657 | |
| 590 | backends." | 658 | Interactively, with a prefix arg, FORCE is t." |
| 591 | (interactive (list nil t)) | 659 | (interactive (list nil current-prefix-arg)) |
| 592 | (cl-labels | 660 | (cl-labels |
| 593 | ((start | 661 | ((start |
| 594 | () | 662 | () |
| 595 | (remove-hook 'post-command-hook #'start 'local) | 663 | (remove-hook 'post-command-hook #'start 'local) |
| 596 | (setq flymake-check-start-time (float-time)) | 664 | (setq flymake-check-start-time (float-time)) |
| 597 | (when interactive | ||
| 598 | (setq flymake--diagnostics-table (make-hash-table) | ||
| 599 | flymake--running-backends nil | ||
| 600 | flymake--disabled-backends nil)) | ||
| 601 | (run-hook-wrapped | 665 | (run-hook-wrapped |
| 602 | 'flymake-diagnostic-functions | 666 | 'flymake-diagnostic-functions |
| 603 | (lambda (backend) | 667 | (lambda (backend) |
| 604 | (cond ((memq backend flymake--running-backends) | 668 | (cond |
| 605 | (flymake-log :debug "Backend %s still running, not restarting" | 669 | ((and (not force) |
| 606 | backend)) | 670 | (flymake--with-backend-state backend state |
| 607 | ((memq backend flymake--disabled-backends) | 671 | (flymake--backend-state-disabled state))) |
| 608 | (flymake-log :debug "Backend %s is disabled, not starting" | 672 | (flymake-log :debug "Backend %s is disabled, not starting" |
| 609 | backend)) | 673 | backend)) |
| 610 | (t | 674 | (t |
| 611 | (flymake--run-backend backend))) | 675 | (flymake--run-backend backend))) |
| 612 | nil)))) | 676 | nil)))) |
| 613 | (if (and deferred | 677 | (if (and deferred |
| 614 | this-command) | 678 | this-command) |
| @@ -623,8 +687,6 @@ backends." | |||
| 623 | ;;;###autoload | 687 | ;;;###autoload |
| 624 | (define-minor-mode flymake-mode nil | 688 | (define-minor-mode flymake-mode nil |
| 625 | :group 'flymake :lighter flymake--mode-line-format :keymap flymake-mode-map | 689 | :group 'flymake :lighter flymake--mode-line-format :keymap flymake-mode-map |
| 626 | (setq flymake--running-backends nil | ||
| 627 | flymake--disabled-backends nil) | ||
| 628 | (cond | 690 | (cond |
| 629 | ;; Turning the mode ON. | 691 | ;; Turning the mode ON. |
| 630 | (flymake-mode | 692 | (flymake-mode |
| @@ -636,7 +698,7 @@ backends." | |||
| 636 | (add-hook 'after-save-hook 'flymake-after-save-hook nil t) | 698 | (add-hook 'after-save-hook 'flymake-after-save-hook nil t) |
| 637 | (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) | 699 | (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t) |
| 638 | 700 | ||
| 639 | (setq flymake--diagnostics-table (make-hash-table)) | 701 | (setq flymake--backend-state (make-hash-table)) |
| 640 | 702 | ||
| 641 | (when flymake-start-syntax-check-on-find-file | 703 | (when flymake-start-syntax-check-on-find-file |
| 642 | (flymake-start))))) | 704 | (flymake-start))))) |
| @@ -797,20 +859,26 @@ applied." | |||
| 797 | 859 | ||
| 798 | (defun flymake--mode-line-format () | 860 | (defun flymake--mode-line-format () |
| 799 | "Produce a pretty minor mode indicator." | 861 | "Produce a pretty minor mode indicator." |
| 800 | (let ((running flymake--running-backends) | 862 | (let* ((known (hash-table-keys flymake--backend-state)) |
| 801 | (reported (cl-plusp | 863 | (running (flymake-running-backends)) |
| 802 | (hash-table-count flymake--diagnostics-table)))) | 864 | (disabled (flymake-disabled-backends)) |
| 865 | (reported (flymake-reporting-backends)) | ||
| 866 | (diags-by-type (make-hash-table)) | ||
| 867 | (all-disabled (and disabled (null running))) | ||
| 868 | (some-waiting (cl-set-difference running reported))) | ||
| 869 | (maphash (lambda (_b state) | ||
| 870 | (mapc (lambda (diag) | ||
| 871 | (push diag | ||
| 872 | (gethash (flymake--diag-type diag) | ||
| 873 | diags-by-type))) | ||
| 874 | (flymake--backend-state-diags state))) | ||
| 875 | flymake--backend-state) | ||
| 803 | `((:propertize " Flymake" | 876 | `((:propertize " Flymake" |
| 804 | mouse-face mode-line-highlight | 877 | mouse-face mode-line-highlight |
| 805 | ,@(when (not reported) | ||
| 806 | `(face compilation-mode-line-fail)) | ||
| 807 | help-echo | 878 | help-echo |
| 808 | ,(concat (format "%s registered backends\n" | 879 | ,(concat (format "%s known backends\n" (length known)) |
| 809 | (length flymake-diagnostic-functions)) | 880 | (format "%s running\n" (length running)) |
| 810 | (format "%s running\n" | 881 | (format "%s disabled\n" (length disabled)) |
| 811 | (length running)) | ||
| 812 | (format "%s disabled\n" | ||
| 813 | (length flymake--disabled-backends)) | ||
| 814 | "mouse-1: go to log buffer ") | 882 | "mouse-1: go to log buffer ") |
| 815 | keymap | 883 | keymap |
| 816 | ,(let ((map (make-sparse-keymap))) | 884 | ,(let ((map (make-sparse-keymap))) |
| @@ -819,69 +887,73 @@ applied." | |||
| 819 | (interactive "e") | 887 | (interactive "e") |
| 820 | (switch-to-buffer "*Flymake log*"))) | 888 | (switch-to-buffer "*Flymake log*"))) |
| 821 | map)) | 889 | map)) |
| 822 | ,@(when running | 890 | ,@(pcase-let ((`(,ind ,face ,explain) |
| 823 | `(":" (:propertize "Run" | 891 | (cond ((null known) |
| 824 | face compilation-mode-line-run | 892 | `("?" mode-line "No known backends")) |
| 825 | help-echo | 893 | (some-waiting |
| 826 | ,(format "%s running backends" | 894 | `("Wait" compilation-mode-line-run |
| 827 | (length running))))) | 895 | ,(format "Waiting for %s running backends" |
| 828 | ,@(when reported | 896 | (length running)))) |
| 829 | (let ((by-type (make-hash-table))) | 897 | (all-disabled |
| 830 | (maphash (lambda (_backend diags) | 898 | `("!" compilation-mode-line-run |
| 831 | (mapc (lambda (diag) | 899 | "All backends disabled")) |
| 832 | (push diag | 900 | (t |
| 833 | (gethash (flymake--diag-type diag) | 901 | `(nil nil nil))))) |
| 834 | by-type))) | 902 | (when ind |
| 835 | diags)) | 903 | `((":" |
| 836 | flymake--diagnostics-table) | 904 | (:propertize ,ind |
| 837 | (cl-loop | 905 | face ,face |
| 838 | for (type . severity) | 906 | help-echo ,explain))))) |
| 839 | in (cl-sort (mapcar (lambda (type) | 907 | ,@(unless (or all-disabled |
| 840 | (cons type (flymake--lookup-type-property | 908 | (null known)) |
| 841 | type | 909 | (cl-loop |
| 842 | 'severity | 910 | for (type . severity) |
| 843 | (warning-numeric-level :error)))) | 911 | in (cl-sort (mapcar (lambda (type) |
| 844 | (cl-union (hash-table-keys by-type) | 912 | (cons type (flymake--lookup-type-property |
| 845 | '(:error :warning))) | 913 | type |
| 846 | #'> | 914 | 'severity |
| 847 | :key #'cdr) | 915 | (warning-numeric-level :error)))) |
| 848 | for diags = (gethash type by-type) | 916 | (cl-union (hash-table-keys diags-by-type) |
| 849 | for face = (flymake--lookup-type-property type | 917 | '(:error :warning))) |
| 850 | 'mode-line-face | 918 | #'> |
| 851 | 'compilation-error) | 919 | :key #'cdr) |
| 852 | when (or diags | 920 | for diags = (gethash type diags-by-type) |
| 853 | (>= severity (warning-numeric-level :warning))) | 921 | for face = (flymake--lookup-type-property type |
| 854 | collect `(:propertize | 922 | 'mode-line-face |
| 855 | ,(format "%d" (length diags)) | 923 | 'compilation-error) |
| 856 | face ,face | 924 | when (or diags |
| 857 | mouse-face mode-line-highlight | 925 | (>= severity (warning-numeric-level :warning))) |
| 858 | keymap | 926 | collect `(:propertize |
| 859 | ,(let ((map (make-sparse-keymap)) | 927 | ,(format "%d" (length diags)) |
| 860 | (type type)) | 928 | face ,face |
| 861 | (define-key map [mode-line mouse-4] | 929 | mouse-face mode-line-highlight |
| 862 | (lambda (_event) | 930 | keymap |
| 863 | (interactive "e") | 931 | ,(let ((map (make-sparse-keymap)) |
| 864 | (flymake-goto-prev-error 1 (list type) t))) | 932 | (type type)) |
| 865 | (define-key map [mode-line mouse-5] | 933 | (define-key map [mode-line mouse-4] |
| 866 | (lambda (_event) | 934 | (lambda (_event) |
| 867 | (interactive "e") | 935 | (interactive "e") |
| 868 | (flymake-goto-next-error 1 (list type) t))) | 936 | (flymake-goto-prev-error 1 (list type) t))) |
| 869 | map) | 937 | (define-key map [mode-line mouse-5] |
| 870 | help-echo | 938 | (lambda (_event) |
| 871 | ,(concat (format "%s diagnostics of type %s\n" | 939 | (interactive "e") |
| 872 | (propertize (format "%d" | 940 | (flymake-goto-next-error 1 (list type) t))) |
| 873 | (length diags)) | 941 | map) |
| 874 | 'face face) | 942 | help-echo |
| 875 | (propertize (format "%s" type) | 943 | ,(concat (format "%s diagnostics of type %s\n" |
| 876 | 'face face)) | 944 | (propertize (format "%d" |
| 877 | "mouse-4/mouse-5: previous/next of this type\n")) | 945 | (length diags)) |
| 878 | into forms | 946 | 'face face) |
| 879 | finally return | 947 | (propertize (format "%s" type) |
| 880 | `((:propertize "[") | 948 | 'face face)) |
| 881 | ,@(cl-loop for (a . rest) on forms by #'cdr | 949 | "mouse-4/mouse-5: previous/next of this type\n")) |
| 882 | collect a when rest collect | 950 | into forms |
| 883 | '(:propertize " ")) | 951 | finally return |
| 884 | (:propertize "]")))))))) | 952 | `((:propertize "[") |
| 953 | ,@(cl-loop for (a . rest) on forms by #'cdr | ||
| 954 | collect a when rest collect | ||
| 955 | '(:propertize " ")) | ||
| 956 | (:propertize "]"))))))) | ||
| 885 | 957 | ||
| 886 | 958 | ||
| 887 | 959 | ||
diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el index 222c8f11848..5e042f2b082 100644 --- a/test/lisp/progmodes/flymake-tests.el +++ b/test/lisp/progmodes/flymake-tests.el | |||
| @@ -36,6 +36,26 @@ | |||
| 36 | 36 | ||
| 37 | ;; | 37 | ;; |
| 38 | ;; | 38 | ;; |
| 39 | (defun flymake-tests--wait-for-backends () | ||
| 40 | ;; Weirdness here... http://debbugs.gnu.org/17647#25 | ||
| 41 | ;; ... meaning `sleep-for', and even | ||
| 42 | ;; `accept-process-output', won't suffice as ways to get | ||
| 43 | ;; process filters and sentinels to run, though they do work | ||
| 44 | ;; fine in a non-interactive batch session. The only thing | ||
| 45 | ;; that will indeed unblock pending process output is | ||
| 46 | ;; reading an input event, so, as a workaround, use a dummy | ||
| 47 | ;; `read-event' with a very short timeout. | ||
| 48 | (unless noninteractive (read-event "" nil 0.1)) | ||
| 49 | (cl-loop repeat 5 | ||
| 50 | for notdone = (cl-set-difference (flymake-running-backends) | ||
| 51 | (flymake-reporting-backends)) | ||
| 52 | while notdone | ||
| 53 | unless noninteractive do (read-event "" nil 0.1) | ||
| 54 | do (sleep-for (+ 0.5 flymake-no-changes-timeout)) | ||
| 55 | finally (when notdone (ert-fail | ||
| 56 | (format "Some backends not reporting yet %s" | ||
| 57 | notdone))))) | ||
| 58 | |||
| 39 | (cl-defun flymake-tests--call-with-fixture (fn file | 59 | (cl-defun flymake-tests--call-with-fixture (fn file |
| 40 | &key (severity-predicate | 60 | &key (severity-predicate |
| 41 | nil sev-pred-supplied-p)) | 61 | nil sev-pred-supplied-p)) |
| @@ -46,7 +66,6 @@ SEVERITY-PREDICATE is used to setup | |||
| 46 | (visiting (find-buffer-visiting file)) | 66 | (visiting (find-buffer-visiting file)) |
| 47 | (buffer (or visiting (find-file-noselect file))) | 67 | (buffer (or visiting (find-file-noselect file))) |
| 48 | (process-environment (cons "LC_ALL=C" process-environment)) | 68 | (process-environment (cons "LC_ALL=C" process-environment)) |
| 49 | (i 0) | ||
| 50 | (warning-minimum-log-level :error)) | 69 | (warning-minimum-log-level :error)) |
| 51 | (unwind-protect | 70 | (unwind-protect |
| 52 | (with-current-buffer buffer | 71 | (with-current-buffer buffer |
| @@ -55,18 +74,7 @@ SEVERITY-PREDICATE is used to setup | |||
| 55 | (setq-local flymake-proc-diagnostic-type-pred severity-predicate)) | 74 | (setq-local flymake-proc-diagnostic-type-pred severity-predicate)) |
| 56 | (goto-char (point-min)) | 75 | (goto-char (point-min)) |
| 57 | (unless flymake-mode (flymake-mode 1)) | 76 | (unless flymake-mode (flymake-mode 1)) |
| 58 | ;; Weirdness here... http://debbugs.gnu.org/17647#25 | 77 | (flymake-tests--wait-for-backends) |
| 59 | ;; ... meaning `sleep-for', and even | ||
| 60 | ;; `accept-process-output', won't suffice as ways to get | ||
| 61 | ;; process filters and sentinels to run, though they do work | ||
| 62 | ;; fine in a non-interactive batch session. The only thing | ||
| 63 | ;; that will indeed unblock pending process output is | ||
| 64 | ;; reading an input event, so, as a workaround, use a dummy | ||
| 65 | ;; `read-event' with a very short timeout. | ||
| 66 | (unless noninteractive (read-event "" nil 0.1)) | ||
| 67 | (while (and (flymake-is-running) (< (setq i (1+ i)) 10)) | ||
| 68 | (unless noninteractive (read-event "" nil 0.1)) | ||
| 69 | (sleep-for (+ 0.5 flymake-no-changes-timeout))) | ||
| 70 | (funcall fn))) | 78 | (funcall fn))) |
| 71 | (and buffer | 79 | (and buffer |
| 72 | (not visiting) | 80 | (not visiting) |
| @@ -119,38 +127,37 @@ SEVERITY-PREDICATE is used to setup | |||
| 119 | (ert-deftest different-diagnostic-types () | 127 | (ert-deftest different-diagnostic-types () |
| 120 | "Test GCC warning via function predicate." | 128 | "Test GCC warning via function predicate." |
| 121 | (skip-unless (and (executable-find "gcc") (executable-find "make"))) | 129 | (skip-unless (and (executable-find "gcc") (executable-find "make"))) |
| 122 | (flymake-tests--with-flymake | 130 | (let ((flymake-wrap-around nil)) |
| 123 | ("errors-and-warnings.c") | 131 | (flymake-tests--with-flymake |
| 124 | (flymake-goto-next-error) | 132 | ("errors-and-warnings.c") |
| 125 | (should (eq 'flymake-error (face-at-point))) | 133 | (flymake-goto-next-error) |
| 126 | (flymake-goto-next-error) | 134 | (should (eq 'flymake-error (face-at-point))) |
| 127 | (should (eq 'flymake-note (face-at-point))) | 135 | (flymake-goto-next-error) |
| 128 | (flymake-goto-next-error) | 136 | (should (eq 'flymake-note (face-at-point))) |
| 129 | (should (eq 'flymake-warning (face-at-point))) | 137 | (flymake-goto-next-error) |
| 130 | (flymake-goto-next-error) | 138 | (should (eq 'flymake-warning (face-at-point))) |
| 131 | (should (eq 'flymake-error (face-at-point))) | 139 | (flymake-goto-next-error) |
| 132 | (flymake-goto-next-error) | 140 | (should (eq 'flymake-error (face-at-point))) |
| 133 | (should (eq 'flymake-warning (face-at-point))) | 141 | (flymake-goto-next-error) |
| 134 | (flymake-goto-next-error) | 142 | (should (eq 'flymake-warning (face-at-point))) |
| 135 | (should (eq 'flymake-warning (face-at-point))) | 143 | (flymake-goto-next-error) |
| 136 | (let ((flymake-wrap-around nil)) | 144 | (should (eq 'flymake-warning (face-at-point))) |
| 137 | (should-error (flymake-goto-next-error nil nil t))) )) | 145 | (should-error (flymake-goto-next-error nil nil t))))) |
| 138 | 146 | ||
| 139 | (ert-deftest included-c-header-files () | 147 | (ert-deftest included-c-header-files () |
| 140 | "Test inclusion of .h header files." | 148 | "Test inclusion of .h header files." |
| 141 | (skip-unless (and (executable-find "gcc") (executable-find "make"))) | 149 | (skip-unless (and (executable-find "gcc") (executable-find "make"))) |
| 142 | (flymake-tests--with-flymake | 150 | (let ((flymake-wrap-around nil)) |
| 143 | ("some-problems.h") | 151 | (flymake-tests--with-flymake |
| 144 | (flymake-goto-next-error) | 152 | ("some-problems.h") |
| 145 | (should (eq 'flymake-warning (face-at-point))) | 153 | (flymake-goto-next-error) |
| 146 | (flymake-goto-next-error) | 154 | (should (eq 'flymake-warning (face-at-point))) |
| 147 | (should (eq 'flymake-error (face-at-point))) | 155 | (flymake-goto-next-error) |
| 148 | (let ((flymake-wrap-around nil)) | 156 | (should (eq 'flymake-error (face-at-point))) |
| 149 | (should-error (flymake-goto-next-error nil nil t))) ) | 157 | (should-error (flymake-goto-next-error nil nil t))) |
| 150 | (flymake-tests--with-flymake | 158 | (flymake-tests--with-flymake |
| 151 | ("no-problems.h") | 159 | ("no-problems.h") |
| 152 | (let ((flymake-wrap-around nil)) | 160 | (should-error (flymake-goto-next-error nil nil t))))) |
| 153 | (should-error (flymake-goto-next-error nil nil t))) )) | ||
| 154 | 161 | ||
| 155 | (defmacro flymake-tests--assert-set (set | 162 | (defmacro flymake-tests--assert-set (set |
| 156 | should | 163 | should |
| @@ -159,19 +166,15 @@ SEVERITY-PREDICATE is used to setup | |||
| 159 | `(progn | 166 | `(progn |
| 160 | ,@(cl-loop | 167 | ,@(cl-loop |
| 161 | for s in should | 168 | for s in should |
| 162 | collect `(should (memq ,s ,set))) | 169 | collect `(should (memq (quote ,s) ,set))) |
| 163 | ,@(cl-loop | 170 | ,@(cl-loop |
| 164 | for s in should-not | 171 | for s in should-not |
| 165 | collect `(should-not (memq ,s ,set))))) | 172 | collect `(should-not (memq (quote ,s) ,set))))) |
| 166 | 173 | ||
| 167 | (ert-deftest dummy-backends () | 174 | (defun flymake-tests--diagnose-words |
| 168 | "Test GCC warning via function predicate." | 175 | (report-fn type words) |
| 169 | (with-temp-buffer | 176 | "Helper. Call REPORT-FN with diagnostics for WORDS in buffer." |
| 170 | (cl-labels | 177 | (funcall report-fn |
| 171 | ((diagnose | ||
| 172 | (report-fn type words) | ||
| 173 | (funcall | ||
| 174 | report-fn | ||
| 175 | (cl-loop | 178 | (cl-loop |
| 176 | for word in words | 179 | for word in words |
| 177 | append | 180 | append |
| @@ -184,32 +187,34 @@ SEVERITY-PREDICATE is used to setup | |||
| 184 | (match-end 0) | 187 | (match-end 0) |
| 185 | type | 188 | type |
| 186 | (concat word " is wrong"))))))) | 189 | (concat word " is wrong"))))))) |
| 187 | (error-backend | 190 | |
| 188 | (report-fn) | 191 | (ert-deftest dummy-backends () |
| 189 | (run-with-timer | 192 | "Test many different kinds of backends." |
| 190 | 0.5 nil | 193 | (with-temp-buffer |
| 191 | #'diagnose report-fn :error '("manha" "prognata"))) | 194 | (cl-letf |
| 192 | (warning-backend | 195 | (((symbol-function 'error-backend) |
| 193 | (report-fn) | 196 | (lambda (report-fn) |
| 194 | (run-with-timer | 197 | (run-with-timer |
| 195 | 0.5 nil | 198 | 0.5 nil |
| 196 | #'diagnose report-fn :warning '("ut" "dolor"))) | 199 | #'flymake-tests--diagnose-words report-fn :error '("manha" "prognata")))) |
| 197 | (sync-backend | 200 | ((symbol-function 'warning-backend) |
| 198 | (report-fn) | 201 | (lambda (report-fn) |
| 199 | (diagnose report-fn :note '("quis" "commodo"))) | 202 | (run-with-timer |
| 200 | (refusing-backend | 203 | 0.5 nil |
| 201 | (_report-fn) | 204 | #'flymake-tests--diagnose-words report-fn :warning '("ut" "dolor")))) |
| 202 | nil) | 205 | ((symbol-function 'sync-backend) |
| 203 | (panicking-backend | 206 | (lambda (report-fn) |
| 204 | (report-fn) | 207 | (flymake-tests--diagnose-words report-fn :note '("quis" "commodo")))) |
| 205 | (run-with-timer | 208 | ((symbol-function 'panicking-backend) |
| 206 | 0.5 nil | 209 | (lambda (report-fn) |
| 207 | report-fn :panic :explanation "The spanish inquisition!")) | 210 | (run-with-timer |
| 208 | (crashing-backend | 211 | 0.5 nil |
| 209 | (_report-fn) | 212 | report-fn :panic :explanation "The spanish inquisition!"))) |
| 210 | ;; HACK: Shoosh log during tests | 213 | ((symbol-function 'crashing-backend) |
| 211 | (setq-local warning-minimum-log-level :emergency) | 214 | (lambda (_report-fn) |
| 212 | (error "crashed"))) | 215 | ;; HACK: Shoosh log during tests |
| 216 | (setq-local warning-minimum-log-level :emergency) | ||
| 217 | (error "crashed")))) | ||
| 213 | (insert "Lorem ipsum dolor sit amet, consectetur adipiscing | 218 | (insert "Lorem ipsum dolor sit amet, consectetur adipiscing |
| 214 | elit, sed do eiusmod tempor incididunt ut labore et dolore | 219 | elit, sed do eiusmod tempor incididunt ut labore et dolore |
| 215 | manha aliqua. Ut enim ad minim veniam, quis nostrud | 220 | manha aliqua. Ut enim ad minim veniam, quis nostrud |
| @@ -220,31 +225,27 @@ SEVERITY-PREDICATE is used to setup | |||
| 220 | sunt in culpa qui officia deserunt mollit anim id est | 225 | sunt in culpa qui officia deserunt mollit anim id est |
| 221 | laborum.") | 226 | laborum.") |
| 222 | (let ((flymake-diagnostic-functions | 227 | (let ((flymake-diagnostic-functions |
| 223 | (list #'error-backend #'warning-backend #'sync-backend | 228 | (list 'error-backend 'warning-backend 'sync-backend |
| 224 | #'refusing-backend #'panicking-backend | 229 | 'panicking-backend |
| 225 | #'crashing-backend | 230 | 'crashing-backend |
| 226 | ))) | 231 | )) |
| 232 | (flymake-wrap-around nil)) | ||
| 227 | (flymake-mode) | 233 | (flymake-mode) |
| 228 | ;; FIXME: accessing some flymake-ui's internals here... | ||
| 229 | (flymake-tests--assert-set flymake--running-backends | ||
| 230 | (#'error-backend #'warning-backend #'panicking-backend) | ||
| 231 | (#'sync-backend #'crashing-backend #'refusing-backend)) | ||
| 232 | 234 | ||
| 233 | (flymake-tests--assert-set flymake--disabled-backends | 235 | (flymake-tests--assert-set (flymake-running-backends) |
| 234 | (#'crashing-backend) | 236 | (error-backend warning-backend panicking-backend) |
| 235 | (#'error-backend #'warning-backend #'sync-backend | 237 | (crashing-backend)) |
| 236 | #'panicking-backend #'refusing-backend)) | ||
| 237 | 238 | ||
| 238 | (cl-loop repeat 10 while (flymake-is-running) | 239 | (flymake-tests--assert-set (flymake-disabled-backends) |
| 239 | unless noninteractive do (read-event "" nil 0.1) | 240 | (crashing-backend) |
| 240 | do (sleep-for (+ 0.5 flymake-no-changes-timeout))) | 241 | (error-backend warning-backend sync-backend |
| 242 | panicking-backend)) | ||
| 241 | 243 | ||
| 242 | (should (eq flymake--running-backends '())) | 244 | (flymake-tests--wait-for-backends) |
| 243 | 245 | ||
| 244 | (flymake-tests--assert-set flymake--disabled-backends | 246 | (flymake-tests--assert-set (flymake-disabled-backends) |
| 245 | (#'crashing-backend #'panicking-backend) | 247 | (crashing-backend panicking-backend) |
| 246 | (#'error-backend #'warning-backend #'sync-backend | 248 | (error-backend warning-backend sync-backend)) |
| 247 | #'refusing-backend)) | ||
| 248 | 249 | ||
| 249 | (goto-char (point-min)) | 250 | (goto-char (point-min)) |
| 250 | (flymake-goto-next-error) | 251 | (flymake-goto-next-error) |
| @@ -265,8 +266,55 @@ SEVERITY-PREDICATE is used to setup | |||
| 265 | (should (eq 'flymake-warning (face-at-point))) ; dolor | 266 | (should (eq 'flymake-warning (face-at-point))) ; dolor |
| 266 | (flymake-goto-next-error) | 267 | (flymake-goto-next-error) |
| 267 | (should (eq 'flymake-error (face-at-point))) ; prognata | 268 | (should (eq 'flymake-error (face-at-point))) ; prognata |
| 268 | (let ((flymake-wrap-around nil)) | 269 | (should-error (flymake-goto-next-error nil nil t)))))) |
| 269 | (should-error (flymake-goto-next-error nil nil t))))))) | 270 | |
| 271 | (ert-deftest recurrent-backend () | ||
| 272 | "Test a backend that calls REPORT-FN multiple times" | ||
| 273 | (with-temp-buffer | ||
| 274 | (let (tick) | ||
| 275 | (cl-letf | ||
| 276 | (((symbol-function 'eager-backend) | ||
| 277 | (lambda (report-fn) | ||
| 278 | (funcall report-fn nil :explanation "very eager but no diagnostics") | ||
| 279 | (display-buffer (current-buffer)) | ||
| 280 | (run-with-timer | ||
| 281 | 0.5 nil | ||
| 282 | (lambda () | ||
| 283 | (flymake-tests--diagnose-words report-fn :warning '("consectetur")) | ||
| 284 | (setq tick t) | ||
| 285 | (run-with-timer | ||
| 286 | 0.5 nil | ||
| 287 | (lambda () | ||
| 288 | (flymake-tests--diagnose-words report-fn :error '("fugiat")) | ||
| 289 | (setq tick t)))))))) | ||
| 290 | (insert "Lorem ipsum dolor sit amet, consectetur adipiscing | ||
| 291 | elit, sed do eiusmod tempor incididunt ut labore et dolore | ||
| 292 | manha aliqua. Ut enim ad minim veniam, quis nostrud | ||
| 293 | exercitation ullamco laboris nisi ut aliquip ex ea commodo | ||
| 294 | consequat. Duis aute irure dolor in reprehenderit in | ||
| 295 | voluptate velit esse cillum dolore eu fugiat nulla | ||
| 296 | pariatur. Excepteur sint occaecat cupidatat non prognata | ||
| 297 | sunt in culpa qui officia deserunt mollit anim id est | ||
| 298 | laborum.") | ||
| 299 | (let ((flymake-diagnostic-functions | ||
| 300 | (list 'eager-backend)) | ||
| 301 | (flymake-wrap-around nil)) | ||
| 302 | (flymake-mode) | ||
| 303 | (flymake-tests--assert-set (flymake-running-backends) | ||
| 304 | (eager-backend) ()) | ||
| 305 | (cl-loop until tick repeat 4 do (sleep-for 0.2)) | ||
| 306 | (setq tick nil) | ||
| 307 | (goto-char (point-max)) | ||
| 308 | (flymake-goto-prev-error) | ||
| 309 | (should (eq 'flymake-warning (face-at-point))) ; consectetur | ||
| 310 | (should-error (flymake-goto-prev-error nil nil t)) | ||
| 311 | (cl-loop until tick repeat 4 do (sleep-for 0.2)) | ||
| 312 | (flymake-goto-next-error) | ||
| 313 | (should (eq 'flymake-error (face-at-point))) ; fugiat | ||
| 314 | (flymake-goto-prev-error) | ||
| 315 | (should (eq 'flymake-warning (face-at-point))) ; back at consectetur | ||
| 316 | (should-error (flymake-goto-prev-error nil nil t)) | ||
| 317 | ))))) | ||
| 270 | 318 | ||
| 271 | (provide 'flymake-tests) | 319 | (provide 'flymake-tests) |
| 272 | 320 | ||