[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Emacs-diffs] scratch/flymake-refactor 740572e 05/52: Completely rewrite
From: |
João Távora |
Subject: |
[Emacs-diffs] scratch/flymake-refactor 740572e 05/52: Completely rewrite flymake's subprocess output processing |
Date: |
Sun, 1 Oct 2017 12:40:42 -0400 (EDT) |
branch: scratch/flymake-refactor
commit 740572ee4835d455636c4489fabf422ce430c609
Author: João Távora <address@hidden>
Commit: João Távora <address@hidden>
Completely rewrite flymake's subprocess output processing
Instead of parsing and matching regexps line-by-line, insert
subprocess output in a separate buffer and parse using
`search-forward-regexp'. This eventually enables multi-line error
patterns and simplifies code all around. Store per-check information
in the subprocess using `process-get' and `process-put'. Treat error
messages, warnings, etc. more generically as "diagnostics". Create
these objects as soon as possible, reusing existing `flymake-ler'
structure. Fix some whitespace.
* lisp/progmodes/flymake-ui.el (cl-lib): Require also when
loading.
(flymake--fix-line-numbers): Rename from
flymake-fix-line-numbers. Simplify.
(flymake-report): Call flymake--fix-line-numbers. Rearrange
plain diagnostics list into alist format expected by
flymake-highlight-err-lines.
* lisp/progmodes/flymake-proc.el (flymake-process-filter): Insert
process output and parse in dedicated output buffer.
(flymake-proc--diagnostics-for-pattern): New helper function.
(flymake-process-sentinel): Call flymake-post-syntax-check with
collected diagnostics. Kill output buffer.
(flymake-post-syntax-check): Receive diagnostics as third argument.
(flymake-parse-output-and-residual, flymake-new-err-info)
(flymake-parse-residual, flymake-parse-err-lines)
(flymake-split-output, flymake-proc-parse-line)
(flymake-output-residual): Delete.
(flymake-start-syntax-check-process): Use make-process. Setup
dedicated an output buffer
---
lisp/progmodes/flymake-proc.el | 225 +++++++++++++++++------------------------
lisp/progmodes/flymake-ui.el | 49 +++++----
2 files changed, 117 insertions(+), 157 deletions(-)
diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el
index 8d630dd..a55b0c7 100644
--- a/lisp/progmodes/flymake-proc.el
+++ b/lisp/progmodes/flymake-proc.el
@@ -105,8 +105,6 @@ NAME is the file name function to use, default
`flymake-get-real-file-name'."
(defvar flymake-processes nil
"List of currently active flymake processes.")
-(defvar-local flymake-output-residual nil)
-
(defun flymake-get-file-name-mode-and-masks (file-name)
"Return the corresponding entry from `flymake-allowed-file-name-masks'."
(unless (stringp file-name)
@@ -397,16 +395,75 @@ Create parent directories as needed."
(write-region nil nil file-name nil 566)
(flymake-log 3 "saved buffer %s in file %s" (buffer-name) file-name))
-(defun flymake-process-filter (process output)
- "Parse OUTPUT and highlight error lines.
-It's flymake process filter."
- (let ((source-buffer (process-buffer process)))
-
- (flymake-log 3 "received %d byte(s) of output from process %d"
- (length output) (process-id process))
- (when (buffer-live-p source-buffer)
- (with-current-buffer source-buffer
- (flymake-parse-output-and-residual output)))))
+(defun flymake-proc--diagnostics-for-pattern (proc pattern)
+ (condition-case err
+ (pcase-let ((`(,regexp ,file-idx ,line-idx ,_col-idx ,message-idx)
+ pattern)
+ (retval))
+ (while (search-forward-regexp regexp nil t)
+ (let ((fname (and file-idx (match-string file-idx)))
+ (message (and message-idx (match-string message-idx)))
+ (line-number (and line-idx (string-to-number
+ (match-string line-idx)))))
+ (with-current-buffer (process-buffer proc)
+ (push (flymake-ler-make-ler
+ fname
+ line-number
+ (if (and message
+ (cond ((stringp flymake-warning-predicate)
+ (string-match flymake-warning-predicate
+ message))
+ ((functionp flymake-warning-predicate)
+ (funcall flymake-warning-predicate
+ message))))
+ "w"
+ "e")
+ message
+ (and fname
+ (funcall (flymake-get-real-file-name-function
+ fname)
+ fname)))
+ retval))))
+ retval)
+ (error
+ (flymake-log 1 "Error parsing process output for pattern %s: %s"
+ pattern err)
+ nil)))
+
+(defun flymake-process-filter (proc string)
+ "Parse STRING and collect diagnostics info."
+ (flymake-log 3 "received %d byte(s) of output from process %d"
+ (length string) (process-id proc))
+ (let ((output-buffer (process-get proc 'flymake-proc--output-buffer)))
+ (when (and (buffer-live-p (process-buffer proc))
+ output-buffer)
+ (with-current-buffer output-buffer
+ (let ((moving (= (point) (process-mark proc)))
+ (inhibit-read-only t)
+ (unprocessed-mark
+ (or (process-get proc 'flymake-proc--unprocessed-mark)
+ (set-marker (make-marker) (point-min)))))
+ (save-excursion
+ ;; Insert the text, advancing the process marker.
+ (goto-char (process-mark proc))
+ (insert string)
+ (set-marker (process-mark proc) (point)))
+ (if moving (goto-char (process-mark proc)))
+
+ ;; check for new diagnostics
+ ;;
+ (save-excursion
+ (goto-char unprocessed-mark)
+ (dolist (pattern flymake-err-line-patterns)
+ (let ((new (flymake-proc--diagnostics-for-pattern proc pattern)))
+ (process-put
+ proc
+ 'flymake-proc--collected-diagnostics
+ (append new
+ (process-get proc
+ 'flymake-proc--collected-diagnostics)))))
+ (process-put proc 'flymake-proc--unprocessed-mark
+ (point-marker))))))))
(defun flymake-process-sentinel (process _event)
"Sentinel for syntax check buffers."
@@ -414,10 +471,12 @@ It's flymake process filter."
(let* ((exit-status (process-exit-status process))
(command (process-command process))
(source-buffer (process-buffer process))
- (cleanup-f (flymake-get-cleanup-function (buffer-file-name
source-buffer))))
+ (cleanup-f (flymake-get-cleanup-function
+ (buffer-file-name source-buffer))))
(flymake-log 2 "process %d exited with code %d"
(process-id process) exit-status)
+ (kill-buffer (process-get process 'flymake-proc--output-buffer))
(condition-case err
(progn
(flymake-log 3 "cleaning up using %s" cleanup-f)
@@ -430,9 +489,9 @@ It's flymake process filter."
(when (buffer-live-p source-buffer)
(with-current-buffer source-buffer
-
- (flymake-parse-residual)
- (flymake-post-syntax-check exit-status command)
+ (flymake-post-syntax-check
+ exit-status command
+ (process-get process 'flymake-proc--collected-diagnostics))
(setq flymake-is-running nil))))
(error
(let ((err-str (format "Error in process sentinel for buffer %s: %s"
@@ -441,79 +500,16 @@ It's flymake process filter."
(with-current-buffer source-buffer
(setq flymake-is-running nil))))))))
-(defun flymake-post-syntax-check (exit-status command)
- (let ((err-count (flymake-get-err-count flymake-new-err-info "e"))
- (warn-count (flymake-get-err-count flymake-new-err-info "w")))
- (if (equal 0 exit-status)
- (flymake-report flymake-new-err-info)
- (if flymake-check-was-interrupted
- (flymake-report-status nil "") ;; STOPPED
- (if (and (zerop err-count) (zerop warn-count))
- (flymake-report-fatal-status "CFGERR"
- (format "Configuration error has occurred
while running %s" command))
- (flymake-report flymake-new-err-info))))
- (setq flymake-new-err-info nil)))
-
-
-(defun flymake-parse-output-and-residual (output)
- "Split OUTPUT into lines, merge in residual if necessary."
- (let* ((buffer-residual flymake-output-residual)
- (total-output (if buffer-residual (concat buffer-residual
output) output))
- (lines-and-residual (flymake-split-output total-output))
- (lines (nth 0 lines-and-residual))
- (new-residual (nth 1 lines-and-residual)))
- (setq flymake-output-residual new-residual)
- (setq flymake-new-err-info
- (flymake-parse-err-lines
- flymake-new-err-info lines))))
-
-(defvar-local flymake-new-err-info nil
- "Same as `flymake-err-info', effective when a syntax check is in progress.")
-
-(defun flymake-parse-residual ()
- "Parse residual if it's non empty."
- (when flymake-output-residual
- (setq flymake-new-err-info
- (flymake-parse-err-lines
- flymake-new-err-info
- (list flymake-output-residual)))
- (setq flymake-output-residual nil)))
-
-(defun flymake-parse-err-lines (err-info-list lines)
- "Parse err LINES, store info in ERR-INFO-LIST."
- (let* ((count (length lines))
- (idx 0)
- (line-err-info nil)
- (real-file-name nil)
- (source-file-name buffer-file-name)
- (get-real-file-name-f (flymake-get-real-file-name-function
source-file-name)))
-
- (while (< idx count)
- (setq line-err-info (flymake-parse-line (nth idx lines)))
- (when line-err-info
- (setq real-file-name (funcall get-real-file-name-f
- (flymake-ler-file line-err-info)))
- (setq line-err-info (flymake-ler-set-full-file line-err-info
real-file-name))
-
- (when (flymake-same-files real-file-name source-file-name)
- (setq line-err-info (flymake-ler-set-file line-err-info nil))
- (setq err-info-list (flymake-add-err-info err-info-list
line-err-info))))
- (flymake-log 3 "parsed `%s', %s line-err-info" (nth idx lines) (if
line-err-info "got" "no"))
- (setq idx (1+ idx)))
- err-info-list))
-
-(defun flymake-split-output (output)
- "Split OUTPUT into lines.
-Return last one as residual if it does not end with newline char.
-Returns ((LINES) RESIDUAL)."
- (when (and output (> (length output) 0))
- (let* ((lines (split-string output "[\n\r]+" t))
- (complete (equal "\n" (char-to-string (aref output (1- (length
output))))))
- (residual nil))
- (when (not complete)
- (setq residual (car (last lines)))
- (setq lines (butlast lines)))
- (list lines residual))))
+(defun flymake-post-syntax-check (exit-status command diagnostics)
+ (if (equal 0 exit-status)
+ (flymake-report diagnostics)
+ (if flymake-check-was-interrupted
+ (flymake-report-status nil "") ;; STOPPED
+ (if (null diagnostics)
+ (flymake-report-fatal-status
+ "CFGERR"
+ (format "Configuration error has occurred while running %s"
command))
+ (flymake-report diagnostics)))))
(defun flymake-reformat-err-line-patterns-from-compile-el (original-list)
"Grab error line patterns from ORIGINAL-LIST in compile.el format.
@@ -572,43 +568,6 @@ Takes a single argument, the error's text and should
return non-nil
if it's a warning.
Instead of a function, it can also be a regular expression.")
-(defun flymake-parse-line (line)
- "Parse LINE to see if it is an error or warning.
-Return its components if so, nil otherwise."
- (let ((raw-file-name nil)
- (line-no 0)
- (err-type "e")
- (err-text nil)
- (patterns flymake-err-line-patterns)
- (matched nil))
- (while (and patterns (not matched))
- (when (string-match (car (car patterns)) line)
- (let* ((file-idx (nth 1 (car patterns)))
- (line-idx (nth 2 (car patterns))))
-
- (setq raw-file-name (if file-idx (match-string file-idx line) nil))
- (setq line-no (if line-idx (string-to-number
- (match-string line-idx line)) 0))
- (setq err-text (if (> (length (car patterns)) 4)
- (match-string (nth 4 (car patterns)) line)
- (flymake-patch-err-text
- (substring line (match-end 0)))))
- (if (null err-text)
- (setq err-text "<no error text>")
- (when (cond ((stringp flymake-warning-predicate)
- (string-match flymake-warning-predicate err-text))
- ((functionp flymake-warning-predicate)
- (funcall flymake-warning-predicate err-text)))
- (setq err-type "w")))
- (flymake-log
- 3 "parse line: file-idx=%s line-idx=%s file=%s line=%s text=%s"
- file-idx line-idx raw-file-name line-no err-text)
- (setq matched t)))
- (setq patterns (cdr patterns)))
- (if matched
- (flymake-ler-make-ler raw-file-name line-no err-type err-text)
- ())))
-
(defun flymake-get-project-include-dirs-imp (basedir)
"Include dirs for the project current file belongs to."
(if (flymake-get-project-include-dirs-from-cache basedir)
@@ -720,11 +679,15 @@ Return its components if so, nil otherwise."
(let ((default-directory (or dir default-directory)))
(when dir
(flymake-log 3 "starting process on dir %s" dir))
- (apply 'start-file-process
- "flymake-proc" (current-buffer) cmd args))))
- (set-process-sentinel process 'flymake-process-sentinel)
- (set-process-filter process 'flymake-process-filter)
- (set-process-query-on-exit-flag process nil)
+ (make-process :name "flymake-proc"
+ :buffer (current-buffer)
+ :command (cons cmd args)
+ :noquery t
+ :filter 'flymake-process-filter
+ :sentinel 'flymake-process-sentinel))))
+ (setf (process-get process 'flymake-proc--output-buffer)
+ (generate-new-buffer
+ (format " *flymake output for %s*" (current-buffer))))
(push process flymake-processes)
(setq flymake-is-running t)
diff --git a/lisp/progmodes/flymake-ui.el b/lisp/progmodes/flymake-ui.el
index 8c7b8a2..f0e4cf6 100644
--- a/lisp/progmodes/flymake-ui.el
+++ b/lisp/progmodes/flymake-ui.el
@@ -32,7 +32,7 @@
;;
;;; Code:
-(eval-when-compile (require 'cl-lib))
+(require 'cl-lib)
(defgroup flymake nil
"Universal on-the-fly syntax checker."
@@ -450,39 +450,36 @@ For the format of LINE-ERR-INFO, see
`flymake-ler-make-ler'."
(flymake-log 0 "switched OFF Flymake mode for buffer %s due to fatal status
%s, warning %s"
(buffer-name) status warning))
-(defun flymake-fix-line-numbers (err-info-list min-line max-line)
- "Replace line numbers with fixed value.
-If line-numbers is less than MIN-LINE, set line numbers to MIN-LINE.
-If line numbers is greater than MAX-LINE, set line numbers to MAX-LINE.
-The reason for this fix is because some compilers might report
-line number outside the file being compiled."
- (let* ((count (length err-info-list))
- (err-info nil)
- (line 0))
- (while (> count 0)
- (setq err-info (nth (1- count) err-info-list))
- (setq line (flymake-er-get-line err-info))
- (when (or (< line min-line) (> line max-line))
- (setq line (if (< line min-line) min-line max-line))
- (setq err-info-list (flymake-set-at err-info-list (1- count)
- (flymake-er-make-er line
-
(flymake-er-get-line-err-info-list err-info)))))
- (setq count (1- count))))
- err-info-list)
+(defun flymake--fix-line-numbers (diagnostic)
+ "Ensure DIAGNOSTIC has sensible error lines"
+ (setf (flymake-ler-line diagnostic)
+ (min (max (flymake-ler-line diagnostic)
+ 1)
+ (line-number-at-pos (point-max) 'absolute))))
(defun flymake-report (diagnostics)
(save-restriction
(widen)
- (setq flymake-err-info
- (flymake-fix-line-numbers
- diagnostics 1 (count-lines (point-min) (point-max))))
+ (mapc #'flymake--fix-line-numbers diagnostics)
(flymake-delete-own-overlays)
+ (setq flymake-err-info
+ (cl-loop with grouped
+ for diag in diagnostics
+ for line = (flymake-ler-line diag)
+ for existing = (assoc line grouped)
+ if existing
+ do (setcdr existing
+ (list diag (second existing)))
+ else
+ do (push (list line (list diag)) grouped)
+ finally (return grouped)))
(flymake-highlight-err-lines flymake-err-info)
(let ((err-count (flymake-get-err-count flymake-err-info "e"))
(warn-count (flymake-get-err-count flymake-err-info "w")))
- (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)"
- (buffer-name) err-count warn-count
- (- (float-time) flymake-check-start-time))
+ (when flymake-check-start-time
+ (flymake-log 2 "%s: %d error(s), %d warning(s) in %.2f second(s)"
+ (buffer-name) err-count warn-count
+ (- (float-time) flymake-check-start-time)))
(if (and (equal 0 err-count) (equal 0 warn-count))
(flymake-report-status "" "")
(flymake-report-status (format "%d/%d" err-count warn-count) "")))))
- [Emacs-diffs] scratch/flymake-refactor dc39d65 04/52: flymake-ui.el provides new flymake-report as a re-entry point for backends., (continued)
- [Emacs-diffs] scratch/flymake-refactor dc39d65 04/52: flymake-ui.el provides new flymake-report as a re-entry point for backends., João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 46e0721 08/52: Fix test failure introduced by previous commit, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 205c444 10/52: Use non-obsolete variable names in flymake-tests.el, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 50a6920 11/52: Make flymake-tests.el friendlier to interactive runs, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 46b4391 16/52: flymake-proc.el parses column numbers from gcc/javac errors, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 4292342 13/52: Further simplify progmodes/flymake-ui.el, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor c5cc7e9 17/52: Make debugging flymake-proc.el easier, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 2693054 14/52: Flymake faces are not just for "lines" anymore, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 991c88f 18/52: flymake-warning face easier to distinguish from flymake-error, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 12aa247 19/52: Protect against timer triggers when no flymake-mode, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 740572e 05/52: Completely rewrite flymake's subprocess output processing,
João Távora <=
- [Emacs-diffs] scratch/flymake-refactor 14db516 22/52: flymake-ui.el checks file names before considering diagnostics, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 61ba20b 21/52: Echo flymake error messages when navigating errors interactively, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor b2c4135 15/52: Clarify logic around flymake-diagnostic-types-alist, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor bac7512 12/52: New flymake-diagnostic-types-alist and more cleanup, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 7140018 25/52: Protect flymake-ui against commands like fill-paragraph, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor d3168cf 09/52: Make lisp/progmodes/flymake-ui.el some 150 lines lighter, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor f3b8a72 20/52: Refactor flymake-tests.el in preparation for more tests, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 593d4303 23/52: Add a new flymake test for multiple errors and warnings, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor d68448f 26/52: More cleanup before advancing to backend redesign, João Távora, 2017/10/01
- [Emacs-diffs] scratch/flymake-refactor 245114e 33/52: Fix autoload conflict between flymake.el and flymake-ui.el, João Távora, 2017/10/01