mirror of
https://fuchsia.googlesource.com/third_party/github.com/pylint-dev/pylint
synced 2024-09-22 00:24:37 +00:00
97718ad764
Emacs tries to guess what the indent string is on a per-file basis. Now pylint.el can get this as --indent-string, so if you have different indent policies on different files, pylint will not complain about the non-PEP8 ones.
255 lines
8.8 KiB
EmacsLisp
255 lines
8.8 KiB
EmacsLisp
;;; pylint.el --- minor mode for running `pylint'
|
|
|
|
;; Copyright (c) 2009, 2010 Ian Eure <ian.eure@gmail.com>
|
|
;; Author: Ian Eure <ian.eure@gmail.com>
|
|
;; Maintainer: Jonathan Kotta <jpkotta@gmail.com>
|
|
|
|
;; Keywords: languages python
|
|
;; Version: 1.02
|
|
|
|
;; pylint.el is free software; you can redistribute it and/or modify it
|
|
;; under the terms of the GNU General Public License as published by the Free
|
|
;; Software Foundation; either version 2, or (at your option) any later
|
|
;; version.
|
|
;;
|
|
;; It is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
;; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
;; FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
;; details.
|
|
;;
|
|
;; You should have received a copy of the GNU General Public License along
|
|
;; with your copy of Emacs; see the file COPYING. If not, write to the Free
|
|
;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
;; MA 02110-1301, USA
|
|
|
|
;;; Commentary:
|
|
;;
|
|
;; Specialized compile mode for pylint. You may want to add the
|
|
;; following to your init.el:
|
|
;;
|
|
;; (autoload 'pylint "pylint")
|
|
;; (add-hook 'python-mode-hook 'pylint-add-menu-items)
|
|
;; (add-hook 'python-mode-hook 'pylint-add-key-bindings)
|
|
;;
|
|
;; There is also a handy command `pylint-insert-ignore-comment' that
|
|
;; makes it easy to insert comments of the form `# pylint:
|
|
;; ignore=msg1,msg2,...'.
|
|
|
|
;;; Code:
|
|
|
|
(require 'compile)
|
|
(require 'tramp)
|
|
|
|
(defgroup pylint nil
|
|
"Minor mode for running the Pylint Python checker"
|
|
:prefix "pylint-"
|
|
:group 'tools
|
|
:group 'languages)
|
|
|
|
(defvar pylint-last-buffer nil
|
|
"The most recent PYLINT buffer.
|
|
A PYLINT buffer becomes most recent when you select PYLINT mode in it.
|
|
Notice that using \\[next-error] or \\[compile-goto-error] modifies
|
|
`complation-last-buffer' rather than `pylint-last-buffer'.")
|
|
|
|
(defconst pylint-regexp-alist
|
|
(let ((base "^\\(.*\\):\\([0-9]+\\):\s+\\(\\[%s.*\\)$"))
|
|
(list
|
|
(list (format base "[FE]") 1 2)
|
|
(list (format base "[RWC]") 1 2 nil 1)))
|
|
"Regexp used to match PYLINT hits. See `compilation-error-regexp-alist'.")
|
|
|
|
(defcustom pylint-options '("--reports=n" "--output-format=parseable")
|
|
"Options to pass to pylint.py"
|
|
:type '(repeat string)
|
|
:group 'pylint)
|
|
|
|
(defcustom pylint-use-python-indent-offset nil
|
|
"If non-nil, use `python-indent-offset' to set indent-string."
|
|
:type 'boolean
|
|
:group 'pylint)
|
|
|
|
(defcustom pylint-command "pylint"
|
|
"PYLINT command."
|
|
:type '(file)
|
|
:group 'pylint)
|
|
|
|
(defcustom pylint-alternate-pylint-command "pylint2"
|
|
"Command for pylint when invoked with C-u."
|
|
:type '(file)
|
|
:group 'pylint)
|
|
|
|
(defcustom pylint-ask-about-save nil
|
|
"Non-nil means \\[pylint] asks which buffers to save before compiling.
|
|
Otherwise, it saves all modified buffers without asking."
|
|
:type 'boolean
|
|
:group 'pylint)
|
|
|
|
(defvar pylint--messages-list ()
|
|
"A list of strings of all pylint messages.")
|
|
|
|
(defvar pylint--messages-list-hist ()
|
|
"Completion history for `pylint--messages-list'.")
|
|
|
|
(defun pylint--sort-messages (a b)
|
|
"Compare function for sorting `pylint--messages-list'.
|
|
|
|
Sorts most recently used elements first using `pylint--messages-list-hist'."
|
|
(let ((idx 0)
|
|
(a-idx most-positive-fixnum)
|
|
(b-idx most-positive-fixnum))
|
|
(dolist (e pylint--messages-list-hist)
|
|
(when (string= e a)
|
|
(setq a-idx idx))
|
|
(when (string= e b)
|
|
(setq b-idx idx))
|
|
(setq idx (1+ idx)))
|
|
(< a-idx b-idx)))
|
|
|
|
(defun pylint--create-messages-list ()
|
|
"Use `pylint-command' to populate `pylint--messages-list'."
|
|
;; example output:
|
|
;; |--we want this--|
|
|
;; v v
|
|
;; :using-cmp-argument (W1640): *Using the cmp argument for list.sort / sorted*
|
|
;; Using the cmp argument for list.sort or the sorted builtin should be avoided,
|
|
;; since it was removed in Python 3. Using either `key` or `functools.cmp_to_key`
|
|
;; should be preferred. This message can't be emitted when using Python >= 3.0.
|
|
(setq pylint--messages-list
|
|
(split-string
|
|
(with-temp-buffer
|
|
(shell-command (concat pylint-command " --list-msgs") (current-buffer))
|
|
(flush-lines "^[^:]")
|
|
(goto-char (point-min))
|
|
(while (not (eobp))
|
|
(delete-char 1) ;; delete ";"
|
|
(re-search-forward " ")
|
|
(delete-region (point) (line-end-position))
|
|
(forward-line 1))
|
|
(buffer-substring-no-properties (point-min) (point-max))))))
|
|
|
|
;;;###autoload
|
|
(defun pylint-insert-ignore-comment (&optional arg)
|
|
"Insert a comment like \"# pylint: disable=msg1,msg2,...\".
|
|
|
|
This command repeatedly uses `completing-read' to match known
|
|
messages, and ultimately inserts a comma-separated list of all
|
|
the selected messages.
|
|
|
|
With prefix argument, only insert a comma-separated list (for
|
|
appending to an existing list)."
|
|
(interactive "*P")
|
|
(unless pylint--messages-list
|
|
(pylint--create-messages-list))
|
|
(setq pylint--messages-list
|
|
(sort pylint--messages-list #'pylint--sort-messages))
|
|
(let ((msgs ())
|
|
(msg "")
|
|
(prefix (if arg
|
|
","
|
|
"# pylint: disable="))
|
|
(sentinel "[DONE]"))
|
|
(while (progn
|
|
(setq msg (completing-read
|
|
"Message: "
|
|
pylint--messages-list
|
|
nil t nil 'pylint--messages-list-hist sentinel))
|
|
(unless (string= sentinel msg)
|
|
(add-to-list 'msgs msg 'append))))
|
|
(setq pylint--messages-list-hist
|
|
(delete sentinel pylint--messages-list-hist))
|
|
(insert prefix (mapconcat 'identity msgs ","))))
|
|
|
|
(define-compilation-mode pylint-mode "PYLINT"
|
|
(setq pylint-last-buffer (current-buffer))
|
|
(set (make-local-variable 'compilation-error-regexp-alist)
|
|
pylint-regexp-alist)
|
|
(set (make-local-variable 'compilation-disable-input) t))
|
|
|
|
(defvar pylint-mode-map
|
|
(let ((map (make-sparse-keymap)))
|
|
(set-keymap-parent map compilation-minor-mode-map)
|
|
(define-key map " " 'scroll-up)
|
|
(define-key map "\^?" 'scroll-down)
|
|
(define-key map "\C-c\C-f" 'next-error-follow-minor-mode)
|
|
|
|
(define-key map "\r" 'compile-goto-error) ;; ?
|
|
(define-key map "n" 'next-error-no-select)
|
|
(define-key map "p" 'previous-error-no-select)
|
|
(define-key map "{" 'compilation-previous-file)
|
|
(define-key map "}" 'compilation-next-file)
|
|
(define-key map "\t" 'compilation-next-error)
|
|
(define-key map [backtab] 'compilation-previous-error)
|
|
map)
|
|
"Keymap for PYLINT buffers.
|
|
`compilation-minor-mode-map' is a cdr of this.")
|
|
|
|
(defun pylint--make-indent-string ()
|
|
"Make a string for the `--indent-string' option."
|
|
(format "--indent-string='%s'"
|
|
(make-string python-indent-offset ?\ )))
|
|
|
|
;;;###autoload
|
|
(defun pylint (&optional arg)
|
|
"Run PYLINT, and collect output in a buffer, much like `compile'.
|
|
|
|
While pylint runs asynchronously, you can use \\[next-error] (M-x next-error),
|
|
or \\<pylint-mode-map>\\[compile-goto-error] in the grep \
|
|
output buffer, to go to the lines where pylint found matches.
|
|
|
|
\\{pylint-mode-map}"
|
|
(interactive "P")
|
|
|
|
(save-some-buffers (not pylint-ask-about-save) nil)
|
|
(let* ((filename (buffer-file-name))
|
|
(filename (or (and (tramp-tramp-file-p filename)
|
|
(aref (tramp-dissect-file-name filename) 3))
|
|
filename))
|
|
(filename (shell-quote-argument filename))
|
|
(pylint-command (if arg
|
|
pylint-alternate-pylint-command
|
|
pylint-command))
|
|
(pylint-options (if (not pylint-use-python-indent-offset)
|
|
pylint-options
|
|
(append pylint-options
|
|
(list (pylint--make-indent-string)))))
|
|
(command (mapconcat
|
|
'identity
|
|
(append `(,pylint-command) pylint-options `(,filename))
|
|
" ")))
|
|
|
|
(compilation-start command 'pylint-mode)))
|
|
|
|
;;;###autoload
|
|
(defun pylint-add-key-bindings ()
|
|
(let ((map (cond
|
|
((boundp 'py-mode-map) py-mode-map)
|
|
((boundp 'python-mode-map) python-mode-map))))
|
|
|
|
;; shortcuts in the tradition of python-mode and ropemacs
|
|
(define-key map (kbd "C-c m l") 'pylint)
|
|
(define-key map (kbd "C-c m p") 'previous-error)
|
|
(define-key map (kbd "C-c m n") 'next-error)
|
|
(define-key map (kbd "C-c m i") 'pylint-insert-ignore-comment)
|
|
nil))
|
|
|
|
;;;###autoload
|
|
(defun pylint-add-menu-items ()
|
|
(let ((map (cond
|
|
((boundp 'py-mode-map) py-mode-map)
|
|
((boundp 'python-mode-map) python-mode-map))))
|
|
|
|
(define-key map [menu-bar Python pylint-separator]
|
|
'("--" . pylint-seperator))
|
|
(define-key map [menu-bar Python next-error]
|
|
'("Next error" . next-error))
|
|
(define-key map [menu-bar Python prev-error]
|
|
'("Previous error" . previous-error))
|
|
(define-key map [menu-bar Python lint]
|
|
'("Pylint" . pylint))
|
|
nil))
|
|
|
|
(provide 'pylint)
|
|
|
|
;;; pylint.el ends here
|