mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2025-01-30 19:53:09 +00:00
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
@ -5141,6 +5141,138 @@ returned as is."
|
||||
"Return non-nil if REGEXP is valid."
|
||||
(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 ()
|
||||
(when (and electric-pair-mode
|
||||
@ -5255,7 +5387,9 @@ returned as is."
|
||||
(make-local-variable 'python-shell-internal-buffer)
|
||||
|
||||
(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)
|
||||
|
Loading…
Reference in New Issue
Block a user