Add a Flymake backend for Python (bug#28808)
Implement new Flymake backend with related customizable settings. * lisp/progmodes/python.el (python-flymake-command) (python-flymake-command-output-pattern) (python-flymake-msg-alist): New defcustom. (python--flymake-parse-output): New function, able to parse python-flymake-command output accordingly to python-flymake-command-output-pattern. (python-flymake): New function implementing the backend interface using python--flymake-parse-output for the real work. (python-mode): Add python-flymake to flymake-diagnostic-functions.
This commit is contained in:
parent
8db2b3a79b
commit
3ad712ebc9
1 changed files with 135 additions and 1 deletions
|
@ -5141,6 +5141,138 @@ returned as is."
|
||||||
"Return non-nil if REGEXP is valid."
|
"Return non-nil if REGEXP is valid."
|
||||||
(ignore-errors (string-match regexp "") t))
|
(ignore-errors (string-match regexp "") t))
|
||||||
|
|
||||||
|
|
||||||
|
;;; Flymake integration
|
||||||
|
|
||||||
|
(defgroup python-flymake nil
|
||||||
|
"Integration between Python and Flymake."
|
||||||
|
:group 'python
|
||||||
|
:link '(custom-group-link :tag "Flymake" flymake)
|
||||||
|
:version "26.1")
|
||||||
|
|
||||||
|
(defcustom python-flymake-command '("pyflakes")
|
||||||
|
"The external tool that will be used to perform the syntax check.
|
||||||
|
This is a non empty list of strings, the checker tool possibly followed by
|
||||||
|
required arguments. Once launched it will receive the Python source to be
|
||||||
|
checked as its standard input.
|
||||||
|
To use `flake8' you would set this to (\"flake8\" \"-\")."
|
||||||
|
:group 'python-flymake
|
||||||
|
:type '(repeat string))
|
||||||
|
|
||||||
|
;; The default regexp accomodates for older pyflakes, which did not
|
||||||
|
;; report the column number, and at the same time it's compatible with
|
||||||
|
;; flake8 output, although it may be redefined to explicitly match the
|
||||||
|
;; TYPE
|
||||||
|
(defcustom python-flymake-command-output-pattern
|
||||||
|
(list
|
||||||
|
"^\\(?:<?stdin>?\\):\\(?1:[0-9]+\\):\\(?:\\(?2:[0-9]+\\):\\)? \\(?3:.*\\)$"
|
||||||
|
1 2 nil 3)
|
||||||
|
"Specify how to parse the output of `python-flymake-command'.
|
||||||
|
The value has 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."
|
||||||
|
:group 'python-flymake
|
||||||
|
:type '(list regexp
|
||||||
|
(integer :tag "Line's index")
|
||||||
|
(choice
|
||||||
|
(const :tag "No column" nil)
|
||||||
|
(integer :tag "Column's index"))
|
||||||
|
(choice
|
||||||
|
(const :tag "No type" nil)
|
||||||
|
(integer :tag "Type's index"))
|
||||||
|
(integer :tag "Message's index")))
|
||||||
|
|
||||||
|
(defcustom python-flymake-msg-alist
|
||||||
|
'(("\\(^redefinition\\|.*unused.*\\|used$\\)" . :warning))
|
||||||
|
"Alist used to associate messages to their types.
|
||||||
|
Each element should be a cons-cell (REGEXP . TYPE), where TYPE must be
|
||||||
|
one defined in the variable `flymake-diagnostic-types-alist'.
|
||||||
|
For example, when using `flake8' a possible configuration could be:
|
||||||
|
|
||||||
|
((\"\\(^redefinition\\|.*unused.*\\|used$\\)\" . :warning)
|
||||||
|
(\"^E999\" . :error)
|
||||||
|
(\"^[EW][0-9]+\" . :note))
|
||||||
|
|
||||||
|
By default messages are considered errors."
|
||||||
|
:group 'python-flymake
|
||||||
|
:type `(alist :key-type (regexp)
|
||||||
|
:value-type (symbol)))
|
||||||
|
|
||||||
|
(defvar-local python--flymake-proc nil)
|
||||||
|
|
||||||
|
(defun python--flymake-parse-output (source proc report-fn)
|
||||||
|
"Collect diagnostics parsing checker tool's output line by line."
|
||||||
|
(let ((rx (nth 0 python-flymake-command-output-pattern))
|
||||||
|
(lineidx (nth 1 python-flymake-command-output-pattern))
|
||||||
|
(colidx (nth 2 python-flymake-command-output-pattern))
|
||||||
|
(typeidx (nth 3 python-flymake-command-output-pattern))
|
||||||
|
(msgidx (nth 4 python-flymake-command-output-pattern)))
|
||||||
|
(with-current-buffer (process-buffer proc)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(cl-loop
|
||||||
|
while (search-forward-regexp rx nil t)
|
||||||
|
for msg = (match-string msgidx)
|
||||||
|
for (beg . end) = (flymake-diag-region
|
||||||
|
source
|
||||||
|
(string-to-number
|
||||||
|
(match-string lineidx))
|
||||||
|
(and colidx
|
||||||
|
(match-string colidx)
|
||||||
|
(string-to-number
|
||||||
|
(match-string colidx))))
|
||||||
|
for type = (or (and typeidx
|
||||||
|
(match-string typeidx)
|
||||||
|
(assoc-default
|
||||||
|
(match-string typeidx)
|
||||||
|
python-flymake-msg-alist
|
||||||
|
#'string-match))
|
||||||
|
(assoc-default msg
|
||||||
|
python-flymake-msg-alist
|
||||||
|
#'string-match)
|
||||||
|
:error)
|
||||||
|
collect (flymake-make-diagnostic
|
||||||
|
source beg end type msg)
|
||||||
|
into diags
|
||||||
|
finally (funcall report-fn diags)))))
|
||||||
|
|
||||||
|
(defun python-flymake (report-fn &rest _args)
|
||||||
|
"Flymake backend for Python.
|
||||||
|
This backend uses `python-flymake-command' (which see) to launch a process
|
||||||
|
that is passed the current buffer's content via stdin.
|
||||||
|
REPORT-FN is Flymake's callback function."
|
||||||
|
(unless (executable-find (car python-flymake-command))
|
||||||
|
(error "Cannot find a suitable checker"))
|
||||||
|
|
||||||
|
(when (process-live-p python--flymake-proc)
|
||||||
|
(kill-process python--flymake-proc))
|
||||||
|
|
||||||
|
(let ((source (current-buffer)))
|
||||||
|
(save-restriction
|
||||||
|
(widen)
|
||||||
|
(setq python--flymake-proc
|
||||||
|
(make-process
|
||||||
|
:name "python-flymake"
|
||||||
|
:noquery t
|
||||||
|
:connection-type 'pipe
|
||||||
|
:buffer (generate-new-buffer " *python-flymake*")
|
||||||
|
:command python-flymake-command
|
||||||
|
:sentinel
|
||||||
|
(lambda (proc _event)
|
||||||
|
(when (eq 'exit (process-status proc))
|
||||||
|
(unwind-protect
|
||||||
|
(when (with-current-buffer source
|
||||||
|
(eq proc python--flymake-proc))
|
||||||
|
(python--flymake-parse-output source proc report-fn))
|
||||||
|
(kill-buffer (process-buffer proc)))))))
|
||||||
|
(process-send-region python--flymake-proc (point-min) (point-max))
|
||||||
|
(process-send-eof python--flymake-proc))))
|
||||||
|
|
||||||
|
|
||||||
(defun python-electric-pair-string-delimiter ()
|
(defun python-electric-pair-string-delimiter ()
|
||||||
(when (and electric-pair-mode
|
(when (and electric-pair-mode
|
||||||
|
@ -5255,7 +5387,9 @@ returned as is."
|
||||||
(make-local-variable 'python-shell-internal-buffer)
|
(make-local-variable 'python-shell-internal-buffer)
|
||||||
|
|
||||||
(when python-indent-guess-indent-offset
|
(when python-indent-guess-indent-offset
|
||||||
(python-indent-guess-indent-offset)))
|
(python-indent-guess-indent-offset))
|
||||||
|
|
||||||
|
(add-hook 'flymake-diagnostic-functions #'python-flymake nil t))
|
||||||
|
|
||||||
|
|
||||||
(provide 'python)
|
(provide 'python)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue