Lele Gaifax writes: > Hi all, > > I'm happily using the new Flymake implementation coupled with the Python > backend (that I'm still hoping it could be merged into Emacs 26, see bug > #28808). Hi, I attach patches for Lele's Python backend, which would close 28808 and I'm also throwing in backends for Ruby and Perl. I think this type of Flymake backends was given a green flag by the maintainers while ago, *for emacs 26*, but I will wait a couple of days for objections. A second point that I wish to raise pertains to what to do in master, going forward. As I had predicted, these backends look ridiculously alike, minus very small differences. They all pipe buffer contents into checker tools, regexp-search the output, and emit flymake diagnostics. Obviously, this makes maintenance hard if small fixes are made to the common structure. The tex-chktex backend recently contributed by Mark is already an example of that in that it is missing a fix that this type of backend requires (I pushed a fix in the meantime). So I'm thinking that, for master (_not_ emacs-26) we could use a declarative flymake-define-simple-backend macro. The problem with these macros it that its easy to make them either too flexible or not flexible enough (or both in the common case :-)). Anyway, here's my idea for that macro preceded by a examples of how it simplifies the backends that I propose. (flymake-define-simple-backend ruby-flymake ruby-flymake-command '("^\\(?:.*.rb\\|-\\):\\([0-9]+\\): \\(.*\\)$" 1 nil nil 2)) (flymake-define-simple-backend perl-flymake perl-flymake-command '("^\\(.+\\) at - line \\([0-9]+\\)" 2 nil nil 1) (lambda (msg) (if (string-match "\\(Scalar value\\|Useless use\\|Unquoted string\\)" msg) :warning :error))) (flymake-define-simple-backend python-flymake python-flymake-command python-flymake-command-output-pattern (lambda (text) (assoc-default text python-flymake-msg-alist #'string-match))) And now the macro. (defvar-local flymake--simple-backend-procs (make-hash-table)) (defmacro flymake-define-simple-backend (name command pattern &optional type-predicate) "Define a simple Flymake backend under NAME. This backend runs the COMMAND syntax tool, passes the current buffer contents to its standard input, and uses PATTERN to examine the output and look for diagnostic messages. PATTERN must evaluate to a list of the form (REGEXP LINE COLUMN TYPE MESSAGE): if REGEXP matches, the LINE'th subexpression gives the line number, the COLUMN'th subexpression gives the column number on that line, the TYPE'th subexpression gives the type of the message and the MESSAGE'th gives the message text itself. If COLUMN or TYPE are nil or that index didn't match, that information is not present on the matched line and a default will be used." (let ((name-once name)) `(defun ,name-once (report-fn &rest _args) "A Flymake backend defined with `flymake-define-simple-backend'." (let* ((command-once ,command) (pattern-once ,pattern) (pred-once (function ,type-predicate)) (process (gethash ',name-once flymake--simple-backend-procs)) (source (current-buffer))) (unless (executable-find (car command-once)) (error "Cannot find a suitable checker")) (when (process-live-p process) (kill-process process)) (save-restriction (widen) (setq process (puthash ',name-once (make-process :name (symbol-name ',name-once) :noquery t :connection-type 'pipe :buffer (generate-new-buffer (format " *simple backend %s*" ',name-once)) :command command-once :sentinel (lambda (proc _event) (when (eq 'exit (process-status proc)) (unwind-protect (if (with-current-buffer source (eq proc (gethash ',name-once flymake--simple-backend-procs))) (with-current-buffer (process-buffer proc) (goto-char (point-min)) (cl-loop while (search-forward-regexp (nth 0 pattern-once) nil t) for msg = (match-string (nth 4 pattern-once)) for (beg . end) = (flymake-diag-region source (string-to-number (match-string (nth 1 pattern-once))) (if (nth 2 pattern-once) (string-to-number (match-string (nth 3 pattern-once))))) for match-target = (if (nth 3 pattern-once) (match-string (nth 3 pattern-once)) msg) for type = (if pred-once (funcall pred-once match-target) (if (string-match "warning" match-target) :warning :error)) collect (flymake-make-diagnostic source beg end type msg) into diags finally (funcall report-fn diags))) (flymake-log :debug "Canceling obsolete check %s" proc)) (kill-buffer (process-buffer proc)))))) flymake--simple-backend-procs)) (process-send-region process (point-min) (point-max)) (process-send-eof process))))))