diff options
Diffstat (limited to 'lisp/progmodes/python.el')
-rw-r--r-- | lisp/progmodes/python.el | 213 |
1 files changed, 173 insertions, 40 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 44df3186b27..d3ffc2db2c9 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -359,6 +359,7 @@ "Python mode specialized rx macro. This variant of `rx' supports common Python named REGEXPS." `(rx-let ((sp-bsnl (or space (and ?\\ ?\n))) + (sp-nl (or space (and (? ?\\) ?\n))) (block-start (seq symbol-start (or "def" "class" "if" "elif" "else" "try" "except" "finally" "for" "while" "with" @@ -583,9 +584,9 @@ the {...} holes that appear within f-strings." finally return (and result-valid result)))) (defvar python-font-lock-keywords-level-1 - `((,(python-rx symbol-start "def" (1+ space) (group symbol-name)) + `((,(python-rx symbol-start "def" (1+ sp-bsnl) (group symbol-name)) (1 font-lock-function-name-face)) - (,(python-rx symbol-start "class" (1+ space) (group symbol-name)) + (,(python-rx symbol-start "class" (1+ sp-bsnl) (group symbol-name)) (1 font-lock-type-face))) "Font lock keywords to use in `python-mode' for level 1 decoration. @@ -725,12 +726,12 @@ sign in chained assignment." ;; [*a] = 5, 6 ;; are handled separately below (,(python-font-lock-assignment-matcher - (python-rx (? (or "[" "(") (* space)) - grouped-assignment-target (* space) ?, (* space) - (* assignment-target (* space) ?, (* space)) - (? assignment-target (* space)) - (? ?, (* space)) - (? (or ")" "]") (* space)) + (python-rx (? (or "[" "(") (* sp-nl)) + grouped-assignment-target (* sp-nl) ?, (* sp-nl) + (* assignment-target (* sp-nl) ?, (* sp-nl)) + (? assignment-target (* sp-nl)) + (? ?, (* sp-nl)) + (? (or ")" "]") (* sp-bsnl)) (group assignment-operator))) (1 font-lock-variable-name-face) (,(python-rx grouped-assignment-target) @@ -745,19 +746,20 @@ sign in chained assignment." ;; c: Collection = {1, 2, 3} ;; d: Mapping[int, str] = {1: 'bar', 2: 'baz'} (,(python-font-lock-assignment-matcher - (python-rx grouped-assignment-target (* space) - (? ?: (* space) (+ not-simple-operator) (* space)) - assignment-operator)) + (python-rx (or line-start ?\;) (* sp-bsnl) + grouped-assignment-target (* sp-bsnl) + (? ?: (* sp-bsnl) (+ not-simple-operator) (* sp-bsnl)) + assignment-operator)) (1 font-lock-variable-name-face)) ;; special cases ;; (a) = 5 ;; [a] = 5, ;; [*a] = 5, 6 (,(python-font-lock-assignment-matcher - (python-rx (or line-start ?\; ?=) (* space) - (or "[" "(") (* space) - grouped-assignment-target (* space) - (or ")" "]") (* space) + (python-rx (or line-start ?\; ?=) (* sp-bsnl) + (or "[" "(") (* sp-nl) + grouped-assignment-target (* sp-nl) + (or ")" "]") (* sp-bsnl) assignment-operator)) (1 font-lock-variable-name-face)) ;; escape sequences within bytes literals @@ -796,6 +798,18 @@ decorators, exceptions, and assignments.") Which one will be chosen depends on the value of `font-lock-maximum-decoration'.") +(defun python-font-lock-extend-region (beg end _old-len) + "Extend font-lock region given by BEG and END to statement boundaries." + (save-excursion + (save-match-data + (goto-char beg) + (python-nav-beginning-of-statement) + (setq beg (point)) + (goto-char end) + (python-nav-end-of-statement) + (setq end (point)) + (cons beg end)))) + (defconst python-syntax-propertize-function (syntax-propertize-rules @@ -1224,8 +1238,14 @@ possibilities can be narrowed to specific indentation points." ;; Add one indentation level. (goto-char start) (+ (current-indentation) python-indent-offset)) + (`(:after-backslash-block-continuation . ,start) + (goto-char start) + (let ((column (current-column))) + (if (= column (+ (current-indentation) python-indent-offset)) + ;; Add one level to avoid same indent as next logical line. + (+ column python-indent-offset) + column))) (`(,(or :inside-paren - :after-backslash-block-continuation :after-backslash-dotted-continuation) . ,start) ;; Use the column given by the context. (goto-char start) @@ -1504,6 +1524,10 @@ marks the next defun after the ones already marked." The name of the defun should be grouped so it can be retrieved via `match-string'.") +(defvar python-nav-beginning-of-block-regexp + (python-rx line-start (* space) block-start) + "Regexp matching block start.") + (defun python-nav--beginning-of-defun (&optional arg) "Internal implementation of `python-nav-beginning-of-defun'. With positive ARG search backwards, else search forwards." @@ -4542,6 +4566,11 @@ the if condition." (not (python-syntax-comment-or-string-p)) python-skeleton-autoinsert))) +(defun python--completion-predicate (_ buffer) + (provided-mode-derived-p + (buffer-local-value 'major-mode buffer) + 'python-mode)) + (defmacro python-skeleton-define (name doc &rest skel) "Define a `python-mode' skeleton using NAME DOC and SKEL. The skeleton will be bound to python-skeleton-NAME and will @@ -4550,6 +4579,7 @@ be added to `python-mode-skeleton-abbrev-table'." (let* ((name (symbol-name name)) (function-name (intern (concat "python-skeleton-" name)))) `(progn + (put ',function-name 'completion-predicate #'python--completion-predicate) (define-abbrev python-mode-skeleton-abbrev-table ,name "" ',function-name :system t) (setq python-skeleton-available @@ -4575,13 +4605,15 @@ The skeleton will be bound to python-skeleton-NAME." (setq skel `(< ,(format "%s:" name) \n \n > _ \n))) - `(define-skeleton ,function-name - ,(or doc - (format "Auxiliary skeleton for %s statement." name)) - nil - (unless (y-or-n-p ,msg) - (signal 'quit t)) - ,@skel))) + `(progn + (put ',function-name 'completion-predicate #'ignore) + (define-skeleton ,function-name + ,(or doc + (format "Auxiliary skeleton for %s statement." name)) + nil + (unless (y-or-n-p ,msg) + (signal 'quit t)) + ,@skel)))) (python-define-auxiliary-skeleton else) @@ -4704,11 +4736,12 @@ def __FFAP_get_module_path(objstr): ;;; Code check (defcustom python-check-command - (or (executable-find "pyflakes") - (executable-find "epylint") - "install pyflakes, pylint or something else") + (cond ((executable-find "pyflakes") "pyflakes") + ((executable-find "epylint") "epylint") + (t "pyflakes")) "Command used to check a Python file." - :type 'string) + :type 'string + :version "29.1") (defcustom python-check-buffer-name "*Python check: %s*" @@ -4887,9 +4920,37 @@ Interactively, prompt for symbol." (defun python-hideshow-forward-sexp-function (_arg) "Python specific `forward-sexp' function for `hs-minor-mode'. Argument ARG is ignored." - (python-nav-end-of-defun) - (unless (python-info-current-line-empty-p) - (backward-char))) + (python-nav-end-of-block)) + +(defun python-hideshow-find-next-block (regexp maxp comments) + "Python specific `hs-find-next-block' function for `hs-minor-mode'. +Call `python-nav-forward-block' to find next block and check if +block-start ends within MAXP. If COMMENTS is not nil, comments +are also searched. REGEXP is passed to `looking-at' to set +`match-data'." + (let* ((next-block (save-excursion + (or (and + (python-info-looking-at-beginning-of-block) + (re-search-forward + (python-rx block-start) maxp t)) + (and (python-nav-forward-block) + (< (point) maxp) + (re-search-forward + (python-rx block-start) maxp t)) + (1+ maxp)))) + (next-comment + (or (when comments + (save-excursion + (cl-loop while (re-search-forward "#" maxp t) + if (python-syntax-context 'comment) + return (point)))) + (1+ maxp))) + (next-block-or-comment (min next-block next-comment))) + (when (<= next-block-or-comment maxp) + (goto-char next-block-or-comment) + (save-excursion + (beginning-of-line) + (looking-at regexp))))) ;;; Imenu @@ -5386,6 +5447,16 @@ instead of the current physical line." (beginning-of-line 1) (looking-at python-nav-beginning-of-defun-regexp)))) +(defun python-info-looking-at-beginning-of-block () + "Check if point is at the beginning of block." + (let ((pos (point))) + (save-excursion + (python-nav-beginning-of-statement) + (beginning-of-line) + (and + (<= (point) pos (+ (point) (current-indentation))) + (looking-at python-nav-beginning-of-block-regexp))))) + (defun python-info-current-line-comment-p () "Return non-nil if current line is a comment line." (char-equal @@ -5627,9 +5698,13 @@ returned as is." 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\" \"-\")." +To use `flake8' you would set this to (\"flake8\" \"-\"). +To use `pylint' you would set this to (\"pylint\" \"--from-stdin\" \"stdin\")." :version "26.1" - :type '(repeat string)) + :type '(choice (const :tag "Pyflakes" ("pyflakes")) + (const :tag "Flake8" ("flake8" "-")) + (const :tag "Pylint" ("pylint" "--from-stdin" "stdin")) + (repeat :tag "Custom command" string))) ;; The default regexp accommodates for older pyflakes, which did not ;; report the column number, and at the same time it's compatible with @@ -5637,7 +5712,7 @@ To use `flake8' you would set this to (\"flake8\" \"-\")." ;; TYPE (defcustom python-flymake-command-output-pattern (list - "^\\(?:<?stdin>?\\):\\(?1:[0-9]+\\):\\(?:\\(?2:[0-9]+\\):\\)? \\(?3:.*\\)$" + "^\\(?:<?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 @@ -5649,7 +5724,6 @@ 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." - :version "26.1" :type '(list regexp (integer :tag "Line's index") (choice @@ -5658,7 +5732,8 @@ be used." (choice (const :tag "No type" nil) (integer :tag "Type's index")) - (integer :tag "Message's index"))) + (integer :tag "Message's index")) + :version "29.1") (defcustom python-flymake-msg-alist '(("\\(^redefinition\\|.*unused.*\\|used$\\)" . :warning)) @@ -5780,7 +5855,9 @@ REPORT-FN is Flymake's callback function." `(,python-font-lock-keywords nil nil nil nil (font-lock-syntactic-face-function - . python-font-lock-syntactic-face-function))) + . python-font-lock-syntactic-face-function) + (font-lock-extend-after-change-region-function + . python-font-lock-extend-region))) (setq-local syntax-propertize-function python-syntax-propertize-function) @@ -5835,17 +5912,19 @@ REPORT-FN is Flymake's callback function." (add-to-list 'hs-special-modes-alist - '(python-mode - "\\s-*\\_<\\(?:def\\|class\\)\\_>" + `(python-mode + ,python-nav-beginning-of-block-regexp ;; Use the empty string as end regexp so it doesn't default to ;; "\\s)". This way parens at end of defun are properly hidden. "" "#" python-hideshow-forward-sexp-function - nil)) + nil + python-nav-beginning-of-block + python-hideshow-find-next-block + python-info-looking-at-beginning-of-block)) (setq-local outline-regexp (python-rx (* space) block-start)) - (setq-local outline-heading-end-regexp ":[^\n]*\n") (setq-local outline-level (lambda () "`outline-level' function for Python mode." @@ -5862,6 +5941,60 @@ REPORT-FN is Flymake's callback function." (add-hook 'flymake-diagnostic-functions #'python-flymake nil t)) +;;; Completion predicates for M-x +;; Commands that only make sense when editing Python code +(dolist (sym '(python-check + python-fill-paragraph + python-indent-dedent-line + python-indent-dedent-line-backspace + python-indent-guess-indent-offset + python-indent-shift-left + python-indent-shift-right + python-mark-defun + python-nav-backward-block + python-nav-backward-defun + python-nav-backward-sexp + python-nav-backward-sexp-safe + python-nav-backward-statement + python-nav-backward-up-list + python-nav-beginning-of-block + python-nav-beginning-of-statement + python-nav-end-of-block + python-nav-end-of-defun + python-nav-end-of-statement + python-nav-forward-block + python-nav-forward-defun + python-nav-forward-sexp + python-nav-forward-sexp-safe + python-nav-forward-statement + python-nav-if-name-main + python-nav-up-list + python-shell-send-buffer + python-shell-send-defun + python-shell-send-statement)) + (put sym 'completion-predicate #'python--completion-predicate)) + +(defun python-shell--completion-predicate (_ buffer) + (provided-mode-derived-p + (buffer-local-value 'major-mode buffer) + 'python-mode 'inferior-python-mode)) + +;; Commands that only make sense in the Python shell or when editing +;; Python code. +(dolist (sym '(python-describe-at-point + python-eldoc-at-point + python-shell-completion-native-toggle + python-shell-completion-native-turn-off + python-shell-completion-native-turn-on + python-shell-completion-native-turn-on-maybe + python-shell-font-lock-cleanup-buffer + python-shell-font-lock-toggle + python-shell-font-lock-turn-off + python-shell-font-lock-turn-on + python-shell-package-enable + python-shell-completion-complete-or-indent )) + (put sym 'completion-predicate #'python-shell--completion-predicate)) + (provide 'python) ;;; python.el ends here |