emacs-devel
[Top][All Lists]
Advanced

[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)




reply via email to

[Prev in Thread] Current Thread [Next in Thread]