diff options
Diffstat (limited to 'lisp/progmodes/python.el')
| -rw-r--r-- | lisp/progmodes/python.el | 204 |
1 files changed, 122 insertions, 82 deletions
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 2301db8ecf6..237302f0530 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el | |||
| @@ -322,6 +322,13 @@ | |||
| 322 | (or "def" "class" "if" "elif" "else" "try" | 322 | (or "def" "class" "if" "elif" "else" "try" |
| 323 | "except" "finally" "for" "while" "with") | 323 | "except" "finally" "for" "while" "with") |
| 324 | symbol-end)) | 324 | symbol-end)) |
| 325 | (dedenter . ,(rx symbol-start | ||
| 326 | (or "elif" "else" "except" "finally") | ||
| 327 | symbol-end)) | ||
| 328 | (block-ender . ,(rx symbol-start | ||
| 329 | (or | ||
| 330 | "break" "continue" "pass" "raise" "return") | ||
| 331 | symbol-end)) | ||
| 325 | (decorator . ,(rx line-start (* space) ?@ (any letter ?_) | 332 | (decorator . ,(rx line-start (* space) ?@ (any letter ?_) |
| 326 | (* (any word ?_)))) | 333 | (* (any word ?_)))) |
| 327 | (defun . ,(rx symbol-start (or "def" "class") symbol-end)) | 334 | (defun . ,(rx symbol-start (or "def" "class") symbol-end)) |
| @@ -631,18 +638,6 @@ It makes underscores and dots word constituent chars.") | |||
| 631 | (defvar python-indent-levels '(0) | 638 | (defvar python-indent-levels '(0) |
| 632 | "Levels of indentation available for `python-indent-line-function'.") | 639 | "Levels of indentation available for `python-indent-line-function'.") |
| 633 | 640 | ||
| 634 | (defvar python-indent-dedenters '("else" "elif" "except" "finally") | ||
| 635 | "List of words that should be dedented. | ||
| 636 | These make `python-indent-calculate-indentation' subtract the value of | ||
| 637 | `python-indent-offset'.") | ||
| 638 | |||
| 639 | (defvar python-indent-block-enders | ||
| 640 | '("break" "continue" "pass" "raise" "return") | ||
| 641 | "List of words that mark the end of a block. | ||
| 642 | These make `python-indent-calculate-indentation' subtract the | ||
| 643 | value of `python-indent-offset' when `python-indent-context' is | ||
| 644 | AFTER-LINE.") | ||
| 645 | |||
| 646 | (defun python-indent-guess-indent-offset () | 641 | (defun python-indent-guess-indent-offset () |
| 647 | "Guess and set `python-indent-offset' for the current buffer." | 642 | "Guess and set `python-indent-offset' for the current buffer." |
| 648 | (interactive) | 643 | (interactive) |
| @@ -693,6 +688,7 @@ Where status can be any of the following symbols: | |||
| 693 | * after-backslash: Previous line ends in a backslash | 688 | * after-backslash: Previous line ends in a backslash |
| 694 | * after-beginning-of-block: Point is after beginning of block | 689 | * after-beginning-of-block: Point is after beginning of block |
| 695 | * after-line: Point is after normal line | 690 | * after-line: Point is after normal line |
| 691 | * dedenter-statement: Point is on a dedenter statement. | ||
| 696 | * no-indent: Point is at beginning of buffer or other special case | 692 | * no-indent: Point is at beginning of buffer or other special case |
| 697 | START is the buffer position where the sexp starts." | 693 | START is the buffer position where the sexp starts." |
| 698 | (save-restriction | 694 | (save-restriction |
| @@ -747,6 +743,8 @@ START is the buffer position where the sexp starts." | |||
| 747 | (when (looking-at (python-rx block-start)) | 743 | (when (looking-at (python-rx block-start)) |
| 748 | (point-marker))))) | 744 | (point-marker))))) |
| 749 | 'after-beginning-of-block) | 745 | 'after-beginning-of-block) |
| 746 | ((when (setq start (python-info-dedenter-statement-p)) | ||
| 747 | 'dedenter-statement)) | ||
| 750 | ;; After normal line | 748 | ;; After normal line |
| 751 | ((setq start (save-excursion | 749 | ((setq start (save-excursion |
| 752 | (back-to-indentation) | 750 | (back-to-indentation) |
| @@ -777,8 +775,7 @@ START is the buffer position where the sexp starts." | |||
| 777 | (goto-char context-start) | 775 | (goto-char context-start) |
| 778 | (+ (current-indentation) python-indent-offset)) | 776 | (+ (current-indentation) python-indent-offset)) |
| 779 | ;; When after a simple line just use previous line | 777 | ;; When after a simple line just use previous line |
| 780 | ;; indentation, in the case current line starts with a | 778 | ;; indentation. |
| 781 | ;; `python-indent-dedenters' de-indent one level. | ||
| 782 | (`after-line | 779 | (`after-line |
| 783 | (let* ((pair (save-excursion | 780 | (let* ((pair (save-excursion |
| 784 | (goto-char context-start) | 781 | (goto-char context-start) |
| @@ -786,25 +783,27 @@ START is the buffer position where the sexp starts." | |||
| 786 | (current-indentation) | 783 | (current-indentation) |
| 787 | (python-info-beginning-of-block-p)))) | 784 | (python-info-beginning-of-block-p)))) |
| 788 | (context-indentation (car pair)) | 785 | (context-indentation (car pair)) |
| 789 | (after-block-start-p (cdr pair)) | 786 | ;; TODO: Separate block enders into its own case. |
| 790 | (adjustment | 787 | (adjustment |
| 791 | (if (or (save-excursion | 788 | (if (save-excursion |
| 792 | (back-to-indentation) | 789 | (python-util-forward-comment -1) |
| 793 | (and | 790 | (python-nav-beginning-of-statement) |
| 794 | ;; De-indent only when dedenters are not | 791 | (looking-at (python-rx block-ender))) |
| 795 | ;; next to a block start. This allows | ||
| 796 | ;; one-liner constructs such as: | ||
| 797 | ;; if condition: print "yay" | ||
| 798 | ;; else: print "wry" | ||
| 799 | (not after-block-start-p) | ||
| 800 | (looking-at (regexp-opt python-indent-dedenters)))) | ||
| 801 | (save-excursion | ||
| 802 | (python-util-forward-comment -1) | ||
| 803 | (python-nav-beginning-of-statement) | ||
| 804 | (looking-at (regexp-opt python-indent-block-enders)))) | ||
| 805 | python-indent-offset | 792 | python-indent-offset |
| 806 | 0))) | 793 | 0))) |
| 807 | (- context-indentation adjustment))) | 794 | (- context-indentation adjustment))) |
| 795 | ;; When point is on a dedenter statement, search for the | ||
| 796 | ;; opening block that corresponds to it and use its | ||
| 797 | ;; indentation. If no opening block is found just remove | ||
| 798 | ;; indentation as this is an invalid python file. | ||
| 799 | (`dedenter-statement | ||
| 800 | (let ((block-start-point | ||
| 801 | (python-info-dedenter-opening-block-position))) | ||
| 802 | (save-excursion | ||
| 803 | (if (not block-start-point) | ||
| 804 | 0 | ||
| 805 | (goto-char block-start-point) | ||
| 806 | (current-indentation))))) | ||
| 808 | ;; When inside of a string, do nothing. just use the current | 807 | ;; When inside of a string, do nothing. just use the current |
| 809 | ;; indentation. XXX: perhaps it would be a good idea to | 808 | ;; indentation. XXX: perhaps it would be a good idea to |
| 810 | ;; invoke standard text indentation here | 809 | ;; invoke standard text indentation here |
| @@ -931,16 +930,25 @@ START is the buffer position where the sexp starts." | |||
| 931 | 930 | ||
| 932 | (defun python-indent-calculate-levels () | 931 | (defun python-indent-calculate-levels () |
| 933 | "Calculate `python-indent-levels' and reset `python-indent-current-level'." | 932 | "Calculate `python-indent-levels' and reset `python-indent-current-level'." |
| 934 | (let* ((indentation (python-indent-calculate-indentation)) | 933 | (if (not (python-info-dedenter-statement-p)) |
| 935 | (remainder (% indentation python-indent-offset)) | 934 | (let* ((indentation (python-indent-calculate-indentation)) |
| 936 | (steps (/ (- indentation remainder) python-indent-offset))) | 935 | (remainder (% indentation python-indent-offset)) |
| 937 | (setq python-indent-levels (list 0)) | 936 | (steps (/ (- indentation remainder) python-indent-offset))) |
| 938 | (dotimes (step steps) | 937 | (setq python-indent-levels (list 0)) |
| 939 | (push (* python-indent-offset (1+ step)) python-indent-levels)) | 938 | (dotimes (step steps) |
| 940 | (when (not (eq 0 remainder)) | 939 | (push (* python-indent-offset (1+ step)) python-indent-levels)) |
| 941 | (push (+ (* python-indent-offset steps) remainder) python-indent-levels)) | 940 | (when (not (eq 0 remainder)) |
| 942 | (setq python-indent-levels (nreverse python-indent-levels)) | 941 | (push (+ (* python-indent-offset steps) remainder) python-indent-levels))) |
| 943 | (setq python-indent-current-level (1- (length python-indent-levels))))) | 942 | (setq python-indent-levels |
| 943 | (or | ||
| 944 | (mapcar (lambda (pos) | ||
| 945 | (save-excursion | ||
| 946 | (goto-char pos) | ||
| 947 | (current-indentation))) | ||
| 948 | (python-info-dedenter-opening-block-positions)) | ||
| 949 | (list 0)))) | ||
| 950 | (setq python-indent-current-level (1- (length python-indent-levels)) | ||
| 951 | python-indent-levels (nreverse python-indent-levels))) | ||
| 944 | 952 | ||
| 945 | (defun python-indent-toggle-levels () | 953 | (defun python-indent-toggle-levels () |
| 946 | "Toggle `python-indent-current-level' over `python-indent-levels'." | 954 | "Toggle `python-indent-current-level' over `python-indent-levels'." |
| @@ -989,7 +997,7 @@ equal to | |||
| 989 | (indent-to next-indent) | 997 | (indent-to next-indent) |
| 990 | (goto-char starting-pos)) | 998 | (goto-char starting-pos)) |
| 991 | (and follow-indentation-p (back-to-indentation))) | 999 | (and follow-indentation-p (back-to-indentation))) |
| 992 | (python-info-closing-block-message)) | 1000 | (python-info-dedenter-opening-block-message)) |
| 993 | 1001 | ||
| 994 | (defun python-indent-line-function () | 1002 | (defun python-indent-line-function () |
| 995 | "`indent-line-function' for Python mode. | 1003 | "`indent-line-function' for Python mode. |
| @@ -1125,14 +1133,7 @@ the line will be re-indented automatically if needed." | |||
| 1125 | (eolp) | 1133 | (eolp) |
| 1126 | (not (equal ?: (char-before (1- (point))))) | 1134 | (not (equal ?: (char-before (1- (point))))) |
| 1127 | (not (python-syntax-comment-or-string-p))) | 1135 | (not (python-syntax-comment-or-string-p))) |
| 1128 | (let ((indentation (current-indentation)) | 1136 | (python-indent-line))))) |
| 1129 | (calculated-indentation (python-indent-calculate-indentation))) | ||
| 1130 | (python-info-closing-block-message) | ||
| 1131 | (when (> indentation calculated-indentation) | ||
| 1132 | (save-excursion | ||
| 1133 | (indent-line-to calculated-indentation) | ||
| 1134 | (when (not (python-info-closing-block-message)) | ||
| 1135 | (indent-line-to indentation))))))))) | ||
| 1136 | 1137 | ||
| 1137 | 1138 | ||
| 1138 | ;;; Navigation | 1139 | ;;; Navigation |
| @@ -3450,49 +3451,88 @@ parent defun name." | |||
| 3450 | (and (python-info-end-of-statement-p) | 3451 | (and (python-info-end-of-statement-p) |
| 3451 | (python-info-statement-ends-block-p))) | 3452 | (python-info-statement-ends-block-p))) |
| 3452 | 3453 | ||
| 3453 | (defun python-info-closing-block () | 3454 | (define-obsolete-function-alias |
| 3454 | "Return the point of the block the current line closes." | 3455 | 'python-info-closing-block |
| 3455 | (let ((closing-word (save-excursion | 3456 | 'python-info-dedenter-opening-block-position "24.4") |
| 3456 | (back-to-indentation) | 3457 | |
| 3457 | (current-word))) | 3458 | (defun python-info-dedenter-opening-block-position () |
| 3458 | (indentation (current-indentation))) | 3459 | "Return the point of the closest block the current line closes. |
| 3459 | (when (member closing-word python-indent-dedenters) | 3460 | Returns nil if point is not on a dedenter statement or no opening |
| 3461 | block can be detected. The latter case meaning current file is | ||
| 3462 | likely an invalid python file." | ||
| 3463 | (let ((positions (python-info-dedenter-opening-block-positions)) | ||
| 3464 | (indentation (current-indentation)) | ||
| 3465 | (position)) | ||
| 3466 | (while (and (not position) | ||
| 3467 | positions) | ||
| 3460 | (save-excursion | 3468 | (save-excursion |
| 3461 | (forward-line -1) | 3469 | (goto-char (car positions)) |
| 3462 | (while (and (> (current-indentation) indentation) | 3470 | (if (<= (current-indentation) indentation) |
| 3463 | (not (bobp)) | 3471 | (setq position (car positions)) |
| 3464 | (not (back-to-indentation)) | 3472 | (setq positions (cdr positions))))) |
| 3465 | (forward-line -1))) | 3473 | position)) |
| 3466 | (back-to-indentation) | 3474 | |
| 3467 | (cond | 3475 | (defun python-info-dedenter-opening-block-positions () |
| 3468 | ((not (equal indentation (current-indentation))) nil) | 3476 | "Return points of blocks the current line may close sorted by closer. |
| 3469 | ((string= closing-word "elif") | 3477 | Returns nil if point is not on a dedenter statement or no opening |
| 3470 | (when (member (current-word) '("if" "elif")) | 3478 | block can be detected. The latter case meaning current file is |
| 3471 | (point-marker))) | 3479 | likely an invalid python file." |
| 3472 | ((string= closing-word "else") | 3480 | (save-excursion |
| 3473 | (when (member (current-word) '("if" "elif" "except" "for" "while")) | 3481 | (let ((dedenter-pos (python-info-dedenter-statement-p))) |
| 3474 | (point-marker))) | 3482 | (when dedenter-pos |
| 3475 | ((string= closing-word "except") | 3483 | (goto-char dedenter-pos) |
| 3476 | (when (member (current-word) '("try")) | 3484 | (let* ((pairs '(("elif" "elif" "if") |
| 3477 | (point-marker))) | 3485 | ("else" "if" "elif" "except" "for" "while") |
| 3478 | ((string= closing-word "finally") | 3486 | ("except" "except" "try") |
| 3479 | (when (member (current-word) '("except" "else")) | 3487 | ("finally" "else" "except" "try"))) |
| 3480 | (point-marker)))))))) | 3488 | (dedenter (match-string-no-properties 0)) |
| 3481 | 3489 | (possible-opening-blocks (cdr (assoc-string dedenter pairs))) | |
| 3482 | (defun python-info-closing-block-message (&optional closing-block-point) | 3490 | (collected-indentations) |
| 3483 | "Message the contents of the block the current line closes. | 3491 | (opening-blocks)) |
| 3484 | With optional argument CLOSING-BLOCK-POINT use that instead of | 3492 | (catch 'exit |
| 3485 | recalculating it calling `python-info-closing-block'." | 3493 | (while (python-nav--syntactically |
| 3486 | (let ((point (or closing-block-point (python-info-closing-block)))) | 3494 | (lambda () |
| 3495 | (re-search-backward (python-rx block-start) nil t)) | ||
| 3496 | #'<) | ||
| 3497 | (let ((indentation (current-indentation))) | ||
| 3498 | (when (and (not (memq indentation collected-indentations)) | ||
| 3499 | (or (not collected-indentations) | ||
| 3500 | (< indentation (apply #'min collected-indentations)))) | ||
| 3501 | (setq collected-indentations | ||
| 3502 | (cons indentation collected-indentations)) | ||
| 3503 | (when (member (match-string-no-properties 0) | ||
| 3504 | possible-opening-blocks) | ||
| 3505 | (setq opening-blocks (cons (point) opening-blocks)))) | ||
| 3506 | (when (zerop indentation) | ||
| 3507 | (throw 'exit nil))))) | ||
| 3508 | ;; sort by closer | ||
| 3509 | (nreverse opening-blocks)))))) | ||
| 3510 | |||
| 3511 | (define-obsolete-function-alias | ||
| 3512 | 'python-info-closing-block-message | ||
| 3513 | 'python-info-dedenter-opening-block-message "24.4") | ||
| 3514 | |||
| 3515 | (defun python-info-dedenter-opening-block-message () | ||
| 3516 | "Message the first line of the block the current statement closes." | ||
| 3517 | (let ((point (python-info-dedenter-opening-block-position))) | ||
| 3487 | (when point | 3518 | (when point |
| 3488 | (save-restriction | 3519 | (save-restriction |
| 3489 | (widen) | 3520 | (widen) |
| 3490 | (message "Closes %s" (save-excursion | 3521 | (message "Closes %s" (save-excursion |
| 3491 | (goto-char point) | 3522 | (goto-char point) |
| 3492 | (back-to-indentation) | ||
| 3493 | (buffer-substring | 3523 | (buffer-substring |
| 3494 | (point) (line-end-position)))))))) | 3524 | (point) (line-end-position)))))))) |
| 3495 | 3525 | ||
| 3526 | (defun python-info-dedenter-statement-p () | ||
| 3527 | "Return point if current statement is a dedenter. | ||
| 3528 | Sets `match-data' to the keyword that starts the dedenter | ||
| 3529 | statement." | ||
| 3530 | (save-excursion | ||
| 3531 | (python-nav-beginning-of-statement) | ||
| 3532 | (when (and (not (python-syntax-context-type)) | ||
| 3533 | (looking-at (python-rx dedenter))) | ||
| 3534 | (point)))) | ||
| 3535 | |||
| 3496 | (defun python-info-line-ends-backslash-p (&optional line-number) | 3536 | (defun python-info-line-ends-backslash-p (&optional line-number) |
| 3497 | "Return non-nil if current line ends with backslash. | 3537 | "Return non-nil if current line ends with backslash. |
| 3498 | With optional argument LINE-NUMBER, check that line instead." | 3538 | With optional argument LINE-NUMBER, check that line instead." |