[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Flymake refactored
From: |
João Távora |
Subject: |
Re: Flymake refactored |
Date: |
Fri, 06 Oct 2017 00:01:56 +0100 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/26.0.60 (gnu/linux) |
João Távora <address@hidden> writes:
> We should come up with a canonic way to launch flymake processes
> and then either hide that behind an abstraction or document it.
Attached is a possible such function: it's just like make-process, but
does all those boring checks and has some good defaults. I didn't give
it much testing, but it seems to work and backends become easier to read.
Of course, some macrology can probably get us further, but I wouldn't
want to straitjacket backend writers.
Here's what the Ruby backend looks like after using the diff below my
sig. Notice how no separate variable is needed.
(defun ruby-flymake (report-fn &rest _args)
(unless (executable-find
(car ruby-flymake-command)) (error "Cannot find a suitable ruby"))
(let ((source (current-buffer)))
(save-restriction
(widen)
(let ((proc
(flymake-easy-make-process
:name "ruby-flymake"
:command '("ruby" "-w" "-c")
:sentinel
(lambda (proc _event)
(with-current-buffer (process-buffer proc)
(goto-char (point-min))
(cl-loop
while (search-forward-regexp
"^\\(?:.*.rb\\|-\\):\\([0-9]+\\): \\(.*\\)$" nil t)
for msg = (match-string 2)
for (beg . end) = (flymake-diag-region
source
(string-to-number (match-string 1)))
for type = (if (string-match "^warning" msg) :warning
:error)
collect (flymake-make-diagnostic source beg end type msg)
into diags
finally (funcall report-fn diags)))))))
(process-send-region proc (point-min) (point-max))
(process-send-eof proc)))))
João
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el
index e747f1a12d..5277e48bc6 100644
--- a/lisp/progmodes/flymake.el
+++ b/lisp/progmodes/flymake.el
@@ -617,6 +617,82 @@ flymake-make-report-fn
(with-current-buffer buffer
(apply #'flymake--handle-report backend token args))))))
+(defvar-local flymake-easy-make-process--processes
+ nil)
+
+;;;###autoload
+(defun flymake-easy-make-process (&rest args)
+ "Like `make-process', but suitable for writing Flymake backends.
+Use this function when writing Flymake backends based on invoking
+external processes as it preserves the semantics explained
+
+ARGS is a plist of keyword-value pairs. The same keywords are
+accepted as in `make-process', but with slightly different
+semantics:
+
+* `:name' is mandatory and `:backend' is a synonym for it. Can
+ be a string or a symbol and must be constant for the backend,
+ i.e. you mustn't generate a different name every time.
+
+* `:buffer' defaults to a temporary buffer, which is always
+ killed after the process exits.
+
+* `:noquery' defaults to t.
+
+* `:connection-type' defaults to 'pipe'
+
+* `:filter', if provided, is only executed if the process is the
+ most recent process created with `flymake-easy-make-process'
+ for a given buffer and `:name'. If these conditions aren't met,
+ the process is killed immediately.
+
+* `:sentinel', if provided, is only executed if the process has
+ exited cleanly and is the most recent process created with
+ `flymake-easy-make-process' for a given buffer and `:name'."
+ (let* ((backend (or (plist-get args :name)
+ (plist-get args :backend)
+ (error "`:name' or `:backend' are mandatory")))
+ (backend (if (symbolp backend) (symbol-name backend) backend))
+ (existing (gethash backend
+ (or flymake-easy-make-process--processes
+ (setq flymake-easy-make-process--processes
+ (make-hash-table :test #'equal))))))
+ (if (process-live-p existing) (kill-process existing))
+ (setq existing
+ (make-process
+ :name backend
+ :command (plist-get args :command)
+ :buffer (if (plist-member args :buffer)
+ (plist-get args :buffer)
+ (generate-new-buffer
+ (concat " *flymake-easy-make-process-"
+ backend
+ "*")))
+ :coding (plist-get args :coding)
+ :noquery (if (plist-member args :noquery)
+ (plist-get args :noquery)
+ t)
+ :connection-type (if (plist-member args :connection-type)
+ (plist-get args :connection-type)
+ 'pipe)
+ :filter (lambda (proc string)
+ (when (eq proc existing)
+ (if (plist-member args :filter)
+ (funcall (plist-get args :filter)
+ proc string)
+ (internal-default-process-filter proc
+ string))))
+ :sentinel (lambda (proc event)
+ (unwind-protect
+ (when (and (plist-member args :sentinel)
+ (eq proc existing)
+ (eq 'exit (process-status proc)))
+ (funcall (plist-get args :sentinel)
+ proc event))
+ (let ((buf (process-buffer proc)))
+ (when buf (kill-buffer buf)))))))
+ (puthash backend existing flymake-easy-make-process--processes)))
+
(defun flymake--collect (fn)
(let (retval)
(maphash (lambda (backend state)
- Re: Flymake refactored, (continued)
Re: Flymake refactored, Simen Heggestøyl, 2017/10/04
Re: Flymake refactored, Mark Oteiza, 2017/10/05
Re: Flymake refactored, João Távora, 2017/10/05
Re: Flymake refactored, Stefan Monnier, 2017/10/05
Re: Flymake refactored, Lele Gaifax, 2017/10/06
Re: Flymake refactored, Eli Zaretskii, 2017/10/06
Re: Flymake refactored, Lele Gaifax, 2017/10/06
Re: Flymake refactored, Eli Zaretskii, 2017/10/06
Re: Flymake refactored, Lele Gaifax, 2017/10/06
Re: Flymake refactored, Mark Oteiza, 2017/10/06
Re: Flymake refactored, Lele Gaifax, 2017/10/06