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