all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: joaotavora@gmail.com (João Távora)
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: "John Wiegley" <jwiegley@gmail.com>,
	emacs-devel@gnu.org, "Mark Oteiza" <mvoteiza@udel.edu>,
	"Simen Heggestøyl" <simenheg@gmail.com>,
	dgutov@yandex.ru, "Steve Purcell" <steve@sanityinc.com>,
	sdl.web@gmail.com
Subject: Re: Flymake refactored
Date: Fri, 06 Oct 2017 00:01:56 +0100	[thread overview]
Message-ID: <87bmll19nf.fsf@gmail.com> (raw)
In-Reply-To: <CALDnm50v6ELytUKLEnFVb86MP7YJ6X4eQ0Ug2-Rr_BQCqua2zQ@mail.gmail.com> ("João Távora"'s message of "Thu, 5 Oct 2017 15:45:38 +0100")

João Távora <joaotavora@gmail.com> 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	other threads:[~2017-10-05 23:01 UTC|newest]

Thread overview: 79+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-09-28 14:27 Flymake refactored João Távora
2017-09-28 19:52 ` Stefan Monnier
2017-09-29  0:22   ` João Távora
2017-09-29  3:11     ` Stefan Monnier
2017-10-01 16:52       ` João Távora
2017-10-01 20:50         ` Stefan Monnier
2017-10-02  1:01           ` João Távora
2017-10-02  3:12             ` Stefan Monnier
2017-10-03  0:33               ` João Távora
2017-10-03  1:09                 ` Stefan Monnier
2017-09-29 12:51   ` Dmitry Gutov
2017-09-29 14:55     ` Ted Zlatanov
2017-09-29 15:03       ` Dmitry Gutov
2017-09-29 16:26         ` Ted Zlatanov
2017-09-29 17:35           ` Dmitry Gutov
2017-09-29 17:56             ` Ted Zlatanov
2017-09-30 15:07               ` Dmitry Gutov
2017-09-30  7:55 ` Marcin Borkowski
2017-09-30 23:43   ` João Távora
2017-10-01  8:53     ` Marcin Borkowski
2017-10-01 11:54       ` Mark Oteiza
2017-10-04 17:37 ` Simen Heggestøyl
2017-10-05  2:08   ` João Távora
2017-10-05  3:52     ` Mark Oteiza
2017-10-05 10:57       ` João Távora
2017-10-05 13:11         ` Stefan Monnier
2017-10-05 14:45           ` João Távora
2017-10-05 23:01             ` João Távora [this message]
2017-10-05 21:22         ` Mark Oteiza
2017-10-05 23:05           ` João Távora
2017-10-06  3:35             ` Stefan Monnier
2017-10-06  7:09               ` Lele Gaifax
2017-10-06  8:14                 ` Eli Zaretskii
2017-10-06  8:19                   ` Lele Gaifax
2017-10-06  9:48                     ` Eli Zaretskii
2017-10-06  9:54                       ` Lele Gaifax
2017-10-06 13:04                 ` Mark Oteiza
2017-10-06 14:47                   ` Lele Gaifax
2017-10-06 15:21                     ` Mark Oteiza
2017-10-06 15:26                       ` Mark Oteiza
2017-10-06 15:28                       ` Lele Gaifax
2017-10-06 16:28                         ` João Távora
2017-10-06 19:24                           ` Lele Gaifax
2017-10-06 15:13               ` João Távora
2017-10-07 13:28                 ` Stefan Monnier
2017-10-07 13:44                   ` Eli Zaretskii
2017-10-07 14:40                     ` Lele Gaifax
2017-10-07 14:52                       ` Eli Zaretskii
2017-10-08  2:06                       ` Stefan Monnier
2017-10-08  9:32                         ` João Távora
2017-10-08 11:24                           ` Lele Gaifax
2017-10-08 14:17                           ` Stefan Monnier
2017-10-08 23:33                             ` João Távora
2017-10-09  3:01                               ` Stefan Monnier
2017-10-09 10:19                                 ` João Távora
2017-10-09 15:50                                   ` [SUSPECTED SPAM] " Stefan Monnier
2017-10-09 16:33                                   ` [PATCH] " Lele Gaifax
2017-10-07  6:31               ` Marcin Borkowski
2017-10-07 13:37                 ` Stefan Monnier
2017-10-07 16:48                   ` Marcin Borkowski
2017-10-06 12:54           ` John Wiegley
2017-10-06 15:17             ` Mark Oteiza
2017-10-06 16:04               ` João Távora
2017-10-06 21:22                 ` Mark Oteiza
2017-10-06 22:03                   ` João Távora
2017-10-07 13:31               ` Stefan Monnier
2017-10-07 16:02                 ` João Távora
2017-10-07 16:07               ` João Távora
2017-10-07 18:18                 ` Mark Oteiza
2017-10-08  9:06                   ` João Távora
2017-10-08 12:51                     ` Mark Oteiza
2017-10-08 23:21                       ` João Távora
2017-10-10 14:27                         ` Mark Oteiza
2017-10-10 15:20                           ` João Távora
2017-10-10 16:10                             ` Mark Oteiza
2017-10-05 11:28     ` Lele Gaifax
2017-10-05 15:12       ` Lele Gaifax
2017-10-10 10:40 ` Lele Gaifax
2017-10-10 12:27   ` João Távora

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87bmll19nf.fsf@gmail.com \
    --to=joaotavora@gmail.com \
    --cc=dgutov@yandex.ru \
    --cc=emacs-devel@gnu.org \
    --cc=jwiegley@gmail.com \
    --cc=monnier@iro.umontreal.ca \
    --cc=mvoteiza@udel.edu \
    --cc=sdl.web@gmail.com \
    --cc=simenheg@gmail.com \
    --cc=steve@sanityinc.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.