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 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.
636These 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.
642These make `python-indent-calculate-indentation' subtract the
643value of `python-indent-offset' when `python-indent-context' is
644AFTER-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
697START is the buffer position where the sexp starts." 693START 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) 3460Returns nil if point is not on a dedenter statement or no opening
3461block can be detected. The latter case meaning current file is
3462likely 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") 3477Returns nil if point is not on a dedenter statement or no opening
3470 (when (member (current-word) '("if" "elif")) 3478block can be detected. The latter case meaning current file is
3471 (point-marker))) 3479likely 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))
3484With optional argument CLOSING-BLOCK-POINT use that instead of 3492 (catch 'exit
3485recalculating 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.
3528Sets `match-data' to the keyword that starts the dedenter
3529statement."
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.
3498With optional argument LINE-NUMBER, check that line instead." 3538With optional argument LINE-NUMBER, check that line instead."